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 <NCollection_Vector.hxx>
17#include <BRepGProp.hxx>
19#include <BRepLProp_SLProps.hxx>
20#include <BRepMesh_IncrementalMesh.hxx>
21#include <BRep_Builder.hxx>
22#include <BRep_Tool.hxx>
23#include <BRepTools.hxx>
24#include <BRepTools_WireExplorer.hxx>
25#include <BVH_Distance.hxx>
26#include <BVH_Tools.hxx>
29#include <GeomAbs_CurveType.hxx>
30#include <GeomAbs_SurfaceType.hxx>
31#include <GProp_GProps.hxx>
32#include <GeomAPI_ProjectPointOnSurf.hxx>
33#include <Geom_Surface.hxx>
34#include <IFSelect_ReturnStatus.hxx>
35#include <IntCurvesFace_Intersector.hxx>
36#include <NCollection_Vector.hxx>
37#include <Poly_Triangulation.hxx>
38#include <Precision.hxx>
39#include <STEPControl_Reader.hxx>
40#include <TopAbs_Orientation.hxx>
41#include <TopAbs_ShapeEnum.hxx>
42#include <TopAbs_State.hxx>
43#include <TopExp_Explorer.hxx>
44#include <TopLoc_Location.hxx>
46#include <TopoDS_Edge.hxx>
47#include <TopoDS_Face.hxx>
48#include <TopoDS_Vertex.hxx>
49#include <TopoDS_Wire.hxx>
54#include <gp_Pnt2d.hxx>
57#include <G4BoundingEnvelope.hh>
58#include <G4AffineTransform.hh>
59#include <G4Exception.hh>
60#include <G4GeometryTolerance.hh>
61#include <G4Polyhedron.hh>
62#include <G4TessellatedSolid.hh>
63#include <G4TriangularFacet.hh>
64#include <G4VGraphicsScene.hh>
65#include <G4VisExtent.hh>
66#include <Randomize.hh>
81constexpr Standard_Real kRelativeDeflection = 0.01;
94class PointToMeshDistance
95 :
public BVH_Distance<Standard_Real, 3, BVH_Vec3d, BRepExtrema_TriangleSet> {
99 Standard_Boolean RejectNode(
const BVH_Vec3d& theCornerMin,
const BVH_Vec3d& theCornerMax,
100 Standard_Real& theMetric)
const override {
102 BVH_Tools<Standard_Real, 3>::PointBoxSquareDistance(myObject, theCornerMin, theCornerMax);
103 return RejectMetric(theMetric);
107 Standard_Boolean Accept(
const Standard_Integer theIndex,
const Standard_Real&)
override {
108 BVH_Vec3d v0, v1, v2;
109 myBVHSet->GetVertices(theIndex, v0, v1, v2);
110 const Standard_Real sq =
111 BVH_Tools<Standard_Real, 3>::PointTriangleSquareDistance(myObject, v0, v1, v2);
112 if (sq < myDistance) {
114 myBestIndex = theIndex;
115 return Standard_True;
117 return Standard_False;
123 Standard_Integer BestIndex()
const {
return myBestIndex; }
126 Standard_Integer myBestIndex{-1};
140 :
public BVH_Traverse<Standard_Real, 3, BRepExtrema_TriangleSet, Standard_Real> {
144 void SetRay(
const BVH_Vec3d& theOrigin,
const BVH_Vec3d& theDir, Standard_Real theTolerance) {
145 myOrigin = theOrigin;
147 myTolerance = theTolerance;
151 myOnSurface = Standard_False;
152 myDegenerate = Standard_False;
156 Standard_Boolean RejectNode(
const BVH_Vec3d& theCornerMin,
const BVH_Vec3d& theCornerMax,
157 Standard_Real& theMetric)
const override {
158 Standard_Real tmin = 0.0;
159 Standard_Real tmax = Precision::Infinite();
160 for (
int k = 0; k < 3; ++k) {
161 const Standard_Real dk = (k == 0) ? myDir.x() : (k == 1) ? myDir.y() : myDir.z();
162 const Standard_Real ok = (k == 0) ? myOrigin.x() : (k == 1) ? myOrigin.y() : myOrigin.z();
163 const Standard_Real ck_min = (k == 0) ? theCornerMin.x()
164 : (k == 1) ? theCornerMin.y()
166 const Standard_Real ck_max = (k == 0) ? theCornerMax.x()
167 : (k == 1) ? theCornerMax.y()
169 if (std::abs(dk) < Precision::Confusion()) {
170 if (ok < ck_min - myTolerance || ok > ck_max + myTolerance) {
171 return Standard_True;
174 Standard_Real t1 = (ck_min - ok) / dk;
175 Standard_Real t2 = (ck_max - ok) / dk;
185 if (tmin > tmax + myTolerance) {
186 return Standard_True;
191 return Standard_False;
195 Standard_Boolean Accept(
const Standard_Integer theIndex,
const Standard_Real&)
override {
196 BVH_Vec3d v0, v1, v2;
197 myBVHSet->GetVertices(theIndex, v0, v1, v2);
198 const BVH_Vec3d edge1 = v1 - v0;
199 const BVH_Vec3d edge2 = v2 - v0;
200 const BVH_Vec3d h = BVH_Vec3d::Cross(myDir, edge2);
201 const Standard_Real a = edge1.Dot(h);
202 if (std::abs(a) < 1e-12) {
203 return Standard_False;
205 const Standard_Real f = 1.0 / a;
206 const BVH_Vec3d s = myOrigin - v0;
207 const Standard_Real u = f * s.Dot(h);
208 if (u < 0.0 || u > 1.0) {
209 return Standard_False;
211 const BVH_Vec3d q = BVH_Vec3d::Cross(s, edge1);
212 const Standard_Real v = f * myDir.Dot(q);
213 if (v < 0.0 || u + v > 1.0) {
214 return Standard_False;
216 const Standard_Real t = f * edge2.Dot(q);
217 if (std::abs(t) <= myTolerance) {
218 myOnSurface = Standard_True;
219 return Standard_True;
221 if (t < -myTolerance) {
222 return Standard_False;
227 constexpr Standard_Real kEdgeTol = 1e-6;
228 if (u < kEdgeTol || v < kEdgeTol || (1.0 - u - v) < kEdgeTol) {
229 myDegenerate = Standard_True;
231 return Standard_True;
235 Standard_Boolean RejectMetric(
const Standard_Real&)
const override {
return Standard_False; }
238 Standard_Boolean Stop()
const override {
return Standard_False; }
240 int Crossings()
const {
return myCrossings; }
241 Standard_Boolean OnSurface()
const {
return myOnSurface; }
242 Standard_Boolean Degenerate()
const {
return myDegenerate; }
247 Standard_Real myTolerance{1e-7};
249 Standard_Boolean myOnSurface{Standard_False};
250 Standard_Boolean myDegenerate{Standard_False};
254gp_Pnt ToPoint(
const G4ThreeVector& point) {
return gp_Pnt(point.x(), point.y(), point.z()); }
257G4double IntersectionTolerance() {
258 return 0.5 * G4GeometryTolerance::GetInstance()->GetSurfaceTolerance();
262TopoDS_Vertex MakeVertex(
const G4ThreeVector& point) {
263 BRep_Builder builder;
264 TopoDS_Vertex vertex;
265 builder.MakeVertex(vertex, ToPoint(point), IntersectionTolerance());
270EInside ToG4Inside(
const TopAbs_State state) {
283G4ThreeVector FallbackNormal() {
return G4ThreeVector(0.0, 0.0, 1.0); }
290bool PointInPolygon2d(Standard_Real u, Standard_Real v,
const std::vector<gp_Pnt2d>& poly) {
291 const std::size_t n = poly.size();
293 for (std::size_t i = 0; i < n; ++i) {
294 const gp_Pnt2d& a = poly[i];
295 const gp_Pnt2d& b = poly[(i + 1) % n];
296 const Standard_Real av = a.Y();
297 const Standard_Real bv = b.Y();
298 if ((av <= v && bv > v) || (bv <= v && av > v)) {
299 const Standard_Real uCross = a.X() + (v - av) * (b.X() - a.X()) / (bv - av);
305 return (crossings % 2) == 1;
312bool PointOnPolygonBoundary2d(Standard_Real u, Standard_Real v,
const std::vector<gp_Pnt2d>& poly,
314 const Standard_Real tol2 = tol * tol;
315 const std::size_t n = poly.size();
316 for (std::size_t i = 0; i < n; ++i) {
317 const gp_Pnt2d& a = poly[i];
318 const gp_Pnt2d& b = poly[(i + 1) % n];
319 const Standard_Real dx = b.X() - a.X();
320 const Standard_Real dy = b.Y() - a.Y();
321 const Standard_Real len2 = dx * dx + dy * dy;
322 Standard_Real px = 0.0;
323 Standard_Real py = 0.0;
324 if (len2 < 1.0e-20) {
328 const Standard_Real t_seg =
329 std::max(0.0, std::min(1.0, ((u - a.X()) * dx + (v - a.Y()) * dy) / len2));
330 px = a.X() + t_seg * dx;
331 py = a.Y() + t_seg * dy;
333 const Standard_Real dist2 = (u - px) * (u - px) + (v - py) * (v - py);
359std::optional<Standard_Real>
360RayPlaneFaceHit(
const gp_Lin& ray,
const gp_Pln& plane,
const std::vector<gp_Pnt2d>& uvPoly,
361 Standard_Real tMin, Standard_Real tMax, Standard_Real tolerance,
362 Standard_Real* u_out =
nullptr, Standard_Real* v_out =
nullptr) {
363 const gp_Dir& lineDir = ray.Direction();
364 const gp_Dir& plnNormal = plane.Axis().Direction();
365 const Standard_Real denom =
366 plnNormal.X() * lineDir.X() + plnNormal.Y() * lineDir.Y() + plnNormal.Z() * lineDir.Z();
369 if (std::abs(denom) < 1.0e-10) {
372 const gp_Pnt& orig = ray.Location();
373 const gp_Pnt& planePt = plane.Location();
374 const Standard_Real numer = plnNormal.X() * (planePt.X() - orig.X()) +
375 plnNormal.Y() * (planePt.Y() - orig.Y()) +
376 plnNormal.Z() * (planePt.Z() - orig.Z());
377 const Standard_Real t = numer / denom;
378 if (t < tMin || t > tMax) {
381 const gp_Pnt hitPt(orig.X() + t * lineDir.X(), orig.Y() + t * lineDir.Y(),
382 orig.Z() + t * lineDir.Z());
383 Standard_Real u = 0.0;
384 Standard_Real v = 0.0;
385 ElSLib::PlaneParameters(plane.Position(), hitPt, u, v);
389 if (!PointInPolygon2d(u, v, uvPoly) && !PointOnPolygonBoundary2d(u, v, uvPoly, tolerance)) {
392 if (u_out !=
nullptr) {
395 if (v_out !=
nullptr) {
404std::optional<G4ThreeVector> TryGetOutwardNormal(
const BRepAdaptor_Surface& surface,
405 const TopoDS_Face& face,
const Standard_Real u,
406 const Standard_Real v) {
407 Standard_Real adjustedU = u;
408 Standard_Real adjustedV = v;
409 const Standard_Real tolerance = IntersectionTolerance();
415 const Standard_Real uFirst = surface.FirstUParameter();
416 const Standard_Real uLast = surface.LastUParameter();
417 const Standard_Real uEpsilon = std::min(
418 std::max(surface.UResolution(tolerance), Precision::PConfusion()), 0.5 * (uLast - uFirst));
419 if (std::abs(adjustedU - uFirst) <= uEpsilon) {
420 adjustedU = std::min(uFirst + uEpsilon, uLast);
421 }
else if (std::abs(adjustedU - uLast) <= uEpsilon) {
422 adjustedU = std::max(uLast - uEpsilon, uFirst);
428 const Standard_Real vFirst = surface.FirstVParameter();
429 const Standard_Real vLast = surface.LastVParameter();
430 const Standard_Real vEpsilon = std::min(
431 std::max(surface.VResolution(tolerance), Precision::PConfusion()), 0.5 * (vLast - vFirst));
432 if (std::abs(adjustedV - vFirst) <= vEpsilon) {
433 adjustedV = std::min(vFirst + vEpsilon, vLast);
434 }
else if (std::abs(adjustedV - vLast) <= vEpsilon) {
435 adjustedV = std::max(vLast - vEpsilon, vFirst);
439 BRepLProp_SLProps props(surface, adjustedU, adjustedV, 1, tolerance);
444 if (!props.IsNormalDefined()) {
445 const Standard_Real vFirst = surface.FirstVParameter();
446 const Standard_Real vLast = surface.LastVParameter();
447 const Standard_Real vMid = 0.5 * (vFirst + vLast);
448 const bool nearVFirst = (adjustedV < vMid);
449 const Standard_Real vRes = std::max(surface.VResolution(tolerance), Precision::PConfusion());
450 Standard_Real finalRetryV = adjustedV;
451 for (
int attempt = 0; attempt < 8 && !props.IsNormalDefined(); ++attempt) {
452 const Standard_Real scale = std::pow(10.0,
static_cast<Standard_Real
>(attempt));
453 const Standard_Real nudge = scale * vRes;
455 nearVFirst ? std::min(adjustedV + nudge, vMid) : std::max(adjustedV - nudge, vMid);
456 props = BRepLProp_SLProps(surface, adjustedU, finalRetryV, 1, tolerance);
458 if (!props.IsNormalDefined()) {
467 constexpr Standard_Real kMaxRetryVDriftFraction = 0.10;
468 if (std::fabs(finalRetryV - adjustedV) > kMaxRetryVDriftFraction * (vLast - vFirst)) {
473 gp_Dir faceNormal = props.Normal();
474 if (face.Orientation() == TopAbs_REVERSED) {
475 faceNormal.Reverse();
478 return G4ThreeVector(faceNormal.X(), faceNormal.Y(), faceNormal.Z());
484 : G4VSolid(name), fShape(shape) {
485 if (fShape.IsNull()) {
486 throw std::invalid_argument(
"G4OCCTSolid: shape must not be null");
498 fClassifierCache.Get().classifier.reset();
499 fIntersectorCache.Get().faceIntersectors.clear();
500 fSphereCache.Get().spheres.clear();
506 STEPControl_Reader reader;
507 if (reader.ReadFile(path.c_str()) != IFSelect_RetDone) {
508 throw std::runtime_error(
"G4OCCTSolid::FromSTEP: failed to read STEP file: " + path);
510 if (reader.TransferRoots() <= 0) {
511 throw std::runtime_error(
"G4OCCTSolid::FromSTEP: no roots transferred from: " + path);
513 TopoDS_Shape shape = reader.OneShape();
514 if (shape.IsNull()) {
515 throw std::runtime_error(
"G4OCCTSolid::FromSTEP: null shape loaded from: " + path);
522void G4OCCTSolid::ComputeBounds() {
526 for (TopExp_Explorer faceEx(fShape, TopAbs_FACE); faceEx.More(); faceEx.Next()) {
527 const TopoDS_Face& face = TopoDS::Face(faceEx.Current());
528 if (BRepAdaptor_Surface(face).GetType() != GeomAbs_Plane) {
531 for (TopExp_Explorer edgeEx(face, TopAbs_EDGE); edgeEx.More(); edgeEx.Next()) {
532 BRepLib::BuildPCurveForEdgeOnPlane(TopoDS::Edge(edgeEx.Current()), face);
537 BRepBndLib::AddOptimal(fShape, boundingBox, Standard_False);
538 if (boundingBox.IsVoid()) {
539 throw std::invalid_argument(
"G4OCCTSolid: shape has no computable bounding box (no geometry)");
542 Standard_Real xMin = 0.0;
543 Standard_Real yMin = 0.0;
544 Standard_Real zMin = 0.0;
545 Standard_Real xMax = 0.0;
546 Standard_Real yMax = 0.0;
547 Standard_Real zMax = 0.0;
548 boundingBox.Get(xMin, yMin, zMin, xMax, yMax, zMax);
551 AxisAlignedBounds{G4ThreeVector(xMin, yMin, zMin), G4ThreeVector(xMax, yMax, zMax)};
554 fFaceBoundsCache.clear();
555 fFaceAdaptorIndex.clear();
556 G4double maxFaceDiag = 0.0;
557 for (TopExp_Explorer ex(fShape, TopAbs_FACE); ex.More(); ex.Next()) {
559 BRepBndLib::AddOptimal(ex.Current(), faceBox, Standard_False);
560 const TopoDS_Face& currentFace = TopoDS::Face(ex.Current());
561 const std::size_t idx = fFaceBoundsCache.size();
562 BRepAdaptor_Surface adaptor(currentFace);
563 std::optional<gp_Pln> maybePlane;
564 std::vector<gp_Pnt2d> uvPolygon;
565 std::optional<G4ThreeVector> outwardNormal;
566 if (adaptor.GetType() == GeomAbs_Plane) {
567 maybePlane = adaptor.Plane();
568 const gp_Ax3& pos = maybePlane->Position();
569 const TopoDS_Wire wire = BRepTools::OuterWire(currentFace);
570 if (!wire.IsNull()) {
575 bool allLinear =
true;
576 std::vector<gp_Pnt2d> poly;
577 for (BRepTools_WireExplorer we(wire, currentFace); we.More(); we.Next()) {
578 const BRepAdaptor_Curve ec(we.Current());
579 if (ec.GetType() != GeomAbs_Line) {
583 const gp_Pnt pt = BRep_Tool::Pnt(we.CurrentVertex());
584 Standard_Real u = 0.0;
585 Standard_Real v = 0.0;
586 ElSLib::PlaneParameters(pos, pt, u, v);
587 poly.emplace_back(u, v);
589 if (allLinear && poly.size() >= 3) {
594 for (TopExp_Explorer wc(currentFace, TopAbs_WIRE); wc.More(); wc.Next()) {
600 if (wireCount == 1) {
601 uvPolygon = std::move(poly);
605 gp_Dir faceNormal = maybePlane->Axis().Direction();
606 if (currentFace.Orientation() == TopAbs_REVERSED) {
607 faceNormal.Reverse();
609 outwardNormal = G4ThreeVector(faceNormal.X(), faceNormal.Y(), faceNormal.Z());
614 fFaceBoundsCache.push_back({currentFace, faceBox, std::move(adaptor), std::move(maybePlane),
615 std::move(uvPolygon), std::move(outwardNormal)});
616 fFaceAdaptorIndex[currentFace.TShape().get()].push_back(idx);
618 if (!faceBox.IsVoid()) {
619 Standard_Real fx0 = 0.0;
620 Standard_Real fy0 = 0.0;
621 Standard_Real fz0 = 0.0;
622 Standard_Real fx1 = 0.0;
623 Standard_Real fy1 = 0.0;
624 Standard_Real fz1 = 0.0;
625 faceBox.Get(fx0, fy0, fz0, fx1, fy1, fz1);
626 const G4double diag = G4ThreeVector(fx1 - fx0, fy1 - fy0, fz1 - fz0).mag();
627 maxFaceDiag = std::max(maxFaceDiag, diag);
633 fBVHDeflection = kRelativeDeflection * maxFaceDiag;
636 fAllFacesPlanar = std::all_of(fFaceBoundsCache.begin(), fFaceBoundsCache.end(),
637 [](
const FaceBounds& fb) { return fb.plane.has_value(); });
643 [[maybe_unused]]
const BRepMesh_IncrementalMesh mesher(fShape, kRelativeDeflection,
648 NCollection_Vector<TopoDS_Shape> faces;
649 for (TopExp_Explorer ex(fShape, TopAbs_FACE); ex.More(); ex.Next()) {
650 faces.Append(ex.Current());
652 if (faces.IsEmpty()) {
653 fTriangleSet.Nullify();
654 fFaceDeflections.clear();
656 fTriangleSet =
new BRepExtrema_TriangleSet(faces);
662 fFaceDeflections.clear();
663 fFaceDeflections.reserve(fFaceBoundsCache.size());
664 for (
const FaceBounds& fb : fFaceBoundsCache) {
668 G4double deflection = fBVHDeflection;
669 if (!fb.box.IsVoid()) {
670 Standard_Real fx0 = 0.0, fy0 = 0.0, fz0 = 0.0;
671 Standard_Real fx1 = 0.0, fy1 = 0.0, fz1 = 0.0;
672 fb.box.Get(fx0, fy0, fz0, fx1, fy1, fz1);
673 const G4double faceDiag = G4ThreeVector(fx1 - fx0, fy1 - fy0, fz1 - fz0).mag();
674 deflection = kRelativeDeflection * faceDiag;
676 fFaceDeflections.push_back(deflection);
680 ComputeInitialSpheres();
683void G4OCCTSolid::ComputeInitialSpheres() {
684 fInitialSpheres.clear();
686 const G4double tol = IntersectionTolerance();
687 const G4ThreeVector& bmin = fCachedBounds.min;
688 const G4ThreeVector& bmax = fCachedBounds.max;
689 const G4ThreeVector centre = 0.5 * (bmin + bmax);
690 const G4ThreeVector halfExt = 0.5 * (bmax - bmin);
695 std::vector<G4ThreeVector> candidates;
696 candidates.reserve(15);
697 candidates.push_back(centre);
698 for (
const G4double s : {-0.5, 0.5}) {
699 candidates.push_back(centre + G4ThreeVector(s * halfExt.x(), 0.0, 0.0));
700 candidates.push_back(centre + G4ThreeVector(0.0, s * halfExt.y(), 0.0));
701 candidates.push_back(centre + G4ThreeVector(0.0, 0.0, s * halfExt.z()));
703 for (
const int sx : {-1, 1}) {
704 for (
const int sy : {-1, 1}) {
705 for (
const int sz : {-1, 1}) {
706 candidates.push_back(centre + G4ThreeVector(0.75 * sx * halfExt.x(),
707 0.75 * sy * halfExt.y(),
708 0.75 * sz * halfExt.z()));
714 BRepClass3d_SolidClassifier localClassifier;
715 localClassifier.Load(fShape);
717 for (
const G4ThreeVector& cand : candidates) {
718 localClassifier.Perform(ToPoint(cand), tol);
719 if (localClassifier.State() != TopAbs_IN) {
722 G4double d = BVHLowerBoundDistance(cand);
723 if (d >= kInfinity || d <= tol) {
725 const auto match = TryFindClosestFace(fFaceBoundsCache, cand);
726 if (!match.has_value() || match->distance <= tol) {
731 fInitialSpheres.push_back({cand, d});
734 std::sort(fInitialSpheres.begin(), fInitialSpheres.end(),
735 [](
const InscribedSphere& a,
const InscribedSphere& b) { return a.radius > b.radius; });
738std::optional<G4OCCTSolid::ClosestFaceMatch>
739G4OCCTSolid::TryFindClosestFace(
const std::vector<FaceBounds>& faceBoundsCache,
740 const G4ThreeVector& point, G4double maxDistance) {
741 if (faceBoundsCache.empty()) {
745 const gp_Pnt queryPoint = ToPoint(point);
746 const TopoDS_Vertex queryVertex = MakeVertex(point);
750 queryBox.Add(queryPoint);
752 std::optional<ClosestFaceMatch> bestMatch;
753 for (std::size_t i = 0; i < faceBoundsCache.size(); ++i) {
754 const FaceBounds& fb = faceBoundsCache[i];
758 const G4double threshold = bestMatch.has_value() ? bestMatch->distance : maxDistance;
759 if (threshold < kInfinity && fb.box.Distance(queryBox) > threshold) {
769 BRepExtrema_DistShapeShape distance(queryVertex, fb.face);
770 if (!distance.IsDone() || distance.NbSolution() == 0) {
773 const G4double candidateDistance = distance.Value();
775 if (bestMatch.has_value() && candidateDistance >= bestMatch->distance) {
778 ClosestFaceMatch match{.face = fb.face, .distance = candidateDistance, .faceIndex = i};
783 if (distance.NbSolution() > 0 && distance.SupportTypeShape2(1) == BRepExtrema_IsInFace) {
784 Standard_Real u = 0.0;
785 Standard_Real v = 0.0;
786 distance.ParOnFaceS2(1, u, v);
787 match.uv = std::make_pair(u, v);
789 bestMatch = std::move(match);
795BRepClass3d_SolidClassifier& G4OCCTSolid::GetOrCreateClassifier()
const {
796 ClassifierCache& cache = fClassifierCache.Get();
797 const std::uint64_t currentGen = fShapeGeneration.load(std::memory_order_acquire);
798 if (cache.generation != currentGen) {
799 cache.classifier.emplace();
800 cache.classifier->Load(fShape);
801 cache.generation = currentGen;
803 return *cache.classifier;
806G4OCCTSolid::IntersectorCache& G4OCCTSolid::GetOrCreateIntersector()
const {
807 IntersectorCache& cache = fIntersectorCache.Get();
808 const std::uint64_t currentGen = fShapeGeneration.load(std::memory_order_acquire);
809 if (cache.generation != currentGen) {
810 const G4double tol = IntersectionTolerance();
811 cache.faceIntersectors.clear();
812 cache.faceIntersectors.reserve(fFaceBoundsCache.size());
813 cache.expandedBoxes.clear();
814 cache.expandedBoxes.reserve(fFaceBoundsCache.size());
815 for (
const auto& fb : fFaceBoundsCache) {
816 cache.faceIntersectors.push_back(
817 std::make_unique<IntCurvesFace_Intersector>(fb.face, tol));
818 Bnd_Box expanded = fb.box;
819 expanded.Enlarge(tol);
820 cache.expandedBoxes.push_back(std::move(expanded));
822 cache.generation = currentGen;
827G4OCCTSolid::SphereCacheData& G4OCCTSolid::GetOrInitSphereCache()
const {
828 SphereCacheData& cache = fSphereCache.Get();
829 const std::uint64_t currentGen = fShapeGeneration.load(std::memory_order_acquire);
830 if (cache.generation != currentGen) {
832 cache.spheres = fInitialSpheres;
833 cache.generation = currentGen;
838void G4OCCTSolid::TryInsertSphere(
const G4ThreeVector& centre, G4double d)
const {
844 const G4double minRadius = IntersectionTolerance();
845 if (d <= minRadius) {
848 SphereCacheData& cache = GetOrInitSphereCache();
852 if (cache.spheres.size() >= kMaxInscribedSpheres && d <= cache.spheres.back().radius) {
858 for (
const InscribedSphere& s : cache.spheres) {
860 const G4double gap = s.radius - d;
861 if ((centre - s.centre).mag2() <= gap * gap) {
868 const InscribedSphere newSphere{centre, d};
869 const auto it = std::lower_bound(
870 cache.spheres.begin(), cache.spheres.end(), newSphere,
871 [](
const InscribedSphere& a,
const InscribedSphere& b) { return a.radius > b.radius; });
872 cache.spheres.insert(it, newSphere);
875 if (cache.spheres.size() > kMaxInscribedSpheres) {
876 cache.spheres.pop_back();
883 const G4double tolerance = IntersectionTolerance();
884 if (p.x() < fCachedBounds.min.x() - tolerance || p.x() > fCachedBounds.max.x() + tolerance ||
885 p.y() < fCachedBounds.min.y() - tolerance || p.y() > fCachedBounds.max.y() + tolerance ||
886 p.z() < fCachedBounds.min.z() - tolerance || p.z() > fCachedBounds.max.z() + tolerance) {
895 const SphereCacheData& sphereCache = GetOrInitSphereCache();
896 for (
const InscribedSphere& s : sphereCache.spheres) {
897 const G4double interiorRadius = s.radius - tolerance;
898 if (interiorRadius > 0.0 && (p - s.centre).mag2() < interiorRadius * interiorRadius) {
907 if (!fTriangleSet.IsNull() && fTriangleSet->Size() > 0) {
912 if (fBVHDeflection > 0.0) {
913 const G4double bvhLB = BVHLowerBoundDistance(p);
914 if (bvhLB < tolerance) {
915 BRepClass3d_SolidClassifier& classifier = GetOrCreateClassifier();
916 classifier.Perform(ToPoint(p), tolerance);
917 return ToG4Inside(classifier.State());
936 const BVH_Vec3d bvhOrigin(p.x(), p.y(), p.z());
937 const Standard_Real bvhTol =
static_cast<Standard_Real
>(tolerance);
938 TriangleRayCast caster;
939 caster.SetBVHSet(fTriangleSet.get());
942 caster.SetRay(bvhOrigin, BVH_Vec3d(0.0, 0.0, 1.0), bvhTol);
945 if (caster.OnSurface()) {
950 if (!caster.Degenerate() && caster.Crossings() > 0) {
951 return (caster.Crossings() % 2 == 1) ? kInside : kOutside;
957 int outsideVotes = 0;
958 if (!caster.Degenerate()) {
959 if (caster.Crossings() % 2 == 1) {
966 const BVH_Vec3d kExtraRays[2] = {
967 BVH_Vec3d(1.0, 0.0, 0.0),
968 BVH_Vec3d(0.0, 1.0, 0.0),
970 for (
const BVH_Vec3d& dir : kExtraRays) {
971 caster.SetRay(bvhOrigin, dir, bvhTol);
973 if (caster.OnSurface()) {
976 if (!caster.Degenerate()) {
977 if (caster.Crossings() % 2 == 1) {
985 if (insideVotes > outsideVotes) {
988 if (outsideVotes > insideVotes) {
994 BRepClass3d_SolidClassifier& classifier = GetOrCreateClassifier();
995 classifier.Perform(ToPoint(p), tolerance);
996 return ToG4Inside(classifier.State());
1006 IntersectorCache& cache = GetOrCreateIntersector();
1007 const gp_Lin ray(ToPoint(p), gp_Dir(0.0, 0.0, 1.0));
1009 bool onSurface =
false;
1010 bool degenerateRay =
false;
1012 for (std::size_t i = 0; i < fFaceBoundsCache.size(); ++i) {
1013 if (cache.expandedBoxes[i].IsOut(ray)) {
1016 const FaceBounds& fb = fFaceBoundsCache[i];
1017 if (!fb.uvPolygon.empty()) {
1022 Standard_Real u_hit = 0.0;
1023 Standard_Real v_hit = 0.0;
1024 const auto t = RayPlaneFaceHit(ray, *fb.plane, fb.uvPolygon, -tolerance,
1025 Precision::Infinite(), tolerance, &u_hit, &v_hit);
1027 const G4double w =
static_cast<G4double
>(*t);
1028 if (std::abs(w) <= tolerance) {
1030 }
else if (w > tolerance) {
1034 if (PointOnPolygonBoundary2d(u_hit, v_hit, fb.uvPolygon, tolerance)) {
1035 degenerateRay =
true;
1042 IntCurvesFace_Intersector& fi = *cache.faceIntersectors[i];
1043 fi.Perform(ray, -tolerance, Precision::Infinite());
1047 for (Standard_Integer j = 1; j <= fi.NbPnt(); ++j) {
1048 const G4double w = fi.WParameter(j);
1049 const TopAbs_State state = fi.State(j);
1050 if (std::abs(w) <= tolerance && (state == TopAbs_IN || state == TopAbs_ON)) {
1052 }
else if (w > tolerance && state == TopAbs_IN) {
1054 }
else if (w > tolerance && state == TopAbs_ON) {
1055 degenerateRay =
true;
1068 if (crossings == 0 || degenerateRay) {
1069 BRepClass3d_SolidClassifier& classifier = GetOrCreateClassifier();
1070 classifier.Perform(ToPoint(p), tolerance);
1071 return ToG4Inside(classifier.State());
1073 return (crossings % 2 == 1) ? kInside : kOutside;
1080 if (fAllFacesPlanar) {
1081 const gp_Pnt pt = ToPoint(p);
1082 const FaceBounds* bestFB =
nullptr;
1083 G4double bestDist = kInfinity;
1084 for (
const FaceBounds& fb : fFaceBoundsCache) {
1085 if (!fb.plane.has_value() || !fb.outwardNormal.has_value()) {
1088 const G4double d = fb.plane->Distance(pt);
1095 return *bestFB->outwardNormal;
1124 const auto projectAndGetNormalFallback = [&](
const FaceBounds& fb) -> G4ThreeVector {
1125 TopLoc_Location loc;
1126 const Handle(Geom_Surface) surface = BRep_Tool::Surface(fb.face, loc);
1127 if (surface.IsNull()) {
1128 return FallbackNormal();
1130 gp_Pnt pLocal = ToPoint(p);
1131 if (!loc.IsIdentity()) {
1132 pLocal.Transform(loc.Transformation().Inverted());
1134 GeomAPI_ProjectPointOnSurf projection(pLocal, surface);
1135 if (projection.NbPoints() == 0) {
1136 return FallbackNormal();
1138 Standard_Real u = 0.0;
1139 Standard_Real v = 0.0;
1140 projection.LowerDistanceParameters(u, v);
1141 return TryGetOutwardNormal(fb.adaptor, fb.face, u, v).value_or(FallbackNormal());
1144 const G4double bvhLB = BVHLowerBoundDistance(p);
1145 const G4double seedDist =
1146 (fBVHDeflection > 0.0 && bvhLB < kInfinity) ? bvhLB + 2.0 * fBVHDeflection : kInfinity;
1147 const auto closestFaceMatch = TryFindClosestFace(fFaceBoundsCache, p, seedDist);
1148 if (!closestFaceMatch.has_value()) {
1149 return FallbackNormal();
1151 const FaceBounds& fb = fFaceBoundsCache[closestFaceMatch->faceIndex];
1156 if (fb.outwardNormal.has_value()) {
1157 return *fb.outwardNormal;
1162 if (closestFaceMatch->uv.has_value()) {
1163 const auto [u, v] = *closestFaceMatch->uv;
1164 const auto result = TryGetOutwardNormal(fb.adaptor, fb.face, u, v);
1165 if (result.has_value()) {
1174 return projectAndGetNormalFallback(fb);
1178 const G4double tolerance = IntersectionTolerance();
1179 const gp_Lin ray(ToPoint(p), gp_Dir(v.x(), v.y(), v.z()));
1181 IntersectorCache& cache = GetOrCreateIntersector();
1183 G4double minDistance = kInfinity;
1184 for (std::size_t i = 0; i < fFaceBoundsCache.size(); ++i) {
1185 if (cache.expandedBoxes[i].IsOut(ray)) {
1188 const FaceBounds& fb = fFaceBoundsCache[i];
1189 if (!fb.uvPolygon.empty()) {
1191 const auto t = RayPlaneFaceHit(ray, *fb.plane, fb.uvPolygon, tolerance, Precision::Infinite(),
1193 if (t && *t < minDistance) {
1194 minDistance =
static_cast<G4double
>(*t);
1197 IntCurvesFace_Intersector& fi = *cache.faceIntersectors[i];
1198 fi.Perform(ray, tolerance, Precision::Infinite());
1202 for (Standard_Integer j = 1; j <= fi.NbPnt(); ++j) {
1203 const G4double w = fi.WParameter(j);
1204 if (w > tolerance && w < minDistance) {
1215 BRepClass3d_SolidClassifier& classifier = GetOrCreateClassifier();
1216 classifier.Perform(ToPoint(p), IntersectionTolerance());
1217 if (classifier.State() == TopAbs_IN || classifier.State() == TopAbs_ON) {
1221 const auto match = TryFindClosestFace(fFaceBoundsCache, p);
1222 if (!match.has_value()) {
1225 return (match->distance <= IntersectionTolerance()) ? 0.0 : match->distance;
1228G4double G4OCCTSolid::AABBLowerBound(
const G4ThreeVector& p)
const {
1229 const G4ThreeVector& mn = fCachedBounds.min;
1230 const G4ThreeVector& mx = fCachedBounds.max;
1231 const G4double dx = std::max({0.0, mn.x() - p.x(), p.x() - mx.x()});
1232 const G4double dy = std::max({0.0, mn.y() - p.y(), p.y() - mx.y()});
1233 const G4double dz = std::max({0.0, mn.z() - p.z(), p.z() - mx.z()});
1234 return std::sqrt(dx * dx + dy * dy + dz * dz);
1237G4double G4OCCTSolid::BVHLowerBoundDistance(
const G4ThreeVector& p)
const {
1238 if (fTriangleSet.IsNull() || fTriangleSet->Size() == 0) {
1241 PointToMeshDistance solver;
1242 solver.SetObject(BVH_Vec3d(p.x(), p.y(), p.z()));
1243 solver.SetBVHSet(fTriangleSet.get());
1244 const Standard_Real meshDistSq = solver.ComputeDistance();
1245 if (!solver.IsDone()) {
1250 const G4double meshDist = std::sqrt(
static_cast<G4double
>(meshDistSq));
1257 G4double deflection = fBVHDeflection;
1258 const Standard_Integer bestIdx = solver.BestIndex();
1260 const Standard_Integer faceId = fTriangleSet->GetFaceID(bestIdx);
1261 if (faceId >= 0 &&
static_cast<std::size_t
>(faceId) < fFaceDeflections.size()) {
1262 deflection = fFaceDeflections[
static_cast<std::size_t
>(faceId)];
1266 return std::max(0.0, meshDist - deflection);
1269G4double G4OCCTSolid::PlanarFaceLowerBoundDistance(
const G4ThreeVector& p)
const {
1270 const gp_Pnt pt = ToPoint(p);
1271 G4double minDist = kInfinity;
1272 for (
const FaceBounds& fb : fFaceBoundsCache) {
1273 if (!fb.plane.has_value()) {
1276 const G4double d =
static_cast<G4double
>(fb.plane->Distance(pt));
1288 const G4double aabbDist = AABBLowerBound(p);
1289 if (aabbDist > IntersectionTolerance()) {
1297 const G4double bvhDist = BVHLowerBoundDistance(p);
1298 if (bvhDist < kInfinity && bvhDist > IntersectionTolerance()) {
1308 const G4bool calcNorm, G4bool* validNorm,
1309 G4ThreeVector* n)
const {
1310 if (validNorm !=
nullptr) {
1314 const G4double tolerance = IntersectionTolerance();
1315 const gp_Lin ray(ToPoint(p), gp_Dir(v.x(), v.y(), v.z()));
1317 IntersectorCache& cache = GetOrCreateIntersector();
1319 G4double minDistance = kInfinity;
1320 std::size_t minFaceIdx = std::numeric_limits<std::size_t>::max();
1321 G4double minU = 0.0;
1322 G4double minV = 0.0;
1323 bool minIsIn =
false;
1324 bool minIsFastPath =
false;
1326 for (std::size_t i = 0; i < fFaceBoundsCache.size(); ++i) {
1327 if (cache.expandedBoxes[i].IsOut(ray)) {
1330 const FaceBounds& fb = fFaceBoundsCache[i];
1331 if (!fb.uvPolygon.empty()) {
1333 const auto t = RayPlaneFaceHit(ray, *fb.plane, fb.uvPolygon, tolerance, Precision::Infinite(),
1335 if (t && *t < minDistance) {
1336 minDistance =
static_cast<G4double
>(*t);
1339 minIsFastPath =
true;
1342 IntCurvesFace_Intersector& fi = *cache.faceIntersectors[i];
1343 fi.Perform(ray, tolerance, Precision::Infinite());
1347 for (Standard_Integer j = 1; j <= fi.NbPnt(); ++j) {
1348 const G4double w = fi.WParameter(j);
1349 if (w > tolerance && w < minDistance) {
1352 minU = fi.UParameter(j);
1353 minV = fi.VParameter(j);
1354 minIsIn = (fi.State(j) == TopAbs_IN || fi.State(j) == TopAbs_ON);
1355 minIsFastPath =
false;
1361 if (minFaceIdx == std::numeric_limits<std::size_t>::max() || minDistance == kInfinity) {
1365 if (calcNorm && validNorm !=
nullptr && n !=
nullptr && minIsIn) {
1366 const FaceBounds& fb = fFaceBoundsCache[minFaceIdx];
1367 if (minIsFastPath && fb.outwardNormal.has_value()) {
1369 *n = *fb.outwardNormal;
1372 const auto outNorm = TryGetOutwardNormal(fb.adaptor, fb.face, minU, minV);
1387 const auto match = TryFindClosestFace(fFaceBoundsCache, p);
1388 if (!match.has_value()) {
1391 return (match->distance <= IntersectionTolerance()) ? 0.0 : match->distance;
1396 if (fAllFacesPlanar) {
1400 d = PlanarFaceLowerBoundDistance(p);
1401 if (d == kInfinity) {
1408 const G4double bvhDist = BVHLowerBoundDistance(p);
1413 TryInsertSphere(p, d);
1418 std::unique_lock<std::mutex> lock(fVolumeAreaMutex);
1419 if (!fCachedVolume) {
1421 BRepGProp::VolumeProperties(fShape, props);
1422 fCachedVolume = props.Mass();
1424 return *fCachedVolume;
1428 std::unique_lock<std::mutex> lock(fVolumeAreaMutex);
1429 if (!fCachedSurfaceArea) {
1431 BRepGProp::SurfaceProperties(fShape, props);
1432 fCachedSurfaceArea = props.Mass();
1434 return *fCachedSurfaceArea;
1437const G4OCCTSolid::SurfaceSamplingCache& G4OCCTSolid::GetOrBuildSurfaceCache()
const {
1441 BRepMesh_IncrementalMesh mesher(fShape, kRelativeDeflection, Standard_True);
1444 std::unique_lock<std::mutex> lock(fSurfaceCacheMutex);
1446 const std::uint64_t currentGen = fShapeGeneration.load(std::memory_order_acquire);
1447 if (fSurfaceCache.has_value() && fSurfaceCacheGeneration == currentGen) {
1448 return *fSurfaceCache;
1454 SurfaceSamplingCache cache;
1456 for (TopExp_Explorer ex(fShape, TopAbs_FACE); ex.More(); ex.Next()) {
1457 const TopoDS_Face& face = TopoDS::Face(ex.Current());
1458 TopLoc_Location loc;
1459 const Handle(Poly_Triangulation) & triangulation = BRep_Tool::Triangulation(face, loc);
1460 if (triangulation.IsNull()) {
1465 const auto faceIndex =
static_cast<std::uint32_t
>(cache.faces.size());
1466 cache.faces.push_back(face);
1468 const gp_Trsf& transform = loc.Transformation();
1469 const bool reverseWinding = face.Orientation() == TopAbs_REVERSED;
1471 for (Standard_Integer i = 1; i <= triangulation->NbTriangles(); ++i) {
1472 Standard_Integer idx1 = 0;
1473 Standard_Integer idx2 = 0;
1474 Standard_Integer idx3 = 0;
1475 triangulation->Triangle(i).Get(idx1, idx2, idx3);
1476 if (reverseWinding) {
1477 std::swap(idx2, idx3);
1480 const gp_Pnt q1 = triangulation->Node(idx1).Transformed(transform);
1481 const gp_Pnt q2 = triangulation->Node(idx2).Transformed(transform);
1482 const gp_Pnt q3 = triangulation->Node(idx3).Transformed(transform);
1484 const G4ThreeVector v1(q1.X(), q1.Y(), q1.Z());
1485 const G4ThreeVector v2(q2.X(), q2.Y(), q2.Z());
1486 const G4ThreeVector v3(q3.X(), q3.Y(), q3.Z());
1488 const G4double area = 0.5 * (v2 - v1).cross(v3 - v1).mag();
1490 cache.totalArea += area;
1491 cache.cumulativeAreas.push_back(cache.totalArea);
1492 cache.triangles.push_back({v1, v2, v3, faceIndex});
1497 fSurfaceCache = std::move(cache);
1498 fSurfaceCacheGeneration = currentGen;
1499 return *fSurfaceCache;
1503 const SurfaceSamplingCache& cache = GetOrBuildSurfaceCache();
1505 if (cache.triangles.empty() || cache.totalArea == 0.0) {
1506 G4ExceptionDescription msg;
1507 msg <<
"Tessellation of solid \"" << GetName()
1508 <<
"\" produced no valid triangles; cannot sample a point on the surface.";
1509 G4Exception(
"G4OCCTSolid::GetPointOnSurface",
"GeomMgt1001", FatalException, msg);
1510 return {0.0, 0.0, 0.0};
1515 const G4double target = G4UniformRand() * cache.totalArea;
1516 const auto it = std::ranges::lower_bound(cache.cumulativeAreas, target);
1517 const std::size_t idx = std::min(
static_cast<std::size_t
>(it - cache.cumulativeAreas.begin()),
1518 cache.triangles.size() - 1);
1519 const SurfaceTriangle& chosen = cache.triangles[idx];
1524 G4double r1 = G4UniformRand();
1525 G4double r2 = G4UniformRand();
1526 if (r1 + r2 > 1.0) {
1531 const G4ThreeVector tessPoint =
1532 chosen.p1 + r1 * (chosen.p2 - chosen.p1) + r2 * (chosen.p3 - chosen.p1);
1538 const TopoDS_Face& face = cache.faces[chosen.faceIndex];
1539 TopLoc_Location loc;
1540 const Handle(Geom_Surface) geomSurface = BRep_Tool::Surface(face, loc);
1541 if (!geomSurface.IsNull()) {
1542 gp_Pnt tessPointLocal(tessPoint.x(), tessPoint.y(), tessPoint.z());
1543 if (!loc.IsIdentity()) {
1544 tessPointLocal.Transform(loc.Transformation().Inverted());
1546 GeomAPI_ProjectPointOnSurf projection(tessPointLocal, geomSurface);
1547 if (projection.NbPoints() > 0) {
1548 gp_Pnt projectedPoint = projection.NearestPoint();
1549 if (!loc.IsIdentity()) {
1550 projectedPoint.Transform(loc.Transformation());
1552 return {projectedPoint.X(), projectedPoint.Y(), projectedPoint.Z()};
1562 return {fCachedBounds.min.x(), fCachedBounds.max.x(), fCachedBounds.min.y(),
1563 fCachedBounds.max.y(), fCachedBounds.min.z(), fCachedBounds.max.z()};
1567 pMin = fCachedBounds.min;
1568 pMax = fCachedBounds.max;
1572 const G4AffineTransform& pTransform, G4double& pMin,
1573 G4double& pMax)
const {
1574 const G4BoundingEnvelope envelope(fCachedBounds.min, fCachedBounds.max);
1575 return envelope.CalculateExtent(pAxis, pVoxelLimit, G4Transform3D(pTransform), pMin, pMax);
1581 const auto currentGeneration = fShapeGeneration.load(std::memory_order_acquire);
1584 std::unique_lock<std::mutex> lock(fPolyhedronMutex);
1588 fPolyhedronCV.wait(lock, [
this, currentGeneration] {
1589 return !fPolyhedronBuilding || fPolyhedronGeneration == currentGeneration;
1593 if (fCachedPolyhedron && fPolyhedronGeneration == currentGeneration) {
1594 return new G4Polyhedron(*fCachedPolyhedron);
1598 fPolyhedronBuilding =
true;
1606 BRepMesh_IncrementalMesh mesher(fShape, kRelativeDeflection, Standard_True);
1609 G4TessellatedSolid tessellatedSolid(GetName() +
"_polyhedron");
1610 G4int facetCount = 0;
1612 for (TopExp_Explorer explorer(fShape, TopAbs_FACE); explorer.More(); explorer.Next()) {
1613 const TopoDS_Face& face = TopoDS::Face(explorer.Current());
1614 TopLoc_Location location;
1615 const Handle(Poly_Triangulation) & triangulation = BRep_Tool::Triangulation(face, location);
1616 if (triangulation.IsNull()) {
1620 const gp_Trsf& transform = location.Transformation();
1621 const bool reverseWinding = face.Orientation() == TopAbs_REVERSED;
1623 for (Standard_Integer triangleIndex = 1; triangleIndex <= triangulation->NbTriangles();
1625 Standard_Integer index1 = 0;
1626 Standard_Integer index2 = 0;
1627 Standard_Integer index3 = 0;
1628 triangulation->Triangle(triangleIndex).Get(index1, index2, index3);
1630 if (reverseWinding) {
1631 std::swap(index2, index3);
1634 const gp_Pnt point1 = triangulation->Node(index1).Transformed(transform);
1635 const gp_Pnt point2 = triangulation->Node(index2).Transformed(transform);
1636 const gp_Pnt point3 = triangulation->Node(index3).Transformed(transform);
1639 new G4TriangularFacet(G4ThreeVector(point1.X(), point1.Y(), point1.Z()),
1640 G4ThreeVector(point2.X(), point2.Y(), point2.Z()),
1641 G4ThreeVector(point3.X(), point3.Y(), point3.Z()), ABSOLUTE);
1642 if (!facet->IsDefined() || !tessellatedSolid.AddFacet(facet)) {
1653 std::unique_ptr<G4Polyhedron> freshPolyhedron;
1654 if (facetCount > 0) {
1655 tessellatedSolid.SetSolidClosed(
true);
1656 G4Polyhedron* tmp = tessellatedSolid.GetPolyhedron();
1657 if (tmp !=
nullptr) {
1658 freshPolyhedron = std::make_unique<G4Polyhedron>(*tmp);
1664 std::unique_lock<std::mutex> lock(fPolyhedronMutex);
1669 bool cacheWritten =
false;
1670 if (freshPolyhedron && fShapeGeneration.load(std::memory_order_acquire) == currentGeneration) {
1671 fCachedPolyhedron = std::make_unique<G4Polyhedron>(*freshPolyhedron);
1672 fPolyhedronGeneration = currentGeneration;
1673 cacheWritten =
true;
1678 fPolyhedronBuilding =
false;
1679 fPolyhedronCV.notify_all();
1684 return new G4Polyhedron(*fCachedPolyhedron);
1688 return freshPolyhedron ?
new G4Polyhedron(*freshPolyhedron) :
nullptr;
1692 os <<
"-----------------------------------------------------------\n"
1693 <<
" *** Dump for solid - " << GetName() <<
" ***\n"
1694 <<
" ===================================================\n"
1695 <<
" Solid type: G4OCCTSolid\n"
1696 <<
" OCCT shape type: " << fShape.ShapeType() <<
"\n"
1697 <<
"-----------------------------------------------------------\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