src/G4OCCTSolidKernel.cc
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: LGPL-2.1-or-later | ||
| 2 | // Copyright (C) 2026 G4OCCT Contributors | ||
| 3 | |||
| 4 | #include "G4OCCTSolidKernel.hh" | ||
| 5 | |||
| 6 | #include <BRepAdaptor_Curve.hxx> | ||
| 7 | #include <BRepBndLib.hxx> | ||
| 8 | #include <BRepExtrema_DistShapeShape.hxx> | ||
| 9 | #include <BRepExtrema_SupportType.hxx> | ||
| 10 | #include <BRepGProp.hxx> | ||
| 11 | #include <BRepLib.hxx> | ||
| 12 | #include <BRepLProp_SLProps.hxx> | ||
| 13 | #include <BRepMesh_IncrementalMesh.hxx> | ||
| 14 | #include <BRepTools.hxx> | ||
| 15 | #include <BRepTools_WireExplorer.hxx> | ||
| 16 | #include <BRep_Builder.hxx> | ||
| 17 | #include <BRep_Tool.hxx> | ||
| 18 | #include <BVH_Distance.hxx> | ||
| 19 | #include <BVH_Tools.hxx> | ||
| 20 | #include <ElSLib.hxx> | ||
| 21 | #include <GProp_GProps.hxx> | ||
| 22 | #include <GeomAPI_ProjectPointOnSurf.hxx> | ||
| 23 | #include <GeomAbs_CurveType.hxx> | ||
| 24 | #include <GeomAbs_SurfaceType.hxx> | ||
| 25 | #include <Geom_Surface.hxx> | ||
| 26 | #include <NCollection_Vector.hxx> | ||
| 27 | #include <Poly_Triangulation.hxx> | ||
| 28 | #include <Precision.hxx> | ||
| 29 | #include <TopAbs_Orientation.hxx> | ||
| 30 | #include <TopAbs_ShapeEnum.hxx> | ||
| 31 | #include <TopAbs_State.hxx> | ||
| 32 | #include <TopExp_Explorer.hxx> | ||
| 33 | #include <TopLoc_Location.hxx> | ||
| 34 | #include <TopoDS.hxx> | ||
| 35 | #include <TopoDS_Edge.hxx> | ||
| 36 | #include <TopoDS_Vertex.hxx> | ||
| 37 | #include <TopoDS_Wire.hxx> | ||
| 38 | #include <gp_Dir.hxx> | ||
| 39 | #include <gp_Lin.hxx> | ||
| 40 | #include <gp_Pln.hxx> | ||
| 41 | #include <gp_Pnt.hxx> | ||
| 42 | #include <gp_Pnt2d.hxx> | ||
| 43 | #include <gp_Vec.hxx> | ||
| 44 | |||
| 45 | #include <G4Exception.hh> | ||
| 46 | #include <G4GeometryTolerance.hh> | ||
| 47 | #include <Randomize.hh> | ||
| 48 | |||
| 49 | #include <algorithm> | ||
| 50 | #include <cmath> | ||
| 51 | #include <optional> | ||
| 52 | #include <ranges> | ||
| 53 | #include <stdexcept> | ||
| 54 | |||
| 55 | namespace { | ||
| 56 | |||
| 57 | 1061106 | class PointToMeshDistance | |
| 58 | : public BVH_Distance<Standard_Real, 3, BVH_Vec3d, BRepExtrema_TriangleSet> { | ||
| 59 | public: | ||
| 60 | 126514032 | Standard_Boolean RejectNode(const BVH_Vec3d& theCornerMin, const BVH_Vec3d& theCornerMax, | |
| 61 | Standard_Real& theMetric) const override { | ||
| 62 | 126514032 | theMetric = | |
| 63 | 126514032 | BVH_Tools<Standard_Real, 3>::PointBoxSquareDistance(myObject, theCornerMin, theCornerMax); | |
| 64 | 126514032 | return RejectMetric(theMetric); | |
| 65 | } | ||
| 66 | |||
| 67 | 75165751 | Standard_Boolean Accept(const Standard_Integer theIndex, const Standard_Real&) override { | |
| 68 | 75165751 | BVH_Vec3d v0, v1, v2; | |
| 69 | 75165751 | myBVHSet->GetVertices(theIndex, v0, v1, v2); | |
| 70 | 75165751 | const Standard_Real sq = | |
| 71 | 75165751 | BVH_Tools<Standard_Real, 3>::PointTriangleSquareDistance(myObject, v0, v1, v2); | |
| 72 |
2/2✓ Branch 0 taken 3987732 times.
✓ Branch 1 taken 71178019 times.
|
75165751 | if (sq < myDistance) { |
| 73 | 3987732 | myDistance = sq; | |
| 74 | 3987732 | myBestIndex = theIndex; | |
| 75 | 3987732 | return Standard_True; | |
| 76 | } | ||
| 77 | 71178019 | return Standard_False; | |
| 78 | 75165751 | } | |
| 79 | |||
| 80 | 1061106 | Standard_Integer BestIndex() const { return myBestIndex; } | |
| 81 | |||
| 82 | private: | ||
| 83 | 1061106 | Standard_Integer myBestIndex{-1}; | |
| 84 | }; | ||
| 85 | |||
| 86 |
1/2✓ Branch 0 taken 661838 times.
✗ Branch 1 not taken.
|
661838 | class TriangleRayCast |
| 87 | : public BVH_Traverse<Standard_Real, 3, BRepExtrema_TriangleSet, Standard_Real> { | ||
| 88 | public: | ||
| 89 | 1555354 | void SetRay(const BVH_Vec3d& theOrigin, const BVH_Vec3d& theDir, Standard_Real theTolerance) { | |
| 90 | 1555354 | myOrigin = theOrigin; | |
| 91 | 1555354 | myDir = theDir; | |
| 92 | 1555354 | myTolerance = theTolerance; | |
| 93 | 1555354 | myCrossings = 0; | |
| 94 | 1555354 | myDegenerate = Standard_False; | |
| 95 | 1555354 | } | |
| 96 | |||
| 97 | 65033096 | Standard_Boolean RejectNode(const BVH_Vec3d& theCornerMin, const BVH_Vec3d& theCornerMax, | |
| 98 | Standard_Real& theMetric) const override { | ||
| 99 | 65033096 | Standard_Real tmin = 0.0; | |
| 100 | 65033096 | Standard_Real tmax = Precision::Infinite(); | |
| 101 |
2/2✓ Branch 0 taken 34749661 times.
✓ Branch 1 taken 158822939 times.
|
193572600 | for (int k = 0; k < 3; ++k) { |
| 102 |
4/4✓ Branch 0 taken 93789843 times.
✓ Branch 1 taken 65033096 times.
✓ Branch 2 taken 51806538 times.
✓ Branch 3 taken 41983305 times.
|
158822939 | const Standard_Real dk = (k == 0) ? myDir.x() : (k == 1) ? myDir.y() : myDir.z(); |
| 103 |
4/4✓ Branch 0 taken 93789843 times.
✓ Branch 1 taken 65033096 times.
✓ Branch 2 taken 51806538 times.
✓ Branch 3 taken 41983305 times.
|
158822939 | const Standard_Real ok = (k == 0) ? myOrigin.x() : (k == 1) ? myOrigin.y() : myOrigin.z(); |
| 104 |
2/2✓ Branch 0 taken 93789843 times.
✓ Branch 1 taken 65033096 times.
|
252612782 | const Standard_Real ck_min = (k == 0) ? theCornerMin.x() |
| 105 |
2/2✓ Branch 0 taken 51806538 times.
✓ Branch 1 taken 41983305 times.
|
93789843 | : (k == 1) ? theCornerMin.y() |
| 106 | 41983305 | : theCornerMin.z(); | |
| 107 |
2/2✓ Branch 0 taken 93789843 times.
✓ Branch 1 taken 65033096 times.
|
252612782 | const Standard_Real ck_max = (k == 0) ? theCornerMax.x() |
| 108 |
2/2✓ Branch 0 taken 51806538 times.
✓ Branch 1 taken 41983305 times.
|
93789843 | : (k == 1) ? theCornerMax.y() |
| 109 | 41983305 | : theCornerMax.z(); | |
| 110 |
2/2✓ Branch 0 taken 111017370 times.
✓ Branch 1 taken 47805569 times.
|
158822939 | if (std::abs(dk) < Precision::Confusion()) { |
| 111 |
4/4✓ Branch 0 taken 97116048 times.
✓ Branch 1 taken 13901322 times.
✓ Branch 2 taken 12832953 times.
✓ Branch 3 taken 84283095 times.
|
111017370 | if (ok < ck_min - myTolerance || ok > ck_max + myTolerance) { |
| 112 | 26734275 | return Standard_True; | |
| 113 | } | ||
| 114 | 84283095 | } else { | |
| 115 | 47805569 | const Standard_Real t1 = (ck_min - ok) / dk; | |
| 116 | 47805569 | const Standard_Real t2 = (ck_max - ok) / dk; | |
| 117 |
2/2✓ Branch 0 taken 26443326 times.
✓ Branch 1 taken 21362243 times.
|
47805569 | if (t1 > tmin) { |
| 118 | 21362243 | tmin = t1; | |
| 119 | 21362243 | } | |
| 120 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 47805569 times.
|
47805569 | if (t2 < tmax) { |
| 121 | 47805569 | tmax = t2; | |
| 122 | 47805569 | } | |
| 123 |
2/2✓ Branch 0 taken 44256409 times.
✓ Branch 1 taken 3549160 times.
|
47805569 | if (tmin > tmax + myTolerance) { |
| 124 | 3549160 | return Standard_True; | |
| 125 | } | ||
| 126 | } | ||
| 127 | 128539504 | } | |
| 128 | 34749661 | theMetric = tmin; | |
| 129 | 34749661 | return Standard_False; | |
| 130 | 65033096 | } | |
| 131 | |||
| 132 | 12576058 | Standard_Boolean Accept(const Standard_Integer theIndex, const Standard_Real&) override { | |
| 133 | 12576058 | BVH_Vec3d v0, v1, v2; | |
| 134 | 12576058 | myBVHSet->GetVertices(theIndex, v0, v1, v2); | |
| 135 | 12576058 | const BVH_Vec3d edge1 = v1 - v0; | |
| 136 | 12576058 | const BVH_Vec3d edge2 = v2 - v0; | |
| 137 | 12576058 | const BVH_Vec3d h = BVH_Vec3d::Cross(myDir, edge2); | |
| 138 | 12576058 | const Standard_Real a = edge1.Dot(h); | |
| 139 |
2/2✓ Branch 0 taken 1279600 times.
✓ Branch 1 taken 11296458 times.
|
12576058 | if (std::abs(a) < 1e-12) { |
| 140 | 1279600 | return Standard_False; | |
| 141 | } | ||
| 142 | 11296458 | const Standard_Real f = 1.0 / a; | |
| 143 | 11296458 | const BVH_Vec3d s = myOrigin - v0; | |
| 144 | 11296458 | const Standard_Real u = f * s.Dot(h); | |
| 145 |
4/4✓ Branch 0 taken 7281318 times.
✓ Branch 1 taken 4015140 times.
✓ Branch 2 taken 2126182 times.
✓ Branch 3 taken 5155136 times.
|
11296458 | if (u < 0.0 || u > 1.0) { |
| 146 | 6141322 | return Standard_False; | |
| 147 | } | ||
| 148 | 5155136 | const BVH_Vec3d q = BVH_Vec3d::Cross(s, edge1); | |
| 149 | 5155136 | const Standard_Real v = f * myDir.Dot(q); | |
| 150 |
4/4✓ Branch 0 taken 4060874 times.
✓ Branch 1 taken 1094262 times.
✓ Branch 2 taken 1592381 times.
✓ Branch 3 taken 2468493 times.
|
5155136 | if (v < 0.0 || u + v > 1.0) { |
| 151 | 2686643 | return Standard_False; | |
| 152 | } | ||
| 153 | 2468493 | const Standard_Real t = f * edge2.Dot(q); | |
| 154 |
2/2✓ Branch 0 taken 232868 times.
✓ Branch 1 taken 2235625 times.
|
2468493 | if (t < -myTolerance) { |
| 155 | 232868 | return Standard_False; | |
| 156 | } | ||
| 157 | 2235625 | ++myCrossings; | |
| 158 | 2235625 | constexpr Standard_Real kEdgeTol = 1e-6; | |
| 159 |
6/6✓ Branch 0 taken 1774804 times.
✓ Branch 1 taken 460821 times.
✓ Branch 2 taken 1083576 times.
✓ Branch 3 taken 691228 times.
✓ Branch 4 taken 57607 times.
✓ Branch 5 taken 1025969 times.
|
2235625 | if (u < kEdgeTol || v < kEdgeTol || (1.0 - u - v) < kEdgeTol) { |
| 160 | 1209656 | myDegenerate = Standard_True; | |
| 161 | 1209656 | } | |
| 162 | 2235625 | return Standard_True; | |
| 163 | 12576058 | } | |
| 164 | |||
| 165 | 8256916 | Standard_Boolean RejectMetric(const Standard_Real&) const override { return Standard_False; } | |
| 166 | 77609154 | Standard_Boolean Stop() const override { return Standard_False; } | |
| 167 | |||
| 168 | 1756364 | int Crossings() const { return myCrossings; } | |
| 169 | 2002112 | Standard_Boolean Degenerate() const { return myDegenerate; } | |
| 170 | |||
| 171 | private: | ||
| 172 | BVH_Vec3d myOrigin; | ||
| 173 | BVH_Vec3d myDir; | ||
| 174 | 661838 | Standard_Real myTolerance{1e-7}; | |
| 175 | 661838 | int myCrossings{0}; | |
| 176 | 661838 | Standard_Boolean myDegenerate{Standard_False}; | |
| 177 | }; | ||
| 178 | |||
| 179 | 3803259 | gp_Pnt ToPoint(const G4ThreeVector& point) { return gp_Pnt(point.x(), point.y(), point.z()); } | |
| 180 | |||
| 181 | 11099016 | G4double IntersectionTolerance() { | |
| 182 | 11099016 | return 0.5 * G4GeometryTolerance::GetInstance()->GetSurfaceTolerance(); | |
| 183 | } | ||
| 184 | |||
| 185 | 339042 | TopoDS_Vertex MakeVertex(const G4ThreeVector& point) { | |
| 186 | BRep_Builder builder; | ||
| 187 | 339042 | TopoDS_Vertex vertex; | |
| 188 |
3/6✓ Branch 0 taken 339042 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 339042 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 339042 times.
✗ Branch 5 not taken.
|
339042 | builder.MakeVertex(vertex, ToPoint(point), IntersectionTolerance()); |
| 189 | 339042 | return vertex; | |
| 190 | 339042 | } | |
| 191 | |||
| 192 | g4occt::detail::G4OCCTSolidKernel::PointClassification | ||
| 193 | 139398 | ToPointClassification(const TopAbs_State state) { | |
| 194 | using PointClassification = g4occt::detail::G4OCCTSolidKernel::PointClassification; | ||
| 195 |
3/4✓ Branch 0 taken 235 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17639 times.
✓ Branch 3 taken 121524 times.
|
139398 | switch (state) { |
| 196 | case TopAbs_IN: | ||
| 197 | 17639 | return PointClassification::kInside; | |
| 198 | case TopAbs_ON: | ||
| 199 | 235 | return PointClassification::kSurface; | |
| 200 | 121524 | case TopAbs_OUT: | |
| 201 | default: | ||
| 202 | 121524 | return PointClassification::kOutside; | |
| 203 | } | ||
| 204 | 139398 | } | |
| 205 | |||
| 206 | 34 | G4ThreeVector FallbackNormal() { return G4ThreeVector(0.0, 0.0, 1.0); } | |
| 207 | |||
| 208 | 673170 | bool PointInPolygon2d(Standard_Real u, Standard_Real v, const std::vector<gp_Pnt2d>& poly) { | |
| 209 | 673170 | const std::size_t n = poly.size(); | |
| 210 | 673170 | int crossings = 0; | |
| 211 |
2/2✓ Branch 0 taken 2827510 times.
✓ Branch 1 taken 490622 times.
|
3318132 | for (std::size_t i = 0; i < n; ++i) { |
| 212 | 2827510 | const gp_Pnt2d& a = poly[i]; | |
| 213 | 2827510 | const gp_Pnt2d& b = poly[(i + 1) % n]; | |
| 214 | 2827510 | const Standard_Real av = a.Y(); | |
| 215 | 2827510 | const Standard_Real bv = b.Y(); | |
| 216 |
6/6✓ Branch 0 taken 1365902 times.
✓ Branch 1 taken 1461608 times.
✓ Branch 2 taken 91274 times.
✓ Branch 3 taken 1274628 times.
✓ Branch 4 taken 1365902 times.
✓ Branch 5 taken 1370334 times.
|
2827510 | if ((av <= v && bv > v) || (bv <= v && av > v)) { |
| 217 | 1457176 | const Standard_Real uCross = a.X() + (v - av) * (b.X() - a.X()) / (bv - av); | |
| 218 |
2/2✓ Branch 0 taken 671247 times.
✓ Branch 1 taken 603381 times.
|
1457176 | if (u < uCross) { |
| 219 | 603381 | ++crossings; | |
| 220 | 603381 | } | |
| 221 | 1274628 | } | |
| 222 | 2644962 | } | |
| 223 | 490622 | return (crossings % 2) == 1; | |
| 224 | } | ||
| 225 | |||
| 226 | 133211 | bool PointOnPolygonBoundary2d(Standard_Real u, Standard_Real v, const std::vector<gp_Pnt2d>& poly, | |
| 227 | Standard_Real tol) { | ||
| 228 | 133211 | const Standard_Real tol2 = tol * tol; | |
| 229 | 133211 | const std::size_t n = poly.size(); | |
| 230 |
2/2✓ Branch 0 taken 490814 times.
✓ Branch 1 taken 133192 times.
|
624006 | for (std::size_t i = 0; i < n; ++i) { |
| 231 | 490814 | const gp_Pnt2d& a = poly[i]; | |
| 232 | 490814 | const gp_Pnt2d& b = poly[(i + 1) % n]; | |
| 233 | 490814 | const Standard_Real dx = b.X() - a.X(); | |
| 234 | 490814 | const Standard_Real dy = b.Y() - a.Y(); | |
| 235 | 490814 | const Standard_Real len2 = dx * dx + dy * dy; | |
| 236 | 490814 | Standard_Real px = 0.0; | |
| 237 | 490814 | Standard_Real py = 0.0; | |
| 238 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 490814 times.
|
490814 | if (len2 < 1.0e-20) { |
| 239 | ✗ | px = a.X(); | |
| 240 | ✗ | py = a.Y(); | |
| 241 | ✗ | } else { | |
| 242 | 490814 | const Standard_Real t_seg = | |
| 243 | 490814 | std::max(0.0, std::min(1.0, ((u - a.X()) * dx + (v - a.Y()) * dy) / len2)); | |
| 244 | 490814 | px = a.X() + t_seg * dx; | |
| 245 | 490814 | py = a.Y() + t_seg * dy; | |
| 246 | } | ||
| 247 | 490814 | const Standard_Real dist2 = (u - px) * (u - px) + (v - py) * (v - py); | |
| 248 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 490795 times.
|
490814 | if (dist2 <= tol2) { |
| 249 | 19 | return true; | |
| 250 | } | ||
| 251 | 490795 | } | |
| 252 | 133192 | return false; | |
| 253 | 133211 | } | |
| 254 | |||
| 255 | 738860 | std::optional<Standard_Real> RayPlaneFaceHit(const gp_Lin& ray, const gp_Pln& plane, | |
| 256 | const std::vector<gp_Pnt2d>& uvPoly, | ||
| 257 | Standard_Real tMin, Standard_Real tolerance, | ||
| 258 | Standard_Real* u_out = nullptr, | ||
| 259 | Standard_Real* v_out = nullptr) { | ||
| 260 | 738860 | const gp_Dir& lineDir = ray.Direction(); | |
| 261 | 738860 | const gp_Dir& plnNormal = plane.Axis().Direction(); | |
| 262 | 738860 | const Standard_Real denom = | |
| 263 | 738860 | plnNormal.X() * lineDir.X() + plnNormal.Y() * lineDir.Y() + plnNormal.Z() * lineDir.Z(); | |
| 264 |
2/2✓ Branch 0 taken 5020 times.
✓ Branch 1 taken 733840 times.
|
738860 | if (std::abs(denom) < 1.0e-10) { |
| 265 | 5020 | return std::nullopt; | |
| 266 | } | ||
| 267 | 733840 | const gp_Pnt& orig = ray.Location(); | |
| 268 | 733840 | const gp_Pnt& planePt = plane.Location(); | |
| 269 | 2201520 | const Standard_Real numer = plnNormal.X() * (planePt.X() - orig.X()) + | |
| 270 | 1467680 | plnNormal.Y() * (planePt.Y() - orig.Y()) + | |
| 271 | 733840 | plnNormal.Z() * (planePt.Z() - orig.Z()); | |
| 272 | 733840 | const Standard_Real t = numer / denom; | |
| 273 |
2/2✓ Branch 0 taken 60670 times.
✓ Branch 1 taken 673170 times.
|
733840 | if (t < tMin) { |
| 274 | 60670 | return std::nullopt; | |
| 275 | } | ||
| 276 | 1346340 | const gp_Pnt hitPt(orig.X() + t * lineDir.X(), orig.Y() + t * lineDir.Y(), | |
| 277 | 673170 | orig.Z() + t * lineDir.Z()); | |
| 278 | 673170 | Standard_Real u = 0.0; | |
| 279 | 673170 | Standard_Real v = 0.0; | |
| 280 | 673170 | ElSLib::PlaneParameters(plane.Position(), hitPt, u, v); | |
| 281 |
4/4✓ Branch 0 taken 133211 times.
✓ Branch 1 taken 539959 times.
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 133192 times.
|
673170 | if (!PointInPolygon2d(u, v, uvPoly) && !PointOnPolygonBoundary2d(u, v, uvPoly, tolerance)) { |
| 282 | 133192 | return std::nullopt; | |
| 283 | } | ||
| 284 |
1/2✓ Branch 0 taken 539978 times.
✗ Branch 1 not taken.
|
539978 | if (u_out != nullptr) { |
| 285 | ✗ | *u_out = u; | |
| 286 | ✗ | } | |
| 287 |
1/2✓ Branch 0 taken 539978 times.
✗ Branch 1 not taken.
|
539978 | if (v_out != nullptr) { |
| 288 | ✗ | *v_out = v; | |
| 289 | ✗ | } | |
| 290 | 539978 | return t; | |
| 291 | 738860 | } | |
| 292 | |||
| 293 | 297435 | std::optional<G4ThreeVector> TryGetOutwardNormal(const BRepAdaptor_Surface& surface, | |
| 294 | const TopoDS_Face& face, const Standard_Real u, | ||
| 295 | const Standard_Real v) { | ||
| 296 | 297435 | Standard_Real adjustedU = u; | |
| 297 | 297435 | Standard_Real adjustedV = v; | |
| 298 | 297435 | const Standard_Real tolerance = IntersectionTolerance(); | |
| 299 | { | ||
| 300 | 297435 | const Standard_Real uFirst = surface.FirstUParameter(); | |
| 301 | 297435 | const Standard_Real uLast = surface.LastUParameter(); | |
| 302 | 297435 | const Standard_Real uEpsilon = std::min( | |
| 303 | 297435 | std::max(surface.UResolution(tolerance), Precision::PConfusion()), 0.5 * (uLast - uFirst)); | |
| 304 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 297395 times.
|
297435 | if (std::abs(adjustedU - uFirst) <= uEpsilon) { |
| 305 | 40 | adjustedU = std::min(uFirst + uEpsilon, uLast); | |
| 306 |
1/2✓ Branch 0 taken 297395 times.
✗ Branch 1 not taken.
|
297435 | } else if (std::abs(adjustedU - uLast) <= uEpsilon) { |
| 307 | ✗ | adjustedU = std::max(uLast - uEpsilon, uFirst); | |
| 308 | ✗ | } | |
| 309 | } | ||
| 310 | { | ||
| 311 | 297435 | const Standard_Real vFirst = surface.FirstVParameter(); | |
| 312 | 297435 | const Standard_Real vLast = surface.LastVParameter(); | |
| 313 | 297435 | const Standard_Real vEpsilon = std::min( | |
| 314 | 297435 | std::max(surface.VResolution(tolerance), Precision::PConfusion()), 0.5 * (vLast - vFirst)); | |
| 315 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 297426 times.
|
297435 | if (std::abs(adjustedV - vFirst) <= vEpsilon) { |
| 316 | 9 | adjustedV = std::min(vFirst + vEpsilon, vLast); | |
| 317 |
2/2✓ Branch 0 taken 297416 times.
✓ Branch 1 taken 10 times.
|
297435 | } else if (std::abs(adjustedV - vLast) <= vEpsilon) { |
| 318 | 10 | adjustedV = std::max(vLast - vEpsilon, vFirst); | |
| 319 | 10 | } | |
| 320 | } | ||
| 321 | |||
| 322 | 297435 | BRepLProp_SLProps props(surface, adjustedU, adjustedV, 1, tolerance); | |
| 323 |
2/4✓ Branch 0 taken 297435 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 297435 times.
|
297435 | if (!props.IsNormalDefined()) { |
| 324 | ✗ | const Standard_Real vFirst = surface.FirstVParameter(); | |
| 325 | ✗ | const Standard_Real vLast = surface.LastVParameter(); | |
| 326 | ✗ | const Standard_Real vMid = 0.5 * (vFirst + vLast); | |
| 327 | ✗ | const bool nearVFirst = (adjustedV < vMid); | |
| 328 | ✗ | const Standard_Real vRes = std::max(surface.VResolution(tolerance), Precision::PConfusion()); | |
| 329 | ✗ | Standard_Real finalRetryV = adjustedV; | |
| 330 | ✗ | for (int attempt = 0; attempt < 8 && !props.IsNormalDefined(); ++attempt) { | |
| 331 | ✗ | const Standard_Real scale = std::pow(10.0, static_cast<Standard_Real>(attempt)); | |
| 332 | ✗ | const Standard_Real nudge = scale * vRes; | |
| 333 | ✗ | finalRetryV = | |
| 334 | ✗ | nearVFirst ? std::min(adjustedV + nudge, vMid) : std::max(adjustedV - nudge, vMid); | |
| 335 | ✗ | props = BRepLProp_SLProps(surface, adjustedU, finalRetryV, 1, tolerance); | |
| 336 | ✗ | } | |
| 337 | ✗ | if (!props.IsNormalDefined()) { | |
| 338 | ✗ | return std::nullopt; | |
| 339 | } | ||
| 340 | ✗ | constexpr Standard_Real kMaxRetryVDriftFraction = 0.10; | |
| 341 | ✗ | if (std::fabs(finalRetryV - adjustedV) > kMaxRetryVDriftFraction * (vLast - vFirst)) { | |
| 342 | ✗ | return std::nullopt; | |
| 343 | } | ||
| 344 | ✗ | } | |
| 345 | |||
| 346 |
1/2✓ Branch 0 taken 297435 times.
✗ Branch 1 not taken.
|
297435 | gp_Dir faceNormal = props.Normal(); |
| 347 |
2/2✓ Branch 0 taken 21681 times.
✓ Branch 1 taken 275754 times.
|
297435 | if (face.Orientation() == TopAbs_REVERSED) { |
| 348 |
1/2✓ Branch 0 taken 21681 times.
✗ Branch 1 not taken.
|
21681 | faceNormal.Reverse(); |
| 349 | 21681 | } | |
| 350 | |||
| 351 |
4/8✓ Branch 0 taken 297435 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 297435 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 297435 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 297435 times.
✗ Branch 7 not taken.
|
297435 | return G4ThreeVector(faceNormal.X(), faceNormal.Y(), faceNormal.Z()); |
| 352 | 297435 | } | |
| 353 | |||
| 354 | } // namespace | ||
| 355 | |||
| 356 | namespace g4occt::detail { | ||
| 357 | |||
| 358 |
2/4✓ Branch 0 taken 263 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 263 times.
✗ Branch 3 not taken.
|
263 | G4OCCTSolidKernel::G4OCCTSolidKernel(const TopoDS_Shape& shape) : fShape(shape) { |
| 359 |
3/4✓ Branch 0 taken 263 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 262 times.
✓ Branch 3 taken 1 time.
|
263 | if (fShape.IsNull()) { |
| 360 |
2/4✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
|
1 | throw std::invalid_argument("G4OCCTSolidKernel: shape must not be null"); |
| 361 | } | ||
| 362 |
2/2✓ Branch 0 taken 261 times.
✓ Branch 1 taken 1 time.
|
262 | ComputeBounds(); |
| 363 | 2 | } | |
| 364 | |||
| 365 | 6 | void G4OCCTSolidKernel::SetShape(const TopoDS_Shape& shape) { | |
| 366 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 time.
|
6 | if (shape.IsNull()) { |
| 367 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1 | throw std::invalid_argument("G4OCCTSolidKernel::SetShape: shape must not be null"); |
| 368 | } | ||
| 369 | 5 | fShape = shape; | |
| 370 | 5 | ComputeBounds(); | |
| 371 | { | ||
| 372 | 5 | std::unique_lock<std::mutex> lock(fVolumeAreaMutex); | |
| 373 | 5 | fCachedVolume.reset(); | |
| 374 | 5 | fCachedSurfaceArea.reset(); | |
| 375 | 5 | } | |
| 376 | { | ||
| 377 | 5 | std::unique_lock<std::mutex> lock(fSurfaceCacheMutex); | |
| 378 | // Reset both the cache payload and its bookkeeping together so waiting | ||
| 379 | // threads never observe "no cache" with stale generation/build metadata. | ||
| 380 | 5 | fSurfaceCache.reset(); | |
| 381 | 5 | fSurfaceCacheGeneration = std::numeric_limits<std::uint64_t>::max(); | |
| 382 | 5 | fSurfaceCacheBuilding = false; | |
| 383 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | lock.unlock(); |
| 384 | 5 | fSurfaceCacheCV.notify_all(); | |
| 385 | 5 | } | |
| 386 | 5 | fShapeGeneration.fetch_add(1, std::memory_order_release); | |
| 387 | 5 | } | |
| 388 | |||
| 389 | 267 | void G4OCCTSolidKernel::ComputeBounds() { | |
| 390 | // Build missing PCurves for planar faces up front so the later polygon fast | ||
| 391 | // paths can derive stable (u, v) polygons from the OCCT face wiring. | ||
| 392 |
4/6✓ Branch 0 taken 7682 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7415 times.
✓ Branch 3 taken 267 times.
✓ Branch 4 taken 7415 times.
✗ Branch 5 not taken.
|
7682 | for (TopExp_Explorer faceEx(fShape, TopAbs_FACE); faceEx.More(); faceEx.Next()) { |
| 393 |
2/4✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7415 times.
✗ Branch 3 not taken.
|
7415 | const TopoDS_Face& face = TopoDS::Face(faceEx.Current()); |
| 394 |
4/6✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7415 times.
✓ Branch 4 taken 2649 times.
✓ Branch 5 taken 4766 times.
|
7415 | if (BRepAdaptor_Surface(face).GetType() != GeomAbs_Plane) { |
| 395 | 4766 | continue; | |
| 396 | } | ||
| 397 |
5/8✓ Branch 0 taken 2649 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18249 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15600 times.
✓ Branch 5 taken 2649 times.
✓ Branch 6 taken 15600 times.
✗ Branch 7 not taken.
|
18249 | for (TopExp_Explorer edgeEx(face, TopAbs_EDGE); edgeEx.More(); edgeEx.Next()) { |
| 398 |
3/6✓ Branch 0 taken 15600 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15600 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15600 times.
✗ Branch 5 not taken.
|
15600 | BRepLib::BuildPCurveForEdgeOnPlane(TopoDS::Edge(edgeEx.Current()), face); |
| 399 | 15600 | } | |
| 400 | 2649 | } | |
| 401 | |||
| 402 | 267 | Bnd_Box boundingBox; | |
| 403 | 267 | BRepBndLib::AddOptimal(fShape, boundingBox, /*useTriangulation=*/Standard_False); | |
| 404 |
2/2✓ Branch 0 taken 266 times.
✓ Branch 1 taken 1 time.
|
267 | if (boundingBox.IsVoid()) { |
| 405 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1 | throw std::invalid_argument( |
| 406 | "G4OCCTSolidKernel: shape has no computable bounding box (no geometry)"); | ||
| 407 | } | ||
| 408 | |||
| 409 | 266 | Standard_Real xMin = 0.0; | |
| 410 | 266 | Standard_Real yMin = 0.0; | |
| 411 | 266 | Standard_Real zMin = 0.0; | |
| 412 | 266 | Standard_Real xMax = 0.0; | |
| 413 | 266 | Standard_Real yMax = 0.0; | |
| 414 | 266 | Standard_Real zMax = 0.0; | |
| 415 | 266 | boundingBox.Get(xMin, yMin, zMin, xMax, yMax, zMax); | |
| 416 | 266 | fCachedBounds = | |
| 417 |
1/2✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
|
266 | AxisAlignedBounds{G4ThreeVector(xMin, yMin, zMin), G4ThreeVector(xMax, yMax, zMax)}; |
| 418 | |||
| 419 | 266 | fFaceBoundsCache.clear(); | |
| 420 | 266 | G4double maxFaceDiag = 0.0; | |
| 421 |
4/6✓ Branch 0 taken 7681 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7415 times.
✓ Branch 3 taken 266 times.
✓ Branch 4 taken 7415 times.
✗ Branch 5 not taken.
|
7681 | for (TopExp_Explorer ex(fShape, TopAbs_FACE); ex.More(); ex.Next()) { |
| 422 |
1/2✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
|
7415 | Bnd_Box faceBox; |
| 423 |
2/4✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7415 times.
✗ Branch 3 not taken.
|
7415 | BRepBndLib::AddOptimal(ex.Current(), faceBox, /*useTriangulation=*/Standard_False); |
| 424 |
2/4✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7415 times.
✗ Branch 3 not taken.
|
7415 | const TopoDS_Face& currentFace = TopoDS::Face(ex.Current()); |
| 425 |
1/2✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
|
7415 | BRepAdaptor_Surface adaptor(currentFace); |
| 426 | 7415 | std::optional<gp_Pln> maybePlane; | |
| 427 | 7415 | std::vector<gp_Pnt2d> uvPolygon; | |
| 428 | 7415 | std::optional<G4ThreeVector> outwardNormal; | |
| 429 |
3/4✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2649 times.
✓ Branch 3 taken 4766 times.
|
7415 | if (adaptor.GetType() == GeomAbs_Plane) { |
| 430 | // For single-wire planar faces we cache a 2D polygon in face coordinates | ||
| 431 | // and the analytical outward normal. That lets Inside/Distance* use a | ||
| 432 | // cheap ray-plane + polygon test instead of the heavier OCCT intersector. | ||
| 433 |
1/2✓ Branch 0 taken 2649 times.
✗ Branch 1 not taken.
|
2649 | maybePlane = adaptor.Plane(); |
| 434 |
1/2✓ Branch 0 taken 2649 times.
✗ Branch 1 not taken.
|
2649 | const gp_Ax3& pos = maybePlane->Position(); |
| 435 |
1/2✓ Branch 0 taken 2649 times.
✗ Branch 1 not taken.
|
2649 | const TopoDS_Wire wire = BRepTools::OuterWire(currentFace); |
| 436 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 2649 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2649 times.
|
2649 | if (!wire.IsNull()) { |
| 437 | 2649 | bool allLinear = true; | |
| 438 | 2649 | std::vector<gp_Pnt2d> poly; | |
| 439 |
5/8✓ Branch 0 taken 2649 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11959 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2115 times.
✓ Branch 5 taken 9844 times.
✓ Branch 6 taken 9310 times.
✗ Branch 7 not taken.
|
12493 | for (BRepTools_WireExplorer we(wire, currentFace); we.More(); we.Next()) { |
| 440 |
2/4✓ Branch 0 taken 9844 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9844 times.
✗ Branch 3 not taken.
|
9844 | const BRepAdaptor_Curve ec(we.Current()); |
| 441 |
3/4✓ Branch 0 taken 9844 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9310 times.
✓ Branch 3 taken 534 times.
|
9844 | if (ec.GetType() != GeomAbs_Line) { |
| 442 | 534 | allLinear = false; | |
| 443 | 534 | break; | |
| 444 | } | ||
| 445 |
2/4✓ Branch 0 taken 9310 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9310 times.
✗ Branch 3 not taken.
|
9310 | const gp_Pnt pt = BRep_Tool::Pnt(we.CurrentVertex()); |
| 446 | 9310 | Standard_Real u = 0.0; | |
| 447 | 9310 | Standard_Real v = 0.0; | |
| 448 |
1/2✓ Branch 0 taken 9310 times.
✗ Branch 1 not taken.
|
9310 | ElSLib::PlaneParameters(pos, pt, u, v); |
| 449 |
1/2✓ Branch 0 taken 9310 times.
✗ Branch 1 not taken.
|
9310 | poly.emplace_back(u, v); |
| 450 | 9844 | } | |
| 451 |
3/4✓ Branch 0 taken 2115 times.
✓ Branch 1 taken 534 times.
✓ Branch 2 taken 2115 times.
✗ Branch 3 not taken.
|
2649 | if (allLinear && poly.size() >= 3) { |
| 452 | 2115 | int wireCount = 0; | |
| 453 |
5/8✓ Branch 0 taken 2115 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4230 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2035 times.
✓ Branch 5 taken 2195 times.
✓ Branch 6 taken 2115 times.
✗ Branch 7 not taken.
|
4310 | for (TopExp_Explorer wc(currentFace, TopAbs_WIRE); wc.More(); wc.Next()) { |
| 454 | 2195 | ++wireCount; | |
| 455 |
2/2✓ Branch 0 taken 2115 times.
✓ Branch 1 taken 80 times.
|
2195 | if (wireCount > 1) { |
| 456 | 80 | break; | |
| 457 | } | ||
| 458 | 2115 | } | |
| 459 |
2/2✓ Branch 0 taken 2035 times.
✓ Branch 1 taken 80 times.
|
2115 | if (wireCount == 1) { |
| 460 | 2035 | uvPolygon = std::move(poly); | |
| 461 |
2/4✓ Branch 0 taken 2035 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2035 times.
✗ Branch 3 not taken.
|
2035 | gp_Dir faceNormal = maybePlane->Axis().Direction(); |
| 462 |
3/4✓ Branch 0 taken 2035 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1034 times.
✓ Branch 3 taken 1001 times.
|
2035 | if (currentFace.Orientation() == TopAbs_REVERSED) { |
| 463 |
1/2✓ Branch 0 taken 1034 times.
✗ Branch 1 not taken.
|
1034 | faceNormal.Reverse(); |
| 464 | 1034 | } | |
| 465 |
4/8✓ Branch 0 taken 2035 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2035 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2035 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2035 times.
✗ Branch 7 not taken.
|
2035 | outwardNormal = G4ThreeVector(faceNormal.X(), faceNormal.Y(), faceNormal.Z()); |
| 466 | 2035 | } | |
| 467 | 2115 | } | |
| 468 | 2649 | } | |
| 469 | 2649 | } | |
| 470 |
5/10✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7415 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7415 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7415 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7415 times.
✗ Branch 9 not taken.
|
7415 | fFaceBoundsCache.push_back({currentFace, faceBox, std::move(adaptor), std::move(maybePlane), |
| 471 | 14830 | std::move(uvPolygon), std::move(outwardNormal)}); | |
| 472 |
2/4✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7415 times.
✗ Branch 3 not taken.
|
7415 | if (!faceBox.IsVoid()) { |
| 473 | 7415 | Standard_Real fx0 = 0.0; | |
| 474 | 7415 | Standard_Real fy0 = 0.0; | |
| 475 | 7415 | Standard_Real fz0 = 0.0; | |
| 476 | 7415 | Standard_Real fx1 = 0.0; | |
| 477 | 7415 | Standard_Real fy1 = 0.0; | |
| 478 | 7415 | Standard_Real fz1 = 0.0; | |
| 479 |
1/2✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
|
7415 | faceBox.Get(fx0, fy0, fz0, fx1, fy1, fz1); |
| 480 |
2/4✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7415 times.
✗ Branch 3 not taken.
|
7415 | const G4double diag = G4ThreeVector(fx1 - fx0, fy1 - fy0, fz1 - fz0).mag(); |
| 481 |
1/2✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
|
7415 | maxFaceDiag = std::max(maxFaceDiag, diag); |
| 482 | 7415 | } | |
| 483 | 7415 | } | |
| 484 | |||
| 485 | 266 | fBVHDeflection = kOCCTRelativeDeflection * maxFaceDiag; | |
| 486 | 266 | fAllFacesPlanar = std::all_of(fFaceBoundsCache.begin(), fFaceBoundsCache.end(), | |
| 487 | 1148 | [](const FaceBounds& fb) { return fb.plane.has_value(); }); | |
| 488 | |||
| 489 | { | ||
| 490 | 266 | [[maybe_unused]] const BRepMesh_IncrementalMesh mesher(fShape, kOCCTRelativeDeflection, | |
| 491 | /*isRelative=*/Standard_True); | ||
| 492 | 266 | } | |
| 493 | |||
| 494 | 266 | NCollection_Vector<TopoDS_Shape> faces; | |
| 495 |
5/8✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7681 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7415 times.
✓ Branch 5 taken 266 times.
✓ Branch 6 taken 7415 times.
✗ Branch 7 not taken.
|
7681 | for (TopExp_Explorer ex(fShape, TopAbs_FACE); ex.More(); ex.Next()) { |
| 496 |
2/4✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7415 times.
✗ Branch 3 not taken.
|
7415 | faces.Append(ex.Current()); |
| 497 | 7415 | } | |
| 498 |
2/4✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 266 times.
|
266 | if (faces.IsEmpty()) { |
| 499 | ✗ | fTriangleSet.Nullify(); | |
| 500 | ✗ | fFaceDeflections.clear(); | |
| 501 | ✗ | } else { | |
| 502 |
3/6✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 266 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 266 times.
✗ Branch 5 not taken.
|
266 | fTriangleSet = new BRepExtrema_TriangleSet(faces); |
| 503 | 266 | fFaceDeflections.clear(); | |
| 504 |
1/2✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
|
266 | fFaceDeflections.reserve(fFaceBoundsCache.size()); |
| 505 |
2/2✓ Branch 0 taken 7415 times.
✓ Branch 1 taken 266 times.
|
7681 | for (const FaceBounds& fb : fFaceBoundsCache) { |
| 506 | 7415 | G4double deflection = fBVHDeflection; | |
| 507 |
2/4✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7415 times.
✗ Branch 3 not taken.
|
7415 | if (!fb.box.IsVoid()) { |
| 508 | 7415 | Standard_Real fx0 = 0.0, fy0 = 0.0, fz0 = 0.0; | |
| 509 | 7415 | Standard_Real fx1 = 0.0, fy1 = 0.0, fz1 = 0.0; | |
| 510 |
1/2✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
|
7415 | fb.box.Get(fx0, fy0, fz0, fx1, fy1, fz1); |
| 511 |
2/4✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7415 times.
✗ Branch 3 not taken.
|
7415 | const G4double faceDiag = G4ThreeVector(fx1 - fx0, fy1 - fy0, fz1 - fz0).mag(); |
| 512 | 7415 | deflection = kOCCTRelativeDeflection * faceDiag; | |
| 513 | 7415 | } | |
| 514 |
1/2✓ Branch 0 taken 7415 times.
✗ Branch 1 not taken.
|
7415 | fFaceDeflections.push_back(deflection); |
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 |
1/2✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
|
266 | ComputeInitialSpheres(); |
| 519 | 266 | } | |
| 520 | |||
| 521 | 266 | void G4OCCTSolidKernel::ComputeInitialSpheres() { | |
| 522 | 266 | fInitialSpheres.clear(); | |
| 523 | |||
| 524 | 266 | const G4double tol = IntersectionTolerance(); | |
| 525 | 266 | const G4ThreeVector& bmin = fCachedBounds.min; | |
| 526 | 266 | const G4ThreeVector& bmax = fCachedBounds.max; | |
| 527 |
1/2✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
|
266 | const G4ThreeVector centre = 0.5 * (bmin + bmax); |
| 528 |
2/4✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 266 times.
✗ Branch 3 not taken.
|
266 | const G4ThreeVector halfExt = 0.5 * (bmax - bmin); |
| 529 | |||
| 530 | 266 | std::vector<G4ThreeVector> candidates; | |
| 531 |
1/2✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
|
266 | candidates.reserve(15); |
| 532 |
1/2✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
|
266 | candidates.push_back(centre); |
| 533 |
2/2✓ Branch 0 taken 532 times.
✓ Branch 1 taken 266 times.
|
798 | for (const G4double s : {-0.5, 0.5}) { |
| 534 |
4/8✓ Branch 0 taken 532 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 532 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 532 times.
✓ Branch 6 taken 532 times.
✗ Branch 7 not taken.
|
532 | candidates.push_back(centre + G4ThreeVector(s * halfExt.x(), 0.0, 0.0)); |
| 535 |
4/8✓ Branch 0 taken 532 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 532 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 532 times.
✓ Branch 6 taken 532 times.
✗ Branch 7 not taken.
|
532 | candidates.push_back(centre + G4ThreeVector(0.0, s * halfExt.y(), 0.0)); |
| 536 |
4/8✓ Branch 0 taken 532 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 532 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 532 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 532 times.
|
532 | candidates.push_back(centre + G4ThreeVector(0.0, 0.0, s * halfExt.z())); |
| 537 | } | ||
| 538 |
2/2✓ Branch 0 taken 266 times.
✓ Branch 1 taken 532 times.
|
798 | for (const int sx : {-1, 1}) { |
| 539 |
2/2✓ Branch 0 taken 532 times.
✓ Branch 1 taken 1064 times.
|
1596 | for (const int sy : {-1, 1}) { |
| 540 |
2/2✓ Branch 0 taken 2128 times.
✓ Branch 1 taken 1064 times.
|
3192 | for (const int sz : {-1, 1}) { |
| 541 |
4/8✓ Branch 0 taken 2128 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2128 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2128 times.
✓ Branch 6 taken 2128 times.
✗ Branch 7 not taken.
|
4256 | candidates.push_back(centre + G4ThreeVector(0.75 * sx * halfExt.x(), |
| 542 |
1/2✓ Branch 0 taken 2128 times.
✗ Branch 1 not taken.
|
2128 | 0.75 * sy * halfExt.y(), |
| 543 |
1/2✓ Branch 0 taken 2128 times.
✗ Branch 1 not taken.
|
2128 | 0.75 * sz * halfExt.z())); |
| 544 | } | ||
| 545 | } | ||
| 546 | } | ||
| 547 | |||
| 548 |
1/2✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
|
266 | BRepClass3d_SolidClassifier localClassifier; |
| 549 |
1/2✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
|
266 | localClassifier.Load(fShape); |
| 550 | |||
| 551 | // Seed the per-thread sphere caches from a small set of AABB-derived sample | ||
| 552 | // points so deep interior queries can often terminate without touching OCCT. | ||
| 553 |
2/2✓ Branch 0 taken 266 times.
✓ Branch 1 taken 3990 times.
|
4256 | for (const G4ThreeVector& cand : candidates) { |
| 554 |
2/4✓ Branch 0 taken 3990 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3990 times.
✗ Branch 3 not taken.
|
3990 | localClassifier.Perform(ToPoint(cand), tol); |
| 555 |
3/4✓ Branch 0 taken 3990 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2720 times.
✓ Branch 3 taken 1270 times.
|
3990 | if (localClassifier.State() != TopAbs_IN) { |
| 556 | 1270 | continue; | |
| 557 | } | ||
| 558 |
1/2✓ Branch 0 taken 2720 times.
✗ Branch 1 not taken.
|
2720 | G4double d = BVHLowerBoundDistance(cand); |
| 559 |
2/4✓ Branch 0 taken 2720 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2720 times.
✗ Branch 3 not taken.
|
2720 | if (d >= G4OCCTSolidKernel::Infinity() || d <= tol) { |
| 560 |
3/4✓ Branch 0 taken 53 times.
✓ Branch 1 taken 2667 times.
✓ Branch 2 taken 53 times.
✗ Branch 3 not taken.
|
2720 | const auto match = TryFindClosestFace(fFaceBoundsCache, cand); |
| 561 |
2/4✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 53 times.
|
53 | if (!match.has_value() || match->distance <= tol) { |
| 562 | ✗ | continue; | |
| 563 | } | ||
| 564 | 53 | d = match->distance; | |
| 565 | 53 | } | |
| 566 |
3/4✓ Branch 0 taken 2720 times.
✓ Branch 1 taken 2667 times.
✓ Branch 2 taken 2720 times.
✗ Branch 3 not taken.
|
53 | fInitialSpheres.push_back({cand, d}); |
| 567 | } | ||
| 568 |
2/2✓ Branch 0 taken 5068 times.
✓ Branch 1 taken 5334 times.
|
266 | std::sort(fInitialSpheres.begin(), fInitialSpheres.end(), |
| 569 | 6466 | [](const InscribedSphere& a, const InscribedSphere& b) { return a.radius > b.radius; }); | |
| 570 | 15736 | } | |
| 571 | |||
| 572 | std::optional<G4OCCTSolidKernel::ClosestFaceMatch> | ||
| 573 | 339042 | G4OCCTSolidKernel::TryFindClosestFace(const std::vector<FaceBounds>& faceBoundsCache, | |
| 574 | const G4ThreeVector& point, G4double maxDistance) { | ||
| 575 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 339042 times.
|
339042 | if (faceBoundsCache.empty()) { |
| 576 | ✗ | return std::nullopt; | |
| 577 | } | ||
| 578 | |||
| 579 | 339042 | const gp_Pnt queryPoint = ToPoint(point); | |
| 580 | 339042 | const TopoDS_Vertex queryVertex = MakeVertex(point); | |
| 581 |
1/2✓ Branch 0 taken 339042 times.
✗ Branch 1 not taken.
|
339042 | Bnd_Box queryBox; |
| 582 |
1/2✓ Branch 0 taken 339042 times.
✗ Branch 1 not taken.
|
339042 | queryBox.Add(queryPoint); |
| 583 | |||
| 584 | 339042 | std::optional<ClosestFaceMatch> bestMatch; | |
| 585 |
2/2✓ Branch 0 taken 25487449 times.
✓ Branch 1 taken 339042 times.
|
25826491 | for (std::size_t i = 0; i < faceBoundsCache.size(); ++i) { |
| 586 | 25487449 | const FaceBounds& fb = faceBoundsCache[i]; | |
| 587 |
2/2✓ Branch 0 taken 17813442 times.
✓ Branch 1 taken 7674007 times.
|
25487449 | const G4double threshold = bestMatch.has_value() ? bestMatch->distance : maxDistance; |
| 588 |
6/8✓ Branch 0 taken 25487449 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25419470 times.
✓ Branch 3 taken 67979 times.
✓ Branch 4 taken 25419470 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 631822 times.
✓ Branch 7 taken 24787648 times.
|
25487449 | if (threshold < G4OCCTSolidKernel::Infinity() && fb.box.Distance(queryBox) > threshold) { |
| 589 | 24787648 | continue; | |
| 590 | } | ||
| 591 | |||
| 592 |
2/4✓ Branch 0 taken 699801 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 699801 times.
|
699801 | BRepExtrema_DistShapeShape distance(queryVertex, fb.face); |
| 593 |
4/8✓ Branch 0 taken 699801 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 699801 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 699801 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 699801 times.
✗ Branch 7 not taken.
|
699801 | if (!distance.IsDone() || distance.NbSolution() == 0) { |
| 594 | ✗ | continue; | |
| 595 | } | ||
| 596 |
1/2✓ Branch 0 taken 699801 times.
✗ Branch 1 not taken.
|
699801 | const G4double candidateDistance = distance.Value(); |
| 597 | |||
| 598 |
4/4✓ Branch 0 taken 360759 times.
✓ Branch 1 taken 339042 times.
✓ Branch 2 taken 264197 times.
✓ Branch 3 taken 96562 times.
|
699801 | if (bestMatch.has_value() && candidateDistance >= bestMatch->distance) { |
| 599 | 96562 | continue; | |
| 600 | } | ||
| 601 |
1/2✓ Branch 0 taken 603239 times.
✗ Branch 1 not taken.
|
603239 | ClosestFaceMatch match{.face = fb.face, .distance = candidateDistance, .faceIndex = i}; |
| 602 |
5/8✓ Branch 0 taken 603239 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 603239 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 603239 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 408720 times.
✓ Branch 7 taken 194519 times.
|
603239 | if (distance.NbSolution() > 0 && distance.SupportTypeShape2(1) == BRepExtrema_IsInFace) { |
| 603 | 408720 | Standard_Real u = 0.0; | |
| 604 | 408720 | Standard_Real v = 0.0; | |
| 605 |
1/2✓ Branch 0 taken 408720 times.
✗ Branch 1 not taken.
|
408720 | distance.ParOnFaceS2(1, u, v); |
| 606 |
1/2✓ Branch 0 taken 408720 times.
✗ Branch 1 not taken.
|
408720 | match.uv = std::make_pair(u, v); |
| 607 | 408720 | } | |
| 608 | 603239 | bestMatch = std::move(match); | |
| 609 | 699801 | } | |
| 610 | |||
| 611 | 339042 | return bestMatch; | |
| 612 | 339042 | } | |
| 613 | |||
| 614 | BRepClass3d_SolidClassifier& | ||
| 615 | 167213 | G4OCCTSolidKernel::GetOrCreateClassifier(ClassifierCache& cache) const { | |
| 616 | 167213 | const std::uint64_t currentGen = fShapeGeneration.load(std::memory_order_acquire); | |
| 617 |
2/2✓ Branch 0 taken 167150 times.
✓ Branch 1 taken 63 times.
|
167213 | if (cache.generation != currentGen) { |
| 618 | 63 | cache.classifier.emplace(); | |
| 619 | 63 | cache.classifier->Load(fShape); | |
| 620 | 63 | cache.generation = currentGen; | |
| 621 | 63 | } | |
| 622 | 167213 | return *cache.classifier; | |
| 623 | } | ||
| 624 | |||
| 625 | G4OCCTSolidKernel::IntersectorCache& | ||
| 626 | 2738970 | G4OCCTSolidKernel::GetOrCreateIntersector(IntersectorCache& cache) const { | |
| 627 | 2738970 | const std::uint64_t currentGen = fShapeGeneration.load(std::memory_order_acquire); | |
| 628 |
2/2✓ Branch 0 taken 2738879 times.
✓ Branch 1 taken 91 times.
|
2738970 | if (cache.generation != currentGen) { |
| 629 | 91 | const G4double tol = IntersectionTolerance(); | |
| 630 | 91 | cache.faceIntersectors.clear(); | |
| 631 | 91 | cache.faceIntersectors.reserve(fFaceBoundsCache.size()); | |
| 632 | 91 | cache.expandedBoxes.clear(); | |
| 633 | 91 | cache.expandedBoxes.reserve(fFaceBoundsCache.size()); | |
| 634 |
2/2✓ Branch 0 taken 91 times.
✓ Branch 1 taken 3657 times.
|
3748 | for (const auto& fb : fFaceBoundsCache) { |
| 635 |
1/2✓ Branch 0 taken 3657 times.
✗ Branch 1 not taken.
|
3657 | cache.faceIntersectors.push_back(std::make_unique<IntCurvesFace_Intersector>(fb.face, tol)); |
| 636 | 3657 | Bnd_Box expanded = fb.box; | |
| 637 | 3657 | expanded.Enlarge(tol); | |
| 638 | 3657 | cache.expandedBoxes.push_back(std::move(expanded)); | |
| 639 | } | ||
| 640 | 91 | cache.generation = currentGen; | |
| 641 | 91 | } | |
| 642 | 2738970 | return cache; | |
| 643 | ✗ | } | |
| 644 | |||
| 645 | G4OCCTSolidKernel::SphereCacheData& | ||
| 646 | 2624704 | G4OCCTSolidKernel::GetOrInitSphereCache(SphereCacheData& cache) const { | |
| 647 | 2624704 | const std::uint64_t currentGen = fShapeGeneration.load(std::memory_order_acquire); | |
| 648 |
2/2✓ Branch 0 taken 2624555 times.
✓ Branch 1 taken 149 times.
|
2624704 | if (cache.generation != currentGen) { |
| 649 | 149 | cache.spheres = fInitialSpheres; | |
| 650 | 149 | cache.generation = currentGen; | |
| 651 | 149 | } | |
| 652 | 2624704 | return cache; | |
| 653 | } | ||
| 654 | |||
| 655 | 40176 | void G4OCCTSolidKernel::TryInsertSphere(SphereCacheData& cache, const G4ThreeVector& centre, | |
| 656 | G4double d) const { | ||
| 657 |
2/2✓ Branch 0 taken 2611 times.
✓ Branch 1 taken 37565 times.
|
40176 | if (d <= 0.0) { |
| 658 | 2611 | return; | |
| 659 | } | ||
| 660 | 37565 | const G4double minRadius = IntersectionTolerance(); | |
| 661 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 37565 times.
|
37565 | if (d <= minRadius) { |
| 662 | ✗ | return; | |
| 663 | } | ||
| 664 | 37565 | GetOrInitSphereCache(cache); | |
| 665 | |||
| 666 |
4/4✓ Branch 0 taken 35719 times.
✓ Branch 1 taken 1846 times.
✓ Branch 2 taken 30224 times.
✓ Branch 3 taken 5495 times.
|
37565 | if (cache.spheres.size() >= kMaxInscribedSpheres && d <= cache.spheres.back().radius) { |
| 667 | 30224 | return; | |
| 668 | } | ||
| 669 | |||
| 670 |
2/2✓ Branch 0 taken 416923 times.
✓ Branch 1 taken 7330 times.
|
424253 | for (const InscribedSphere& s : cache.spheres) { |
| 671 |
2/2✓ Branch 0 taken 203873 times.
✓ Branch 1 taken 213050 times.
|
416923 | if (s.radius >= d) { |
| 672 | 213050 | const G4double gap = s.radius - d; | |
| 673 |
3/4✓ Branch 0 taken 213050 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 213039 times.
|
213050 | if ((centre - s.centre).mag2() <= gap * gap) { |
| 674 | 11 | return; | |
| 675 | } | ||
| 676 | 213039 | } | |
| 677 | } | ||
| 678 | |||
| 679 | 7330 | const InscribedSphere newSphere{centre, d}; | |
| 680 |
1/2✓ Branch 0 taken 7330 times.
✗ Branch 1 not taken.
|
7330 | const auto it = std::lower_bound( |
| 681 | 7330 | cache.spheres.begin(), cache.spheres.end(), newSphere, | |
| 682 | 42311 | [](const InscribedSphere& a, const InscribedSphere& b) { return a.radius > b.radius; }); | |
| 683 |
1/2✓ Branch 0 taken 7330 times.
✗ Branch 1 not taken.
|
7330 | cache.spheres.insert(it, newSphere); |
| 684 |
2/2✓ Branch 0 taken 1836 times.
✓ Branch 1 taken 5494 times.
|
7330 | if (cache.spheres.size() > kMaxInscribedSpheres) { |
| 685 | 5494 | cache.spheres.pop_back(); | |
| 686 | 5494 | } | |
| 687 | 40176 | } | |
| 688 | |||
| 689 | G4OCCTSolidKernel::PointClassification | ||
| 690 | 5063958 | G4OCCTSolidKernel::ClassifyPoint(const G4ThreeVector& p, ClassifierCache& classifierCache, | |
| 691 | IntersectorCache& intersectorCache, | ||
| 692 | SphereCacheData& sphereCache) const { | ||
| 693 | 5063958 | const G4double tolerance = IntersectionTolerance(); | |
| 694 |
4/4✓ Branch 0 taken 5063955 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 4257543 times.
✓ Branch 3 taken 806412 times.
|
5063958 | if (p.x() < fCachedBounds.min.x() - tolerance || p.x() > fCachedBounds.max.x() + tolerance || |
| 695 |
3/4✓ Branch 0 taken 4257543 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3393540 times.
✓ Branch 3 taken 864003 times.
|
4257543 | p.y() < fCachedBounds.min.y() - tolerance || p.y() > fCachedBounds.max.y() + tolerance || |
| 696 |
3/4✓ Branch 0 taken 3393540 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 806401 times.
✓ Branch 3 taken 2587139 times.
|
3393540 | p.z() < fCachedBounds.min.z() - tolerance || p.z() > fCachedBounds.max.z() + tolerance) { |
| 697 | 2476819 | return PointClassification::kOutside; | |
| 698 | } | ||
| 699 | |||
| 700 | 2587139 | const SphereCacheData& localSphereCache = GetOrInitSphereCache(sphereCache); | |
| 701 |
2/2✓ Branch 0 taken 4962399 times.
✓ Branch 1 taken 743633 times.
|
5706032 | for (const InscribedSphere& s : localSphereCache.spheres) { |
| 702 | 4962399 | const G4double interiorRadius = s.radius - tolerance; | |
| 703 |
5/10✗ Branch 0 not taken.
✓ Branch 1 taken 4962399 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4962399 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4962399 times.
✓ Branch 6 taken 3118893 times.
✓ Branch 7 taken 1843506 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
4962399 | if (interiorRadius > 0.0 && (p - s.centre).mag2() < interiorRadius * interiorRadius) { |
| 704 | 1843506 | return PointClassification::kInside; | |
| 705 | } | ||
| 706 | } | ||
| 707 | |||
| 708 |
2/4✓ Branch 0 taken 743633 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 743633 times.
|
743633 | if (!fTriangleSet.IsNull() && fTriangleSet->Size() > 0) { |
| 709 | // Near the tessellated surface we deliberately fall back to the exact OCCT | ||
| 710 | // classifier; the BVH lower bound is only used to prove that we are safely | ||
| 711 | // away from the true analytical boundary by more than the mesh deflection. | ||
| 712 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 743633 times.
|
743633 | if (fBVHDeflection > 0.0) { |
| 713 | 743633 | const G4double bvhLB = BVHLowerBoundDistance(p); | |
| 714 |
2/2✓ Branch 0 taken 81795 times.
✓ Branch 1 taken 661838 times.
|
743633 | if (bvhLB < tolerance) { |
| 715 | 81795 | auto& classifier = GetOrCreateClassifier(classifierCache); | |
| 716 | 81795 | classifier.Perform(ToPoint(p), tolerance); | |
| 717 | 81795 | return ToPointClassification(classifier.State()); | |
| 718 | } | ||
| 719 | 661838 | } | |
| 720 | |||
| 721 | 661838 | const BVH_Vec3d bvhOrigin(p.x(), p.y(), p.z()); | |
| 722 | 661838 | const Standard_Real bvhTol = static_cast<Standard_Real>(tolerance); | |
| 723 | 661838 | TriangleRayCast caster; | |
| 724 |
2/4✓ Branch 0 taken 661838 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 661838 times.
✗ Branch 3 not taken.
|
661838 | caster.SetBVHSet(fTriangleSet.get()); |
| 725 |
2/4✓ Branch 0 taken 661838 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 661838 times.
✗ Branch 3 not taken.
|
661838 | caster.SetRay(bvhOrigin, BVH_Vec3d(0.0, 0.0, 1.0), bvhTol); |
| 726 |
1/2✓ Branch 0 taken 661838 times.
✗ Branch 1 not taken.
|
661838 | caster.Select(); |
| 727 | |||
| 728 |
6/8✓ Branch 0 taken 661838 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 604232 times.
✓ Branch 3 taken 57606 times.
✓ Branch 4 taken 604232 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 215080 times.
✓ Branch 7 taken 389152 times.
|
661838 | if (!caster.Degenerate() && caster.Crossings() > 0) { |
| 729 |
1/2✓ Branch 0 taken 215080 times.
✗ Branch 1 not taken.
|
215080 | return (caster.Crossings() % 2 == 1) ? PointClassification::kInside |
| 730 | : PointClassification::kOutside; | ||
| 731 | } | ||
| 732 | |||
| 733 | // Edge/vertex hits can make one parity ray ambiguous, so we cast two | ||
| 734 | // additional orthogonal rays and let non-degenerate rays vote before | ||
| 735 | // paying for the exact classifier fallback. | ||
| 736 | 446758 | int insideVotes = 0; | |
| 737 | 446758 | int outsideVotes = 0; | |
| 738 |
3/4✓ Branch 0 taken 446758 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 389152 times.
✓ Branch 3 taken 57606 times.
|
446758 | if (!caster.Degenerate()) { |
| 739 |
2/4✓ Branch 0 taken 389152 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 389152 times.
|
389152 | if (caster.Crossings() % 2 == 1) { |
| 740 | ✗ | ++insideVotes; | |
| 741 | ✗ | } else { | |
| 742 | 389152 | ++outsideVotes; | |
| 743 | } | ||
| 744 | 389152 | } | |
| 745 | |||
| 746 | 446758 | const BVH_Vec3d kExtraRays[2] = { | |
| 747 |
1/2✓ Branch 0 taken 446758 times.
✗ Branch 1 not taken.
|
446758 | BVH_Vec3d(1.0, 0.0, 0.0), |
| 748 |
1/2✓ Branch 0 taken 446758 times.
✗ Branch 1 not taken.
|
446758 | BVH_Vec3d(0.0, 1.0, 0.0), |
| 749 | }; | ||
| 750 |
2/2✓ Branch 0 taken 893516 times.
✓ Branch 1 taken 446758 times.
|
1340274 | for (const BVH_Vec3d& dir : kExtraRays) { |
| 751 |
1/2✓ Branch 0 taken 893516 times.
✗ Branch 1 not taken.
|
893516 | caster.SetRay(bvhOrigin, dir, bvhTol); |
| 752 |
1/2✓ Branch 0 taken 893516 times.
✗ Branch 1 not taken.
|
893516 | caster.Select(); |
| 753 |
3/4✓ Branch 0 taken 893516 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 547900 times.
✓ Branch 3 taken 345616 times.
|
893516 | if (!caster.Degenerate()) { |
| 754 |
3/4✓ Branch 0 taken 547900 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 57722 times.
✓ Branch 3 taken 490178 times.
|
547900 | if (caster.Crossings() % 2 == 1) { |
| 755 | 57722 | ++insideVotes; | |
| 756 | 57722 | } else { | |
| 757 | 490178 | ++outsideVotes; | |
| 758 | } | ||
| 759 | 547900 | } | |
| 760 | } | ||
| 761 | |||
| 762 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 446756 times.
|
446758 | if (insideVotes > outsideVotes) { |
| 763 | 2 | return PointClassification::kInside; | |
| 764 | } | ||
| 765 |
2/2✓ Branch 0 taken 57603 times.
✓ Branch 1 taken 389153 times.
|
446756 | if (outsideVotes > insideVotes) { |
| 766 | 389153 | return PointClassification::kOutside; | |
| 767 | } | ||
| 768 | |||
| 769 |
1/2✓ Branch 0 taken 57603 times.
✗ Branch 1 not taken.
|
57603 | auto& classifier = GetOrCreateClassifier(classifierCache); |
| 770 |
2/4✓ Branch 0 taken 57603 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 57603 times.
✗ Branch 3 not taken.
|
57603 | classifier.Perform(ToPoint(p), tolerance); |
| 771 |
2/4✓ Branch 0 taken 57603 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 57603 times.
✗ Branch 3 not taken.
|
57603 | return ToPointClassification(classifier.State()); |
| 772 | 661838 | } | |
| 773 | |||
| 774 | ✗ | IntersectorCache& cache = GetOrCreateIntersector(intersectorCache); | |
| 775 | ✗ | const gp_Lin ray(ToPoint(p), gp_Dir(0.0, 0.0, 1.0)); | |
| 776 | ✗ | int crossings = 0; | |
| 777 | ✗ | bool onSurface = false; | |
| 778 | ✗ | bool degenerateRay = false; | |
| 779 | |||
| 780 | ✗ | for (std::size_t i = 0; i < fFaceBoundsCache.size(); ++i) { | |
| 781 | ✗ | if (cache.expandedBoxes[i].IsOut(ray)) { | |
| 782 | ✗ | continue; | |
| 783 | } | ||
| 784 | ✗ | const FaceBounds& fb = fFaceBoundsCache[i]; | |
| 785 | ✗ | if (!fb.uvPolygon.empty()) { | |
| 786 | // Fast path for single-wire planar faces: intersect the analytical plane | ||
| 787 | // and then test the hit against the cached 2D polygon in face space. | ||
| 788 | ✗ | Standard_Real u_hit = 0.0; | |
| 789 | ✗ | Standard_Real v_hit = 0.0; | |
| 790 | const auto t = | ||
| 791 | ✗ | RayPlaneFaceHit(ray, *fb.plane, fb.uvPolygon, -tolerance, tolerance, &u_hit, &v_hit); | |
| 792 | ✗ | if (t) { | |
| 793 | ✗ | const G4double w = static_cast<G4double>(*t); | |
| 794 | ✗ | if (std::abs(w) <= tolerance) { | |
| 795 | ✗ | onSurface = true; | |
| 796 | ✗ | } else if (w > tolerance) { | |
| 797 | ✗ | if (PointOnPolygonBoundary2d(u_hit, v_hit, fb.uvPolygon, tolerance)) { | |
| 798 | ✗ | degenerateRay = true; | |
| 799 | ✗ | } else { | |
| 800 | ✗ | ++crossings; | |
| 801 | } | ||
| 802 | ✗ | } | |
| 803 | ✗ | } | |
| 804 | ✗ | } else { | |
| 805 | ✗ | IntCurvesFace_Intersector& fi = *cache.faceIntersectors[i]; | |
| 806 | ✗ | fi.Perform(ray, -tolerance, Precision::Infinite()); | |
| 807 | ✗ | if (!fi.IsDone()) { | |
| 808 | ✗ | continue; | |
| 809 | } | ||
| 810 | ✗ | for (Standard_Integer j = 1; j <= fi.NbPnt(); ++j) { | |
| 811 | ✗ | const G4double w = fi.WParameter(j); | |
| 812 | ✗ | const TopAbs_State state = fi.State(j); | |
| 813 | ✗ | if (std::abs(w) <= tolerance && (state == TopAbs_IN || state == TopAbs_ON)) { | |
| 814 | ✗ | onSurface = true; | |
| 815 | ✗ | } else if (w > tolerance && state == TopAbs_IN) { | |
| 816 | ✗ | ++crossings; | |
| 817 | ✗ | } else if (w > tolerance && state == TopAbs_ON) { | |
| 818 | ✗ | degenerateRay = true; | |
| 819 | ✗ | } | |
| 820 | ✗ | } | |
| 821 | } | ||
| 822 | ✗ | } | |
| 823 | |||
| 824 | ✗ | if (onSurface) { | |
| 825 | ✗ | return PointClassification::kSurface; | |
| 826 | } | ||
| 827 | ✗ | if (crossings == 0 || degenerateRay) { | |
| 828 | ✗ | auto& classifier = GetOrCreateClassifier(classifierCache); | |
| 829 | ✗ | classifier.Perform(ToPoint(p), tolerance); | |
| 830 | ✗ | return ToPointClassification(classifier.State()); | |
| 831 | } | ||
| 832 | ✗ | return (crossings % 2 == 1) ? PointClassification::kInside : PointClassification::kOutside; | |
| 833 | 5063958 | } | |
| 834 | |||
| 835 | 465852 | G4ThreeVector G4OCCTSolidKernel::SurfaceNormal(const G4ThreeVector& p) const { | |
| 836 |
2/2✓ Branch 0 taken 271063 times.
✓ Branch 1 taken 194789 times.
|
465852 | if (fAllFacesPlanar) { |
| 837 | 194789 | const gp_Pnt pt = ToPoint(p); | |
| 838 | 194789 | const FaceBounds* bestFB = nullptr; | |
| 839 | 194789 | G4double bestDist = G4OCCTSolidKernel::Infinity(); | |
| 840 |
2/2✓ Branch 0 taken 1674300 times.
✓ Branch 1 taken 194789 times.
|
1869089 | for (const FaceBounds& fb : fFaceBoundsCache) { |
| 841 |
2/4✓ Branch 0 taken 1674300 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1674300 times.
|
1674300 | if (!fb.plane.has_value() || !fb.outwardNormal.has_value()) { |
| 842 | ✗ | continue; | |
| 843 | } | ||
| 844 | 1674300 | const G4double d = fb.plane->Distance(pt); | |
| 845 |
2/2✓ Branch 0 taken 1107125 times.
✓ Branch 1 taken 567175 times.
|
1674300 | if (d < bestDist) { |
| 846 | 567175 | bestDist = d; | |
| 847 | 567175 | bestFB = &fb; | |
| 848 | 567175 | } | |
| 849 | } | ||
| 850 |
1/2✓ Branch 0 taken 194789 times.
✗ Branch 1 not taken.
|
194789 | if (bestFB) { |
| 851 | 194789 | return *bestFB->outwardNormal; | |
| 852 | } | ||
| 853 | ✗ | } | |
| 854 | |||
| 855 | 271097 | const auto projectAndGetNormalFallback = [&](const FaceBounds& fb) -> G4ThreeVector { | |
| 856 | 34 | TopLoc_Location loc; | |
| 857 |
2/4✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
|
34 | const Handle(Geom_Surface) surface = BRep_Tool::Surface(fb.face, loc); |
| 858 |
2/4✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
|
34 | if (surface.IsNull()) { |
| 859 | ✗ | return FallbackNormal(); | |
| 860 | } | ||
| 861 |
1/2✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
|
34 | gp_Pnt pLocal = ToPoint(p); |
| 862 |
2/4✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
|
34 | if (!loc.IsIdentity()) { |
| 863 | ✗ | pLocal.Transform(loc.Transformation().Inverted()); | |
| 864 | ✗ | } | |
| 865 |
1/2✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
|
34 | GeomAPI_ProjectPointOnSurf projection(pLocal, surface); |
| 866 |
2/4✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
|
34 | if (projection.NbPoints() == 0) { |
| 867 | ✗ | return FallbackNormal(); | |
| 868 | } | ||
| 869 | 34 | Standard_Real u = 0.0; | |
| 870 | 34 | Standard_Real v = 0.0; | |
| 871 |
1/2✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
|
34 | projection.LowerDistanceParameters(u, v); |
| 872 |
3/6✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
|
34 | return TryGetOutwardNormal(fb.adaptor, fb.face, u, v).value_or(FallbackNormal()); |
| 873 | 34 | }; | |
| 874 | |||
| 875 | 271063 | const G4double bvhLB = BVHLowerBoundDistance(p); | |
| 876 | // The BVH gives a lower bound to the true surface distance. Adding | ||
| 877 | // 2×deflection turns that into a conservative face-search radius that still | ||
| 878 | // prunes obviously distant faces before exact OCCT distance checks. | ||
| 879 |
2/4✓ Branch 0 taken 271063 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 271063 times.
|
271063 | const G4double seedDist = (fBVHDeflection > 0.0 && bvhLB < G4OCCTSolidKernel::Infinity()) |
| 880 | 271063 | ? bvhLB + 2.0 * fBVHDeflection | |
| 881 | ✗ | : G4OCCTSolidKernel::Infinity(); | |
| 882 | 271063 | const auto closestFaceMatch = TryFindClosestFace(fFaceBoundsCache, p, seedDist); | |
| 883 |
1/2✓ Branch 0 taken 271063 times.
✗ Branch 1 not taken.
|
271063 | if (!closestFaceMatch.has_value()) { |
| 884 | ✗ | return FallbackNormal(); | |
| 885 | } | ||
| 886 | 271063 | const FaceBounds& fb = fFaceBoundsCache[closestFaceMatch->faceIndex]; | |
| 887 | |||
| 888 |
2/2✓ Branch 0 taken 8225 times.
✓ Branch 1 taken 262838 times.
|
271063 | if (fb.outwardNormal.has_value()) { |
| 889 |
1/2✓ Branch 0 taken 8225 times.
✗ Branch 1 not taken.
|
8225 | return *fb.outwardNormal; |
| 890 | } | ||
| 891 |
2/2✓ Branch 0 taken 262804 times.
✓ Branch 1 taken 34 times.
|
262838 | if (closestFaceMatch->uv.has_value()) { |
| 892 | 788412 | const auto [u, v] = *closestFaceMatch->uv; | |
| 893 |
3/6✓ Branch 0 taken 262804 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 262804 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 262804 times.
✗ Branch 5 not taken.
|
788412 | const auto result = TryGetOutwardNormal(fb.adaptor, fb.face, u, v); |
| 894 |
1/2✓ Branch 0 taken 262804 times.
✗ Branch 1 not taken.
|
262804 | if (result.has_value()) { |
| 895 |
1/2✓ Branch 0 taken 262804 times.
✗ Branch 1 not taken.
|
262804 | return *result; |
| 896 | } | ||
| 897 | 262804 | } | |
| 898 |
1/2✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
|
34 | return projectAndGetNormalFallback(fb); |
| 899 | 465852 | } | |
| 900 | |||
| 901 | 2679561 | G4double G4OCCTSolidKernel::DistanceToIn(const G4ThreeVector& p, const G4ThreeVector& v, | |
| 902 | IntersectorCache& intersectorCache) const { | ||
| 903 | 2679561 | const G4double tolerance = IntersectionTolerance(); | |
| 904 | 2679561 | const gp_Lin ray(ToPoint(p), gp_Dir(v.x(), v.y(), v.z())); | |
| 905 | 2679561 | IntersectorCache& cache = GetOrCreateIntersector(intersectorCache); | |
| 906 | |||
| 907 | 2679561 | G4double minDistance = G4OCCTSolidKernel::Infinity(); | |
| 908 |
2/2✓ Branch 0 taken 184694833 times.
✓ Branch 1 taken 2679561 times.
|
187374394 | for (std::size_t i = 0; i < fFaceBoundsCache.size(); ++i) { |
| 909 |
2/2✓ Branch 0 taken 182800041 times.
✓ Branch 1 taken 1894792 times.
|
184694833 | if (cache.expandedBoxes[i].IsOut(ray)) { |
| 910 | 182800041 | continue; | |
| 911 | } | ||
| 912 | 1894792 | const FaceBounds& fb = fFaceBoundsCache[i]; | |
| 913 |
2/2✓ Branch 0 taken 1212395 times.
✓ Branch 1 taken 682397 times.
|
1894792 | if (!fb.uvPolygon.empty()) { |
| 914 | 682397 | const auto t = RayPlaneFaceHit(ray, *fb.plane, fb.uvPolygon, tolerance, tolerance); | |
| 915 |
4/4✓ Branch 0 taken 515163 times.
✓ Branch 1 taken 167234 times.
✓ Branch 2 taken 137022 times.
✓ Branch 3 taken 378141 times.
|
682397 | if (t && *t < minDistance) { |
| 916 | 378141 | minDistance = static_cast<G4double>(*t); | |
| 917 | 378141 | } | |
| 918 | 682397 | } else { | |
| 919 | 1212395 | IntCurvesFace_Intersector& fi = *cache.faceIntersectors[i]; | |
| 920 | 1212395 | fi.Perform(ray, tolerance, Precision::Infinite()); | |
| 921 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1212395 times.
|
1212395 | if (!fi.IsDone()) { |
| 922 | ✗ | continue; | |
| 923 | } | ||
| 924 |
2/2✓ Branch 0 taken 686513 times.
✓ Branch 1 taken 1212395 times.
|
1898908 | for (Standard_Integer j = 1; j <= fi.NbPnt(); ++j) { |
| 925 | 686513 | const G4double w = fi.WParameter(j); | |
| 926 |
3/4✓ Branch 0 taken 686513 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 259497 times.
✓ Branch 3 taken 427016 times.
|
686513 | if (w > tolerance && w < minDistance) { |
| 927 | 427016 | minDistance = w; | |
| 928 | 427016 | } | |
| 929 | 686513 | } | |
| 930 | } | ||
| 931 | 1894792 | } | |
| 932 | 2679561 | return minDistance; | |
| 933 | } | ||
| 934 | |||
| 935 | 27815 | G4double G4OCCTSolidKernel::ExactDistanceToIn(const G4ThreeVector& p, | |
| 936 | ClassifierCache& classifierCache) const { | ||
| 937 | 27815 | auto& classifier = GetOrCreateClassifier(classifierCache); | |
| 938 | 27815 | classifier.Perform(ToPoint(p), IntersectionTolerance()); | |
| 939 |
4/4✓ Branch 0 taken 27813 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 27809 times.
|
27815 | if (classifier.State() == TopAbs_IN || classifier.State() == TopAbs_ON) { |
| 940 | 6 | return 0.0; | |
| 941 | } | ||
| 942 | |||
| 943 | 27809 | const auto match = TryFindClosestFace(fFaceBoundsCache, p); | |
| 944 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 27809 times.
|
27809 | if (!match.has_value()) { |
| 945 | ✗ | return G4OCCTSolidKernel::Infinity(); | |
| 946 | } | ||
| 947 |
2/4✓ Branch 0 taken 27809 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 27809 times.
|
27809 | return (match->distance <= IntersectionTolerance()) ? 0.0 : match->distance; |
| 948 | 27815 | } | |
| 949 | |||
| 950 | 2502255 | G4double G4OCCTSolidKernel::AABBLowerBound(const G4ThreeVector& p) const { | |
| 951 | 2502255 | const G4ThreeVector& mn = fCachedBounds.min; | |
| 952 | 2502255 | const G4ThreeVector& mx = fCachedBounds.max; | |
| 953 | 2502255 | const G4double dx = std::max({0.0, mn.x() - p.x(), p.x() - mx.x()}); | |
| 954 | 2502255 | const G4double dy = std::max({0.0, mn.y() - p.y(), p.y() - mx.y()}); | |
| 955 | 2502255 | const G4double dz = std::max({0.0, mn.z() - p.z(), p.z() - mx.z()}); | |
| 956 | 2502255 | return std::sqrt(dx * dx + dy * dy + dz * dz); | |
| 957 | } | ||
| 958 | |||
| 959 | 1061106 | G4double G4OCCTSolidKernel::BVHLowerBoundDistance(const G4ThreeVector& p) const { | |
| 960 |
2/4✓ Branch 0 taken 1061106 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1061106 times.
|
1061106 | if (fTriangleSet.IsNull() || fTriangleSet->Size() == 0) { |
| 961 | ✗ | return G4OCCTSolidKernel::Infinity(); | |
| 962 | } | ||
| 963 | 1061106 | PointToMeshDistance solver; | |
| 964 |
5/10✓ Branch 0 taken 1061106 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1061106 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1061106 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1061106 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1061106 times.
✗ Branch 9 not taken.
|
1061106 | solver.SetObject(BVH_Vec3d(p.x(), p.y(), p.z())); |
| 965 |
2/4✓ Branch 0 taken 1061106 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1061106 times.
✗ Branch 3 not taken.
|
1061106 | solver.SetBVHSet(fTriangleSet.get()); |
| 966 |
1/2✓ Branch 0 taken 1061106 times.
✗ Branch 1 not taken.
|
1061106 | const Standard_Real meshDistSq = solver.ComputeDistance(); |
| 967 |
2/4✓ Branch 0 taken 1061106 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1061106 times.
|
1061106 | if (!solver.IsDone()) { |
| 968 | ✗ | return G4OCCTSolidKernel::Infinity(); | |
| 969 | } | ||
| 970 | 1061106 | const G4double meshDist = std::sqrt(static_cast<G4double>(meshDistSq)); | |
| 971 | |||
| 972 | 1061106 | G4double deflection = fBVHDeflection; | |
| 973 |
1/2✓ Branch 0 taken 1061106 times.
✗ Branch 1 not taken.
|
1061106 | const Standard_Integer bestIdx = solver.BestIndex(); |
| 974 |
1/2✓ Branch 0 taken 1061106 times.
✗ Branch 1 not taken.
|
1061106 | if (bestIdx >= 0) { |
| 975 |
2/4✓ Branch 0 taken 1061106 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1061106 times.
✗ Branch 3 not taken.
|
1061106 | const Standard_Integer faceId = fTriangleSet->GetFaceID(bestIdx); |
| 976 |
2/4✓ Branch 0 taken 1061106 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1061106 times.
|
1061106 | if (faceId >= 0 && static_cast<std::size_t>(faceId) < fFaceDeflections.size()) { |
| 977 | 1061106 | deflection = fFaceDeflections[static_cast<std::size_t>(faceId)]; | |
| 978 | 1061106 | } | |
| 979 | 1061106 | } | |
| 980 | |||
| 981 | // The tessellated distance can overestimate how far we are from the | ||
| 982 | // analytical surface by up to the local mesh deflection, so subtract it and | ||
| 983 | // clamp at zero to preserve a conservative lower bound. | ||
| 984 |
1/2✓ Branch 0 taken 1061106 times.
✗ Branch 1 not taken.
|
1061106 | return std::max(0.0, meshDist - deflection); |
| 985 | 1061106 | } | |
| 986 | |||
| 987 | 20179 | G4double G4OCCTSolidKernel::PlanarFaceLowerBoundDistance(const G4ThreeVector& p) const { | |
| 988 | 20179 | const gp_Pnt pt = ToPoint(p); | |
| 989 | 20179 | G4double minDist = G4OCCTSolidKernel::Infinity(); | |
| 990 |
2/2✓ Branch 0 taken 184423 times.
✓ Branch 1 taken 20179 times.
|
204602 | for (const FaceBounds& fb : fFaceBoundsCache) { |
| 991 |
1/2✓ Branch 0 taken 184423 times.
✗ Branch 1 not taken.
|
184423 | if (!fb.plane.has_value()) { |
| 992 | ✗ | continue; | |
| 993 | } | ||
| 994 | 184423 | const G4double d = static_cast<G4double>(fb.plane->Distance(pt)); | |
| 995 |
2/2✓ Branch 0 taken 132064 times.
✓ Branch 1 taken 52359 times.
|
184423 | if (d < minDist) { |
| 996 | 52359 | minDist = d; | |
| 997 | 52359 | } | |
| 998 | } | ||
| 999 | 20179 | return minDist; | |
| 1000 | } | ||
| 1001 | |||
| 1002 | 2502255 | G4double G4OCCTSolidKernel::DistanceToIn(const G4ThreeVector& p, | |
| 1003 | ClassifierCache& classifierCache) const { | ||
| 1004 | 2502255 | const G4double aabbDist = AABBLowerBound(p); | |
| 1005 |
2/2✓ Branch 0 taken 2478562 times.
✓ Branch 1 taken 23693 times.
|
2502255 | if (aabbDist > IntersectionTolerance()) { |
| 1006 | 2478562 | return aabbDist; | |
| 1007 | } | ||
| 1008 | |||
| 1009 | // Once the point is inside or very near the AABB, the BVH lower bound is the | ||
| 1010 | // next cheap filter. Only near-surface cases fall through to the exact OCCT | ||
| 1011 | // distance query. | ||
| 1012 | 23693 | const G4double bvhDist = BVHLowerBoundDistance(p); | |
| 1013 |
3/4✓ Branch 0 taken 23693 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2368 times.
✓ Branch 3 taken 21325 times.
|
23693 | if (bvhDist < G4OCCTSolidKernel::Infinity() && bvhDist > IntersectionTolerance()) { |
| 1014 | 21325 | return bvhDist; | |
| 1015 | } | ||
| 1016 | 2368 | return ExactDistanceToIn(p, classifierCache); | |
| 1017 | 2502255 | } | |
| 1018 | |||
| 1019 | 59409 | G4double G4OCCTSolidKernel::DistanceToOut(const G4ThreeVector& p, const G4ThreeVector& v, | |
| 1020 | IntersectorCache& intersectorCache, const G4bool calcNorm, | ||
| 1021 | G4bool* validNorm, G4ThreeVector* n) const { | ||
| 1022 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 59407 times.
|
59409 | if (validNorm != nullptr) { |
| 1023 | 59407 | *validNorm = false; | |
| 1024 | 59407 | } | |
| 1025 | |||
| 1026 | 59409 | const G4double tolerance = IntersectionTolerance(); | |
| 1027 | 59409 | const gp_Lin ray(ToPoint(p), gp_Dir(v.x(), v.y(), v.z())); | |
| 1028 | 59409 | IntersectorCache& cache = GetOrCreateIntersector(intersectorCache); | |
| 1029 | |||
| 1030 | 59409 | G4double minDistance = G4OCCTSolidKernel::Infinity(); | |
| 1031 | 59409 | std::size_t minFaceIdx = std::numeric_limits<std::size_t>::max(); | |
| 1032 | 59409 | G4double minU = 0.0; | |
| 1033 | 59409 | G4double minV = 0.0; | |
| 1034 | 59409 | bool minIsIn = false; | |
| 1035 | 59409 | bool minIsFastPath = false; | |
| 1036 | |||
| 1037 |
2/2✓ Branch 0 taken 317526 times.
✓ Branch 1 taken 59409 times.
|
376935 | for (std::size_t i = 0; i < fFaceBoundsCache.size(); ++i) { |
| 1038 |
2/2✓ Branch 0 taken 198870 times.
✓ Branch 1 taken 118656 times.
|
317526 | if (cache.expandedBoxes[i].IsOut(ray)) { |
| 1039 | 198870 | continue; | |
| 1040 | } | ||
| 1041 | 118656 | const FaceBounds& fb = fFaceBoundsCache[i]; | |
| 1042 |
2/2✓ Branch 0 taken 62193 times.
✓ Branch 1 taken 56463 times.
|
118656 | if (!fb.uvPolygon.empty()) { |
| 1043 | 56463 | const auto t = RayPlaneFaceHit(ray, *fb.plane, fb.uvPolygon, tolerance, tolerance); | |
| 1044 |
4/4✓ Branch 0 taken 24815 times.
✓ Branch 1 taken 31648 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 24813 times.
|
56463 | if (t && *t < minDistance) { |
| 1045 | 24813 | minDistance = static_cast<G4double>(*t); | |
| 1046 | 24813 | minFaceIdx = i; | |
| 1047 | 24813 | minIsIn = true; | |
| 1048 | 24813 | minIsFastPath = true; | |
| 1049 | 24813 | } | |
| 1050 | 56463 | } else { | |
| 1051 | 62193 | IntCurvesFace_Intersector& fi = *cache.faceIntersectors[i]; | |
| 1052 | 62193 | fi.Perform(ray, tolerance, Precision::Infinite()); | |
| 1053 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62193 times.
|
62193 | if (!fi.IsDone()) { |
| 1054 | ✗ | continue; | |
| 1055 | } | ||
| 1056 |
2/2✓ Branch 0 taken 35871 times.
✓ Branch 1 taken 62193 times.
|
98064 | for (Standard_Integer j = 1; j <= fi.NbPnt(); ++j) { |
| 1057 | 35871 | const G4double w = fi.WParameter(j); | |
| 1058 |
3/4✓ Branch 0 taken 35871 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1274 times.
✓ Branch 3 taken 34597 times.
|
35871 | if (w > tolerance && w < minDistance) { |
| 1059 | 34597 | minDistance = w; | |
| 1060 | 34597 | minFaceIdx = i; | |
| 1061 | 34597 | minU = fi.UParameter(j); | |
| 1062 | 34597 | minV = fi.VParameter(j); | |
| 1063 |
2/2✓ Branch 0 taken 34574 times.
✓ Branch 1 taken 23 times.
|
34597 | minIsIn = (fi.State(j) == TopAbs_IN || fi.State(j) == TopAbs_ON); |
| 1064 | 34597 | minIsFastPath = false; | |
| 1065 | 34597 | } | |
| 1066 | 35871 | } | |
| 1067 | } | ||
| 1068 | 118656 | } | |
| 1069 | |||
| 1070 |
2/4✓ Branch 0 taken 59409 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 59409 times.
|
59409 | if (minFaceIdx == std::numeric_limits<std::size_t>::max() || |
| 1071 | 59409 | minDistance == G4OCCTSolidKernel::Infinity()) { | |
| 1072 | ✗ | return 0.0; | |
| 1073 | } | ||
| 1074 | |||
| 1075 |
7/8✓ Branch 0 taken 59405 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 59404 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 59403 times.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
✓ Branch 7 taken 59403 times.
|
59409 | if (calcNorm && validNorm != nullptr && n != nullptr && minIsIn) { |
| 1076 | 59403 | const FaceBounds& fb = fFaceBoundsCache[minFaceIdx]; | |
| 1077 |
3/4✓ Branch 0 taken 24806 times.
✓ Branch 1 taken 34597 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 24806 times.
|
59403 | if (minIsFastPath && fb.outwardNormal.has_value()) { |
| 1078 | 24806 | *n = *fb.outwardNormal; | |
| 1079 | 24806 | *validNorm = true; | |
| 1080 | 24806 | } else { | |
| 1081 | 34597 | const auto outNorm = TryGetOutwardNormal(fb.adaptor, fb.face, minU, minV); | |
| 1082 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 34597 times.
|
34597 | if (outNorm) { |
| 1083 |
1/2✓ Branch 0 taken 34597 times.
✗ Branch 1 not taken.
|
34597 | *n = *outNorm; |
| 1084 | 34597 | *validNorm = true; | |
| 1085 | 34597 | } | |
| 1086 | 34597 | } | |
| 1087 | 59403 | } | |
| 1088 | 59409 | return minDistance; | |
| 1089 | 59409 | } | |
| 1090 | |||
| 1091 | 40117 | G4double G4OCCTSolidKernel::ExactDistanceToOut(const G4ThreeVector& p) const { | |
| 1092 | 40117 | const auto match = TryFindClosestFace(fFaceBoundsCache, p); | |
| 1093 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 40117 times.
|
40117 | if (!match.has_value()) { |
| 1094 | ✗ | return 0.0; | |
| 1095 | } | ||
| 1096 |
2/4✓ Branch 0 taken 40117 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 40117 times.
|
40117 | return (match->distance <= IntersectionTolerance()) ? 0.0 : match->distance; |
| 1097 | 40117 | } | |
| 1098 | |||
| 1099 | 40176 | G4double G4OCCTSolidKernel::DistanceToOut(const G4ThreeVector& p, | |
| 1100 | SphereCacheData& sphereCache) const { | ||
| 1101 | G4double d; | ||
| 1102 |
2/2✓ Branch 0 taken 20179 times.
✓ Branch 1 taken 19997 times.
|
40176 | if (fAllFacesPlanar) { |
| 1103 | // For all-planar solids the analytical plane distances are already exact | ||
| 1104 | // lower bounds, so they avoid BVH traversal entirely. | ||
| 1105 | 20179 | d = PlanarFaceLowerBoundDistance(p); | |
| 1106 |
1/2✓ Branch 0 taken 20179 times.
✗ Branch 1 not taken.
|
20179 | if (d == G4OCCTSolidKernel::Infinity()) { |
| 1107 | ✗ | d = ExactDistanceToOut(p); | |
| 1108 | ✗ | } | |
| 1109 | 20179 | } else { | |
| 1110 | 19997 | const G4double bvhDist = BVHLowerBoundDistance(p); | |
| 1111 |
1/2✓ Branch 0 taken 19997 times.
✗ Branch 1 not taken.
|
19997 | d = (bvhDist < G4OCCTSolidKernel::Infinity()) ? bvhDist : ExactDistanceToOut(p); |
| 1112 | } | ||
| 1113 | // Feed the just-computed lower bound back into the adaptive sphere cache so | ||
| 1114 | // future interior queries can terminate earlier. | ||
| 1115 | 40176 | TryInsertSphere(sphereCache, p, d); | |
| 1116 | 40176 | return d; | |
| 1117 | } | ||
| 1118 | |||
| 1119 | 6 | G4double G4OCCTSolidKernel::GetCubicVolume() { | |
| 1120 | 6 | std::unique_lock<std::mutex> lock(fVolumeAreaMutex); | |
| 1121 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5 times.
|
6 | if (!fCachedVolume) { |
| 1122 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | GProp_GProps props; |
| 1123 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | BRepGProp::VolumeProperties(fShape, props); |
| 1124 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | fCachedVolume = props.Mass(); |
| 1125 | 5 | } | |
| 1126 | 6 | return *fCachedVolume; | |
| 1127 | 6 | } | |
| 1128 | |||
| 1129 | 3 | G4double G4OCCTSolidKernel::GetSurfaceArea() { | |
| 1130 | 3 | std::unique_lock<std::mutex> lock(fVolumeAreaMutex); | |
| 1131 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
|
3 | if (!fCachedSurfaceArea) { |
| 1132 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | GProp_GProps props; |
| 1133 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | BRepGProp::SurfaceProperties(fShape, props); |
| 1134 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | fCachedSurfaceArea = props.Mass(); |
| 1135 | 2 | } | |
| 1136 | 3 | return *fCachedSurfaceArea; | |
| 1137 | 3 | } | |
| 1138 | |||
| 1139 | 203 | const G4OCCTSolidKernel::SurfaceSamplingCache& G4OCCTSolidKernel::GetOrBuildSurfaceCache() const { | |
| 1140 | 203 | const std::uint64_t currentGen = fShapeGeneration.load(std::memory_order_acquire); | |
| 1141 | { | ||
| 1142 | 203 | std::unique_lock<std::mutex> lock(fSurfaceCacheMutex); | |
| 1143 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 203 times.
|
203 | while (fSurfaceCacheBuilding) { |
| 1144 | ✗ | fSurfaceCacheCV.wait(lock); | |
| 1145 | ✗ | if (fSurfaceCache.has_value() && fSurfaceCacheGeneration == currentGen) { | |
| 1146 | ✗ | return *fSurfaceCache; | |
| 1147 | } | ||
| 1148 | } | ||
| 1149 |
3/4✓ Branch 0 taken 198 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 198 times.
|
203 | if (fSurfaceCache.has_value() && fSurfaceCacheGeneration == currentGen) { |
| 1150 | 198 | return *fSurfaceCache; | |
| 1151 | } | ||
| 1152 | // Claim the build so only one thread pays for meshing/tessellation work on | ||
| 1153 | // a cold cache miss; waiters sleep on the condition variable above. | ||
| 1154 | 5 | fSurfaceCacheBuilding = true; | |
| 1155 | 203 | } | |
| 1156 | |||
| 1157 | 5 | SurfaceSamplingCache cache; | |
| 1158 | try { | ||
| 1159 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | BRepMesh_IncrementalMesh mesher(fShape, kOCCTRelativeDeflection, /*isRelative=*/Standard_True); |
| 1160 | (void)mesher; | ||
| 1161 | |||
| 1162 |
5/8✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 25 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 25 times.
✗ Branch 7 not taken.
|
30 | for (TopExp_Explorer ex(fShape, TopAbs_FACE); ex.More(); ex.Next()) { |
| 1163 |
2/4✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
|
25 | const TopoDS_Face& face = TopoDS::Face(ex.Current()); |
| 1164 |
1/2✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
|
25 | TopLoc_Location loc; |
| 1165 |
1/2✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
|
25 | const Handle(Poly_Triangulation) & triangulation = BRep_Tool::Triangulation(face, loc); |
| 1166 |
2/4✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
|
25 | if (triangulation.IsNull()) { |
| 1167 | ✗ | continue; | |
| 1168 | } | ||
| 1169 | |||
| 1170 | 25 | const auto faceIndex = static_cast<std::uint32_t>(cache.faces.size()); | |
| 1171 |
1/2✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
|
25 | cache.faces.push_back(face); |
| 1172 | |||
| 1173 |
1/2✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
|
25 | const gp_Trsf& transform = loc.Transformation(); |
| 1174 |
1/2✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
|
25 | const bool reverseWinding = face.Orientation() == TopAbs_REVERSED; |
| 1175 | |||
| 1176 |
4/6✓ Branch 0 taken 1051 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1051 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1026 times.
✓ Branch 5 taken 25 times.
|
1051 | for (Standard_Integer i = 1; i <= triangulation->NbTriangles(); ++i) { |
| 1177 | 1026 | Standard_Integer idx1 = 0; | |
| 1178 | 1026 | Standard_Integer idx2 = 0; | |
| 1179 | 1026 | Standard_Integer idx3 = 0; | |
| 1180 |
3/6✓ Branch 0 taken 1026 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1026 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1026 times.
✗ Branch 5 not taken.
|
1026 | triangulation->Triangle(i).Get(idx1, idx2, idx3); |
| 1181 |
2/2✓ Branch 0 taken 1002 times.
✓ Branch 1 taken 24 times.
|
1026 | if (reverseWinding) { |
| 1182 | 24 | std::swap(idx2, idx3); | |
| 1183 | 24 | } | |
| 1184 | |||
| 1185 |
3/6✓ Branch 0 taken 1026 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1026 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1026 times.
✗ Branch 5 not taken.
|
1026 | const gp_Pnt q1 = triangulation->Node(idx1).Transformed(transform); |
| 1186 |
3/6✓ Branch 0 taken 1026 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1026 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1026 times.
✗ Branch 5 not taken.
|
1026 | const gp_Pnt q2 = triangulation->Node(idx2).Transformed(transform); |
| 1187 |
3/6✓ Branch 0 taken 1026 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1026 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1026 times.
✗ Branch 5 not taken.
|
1026 | const gp_Pnt q3 = triangulation->Node(idx3).Transformed(transform); |
| 1188 | |||
| 1189 |
4/8✓ Branch 0 taken 1026 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1026 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1026 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1026 times.
✗ Branch 7 not taken.
|
1026 | const G4ThreeVector v1(q1.X(), q1.Y(), q1.Z()); |
| 1190 |
4/8✓ Branch 0 taken 1026 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1026 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1026 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1026 times.
✗ Branch 7 not taken.
|
1026 | const G4ThreeVector v2(q2.X(), q2.Y(), q2.Z()); |
| 1191 |
4/8✓ Branch 0 taken 1026 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1026 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1026 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1026 times.
✗ Branch 7 not taken.
|
1026 | const G4ThreeVector v3(q3.X(), q3.Y(), q3.Z()); |
| 1192 | |||
| 1193 |
4/8✓ Branch 0 taken 1026 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1026 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1026 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1026 times.
✗ Branch 7 not taken.
|
1026 | const G4double area = 0.5 * (v2 - v1).cross(v3 - v1).mag(); |
| 1194 |
2/2✓ Branch 0 taken 1024 times.
✓ Branch 1 taken 2 times.
|
1026 | if (area > 0.0) { |
| 1195 | // Prefix sums let GetPointOnSurface() perform an area-weighted random | ||
| 1196 | // triangle selection with one lower_bound on cumulativeAreas. | ||
| 1197 | 1024 | cache.totalArea += area; | |
| 1198 |
1/2✓ Branch 0 taken 1024 times.
✗ Branch 1 not taken.
|
1024 | cache.cumulativeAreas.push_back(cache.totalArea); |
| 1199 |
4/8✓ Branch 0 taken 1024 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1024 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1024 times.
✓ Branch 6 taken 1024 times.
✗ Branch 7 not taken.
|
1024 | cache.triangles.push_back({v1, v2, v3, faceIndex}); |
| 1200 | 1024 | } | |
| 1201 | 1026 | } | |
| 1202 | 25 | } | |
| 1203 | 5 | } catch (...) { | |
| 1204 | ✗ | std::unique_lock<std::mutex> lock(fSurfaceCacheMutex); | |
| 1205 | ✗ | fSurfaceCacheBuilding = false; | |
| 1206 | ✗ | lock.unlock(); | |
| 1207 | ✗ | fSurfaceCacheCV.notify_all(); | |
| 1208 | ✗ | throw; | |
| 1209 | ✗ | } | |
| 1210 | |||
| 1211 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | std::unique_lock<std::mutex> lock(fSurfaceCacheMutex); |
| 1212 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
5 | if (!(fSurfaceCache.has_value() && fSurfaceCacheGeneration == currentGen)) { |
| 1213 | 5 | fSurfaceCache = std::move(cache); | |
| 1214 | 5 | fSurfaceCacheGeneration = currentGen; | |
| 1215 | 5 | } | |
| 1216 | 5 | fSurfaceCacheBuilding = false; | |
| 1217 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | lock.unlock(); |
| 1218 | 5 | fSurfaceCacheCV.notify_all(); | |
| 1219 | 5 | return *fSurfaceCache; | |
| 1220 | 203 | } | |
| 1221 | |||
| 1222 | 200 | G4ThreeVector G4OCCTSolidKernel::GetPointOnSurface(const char* diagnosticName) const { | |
| 1223 | 200 | const SurfaceSamplingCache& cache = GetOrBuildSurfaceCache(); | |
| 1224 | |||
| 1225 |
2/4✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 200 times.
|
200 | if (cache.triangles.empty() || cache.totalArea == 0.0) { |
| 1226 | ✗ | G4ExceptionDescription msg; | |
| 1227 | ✗ | msg << "Tessellation of the OCCT shape"; | |
| 1228 | ✗ | if (diagnosticName != nullptr) { | |
| 1229 | ✗ | msg << " for solid \"" << diagnosticName << "\""; | |
| 1230 | ✗ | } | |
| 1231 | ✗ | msg << " produced no valid triangles; cannot sample a point on the surface."; | |
| 1232 | ✗ | G4Exception("G4OCCTSolidKernel::GetPointOnSurface", "GeomMgt1001", FatalException, msg); | |
| 1233 | ✗ | return {0.0, 0.0, 0.0}; | |
| 1234 | ✗ | } | |
| 1235 | |||
| 1236 | 200 | const G4double target = G4UniformRand() * cache.totalArea; | |
| 1237 | 200 | const auto it = std::ranges::lower_bound(cache.cumulativeAreas, target); | |
| 1238 | 400 | const std::size_t idx = std::min(static_cast<std::size_t>(it - cache.cumulativeAreas.begin()), | |
| 1239 | 200 | cache.triangles.size() - 1); | |
| 1240 | 200 | const SurfaceTriangle& chosen = cache.triangles[idx]; | |
| 1241 | |||
| 1242 | 200 | G4double r1 = G4UniformRand(); | |
| 1243 | 200 | G4double r2 = G4UniformRand(); | |
| 1244 |
2/2✓ Branch 0 taken 90 times.
✓ Branch 1 taken 110 times.
|
200 | if (r1 + r2 > 1.0) { |
| 1245 | 110 | r1 = 1.0 - r1; | |
| 1246 | 110 | r2 = 1.0 - r2; | |
| 1247 | 110 | } | |
| 1248 | |||
| 1249 | const G4ThreeVector tessPoint = | ||
| 1250 |
5/10✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 200 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 200 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 200 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 200 times.
✗ Branch 9 not taken.
|
200 | chosen.p1 + r1 * (chosen.p2 - chosen.p1) + r2 * (chosen.p3 - chosen.p1); |
| 1251 | |||
| 1252 | 200 | const TopoDS_Face& face = cache.faces[chosen.faceIndex]; | |
| 1253 |
1/2✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
|
200 | TopLoc_Location loc; |
| 1254 |
2/4✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 200 times.
✗ Branch 3 not taken.
|
200 | const Handle(Geom_Surface) geomSurface = BRep_Tool::Surface(face, loc); |
| 1255 |
2/4✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 200 times.
✗ Branch 3 not taken.
|
200 | if (!geomSurface.IsNull()) { |
| 1256 |
4/8✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 200 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 200 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 200 times.
✗ Branch 7 not taken.
|
200 | gp_Pnt tessPointLocal(tessPoint.x(), tessPoint.y(), tessPoint.z()); |
| 1257 |
2/4✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 200 times.
|
200 | if (!loc.IsIdentity()) { |
| 1258 | ✗ | tessPointLocal.Transform(loc.Transformation().Inverted()); | |
| 1259 | ✗ | } | |
| 1260 |
1/2✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
|
200 | GeomAPI_ProjectPointOnSurf projection(tessPointLocal, geomSurface); |
| 1261 |
2/4✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 200 times.
✗ Branch 3 not taken.
|
200 | if (projection.NbPoints() > 0) { |
| 1262 |
1/2✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
|
200 | gp_Pnt projectedPoint = projection.NearestPoint(); |
| 1263 |
2/4✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 200 times.
|
200 | if (!loc.IsIdentity()) { |
| 1264 | ✗ | projectedPoint.Transform(loc.Transformation()); | |
| 1265 | ✗ | } | |
| 1266 |
4/8✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 200 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 200 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 200 times.
✗ Branch 7 not taken.
|
200 | return {projectedPoint.X(), projectedPoint.Y(), projectedPoint.Z()}; |
| 1267 | } | ||
| 1268 | 200 | } | |
| 1269 | |||
| 1270 | ✗ | return tessPoint; | |
| 1271 | 200 | } | |
| 1272 | |||
| 1273 | } // namespace g4occt::detail | ||
| 1274 |