9#include <BRepAdaptor_Curve.hxx>
10#include <BRepAdaptor_Surface.hxx>
11#include <BRepBndLib.hxx>
12#include <BRepClass3d_SolidClassifier.hxx>
13#include <BRepExtrema_DistShapeShape.hxx>
14#include <BRepExtrema_SupportType.hxx>
15#include <BRepExtrema_TriangleSet.hxx>
16#include <BRepGProp.hxx>
18#include <BRepLProp_SLProps.hxx>
19#include <BRepMesh_IncrementalMesh.hxx>
20#include <BRep_Builder.hxx>
21#include <BRep_Tool.hxx>
22#include <BRepTools.hxx>
23#include <BRepTools_WireExplorer.hxx>
24#include <BVH_Distance.hxx>
25#include <BVH_Tools.hxx>
28#include <GeomAbs_CurveType.hxx>
29#include <GeomAbs_SurfaceType.hxx>
30#include <GProp_GProps.hxx>
31#include <GeomAPI_ProjectPointOnSurf.hxx>
32#include <Geom_Surface.hxx>
33#include <IFSelect_ReturnStatus.hxx>
34#include <IntCurvesFace_Intersector.hxx>
35#include <Poly_Triangulation.hxx>
36#include <Precision.hxx>
37#include <STEPControl_Reader.hxx>
38#include <TopAbs_Orientation.hxx>
39#include <TopAbs_ShapeEnum.hxx>
40#include <TopAbs_State.hxx>
41#include <TopExp_Explorer.hxx>
42#include <TopLoc_Location.hxx>
44#include <TopoDS_Edge.hxx>
45#include <TopoDS_Face.hxx>
46#include <TopoDS_Vertex.hxx>
47#include <TopoDS_Wire.hxx>
52#include <gp_Pnt2d.hxx>
55#include <G4BoundingEnvelope.hh>
56#include <G4AffineTransform.hh>
57#include <G4Exception.hh>
58#include <G4GeometryTolerance.hh>
59#include <G4Polyhedron.hh>
60#include <G4TessellatedSolid.hh>
61#include <G4TriangularFacet.hh>
62#include <G4VGraphicsScene.hh>
63#include <G4VisExtent.hh>
64#include <Randomize.hh>
79constexpr Standard_Real kRelativeDeflection = 0.01;
92class PointToMeshDistance
93 :
public BVH_Distance<Standard_Real, 3, BVH_Vec3d, BRepExtrema_TriangleSet> {
97 Standard_Boolean RejectNode(
const BVH_Vec3d& theCornerMin,
const BVH_Vec3d& theCornerMax,
98 Standard_Real& theMetric)
const override {
100 BVH_Tools<Standard_Real, 3>::PointBoxSquareDistance(myObject, theCornerMin, theCornerMax);
101 return RejectMetric(theMetric);
105 Standard_Boolean Accept(
const Standard_Integer theIndex,
const Standard_Real&)
override {
106 BVH_Vec3d v0, v1, v2;
107 myBVHSet->GetVertices(theIndex, v0, v1, v2);
108 const Standard_Real sq =
109 BVH_Tools<Standard_Real, 3>::PointTriangleSquareDistance(myObject, v0, v1, v2);
110 if (sq < myDistance) {
112 myBestIndex = theIndex;
113 return Standard_True;
115 return Standard_False;
121 Standard_Integer BestIndex()
const {
return myBestIndex; }
124 Standard_Integer myBestIndex{-1};
138 :
public BVH_Traverse<Standard_Real, 3, BRepExtrema_TriangleSet, Standard_Real> {
142 void SetRay(
const BVH_Vec3d& theOrigin,
const BVH_Vec3d& theDir, Standard_Real theTolerance) {
143 myOrigin = theOrigin;
145 myTolerance = theTolerance;
149 myOnSurface = Standard_False;
150 myDegenerate = Standard_False;
154 Standard_Boolean RejectNode(
const BVH_Vec3d& theCornerMin,
const BVH_Vec3d& theCornerMax,
155 Standard_Real& theMetric)
const override {
156 Standard_Real tmin = 0.0;
157 Standard_Real tmax = Precision::Infinite();
158 for (
int k = 0; k < 3; ++k) {
159 const Standard_Real dk = (k == 0) ? myDir.x() : (k == 1) ? myDir.y() : myDir.z();
160 const Standard_Real ok = (k == 0) ? myOrigin.x() : (k == 1) ? myOrigin.y() : myOrigin.z();
161 const Standard_Real ck_min = (k == 0) ? theCornerMin.x()
162 : (k == 1) ? theCornerMin.y()
164 const Standard_Real ck_max = (k == 0) ? theCornerMax.x()
165 : (k == 1) ? theCornerMax.y()
167 if (std::abs(dk) < Precision::Confusion()) {
168 if (ok < ck_min - myTolerance || ok > ck_max + myTolerance) {
169 return Standard_True;
172 Standard_Real t1 = (ck_min - ok) / dk;
173 Standard_Real t2 = (ck_max - ok) / dk;
183 if (tmin > tmax + myTolerance) {
184 return Standard_True;
189 return Standard_False;
193 Standard_Boolean Accept(
const Standard_Integer theIndex,
const Standard_Real&)
override {
194 BVH_Vec3d v0, v1, v2;
195 myBVHSet->GetVertices(theIndex, v0, v1, v2);
196 const BVH_Vec3d edge1 = v1 - v0;
197 const BVH_Vec3d edge2 = v2 - v0;
198 const BVH_Vec3d h = BVH_Vec3d::Cross(myDir, edge2);
199 const Standard_Real a = edge1.Dot(h);
200 if (std::abs(a) < 1e-12) {
201 return Standard_False;
203 const Standard_Real f = 1.0 / a;
204 const BVH_Vec3d s = myOrigin - v0;
205 const Standard_Real u = f * s.Dot(h);
206 if (u < 0.0 || u > 1.0) {
207 return Standard_False;
209 const BVH_Vec3d q = BVH_Vec3d::Cross(s, edge1);
210 const Standard_Real v = f * myDir.Dot(q);
211 if (v < 0.0 || u + v > 1.0) {
212 return Standard_False;
214 const Standard_Real t = f * edge2.Dot(q);
215 if (std::abs(t) <= myTolerance) {
216 myOnSurface = Standard_True;
217 return Standard_True;
219 if (t < -myTolerance) {
220 return Standard_False;
225 constexpr Standard_Real kEdgeTol = 1e-6;
226 if (u < kEdgeTol || v < kEdgeTol || (1.0 - u - v) < kEdgeTol) {
227 myDegenerate = Standard_True;
229 return Standard_True;
233 Standard_Boolean RejectMetric(
const Standard_Real&)
const override {
return Standard_False; }
236 Standard_Boolean Stop()
const override {
return Standard_False; }
238 int Crossings()
const {
return myCrossings; }
239 Standard_Boolean OnSurface()
const {
return myOnSurface; }
240 Standard_Boolean Degenerate()
const {
return myDegenerate; }
245 Standard_Real myTolerance{1e-7};
247 Standard_Boolean myOnSurface{Standard_False};
248 Standard_Boolean myDegenerate{Standard_False};
252gp_Pnt ToPoint(
const G4ThreeVector& point) {
return gp_Pnt(point.x(), point.y(), point.z()); }
255G4double IntersectionTolerance() {
256 return 0.5 * G4GeometryTolerance::GetInstance()->GetSurfaceTolerance();
260TopoDS_Vertex MakeVertex(
const G4ThreeVector& point) {
261 BRep_Builder builder;
262 TopoDS_Vertex vertex;
263 builder.MakeVertex(vertex, ToPoint(point), IntersectionTolerance());
268EInside ToG4Inside(
const TopAbs_State state) {
281G4ThreeVector FallbackNormal() {
return G4ThreeVector(0.0, 0.0, 1.0); }
288bool PointInPolygon2d(Standard_Real u, Standard_Real v,
const std::vector<gp_Pnt2d>& poly) {
289 const std::size_t n = poly.size();
291 for (std::size_t i = 0; i < n; ++i) {
292 const gp_Pnt2d& a = poly[i];
293 const gp_Pnt2d& b = poly[(i + 1) % n];
294 const Standard_Real av = a.Y();
295 const Standard_Real bv = b.Y();
296 if ((av <= v && bv > v) || (bv <= v && av > v)) {
297 const Standard_Real uCross = a.X() + (v - av) * (b.X() - a.X()) / (bv - av);
303 return (crossings % 2) == 1;
310bool PointOnPolygonBoundary2d(Standard_Real u, Standard_Real v,
const std::vector<gp_Pnt2d>& poly,
312 const Standard_Real tol2 = tol * tol;
313 const std::size_t n = poly.size();
314 for (std::size_t i = 0; i < n; ++i) {
315 const gp_Pnt2d& a = poly[i];
316 const gp_Pnt2d& b = poly[(i + 1) % n];
317 const Standard_Real dx = b.X() - a.X();
318 const Standard_Real dy = b.Y() - a.Y();
319 const Standard_Real len2 = dx * dx + dy * dy;
320 Standard_Real px = 0.0;
321 Standard_Real py = 0.0;
322 if (len2 < 1.0e-20) {
326 const Standard_Real t_seg =
327 std::max(0.0, std::min(1.0, ((u - a.X()) * dx + (v - a.Y()) * dy) / len2));
328 px = a.X() + t_seg * dx;
329 py = a.Y() + t_seg * dy;
331 const Standard_Real dist2 = (u - px) * (u - px) + (v - py) * (v - py);
357std::optional<Standard_Real>
358RayPlaneFaceHit(
const gp_Lin& ray,
const gp_Pln& plane,
const std::vector<gp_Pnt2d>& uvPoly,
359 Standard_Real tMin, Standard_Real tMax, Standard_Real tolerance,
360 Standard_Real* u_out =
nullptr, Standard_Real* v_out =
nullptr) {
361 const gp_Dir& lineDir = ray.Direction();
362 const gp_Dir& plnNormal = plane.Axis().Direction();
363 const Standard_Real denom =
364 plnNormal.X() * lineDir.X() + plnNormal.Y() * lineDir.Y() + plnNormal.Z() * lineDir.Z();
367 if (std::abs(denom) < 1.0e-10) {
370 const gp_Pnt& orig = ray.Location();
371 const gp_Pnt& planePt = plane.Location();
372 const Standard_Real numer = plnNormal.X() * (planePt.X() - orig.X()) +
373 plnNormal.Y() * (planePt.Y() - orig.Y()) +
374 plnNormal.Z() * (planePt.Z() - orig.Z());
375 const Standard_Real t = numer / denom;
376 if (t < tMin || t > tMax) {
379 const gp_Pnt hitPt(orig.X() + t * lineDir.X(), orig.Y() + t * lineDir.Y(),
380 orig.Z() + t * lineDir.Z());
381 Standard_Real u = 0.0;
382 Standard_Real v = 0.0;
383 ElSLib::PlaneParameters(plane.Position(), hitPt, u, v);
387 if (!PointInPolygon2d(u, v, uvPoly) && !PointOnPolygonBoundary2d(u, v, uvPoly, tolerance)) {
390 if (u_out !=
nullptr) {
393 if (v_out !=
nullptr) {
402std::optional<G4ThreeVector> TryGetOutwardNormal(
const BRepAdaptor_Surface& surface,
403 const TopoDS_Face& face,
const Standard_Real u,
404 const Standard_Real v) {
405 Standard_Real adjustedU = u;
406 Standard_Real adjustedV = v;
407 const Standard_Real tolerance = IntersectionTolerance();
413 const Standard_Real uFirst = surface.FirstUParameter();
414 const Standard_Real uLast = surface.LastUParameter();
415 const Standard_Real uEpsilon = std::min(
416 std::max(surface.UResolution(tolerance), Precision::PConfusion()), 0.5 * (uLast - uFirst));
417 if (std::abs(adjustedU - uFirst) <= uEpsilon) {
418 adjustedU = std::min(uFirst + uEpsilon, uLast);
419 }
else if (std::abs(adjustedU - uLast) <= uEpsilon) {
420 adjustedU = std::max(uLast - uEpsilon, uFirst);
426 const Standard_Real vFirst = surface.FirstVParameter();
427 const Standard_Real vLast = surface.LastVParameter();
428 const Standard_Real vEpsilon = std::min(
429 std::max(surface.VResolution(tolerance), Precision::PConfusion()), 0.5 * (vLast - vFirst));
430 if (std::abs(adjustedV - vFirst) <= vEpsilon) {
431 adjustedV = std::min(vFirst + vEpsilon, vLast);
432 }
else if (std::abs(adjustedV - vLast) <= vEpsilon) {
433 adjustedV = std::max(vLast - vEpsilon, vFirst);
437 BRepLProp_SLProps props(surface, adjustedU, adjustedV, 1, tolerance);
442 if (!props.IsNormalDefined()) {
443 const Standard_Real vFirst = surface.FirstVParameter();
444 const Standard_Real vLast = surface.LastVParameter();
445 const Standard_Real vMid = 0.5 * (vFirst + vLast);
446 const bool nearVFirst = (adjustedV < vMid);
447 const Standard_Real vRes = std::max(surface.VResolution(tolerance), Precision::PConfusion());
448 Standard_Real finalRetryV = adjustedV;
449 for (
int attempt = 0; attempt < 8 && !props.IsNormalDefined(); ++attempt) {
450 const Standard_Real scale = std::pow(10.0,
static_cast<Standard_Real
>(attempt));
451 const Standard_Real nudge = scale * vRes;
453 nearVFirst ? std::min(adjustedV + nudge, vMid) : std::max(adjustedV - nudge, vMid);
454 props = BRepLProp_SLProps(surface, adjustedU, finalRetryV, 1, tolerance);
456 if (!props.IsNormalDefined()) {
465 constexpr Standard_Real kMaxRetryVDriftFraction = 0.10;
466 if (std::fabs(finalRetryV - adjustedV) > kMaxRetryVDriftFraction * (vLast - vFirst)) {
471 gp_Dir faceNormal = props.Normal();
472 if (face.Orientation() == TopAbs_REVERSED) {
473 faceNormal.Reverse();
476 return G4ThreeVector(faceNormal.X(), faceNormal.Y(), faceNormal.Z());
482 : G4VSolid(name), fShape(shape) {
483 if (fShape.IsNull()) {
484 throw std::invalid_argument(
"G4OCCTSolid: shape must not be null");
496 fClassifierCache.Get().classifier.reset();
497 fIntersectorCache.Get().faceIntersectors.clear();
498 fSphereCache.Get().spheres.clear();
504 STEPControl_Reader reader;
505 if (reader.ReadFile(path.c_str()) != IFSelect_RetDone) {
506 throw std::runtime_error(
"G4OCCTSolid::FromSTEP: failed to read STEP file: " + path);
508 if (reader.TransferRoots() <= 0) {
509 throw std::runtime_error(
"G4OCCTSolid::FromSTEP: no roots transferred from: " + path);
511 TopoDS_Shape shape = reader.OneShape();
512 if (shape.IsNull()) {
513 throw std::runtime_error(
"G4OCCTSolid::FromSTEP: null shape loaded from: " + path);
520void G4OCCTSolid::ComputeBounds() {
524 for (TopExp_Explorer faceEx(fShape, TopAbs_FACE); faceEx.More(); faceEx.Next()) {
525 const TopoDS_Face& face = TopoDS::Face(faceEx.Current());
526 if (BRepAdaptor_Surface(face).GetType() != GeomAbs_Plane) {
529 for (TopExp_Explorer edgeEx(face, TopAbs_EDGE); edgeEx.More(); edgeEx.Next()) {
530 BRepLib::BuildPCurveForEdgeOnPlane(TopoDS::Edge(edgeEx.Current()), face);
535 BRepBndLib::AddOptimal(fShape, boundingBox, Standard_False);
536 if (boundingBox.IsVoid()) {
537 throw std::invalid_argument(
"G4OCCTSolid: shape has no computable bounding box (no geometry)");
540 Standard_Real xMin = 0.0;
541 Standard_Real yMin = 0.0;
542 Standard_Real zMin = 0.0;
543 Standard_Real xMax = 0.0;
544 Standard_Real yMax = 0.0;
545 Standard_Real zMax = 0.0;
546 boundingBox.Get(xMin, yMin, zMin, xMax, yMax, zMax);
549 AxisAlignedBounds{G4ThreeVector(xMin, yMin, zMin), G4ThreeVector(xMax, yMax, zMax)};
552 fFaceBoundsCache.clear();
553 fFaceAdaptorIndex.clear();
554 G4double maxFaceDiag = 0.0;
555 for (TopExp_Explorer ex(fShape, TopAbs_FACE); ex.More(); ex.Next()) {
557 BRepBndLib::AddOptimal(ex.Current(), faceBox, Standard_False);
558 const TopoDS_Face& currentFace = TopoDS::Face(ex.Current());
559 const std::size_t idx = fFaceBoundsCache.size();
560 BRepAdaptor_Surface adaptor(currentFace);
561 std::optional<gp_Pln> maybePlane;
562 std::vector<gp_Pnt2d> uvPolygon;
563 std::optional<G4ThreeVector> outwardNormal;
564 if (adaptor.GetType() == GeomAbs_Plane) {
565 maybePlane = adaptor.Plane();
566 const gp_Ax3& pos = maybePlane->Position();
567 const TopoDS_Wire wire = BRepTools::OuterWire(currentFace);
568 if (!wire.IsNull()) {
573 bool allLinear =
true;
574 std::vector<gp_Pnt2d> poly;
575 for (BRepTools_WireExplorer we(wire, currentFace); we.More(); we.Next()) {
576 const BRepAdaptor_Curve ec(we.Current());
577 if (ec.GetType() != GeomAbs_Line) {
581 const gp_Pnt pt = BRep_Tool::Pnt(we.CurrentVertex());
582 Standard_Real u = 0.0;
583 Standard_Real v = 0.0;
584 ElSLib::PlaneParameters(pos, pt, u, v);
585 poly.emplace_back(u, v);
587 if (allLinear && poly.size() >= 3) {
592 for (TopExp_Explorer wc(currentFace, TopAbs_WIRE); wc.More(); wc.Next()) {
598 if (wireCount == 1) {
599 uvPolygon = std::move(poly);
603 gp_Dir faceNormal = maybePlane->Axis().Direction();
604 if (currentFace.Orientation() == TopAbs_REVERSED) {
605 faceNormal.Reverse();
607 outwardNormal = G4ThreeVector(faceNormal.X(), faceNormal.Y(), faceNormal.Z());
612 fFaceBoundsCache.push_back({currentFace, faceBox, std::move(adaptor), std::move(maybePlane),
613 std::move(uvPolygon), std::move(outwardNormal)});
614 fFaceAdaptorIndex[currentFace.TShape().get()].push_back(idx);
616 if (!faceBox.IsVoid()) {
617 Standard_Real fx0 = 0.0;
618 Standard_Real fy0 = 0.0;
619 Standard_Real fz0 = 0.0;
620 Standard_Real fx1 = 0.0;
621 Standard_Real fy1 = 0.0;
622 Standard_Real fz1 = 0.0;
623 faceBox.Get(fx0, fy0, fz0, fx1, fy1, fz1);
624 const G4double diag = G4ThreeVector(fx1 - fx0, fy1 - fy0, fz1 - fz0).mag();
625 maxFaceDiag = std::max(maxFaceDiag, diag);
631 fBVHDeflection = kRelativeDeflection * maxFaceDiag;
634 fAllFacesPlanar = std::all_of(fFaceBoundsCache.begin(), fFaceBoundsCache.end(),
635 [](
const FaceBounds& fb) { return fb.plane.has_value(); });
641 [[maybe_unused]]
const BRepMesh_IncrementalMesh mesher(fShape, kRelativeDeflection,
646 BRepExtrema_ShapeList faces;
647 for (TopExp_Explorer ex(fShape, TopAbs_FACE); ex.More(); ex.Next()) {
648 faces.Append(ex.Current());
650 if (faces.IsEmpty()) {
651 fTriangleSet.Nullify();
652 fFaceDeflections.clear();
654 fTriangleSet =
new BRepExtrema_TriangleSet(faces);
660 fFaceDeflections.clear();
661 fFaceDeflections.reserve(fFaceBoundsCache.size());
662 for (
const FaceBounds& fb : fFaceBoundsCache) {
666 G4double deflection = fBVHDeflection;
667 if (!fb.box.IsVoid()) {
668 Standard_Real fx0 = 0.0, fy0 = 0.0, fz0 = 0.0;
669 Standard_Real fx1 = 0.0, fy1 = 0.0, fz1 = 0.0;
670 fb.box.Get(fx0, fy0, fz0, fx1, fy1, fz1);
671 const G4double faceDiag = G4ThreeVector(fx1 - fx0, fy1 - fy0, fz1 - fz0).mag();
672 deflection = kRelativeDeflection * faceDiag;
674 fFaceDeflections.push_back(deflection);
678 ComputeInitialSpheres();
681void G4OCCTSolid::ComputeInitialSpheres() {
682 fInitialSpheres.clear();
684 const G4double tol = IntersectionTolerance();
685 const G4ThreeVector& bmin = fCachedBounds.min;
686 const G4ThreeVector& bmax = fCachedBounds.max;
687 const G4ThreeVector centre = 0.5 * (bmin + bmax);
688 const G4ThreeVector halfExt = 0.5 * (bmax - bmin);
693 std::vector<G4ThreeVector> candidates;
694 candidates.reserve(15);
695 candidates.push_back(centre);
696 for (
const G4double s : {-0.5, 0.5}) {
697 candidates.push_back(centre + G4ThreeVector(s * halfExt.x(), 0.0, 0.0));
698 candidates.push_back(centre + G4ThreeVector(0.0, s * halfExt.y(), 0.0));
699 candidates.push_back(centre + G4ThreeVector(0.0, 0.0, s * halfExt.z()));
701 for (
const int sx : {-1, 1}) {
702 for (
const int sy : {-1, 1}) {
703 for (
const int sz : {-1, 1}) {
704 candidates.push_back(centre + G4ThreeVector(0.75 * sx * halfExt.x(),
705 0.75 * sy * halfExt.y(),
706 0.75 * sz * halfExt.z()));
712 BRepClass3d_SolidClassifier localClassifier;
713 localClassifier.Load(fShape);
715 for (
const G4ThreeVector& cand : candidates) {
716 localClassifier.Perform(ToPoint(cand), tol);
717 if (localClassifier.State() != TopAbs_IN) {
720 G4double d = BVHLowerBoundDistance(cand);
721 if (d >= kInfinity || d <= tol) {
723 const auto match = TryFindClosestFace(fFaceBoundsCache, cand);
724 if (!match.has_value() || match->distance <= tol) {
729 fInitialSpheres.push_back({cand, d});
732 std::sort(fInitialSpheres.begin(), fInitialSpheres.end(),
733 [](
const InscribedSphere& a,
const InscribedSphere& b) { return a.radius > b.radius; });
736std::optional<G4OCCTSolid::ClosestFaceMatch>
737G4OCCTSolid::TryFindClosestFace(
const std::vector<FaceBounds>& faceBoundsCache,
738 const G4ThreeVector& point, G4double maxDistance) {
739 if (faceBoundsCache.empty()) {
743 const gp_Pnt queryPoint = ToPoint(point);
744 const TopoDS_Vertex queryVertex = MakeVertex(point);
748 queryBox.Add(queryPoint);
750 std::optional<ClosestFaceMatch> bestMatch;
751 for (std::size_t i = 0; i < faceBoundsCache.size(); ++i) {
752 const FaceBounds& fb = faceBoundsCache[i];
756 const G4double threshold = bestMatch.has_value() ? bestMatch->distance : maxDistance;
757 if (threshold < kInfinity && fb.box.Distance(queryBox) > threshold) {
767 BRepExtrema_DistShapeShape distance(queryVertex, fb.face);
768 if (!distance.IsDone() || distance.NbSolution() == 0) {
771 const G4double candidateDistance = distance.Value();
773 if (bestMatch.has_value() && candidateDistance >= bestMatch->distance) {
776 ClosestFaceMatch match{.face = fb.face, .distance = candidateDistance, .faceIndex = i};
781 if (distance.NbSolution() > 0 && distance.SupportTypeShape2(1) == BRepExtrema_IsInFace) {
782 Standard_Real u = 0.0;
783 Standard_Real v = 0.0;
784 distance.ParOnFaceS2(1, u, v);
785 match.uv = std::make_pair(u, v);
787 bestMatch = std::move(match);
793BRepClass3d_SolidClassifier& G4OCCTSolid::GetOrCreateClassifier()
const {
794 ClassifierCache& cache = fClassifierCache.Get();
795 const std::uint64_t currentGen = fShapeGeneration.load(std::memory_order_acquire);
796 if (cache.generation != currentGen) {
797 cache.classifier.emplace();
798 cache.classifier->Load(fShape);
799 cache.generation = currentGen;
801 return *cache.classifier;
804G4OCCTSolid::IntersectorCache& G4OCCTSolid::GetOrCreateIntersector()
const {
805 IntersectorCache& cache = fIntersectorCache.Get();
806 const std::uint64_t currentGen = fShapeGeneration.load(std::memory_order_acquire);
807 if (cache.generation != currentGen) {
808 const G4double tol = IntersectionTolerance();
809 cache.faceIntersectors.clear();
810 cache.faceIntersectors.reserve(fFaceBoundsCache.size());
811 cache.expandedBoxes.clear();
812 cache.expandedBoxes.reserve(fFaceBoundsCache.size());
813 for (
const auto& fb : fFaceBoundsCache) {
814 cache.faceIntersectors.push_back(
815 std::make_unique<IntCurvesFace_Intersector>(fb.face, tol));
816 Bnd_Box expanded = fb.box;
817 expanded.Enlarge(tol);
818 cache.expandedBoxes.push_back(std::move(expanded));
820 cache.generation = currentGen;
825G4OCCTSolid::SphereCacheData& G4OCCTSolid::GetOrInitSphereCache()
const {
826 SphereCacheData& cache = fSphereCache.Get();
827 const std::uint64_t currentGen = fShapeGeneration.load(std::memory_order_acquire);
828 if (cache.generation != currentGen) {
830 cache.spheres = fInitialSpheres;
831 cache.generation = currentGen;
836void G4OCCTSolid::TryInsertSphere(
const G4ThreeVector& centre, G4double d)
const {
842 const G4double minRadius = IntersectionTolerance();
843 if (d <= minRadius) {
846 SphereCacheData& cache = GetOrInitSphereCache();
850 if (cache.spheres.size() >= kMaxInscribedSpheres && d <= cache.spheres.back().radius) {
856 for (
const InscribedSphere& s : cache.spheres) {
858 const G4double gap = s.radius - d;
859 if ((centre - s.centre).mag2() <= gap * gap) {
866 const InscribedSphere newSphere{centre, d};
867 const auto it = std::lower_bound(
868 cache.spheres.begin(), cache.spheres.end(), newSphere,
869 [](
const InscribedSphere& a,
const InscribedSphere& b) { return a.radius > b.radius; });
870 cache.spheres.insert(it, newSphere);
873 if (cache.spheres.size() > kMaxInscribedSpheres) {
874 cache.spheres.pop_back();
881 const G4double tolerance = IntersectionTolerance();
882 if (p.x() < fCachedBounds.min.x() - tolerance || p.x() > fCachedBounds.max.x() + tolerance ||
883 p.y() < fCachedBounds.min.y() - tolerance || p.y() > fCachedBounds.max.y() + tolerance ||
884 p.z() < fCachedBounds.min.z() - tolerance || p.z() > fCachedBounds.max.z() + tolerance) {
893 const SphereCacheData& sphereCache = GetOrInitSphereCache();
894 for (
const InscribedSphere& s : sphereCache.spheres) {
895 const G4double interiorRadius = s.radius - tolerance;
896 if (interiorRadius > 0.0 && (p - s.centre).mag2() < interiorRadius * interiorRadius) {
905 if (!fTriangleSet.IsNull() && fTriangleSet->Size() > 0) {
910 if (fBVHDeflection > 0.0) {
911 const G4double bvhLB = BVHLowerBoundDistance(p);
912 if (bvhLB < tolerance) {
913 BRepClass3d_SolidClassifier& classifier = GetOrCreateClassifier();
914 classifier.Perform(ToPoint(p), tolerance);
915 return ToG4Inside(classifier.State());
934 const BVH_Vec3d bvhOrigin(p.x(), p.y(), p.z());
935 const Standard_Real bvhTol =
static_cast<Standard_Real
>(tolerance);
936 TriangleRayCast caster;
937 caster.SetBVHSet(fTriangleSet.get());
940 caster.SetRay(bvhOrigin, BVH_Vec3d(0.0, 0.0, 1.0), bvhTol);
943 if (caster.OnSurface()) {
948 if (!caster.Degenerate() && caster.Crossings() > 0) {
949 return (caster.Crossings() % 2 == 1) ? kInside : kOutside;
955 int outsideVotes = 0;
956 if (!caster.Degenerate()) {
957 if (caster.Crossings() % 2 == 1) {
964 const BVH_Vec3d kExtraRays[2] = {
965 BVH_Vec3d(1.0, 0.0, 0.0),
966 BVH_Vec3d(0.0, 1.0, 0.0),
968 for (
const BVH_Vec3d& dir : kExtraRays) {
969 caster.SetRay(bvhOrigin, dir, bvhTol);
971 if (caster.OnSurface()) {
974 if (!caster.Degenerate()) {
975 if (caster.Crossings() % 2 == 1) {
983 if (insideVotes > outsideVotes) {
986 if (outsideVotes > insideVotes) {
992 BRepClass3d_SolidClassifier& classifier = GetOrCreateClassifier();
993 classifier.Perform(ToPoint(p), tolerance);
994 return ToG4Inside(classifier.State());
1004 IntersectorCache& cache = GetOrCreateIntersector();
1005 const gp_Lin ray(ToPoint(p), gp_Dir(0.0, 0.0, 1.0));
1007 bool onSurface =
false;
1008 bool degenerateRay =
false;
1010 for (std::size_t i = 0; i < fFaceBoundsCache.size(); ++i) {
1011 if (cache.expandedBoxes[i].IsOut(ray)) {
1014 const FaceBounds& fb = fFaceBoundsCache[i];
1015 if (!fb.uvPolygon.empty()) {
1020 Standard_Real u_hit = 0.0;
1021 Standard_Real v_hit = 0.0;
1022 const auto t = RayPlaneFaceHit(ray, *fb.plane, fb.uvPolygon, -tolerance,
1023 Precision::Infinite(), tolerance, &u_hit, &v_hit);
1025 const G4double w =
static_cast<G4double
>(*t);
1026 if (std::abs(w) <= tolerance) {
1028 }
else if (w > tolerance) {
1032 if (PointOnPolygonBoundary2d(u_hit, v_hit, fb.uvPolygon, tolerance)) {
1033 degenerateRay =
true;
1040 IntCurvesFace_Intersector& fi = *cache.faceIntersectors[i];
1041 fi.Perform(ray, -tolerance, Precision::Infinite());
1045 for (Standard_Integer j = 1; j <= fi.NbPnt(); ++j) {
1046 const G4double w = fi.WParameter(j);
1047 const TopAbs_State state = fi.State(j);
1048 if (std::abs(w) <= tolerance && (state == TopAbs_IN || state == TopAbs_ON)) {
1050 }
else if (w > tolerance && state == TopAbs_IN) {
1052 }
else if (w > tolerance && state == TopAbs_ON) {
1053 degenerateRay =
true;
1066 if (crossings == 0 || degenerateRay) {
1067 BRepClass3d_SolidClassifier& classifier = GetOrCreateClassifier();
1068 classifier.Perform(ToPoint(p), tolerance);
1069 return ToG4Inside(classifier.State());
1071 return (crossings % 2 == 1) ? kInside : kOutside;
1078 if (fAllFacesPlanar) {
1079 const gp_Pnt pt = ToPoint(p);
1080 const FaceBounds* bestFB =
nullptr;
1081 G4double bestDist = kInfinity;
1082 for (
const FaceBounds& fb : fFaceBoundsCache) {
1083 if (!fb.plane.has_value() || !fb.outwardNormal.has_value()) {
1086 const G4double d = fb.plane->Distance(pt);
1093 return *bestFB->outwardNormal;
1122 const auto projectAndGetNormalFallback = [&](
const FaceBounds& fb) -> G4ThreeVector {
1123 TopLoc_Location loc;
1124 const Handle(Geom_Surface) surface = BRep_Tool::Surface(fb.face, loc);
1125 if (surface.IsNull()) {
1126 return FallbackNormal();
1128 gp_Pnt pLocal = ToPoint(p);
1129 if (!loc.IsIdentity()) {
1130 pLocal.Transform(loc.Transformation().Inverted());
1132 GeomAPI_ProjectPointOnSurf projection(pLocal, surface);
1133 if (projection.NbPoints() == 0) {
1134 return FallbackNormal();
1136 Standard_Real u = 0.0;
1137 Standard_Real v = 0.0;
1138 projection.LowerDistanceParameters(u, v);
1139 return TryGetOutwardNormal(fb.adaptor, fb.face, u, v).value_or(FallbackNormal());
1142 const G4double bvhLB = BVHLowerBoundDistance(p);
1143 const G4double seedDist =
1144 (fBVHDeflection > 0.0 && bvhLB < kInfinity) ? bvhLB + 2.0 * fBVHDeflection : kInfinity;
1145 const auto closestFaceMatch = TryFindClosestFace(fFaceBoundsCache, p, seedDist);
1146 if (!closestFaceMatch.has_value()) {
1147 return FallbackNormal();
1149 const FaceBounds& fb = fFaceBoundsCache[closestFaceMatch->faceIndex];
1154 if (fb.outwardNormal.has_value()) {
1155 return *fb.outwardNormal;
1160 if (closestFaceMatch->uv.has_value()) {
1161 const auto [u, v] = *closestFaceMatch->uv;
1162 const auto result = TryGetOutwardNormal(fb.adaptor, fb.face, u, v);
1163 if (result.has_value()) {
1172 return projectAndGetNormalFallback(fb);
1176 const G4double tolerance = IntersectionTolerance();
1177 const gp_Lin ray(ToPoint(p), gp_Dir(v.x(), v.y(), v.z()));
1179 IntersectorCache& cache = GetOrCreateIntersector();
1181 G4double minDistance = kInfinity;
1182 for (std::size_t i = 0; i < fFaceBoundsCache.size(); ++i) {
1183 if (cache.expandedBoxes[i].IsOut(ray)) {
1186 const FaceBounds& fb = fFaceBoundsCache[i];
1187 if (!fb.uvPolygon.empty()) {
1189 const auto t = RayPlaneFaceHit(ray, *fb.plane, fb.uvPolygon, tolerance, Precision::Infinite(),
1191 if (t && *t < minDistance) {
1192 minDistance =
static_cast<G4double
>(*t);
1195 IntCurvesFace_Intersector& fi = *cache.faceIntersectors[i];
1196 fi.Perform(ray, tolerance, Precision::Infinite());
1200 for (Standard_Integer j = 1; j <= fi.NbPnt(); ++j) {
1201 const G4double w = fi.WParameter(j);
1202 if (w > tolerance && w < minDistance) {
1213 BRepClass3d_SolidClassifier& classifier = GetOrCreateClassifier();
1214 classifier.Perform(ToPoint(p), IntersectionTolerance());
1215 if (classifier.State() == TopAbs_IN || classifier.State() == TopAbs_ON) {
1219 const auto match = TryFindClosestFace(fFaceBoundsCache, p);
1220 if (!match.has_value()) {
1223 return (match->distance <= IntersectionTolerance()) ? 0.0 : match->distance;
1226G4double G4OCCTSolid::AABBLowerBound(
const G4ThreeVector& p)
const {
1227 const G4ThreeVector& mn = fCachedBounds.min;
1228 const G4ThreeVector& mx = fCachedBounds.max;
1229 const G4double dx = std::max({0.0, mn.x() - p.x(), p.x() - mx.x()});
1230 const G4double dy = std::max({0.0, mn.y() - p.y(), p.y() - mx.y()});
1231 const G4double dz = std::max({0.0, mn.z() - p.z(), p.z() - mx.z()});
1232 return std::sqrt(dx * dx + dy * dy + dz * dz);
1235G4double G4OCCTSolid::BVHLowerBoundDistance(
const G4ThreeVector& p)
const {
1236 if (fTriangleSet.IsNull() || fTriangleSet->Size() == 0) {
1239 PointToMeshDistance solver;
1240 solver.SetObject(BVH_Vec3d(p.x(), p.y(), p.z()));
1241 solver.SetBVHSet(fTriangleSet.get());
1242 const Standard_Real meshDistSq = solver.ComputeDistance();
1243 if (!solver.IsDone()) {
1248 const G4double meshDist = std::sqrt(
static_cast<G4double
>(meshDistSq));
1255 G4double deflection = fBVHDeflection;
1256 const Standard_Integer bestIdx = solver.BestIndex();
1258 const Standard_Integer faceId = fTriangleSet->GetFaceID(bestIdx);
1259 if (faceId >= 0 &&
static_cast<std::size_t
>(faceId) < fFaceDeflections.size()) {
1260 deflection = fFaceDeflections[
static_cast<std::size_t
>(faceId)];
1264 return std::max(0.0, meshDist - deflection);
1267G4double G4OCCTSolid::PlanarFaceLowerBoundDistance(
const G4ThreeVector& p)
const {
1268 const gp_Pnt pt = ToPoint(p);
1269 G4double minDist = kInfinity;
1270 for (
const FaceBounds& fb : fFaceBoundsCache) {
1271 if (!fb.plane.has_value()) {
1274 const G4double d =
static_cast<G4double
>(fb.plane->Distance(pt));
1286 const G4double aabbDist = AABBLowerBound(p);
1287 if (aabbDist > IntersectionTolerance()) {
1295 const G4double bvhDist = BVHLowerBoundDistance(p);
1296 if (bvhDist < kInfinity && bvhDist > IntersectionTolerance()) {
1306 const G4bool calcNorm, G4bool* validNorm,
1307 G4ThreeVector* n)
const {
1308 if (validNorm !=
nullptr) {
1312 const G4double tolerance = IntersectionTolerance();
1313 const gp_Lin ray(ToPoint(p), gp_Dir(v.x(), v.y(), v.z()));
1315 IntersectorCache& cache = GetOrCreateIntersector();
1317 G4double minDistance = kInfinity;
1318 std::size_t minFaceIdx = std::numeric_limits<std::size_t>::max();
1319 G4double minU = 0.0;
1320 G4double minV = 0.0;
1321 bool minIsIn =
false;
1322 bool minIsFastPath =
false;
1324 for (std::size_t i = 0; i < fFaceBoundsCache.size(); ++i) {
1325 if (cache.expandedBoxes[i].IsOut(ray)) {
1328 const FaceBounds& fb = fFaceBoundsCache[i];
1329 if (!fb.uvPolygon.empty()) {
1331 const auto t = RayPlaneFaceHit(ray, *fb.plane, fb.uvPolygon, tolerance, Precision::Infinite(),
1333 if (t && *t < minDistance) {
1334 minDistance =
static_cast<G4double
>(*t);
1337 minIsFastPath =
true;
1340 IntCurvesFace_Intersector& fi = *cache.faceIntersectors[i];
1341 fi.Perform(ray, tolerance, Precision::Infinite());
1345 for (Standard_Integer j = 1; j <= fi.NbPnt(); ++j) {
1346 const G4double w = fi.WParameter(j);
1347 if (w > tolerance && w < minDistance) {
1350 minU = fi.UParameter(j);
1351 minV = fi.VParameter(j);
1352 minIsIn = (fi.State(j) == TopAbs_IN || fi.State(j) == TopAbs_ON);
1353 minIsFastPath =
false;
1359 if (minFaceIdx == std::numeric_limits<std::size_t>::max() || minDistance == kInfinity) {
1363 if (calcNorm && validNorm !=
nullptr && n !=
nullptr && minIsIn) {
1364 const FaceBounds& fb = fFaceBoundsCache[minFaceIdx];
1365 if (minIsFastPath && fb.outwardNormal.has_value()) {
1367 *n = *fb.outwardNormal;
1370 const auto outNorm = TryGetOutwardNormal(fb.adaptor, fb.face, minU, minV);
1385 const auto match = TryFindClosestFace(fFaceBoundsCache, p);
1386 if (!match.has_value()) {
1389 return (match->distance <= IntersectionTolerance()) ? 0.0 : match->distance;
1394 if (fAllFacesPlanar) {
1398 d = PlanarFaceLowerBoundDistance(p);
1399 if (d == kInfinity) {
1406 const G4double bvhDist = BVHLowerBoundDistance(p);
1411 TryInsertSphere(p, d);
1416 std::unique_lock<std::mutex> lock(fVolumeAreaMutex);
1417 if (!fCachedVolume) {
1419 BRepGProp::VolumeProperties(fShape, props);
1420 fCachedVolume = props.Mass();
1422 return *fCachedVolume;
1426 std::unique_lock<std::mutex> lock(fVolumeAreaMutex);
1427 if (!fCachedSurfaceArea) {
1429 BRepGProp::SurfaceProperties(fShape, props);
1430 fCachedSurfaceArea = props.Mass();
1432 return *fCachedSurfaceArea;
1435const G4OCCTSolid::SurfaceSamplingCache& G4OCCTSolid::GetOrBuildSurfaceCache()
const {
1439 BRepMesh_IncrementalMesh mesher(fShape, kRelativeDeflection, Standard_True);
1442 std::unique_lock<std::mutex> lock(fSurfaceCacheMutex);
1444 const std::uint64_t currentGen = fShapeGeneration.load(std::memory_order_acquire);
1445 if (fSurfaceCache.has_value() && fSurfaceCacheGeneration == currentGen) {
1446 return *fSurfaceCache;
1452 SurfaceSamplingCache cache;
1454 for (TopExp_Explorer ex(fShape, TopAbs_FACE); ex.More(); ex.Next()) {
1455 const TopoDS_Face& face = TopoDS::Face(ex.Current());
1456 TopLoc_Location loc;
1457 const Handle(Poly_Triangulation) & triangulation = BRep_Tool::Triangulation(face, loc);
1458 if (triangulation.IsNull()) {
1463 const auto faceIndex =
static_cast<std::uint32_t
>(cache.faces.size());
1464 cache.faces.push_back(face);
1466 const gp_Trsf& transform = loc.Transformation();
1467 const bool reverseWinding = face.Orientation() == TopAbs_REVERSED;
1469 for (Standard_Integer i = 1; i <= triangulation->NbTriangles(); ++i) {
1470 Standard_Integer idx1 = 0;
1471 Standard_Integer idx2 = 0;
1472 Standard_Integer idx3 = 0;
1473 triangulation->Triangle(i).Get(idx1, idx2, idx3);
1474 if (reverseWinding) {
1475 std::swap(idx2, idx3);
1478 const gp_Pnt q1 = triangulation->Node(idx1).Transformed(transform);
1479 const gp_Pnt q2 = triangulation->Node(idx2).Transformed(transform);
1480 const gp_Pnt q3 = triangulation->Node(idx3).Transformed(transform);
1482 const G4ThreeVector v1(q1.X(), q1.Y(), q1.Z());
1483 const G4ThreeVector v2(q2.X(), q2.Y(), q2.Z());
1484 const G4ThreeVector v3(q3.X(), q3.Y(), q3.Z());
1486 const G4double area = 0.5 * (v2 - v1).cross(v3 - v1).mag();
1488 cache.totalArea += area;
1489 cache.cumulativeAreas.push_back(cache.totalArea);
1490 cache.triangles.push_back({v1, v2, v3, faceIndex});
1495 fSurfaceCache = std::move(cache);
1496 fSurfaceCacheGeneration = currentGen;
1497 return *fSurfaceCache;
1501 const SurfaceSamplingCache& cache = GetOrBuildSurfaceCache();
1503 if (cache.triangles.empty() || cache.totalArea == 0.0) {
1504 G4ExceptionDescription msg;
1505 msg <<
"Tessellation of solid \"" << GetName()
1506 <<
"\" produced no valid triangles; cannot sample a point on the surface.";
1507 G4Exception(
"G4OCCTSolid::GetPointOnSurface",
"GeomMgt1001", FatalException, msg);
1508 return {0.0, 0.0, 0.0};
1513 const G4double target = G4UniformRand() * cache.totalArea;
1514 const auto it = std::ranges::lower_bound(cache.cumulativeAreas, target);
1515 const std::size_t idx = std::min(
static_cast<std::size_t
>(it - cache.cumulativeAreas.begin()),
1516 cache.triangles.size() - 1);
1517 const SurfaceTriangle& chosen = cache.triangles[idx];
1522 G4double r1 = G4UniformRand();
1523 G4double r2 = G4UniformRand();
1524 if (r1 + r2 > 1.0) {
1529 const G4ThreeVector tessPoint =
1530 chosen.p1 + r1 * (chosen.p2 - chosen.p1) + r2 * (chosen.p3 - chosen.p1);
1536 const TopoDS_Face& face = cache.faces[chosen.faceIndex];
1537 TopLoc_Location loc;
1538 const Handle(Geom_Surface) geomSurface = BRep_Tool::Surface(face, loc);
1539 if (!geomSurface.IsNull()) {
1540 gp_Pnt tessPointLocal(tessPoint.x(), tessPoint.y(), tessPoint.z());
1541 if (!loc.IsIdentity()) {
1542 tessPointLocal.Transform(loc.Transformation().Inverted());
1544 GeomAPI_ProjectPointOnSurf projection(tessPointLocal, geomSurface);
1545 if (projection.NbPoints() > 0) {
1546 gp_Pnt projectedPoint = projection.NearestPoint();
1547 if (!loc.IsIdentity()) {
1548 projectedPoint.Transform(loc.Transformation());
1550 return {projectedPoint.X(), projectedPoint.Y(), projectedPoint.Z()};
1560 return {fCachedBounds.min.x(), fCachedBounds.max.x(), fCachedBounds.min.y(),
1561 fCachedBounds.max.y(), fCachedBounds.min.z(), fCachedBounds.max.z()};
1565 pMin = fCachedBounds.min;
1566 pMax = fCachedBounds.max;
1570 const G4AffineTransform& pTransform, G4double& pMin,
1571 G4double& pMax)
const {
1572 const G4BoundingEnvelope envelope(fCachedBounds.min, fCachedBounds.max);
1573 return envelope.CalculateExtent(pAxis, pVoxelLimit, G4Transform3D(pTransform), pMin, pMax);
1579 const auto currentGeneration = fShapeGeneration.load(std::memory_order_acquire);
1582 std::unique_lock<std::mutex> lock(fPolyhedronMutex);
1586 fPolyhedronCV.wait(lock, [
this, currentGeneration] {
1587 return !fPolyhedronBuilding || fPolyhedronGeneration == currentGeneration;
1591 if (fCachedPolyhedron && fPolyhedronGeneration == currentGeneration) {
1592 return new G4Polyhedron(*fCachedPolyhedron);
1596 fPolyhedronBuilding =
true;
1604 BRepMesh_IncrementalMesh mesher(fShape, kRelativeDeflection, Standard_True);
1607 G4TessellatedSolid tessellatedSolid(GetName() +
"_polyhedron");
1608 G4int facetCount = 0;
1610 for (TopExp_Explorer explorer(fShape, TopAbs_FACE); explorer.More(); explorer.Next()) {
1611 const TopoDS_Face& face = TopoDS::Face(explorer.Current());
1612 TopLoc_Location location;
1613 const Handle(Poly_Triangulation) & triangulation = BRep_Tool::Triangulation(face, location);
1614 if (triangulation.IsNull()) {
1618 const gp_Trsf& transform = location.Transformation();
1619 const bool reverseWinding = face.Orientation() == TopAbs_REVERSED;
1621 for (Standard_Integer triangleIndex = 1; triangleIndex <= triangulation->NbTriangles();
1623 Standard_Integer index1 = 0;
1624 Standard_Integer index2 = 0;
1625 Standard_Integer index3 = 0;
1626 triangulation->Triangle(triangleIndex).Get(index1, index2, index3);
1628 if (reverseWinding) {
1629 std::swap(index2, index3);
1632 const gp_Pnt point1 = triangulation->Node(index1).Transformed(transform);
1633 const gp_Pnt point2 = triangulation->Node(index2).Transformed(transform);
1634 const gp_Pnt point3 = triangulation->Node(index3).Transformed(transform);
1637 new G4TriangularFacet(G4ThreeVector(point1.X(), point1.Y(), point1.Z()),
1638 G4ThreeVector(point2.X(), point2.Y(), point2.Z()),
1639 G4ThreeVector(point3.X(), point3.Y(), point3.Z()), ABSOLUTE);
1640 if (!facet->IsDefined() || !tessellatedSolid.AddFacet(facet)) {
1651 std::unique_ptr<G4Polyhedron> freshPolyhedron;
1652 if (facetCount > 0) {
1653 tessellatedSolid.SetSolidClosed(
true);
1654 G4Polyhedron* tmp = tessellatedSolid.GetPolyhedron();
1655 if (tmp !=
nullptr) {
1656 freshPolyhedron = std::make_unique<G4Polyhedron>(*tmp);
1662 std::unique_lock<std::mutex> lock(fPolyhedronMutex);
1667 bool cacheWritten =
false;
1668 if (freshPolyhedron && fShapeGeneration.load(std::memory_order_acquire) == currentGeneration) {
1669 fCachedPolyhedron = std::make_unique<G4Polyhedron>(*freshPolyhedron);
1670 fPolyhedronGeneration = currentGeneration;
1671 cacheWritten =
true;
1676 fPolyhedronBuilding =
false;
1677 fPolyhedronCV.notify_all();
1682 return new G4Polyhedron(*fCachedPolyhedron);
1686 return freshPolyhedron ?
new G4Polyhedron(*freshPolyhedron) :
nullptr;
1690 os <<
"-----------------------------------------------------------\n"
1691 <<
" *** Dump for solid - " << GetName() <<
" ***\n"
1692 <<
" ===================================================\n"
1693 <<
" Solid type: G4OCCTSolid\n"
1694 <<
" OCCT shape type: " << fShape.ShapeType() <<
"\n"
1695 <<
"-----------------------------------------------------------\n";
Declaration of G4OCCTSolid.
Geant4 solid wrapping an Open CASCADE Technology (OCCT) TopoDS_Shape.
G4double ExactDistanceToOut(const G4ThreeVector &p) const
EInside Inside(const G4ThreeVector &p) const override
Return kInside, kSurface, or kOutside for point p.
G4double GetCubicVolume() override
G4double DistanceToOut(const G4ThreeVector &p, const G4ThreeVector &v, const G4bool calcNorm=false, G4bool *validNorm=nullptr, G4ThreeVector *n=nullptr) const override
G4Polyhedron * CreatePolyhedron() const override
Create a polyhedron representation for visualisation.
G4double DistanceToIn(const G4ThreeVector &p, const G4ThreeVector &v) const override
void BoundingLimits(G4ThreeVector &pMin, G4ThreeVector &pMax) const override
Return the axis-aligned bounding box limits.
G4OCCTSolid(const G4String &name, const TopoDS_Shape &shape)
G4ThreeVector SurfaceNormal(const G4ThreeVector &p) const override
Return the outward unit normal at surface point p.
G4bool CalculateExtent(const EAxis pAxis, const G4VoxelLimits &pVoxelLimit, const G4AffineTransform &pTransform, G4double &pMin, G4double &pMax) const override
Calculate the extent of the solid in the given axis.
G4double GetSurfaceArea() override
G4GeometryType GetEntityType() const override
Return a string identifying the entity type.
G4ThreeVector GetPointOnSurface() const override
G4VisExtent GetExtent() const override
Return the axis-aligned bounding box extent.
std::ostream & StreamInfo(std::ostream &os) const override
Stream a human-readable description.
static G4OCCTSolid * FromSTEP(const G4String &name, const std::string &path)
void DescribeYourselfTo(G4VGraphicsScene &scene) const override
Describe the solid to the graphics scene.
G4double ExactDistanceToIn(const G4ThreeVector &p) const