G4OCCT 0.1.0
Geant4 interface to Open CASCADE Technology (OCCT) geometry definitions
Loading...
Searching...
No Matches
G4OCCTSolid.cc
Go to the documentation of this file.
1// SPDX-License-Identifier: LGPL-2.1-or-later
2// Copyright (C) 2026 G4OCCT Contributors
3
6
8
10
11#include <BRepMesh_IncrementalMesh.hxx>
12#include <BRep_Tool.hxx>
13#include <IFSelect_ReturnStatus.hxx>
14#include <Poly_Triangulation.hxx>
15#include <STEPControl_Reader.hxx>
16#include <TopAbs_Orientation.hxx>
17#include <TopAbs_ShapeEnum.hxx>
18#include <TopExp_Explorer.hxx>
19#include <TopLoc_Location.hxx>
20#include <TopoDS.hxx>
21#include <TopoDS_Face.hxx>
22#include <gp_Pnt.hxx>
23
24#include <G4AffineTransform.hh>
25#include <G4BoundingEnvelope.hh>
26#include <G4Exception.hh>
27#include <G4Polyhedron.hh>
28#include <G4TessellatedSolid.hh>
29#include <G4ThreadLocalSingleton.hh>
30#include <G4TriangularFacet.hh>
31#include <G4VGraphicsScene.hh>
32#include <G4VisExtent.hh>
33
34#include <condition_variable>
35#include <cstdint>
36#include <limits>
37#include <memory>
38#include <mutex>
39#include <stdexcept>
40#include <utility>
41
43
45public:
46 explicit Impl(const TopoDS_Shape& shape) : kernel(shape) {}
47
49 mutable G4ThreadLocalSingleton<G4OCCTSolidKernel::ClassifierCache> classifierCache;
50 mutable G4ThreadLocalSingleton<G4OCCTSolidKernel::IntersectorCache> intersectorCache;
51 mutable G4ThreadLocalSingleton<G4OCCTSolidKernel::SphereCacheData> sphereCache;
52
53 mutable std::unique_ptr<G4Polyhedron> cachedPolyhedron;
54 mutable std::uint64_t polyhedronGeneration{std::numeric_limits<std::uint64_t>::max()};
55 mutable bool polyhedronBuilding{false};
56 mutable std::mutex polyhedronMutex;
57 mutable std::condition_variable polyhedronCV;
58};
59
60namespace {
61
62EInside ToG4Inside(const G4OCCTSolidKernel::PointClassification classification) {
63 switch (classification) {
65 return kInside;
67 return kSurface;
69 default:
70 return kOutside;
71 }
72}
73
74} // namespace
75
76G4OCCTSolid::G4OCCTSolid(const G4String& name, const TopoDS_Shape& shape)
77 : G4VSolid(name), fImpl(std::make_unique<Impl>(shape)) {}
78
80
81G4OCCTSolid* G4OCCTSolid::FromSTEP(const G4String& name, const std::string& path) {
82 STEPControl_Reader reader;
83 const IFSelect_ReturnStatus status = reader.ReadFile(path.c_str());
84 if (status != IFSelect_RetDone) {
85 throw std::runtime_error("G4OCCTSolid::FromSTEP: failed to read STEP file \"" + path + "\"");
86 }
87
88 const Standard_Integer nRoots = reader.NbRootsForTransfer();
89 if (nRoots == 0) {
90 throw std::runtime_error("G4OCCTSolid::FromSTEP: STEP file \"" + path +
91 "\" contains no root "
92 "shapes");
93 }
94 if (reader.TransferRoots() <= 0) {
95 throw std::runtime_error("G4OCCTSolid::FromSTEP: failed to transfer STEP roots from \"" + path +
96 "\"");
97 }
98
99 const TopoDS_Shape shape = reader.OneShape();
100 if (shape.IsNull()) {
101 throw std::runtime_error("G4OCCTSolid::FromSTEP: STEP file \"" + path +
102 "\" yielded a null shape");
103 }
104 return new G4OCCTSolid(name, shape);
105}
106
107EInside G4OCCTSolid::Inside(const G4ThreeVector& p) const {
108 return ToG4Inside(fImpl->kernel.ClassifyPoint(p, *fImpl->classifierCache.Instance(),
109 *fImpl->intersectorCache.Instance(),
110 *fImpl->sphereCache.Instance()));
111}
112
113G4ThreeVector G4OCCTSolid::SurfaceNormal(const G4ThreeVector& p) const {
114 return fImpl->kernel.SurfaceNormal(p);
115}
116
117G4double G4OCCTSolid::DistanceToIn(const G4ThreeVector& p, const G4ThreeVector& v) const {
118 return fImpl->kernel.DistanceToIn(p, v, *fImpl->intersectorCache.Instance());
119}
120
121G4double G4OCCTSolid::DistanceToIn(const G4ThreeVector& p) const {
122 return fImpl->kernel.DistanceToIn(p, *fImpl->classifierCache.Instance());
123}
124
125G4double G4OCCTSolid::DistanceToOut(const G4ThreeVector& p, const G4ThreeVector& v,
126 const G4bool calcNorm, G4bool* validNorm,
127 G4ThreeVector* n) const {
128 return fImpl->kernel.DistanceToOut(p, v, *fImpl->intersectorCache.Instance(), calcNorm, validNorm,
129 n);
130}
131
132G4double G4OCCTSolid::DistanceToOut(const G4ThreeVector& p) const {
133 return fImpl->kernel.DistanceToOut(p, *fImpl->sphereCache.Instance());
134}
135
136G4ThreeVector G4OCCTSolid::GetPointOnSurface() const {
137 return fImpl->kernel.GetPointOnSurface(GetName().c_str());
138}
139
140G4double G4OCCTSolid::ExactDistanceToIn(const G4ThreeVector& p) const {
141 return fImpl->kernel.ExactDistanceToIn(p, *fImpl->classifierCache.Instance());
142}
143
144G4double G4OCCTSolid::ExactDistanceToOut(const G4ThreeVector& p) const {
145 return fImpl->kernel.ExactDistanceToOut(p);
146}
147
148G4double G4OCCTSolid::GetCubicVolume() { return fImpl->kernel.GetCubicVolume(); }
149
150G4double G4OCCTSolid::GetSurfaceArea() { return fImpl->kernel.GetSurfaceArea(); }
151
152G4GeometryType G4OCCTSolid::GetEntityType() const { return "G4OCCTSolid"; }
153
154G4VisExtent G4OCCTSolid::GetExtent() const {
155 const auto& bounds = fImpl->kernel.Bounds();
156 return {bounds.min.x(), bounds.max.x(), bounds.min.y(),
157 bounds.max.y(), bounds.min.z(), bounds.max.z()};
158}
159
160void G4OCCTSolid::BoundingLimits(G4ThreeVector& pMin, G4ThreeVector& pMax) const {
161 const auto& bounds = fImpl->kernel.Bounds();
162 pMin = bounds.min;
163 pMax = bounds.max;
164}
165
166G4bool G4OCCTSolid::CalculateExtent(const EAxis pAxis, const G4VoxelLimits& pVoxelLimit,
167 const G4AffineTransform& pTransform, G4double& pMin,
168 G4double& pMax) const {
169 const auto& bounds = fImpl->kernel.Bounds();
170 const G4BoundingEnvelope envelope(bounds.min, bounds.max);
171 return envelope.CalculateExtent(pAxis, pVoxelLimit, G4Transform3D(pTransform), pMin, pMax);
172}
173
174void G4OCCTSolid::DescribeYourselfTo(G4VGraphicsScene& scene) const { scene.AddSolid(*this); }
175
176G4Polyhedron* G4OCCTSolid::CreatePolyhedron() const {
177 const auto currentGeneration = fImpl->kernel.ShapeGeneration();
178
179 {
180 std::unique_lock<std::mutex> lock(fImpl->polyhedronMutex);
181 fImpl->polyhedronCV.wait(lock, [this, currentGeneration] {
182 return !fImpl->polyhedronBuilding || fImpl->polyhedronGeneration == currentGeneration;
183 });
184 if (fImpl->cachedPolyhedron && fImpl->polyhedronGeneration == currentGeneration) {
185 return new G4Polyhedron(*fImpl->cachedPolyhedron);
186 }
187 fImpl->polyhedronBuilding = true;
188 }
189
190 std::unique_ptr<G4Polyhedron> freshPolyhedron;
191 try {
192 BRepMesh_IncrementalMesh mesher(fImpl->kernel.Shape(), g4occt::detail::kOCCTRelativeDeflection,
193 /*isRelative=*/Standard_True);
194 (void)mesher;
195
196 G4TessellatedSolid tessellatedSolid(GetName() + "_polyhedron");
197 G4int facetCount = 0;
198
199 for (TopExp_Explorer explorer(fImpl->kernel.Shape(), TopAbs_FACE); explorer.More();
200 explorer.Next()) {
201 const TopoDS_Face& face = TopoDS::Face(explorer.Current());
202 TopLoc_Location location;
203 const Handle(Poly_Triangulation) & triangulation = BRep_Tool::Triangulation(face, location);
204 if (triangulation.IsNull()) {
205 continue;
206 }
207
208 const gp_Trsf& transform = location.Transformation();
209 const bool reverseWinding = face.Orientation() == TopAbs_REVERSED;
210
211 for (Standard_Integer triangleIndex = 1; triangleIndex <= triangulation->NbTriangles();
212 ++triangleIndex) {
213 Standard_Integer index1 = 0;
214 Standard_Integer index2 = 0;
215 Standard_Integer index3 = 0;
216 triangulation->Triangle(triangleIndex).Get(index1, index2, index3);
217
218 if (reverseWinding) {
219 std::swap(index2, index3);
220 }
221
222 const gp_Pnt point1 = triangulation->Node(index1).Transformed(transform);
223 const gp_Pnt point2 = triangulation->Node(index2).Transformed(transform);
224 const gp_Pnt point3 = triangulation->Node(index3).Transformed(transform);
225
226 auto* facet =
227 new G4TriangularFacet(G4ThreeVector(point1.X(), point1.Y(), point1.Z()),
228 G4ThreeVector(point2.X(), point2.Y(), point2.Z()),
229 G4ThreeVector(point3.X(), point3.Y(), point3.Z()), ABSOLUTE);
230 if (!facet->IsDefined() || !tessellatedSolid.AddFacet(facet)) {
231 delete facet;
232 continue;
233 }
234 ++facetCount;
235 }
236 }
237
238 if (facetCount > 0) {
239 tessellatedSolid.SetSolidClosed(true);
240 G4Polyhedron* tmp = tessellatedSolid.GetPolyhedron();
241 if (tmp != nullptr) {
242 freshPolyhedron = std::make_unique<G4Polyhedron>(*tmp);
243 }
244 }
245 } catch (...) {
246 std::unique_lock<std::mutex> lock(fImpl->polyhedronMutex);
247 fImpl->polyhedronBuilding = false;
248 lock.unlock();
249 fImpl->polyhedronCV.notify_all();
250 throw;
251 }
252
253 {
254 std::unique_lock<std::mutex> lock(fImpl->polyhedronMutex);
255 bool cacheWritten = false;
256 if (freshPolyhedron && fImpl->kernel.ShapeGeneration() == currentGeneration) {
257 fImpl->cachedPolyhedron = std::make_unique<G4Polyhedron>(*freshPolyhedron);
258 fImpl->polyhedronGeneration = currentGeneration;
259 cacheWritten = true;
260 }
261 fImpl->polyhedronBuilding = false;
262 fImpl->polyhedronCV.notify_all();
263 if (cacheWritten) {
264 return new G4Polyhedron(*fImpl->cachedPolyhedron);
265 }
266 }
267
268 return freshPolyhedron ? new G4Polyhedron(*freshPolyhedron) : nullptr;
269}
270
271std::ostream& G4OCCTSolid::StreamInfo(std::ostream& os) const {
272 os << "-----------------------------------------------------------\n"
273 << " *** Dump for solid - " << GetName() << " ***\n"
274 << " ===================================================\n"
275 << " Solid type: G4OCCTSolid\n"
276 << " OCCT shape type: " << fImpl->kernel.Shape().ShapeType() << "\n"
277 << "-----------------------------------------------------------\n";
278 return os;
279}
280
281const TopoDS_Shape& G4OCCTSolid::GetOCCTShape() const { return fImpl->kernel.Shape(); }
282
283void G4OCCTSolid::SetOCCTShape(const TopoDS_Shape& shape) {
284 fImpl->kernel.SetShape(shape);
285 std::unique_lock<std::mutex> lock(fImpl->polyhedronMutex);
286 fImpl->cachedPolyhedron.reset();
287 fImpl->polyhedronGeneration = std::numeric_limits<std::uint64_t>::max();
288}
Shared OCCT-backed solid query kernel for adapter frontends.
Declaration of G4OCCTSolid.
Impl(const TopoDS_Shape &shape)
G4OCCTSolidKernel kernel
G4ThreadLocalSingleton< G4OCCTSolidKernel::SphereCacheData > sphereCache
G4ThreadLocalSingleton< G4OCCTSolidKernel::IntersectorCache > intersectorCache
std::unique_ptr< G4Polyhedron > cachedPolyhedron
std::condition_variable polyhedronCV
G4ThreadLocalSingleton< G4OCCTSolidKernel::ClassifierCache > classifierCache
std::uint64_t polyhedronGeneration
std::mutex polyhedronMutex
Geant4 solid wrapping an Open CASCADE Technology (OCCT) TopoDS_Shape.
G4double ExactDistanceToOut(const G4ThreeVector &p) const
EInside Inside(const G4ThreeVector &p) const override
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
G4double DistanceToIn(const G4ThreeVector &p, const G4ThreeVector &v) const override
void SetOCCTShape(const TopoDS_Shape &shape)
void BoundingLimits(G4ThreeVector &pMin, G4ThreeVector &pMax) const override
G4OCCTSolid(const G4String &name, const TopoDS_Shape &shape)
G4ThreeVector SurfaceNormal(const G4ThreeVector &p) const override
G4bool CalculateExtent(const EAxis pAxis, const G4VoxelLimits &pVoxelLimit, const G4AffineTransform &pTransform, G4double &pMin, G4double &pMax) const override
~G4OCCTSolid() override
G4double GetSurfaceArea() override
G4GeometryType GetEntityType() const override
const TopoDS_Shape & GetOCCTShape() const
G4ThreeVector GetPointOnSurface() const override
G4VisExtent GetExtent() const override
std::ostream & StreamInfo(std::ostream &os) const override
static G4OCCTSolid * FromSTEP(const G4String &name, const std::string &path)
void DescribeYourselfTo(G4VGraphicsScene &scene) const override
G4double ExactDistanceToIn(const G4ThreeVector &p) const
Shared OCCT-backed query kernel used by adapter frontends.
PointClassification
Classification result for point-in-solid queries.
constexpr Standard_Real kOCCTRelativeDeflection