G4OCCT 0.1.0
Geant4 interface to Open CASCADE Technology (OCCT) geometry definitions
Loading...
Searching...
No Matches
TGeoOCCTSolidBridge.cc
Go to the documentation of this file.
1// SPDX-License-Identifier: LGPL-2.1-or-later
2// Copyright (C) 2026 G4OCCT Contributors
3
5
7
8#include <IFSelect_ReturnStatus.hxx>
9#include <STEPControl_Reader.hxx>
10
11#include <G4ThreeVector.hh>
12
13#include <atomic>
14#include <cstdint>
15#include <limits>
16#include <memory>
17#include <mutex>
18#include <stdexcept>
19#include <unordered_map>
20
21namespace g4occt::detail {
22
23namespace {
24
25 constexpr double kCmToMm = 10.0;
26 constexpr double kMmToCm = 0.1;
27 constexpr double kMm3ToCm3 = 0.001;
28
29 G4ThreeVector ToKernelPoint(const double* point_cm) {
30 return G4ThreeVector(point_cm[0] * kCmToMm, point_cm[1] * kCmToMm, point_cm[2] * kCmToMm);
31 }
32
33 G4ThreeVector ToKernelDirection(const double* dir) {
34 return G4ThreeVector(dir[0], dir[1], dir[2]);
35 }
36
37 void WriteNormal(const G4ThreeVector& normal, double* out) {
38 out[0] = normal.x();
39 out[1] = normal.y();
40 out[2] = normal.z();
41 }
42
44 ToBridgePointClassification(const G4OCCTSolidKernel::PointClassification classification) {
45 switch (classification) {
52 }
53 throw std::runtime_error("TGeoOCCTSolidBridge::ClassifyCm: unknown point classification");
54 }
55
56 struct ThreadCaches {
57 G4OCCTSolidKernel::ClassifierCache classifier;
58 G4OCCTSolidKernel::IntersectorCache intersector;
59 G4OCCTSolidKernel::SphereCacheData sphere;
60 };
61
62 struct ThreadCacheEntry {
63 std::weak_ptr<const void> lifetime;
64 std::unique_ptr<ThreadCaches> caches;
65 };
66
67} // namespace
68
70public:
71 explicit Impl(const TopoDS_Shape& shape)
73 , cacheLifetime(std::make_shared<const std::uint8_t>(0))
74 , kernel(shape) {}
76
77 ThreadCaches& CachesForThisThread() const {
78 auto& caches = ThreadLocalCaches();
79 auto existing = caches.find(cacheKey);
80 if (existing != caches.end()) {
81 return *existing->second.caches;
82 }
83
84 for (auto it = caches.begin(); it != caches.end();) {
85 if (it->second.lifetime.expired()) {
86 it = caches.erase(it);
87 } else {
88 ++it;
89 }
90 }
91
92 auto it = caches
93 .emplace(cacheKey, ThreadCacheEntry{std::weak_ptr<const void>{cacheLifetime},
94 std::make_unique<ThreadCaches>()})
95 .first;
96 return *it->second.caches;
97 }
98
99 static std::unordered_map<std::uint64_t, ThreadCacheEntry>& ThreadLocalCaches() {
100 static thread_local std::unordered_map<std::uint64_t, ThreadCacheEntry> caches;
101 return caches;
102 }
103
104 static std::uint64_t NextCacheKey() {
105 static std::atomic<std::uint64_t> nextKey{1};
106 return nextKey.fetch_add(1, std::memory_order_relaxed);
107 }
108
109 const std::uint64_t cacheKey;
110 std::shared_ptr<const void> cacheLifetime;
112 mutable std::shared_ptr<const DisplayMeshCm> displayMesh;
113 mutable std::uint64_t displayMeshGeneration{std::numeric_limits<std::uint64_t>::max()};
114 mutable std::mutex displayMeshMutex;
115};
116
118
120 : fImpl(std::make_unique<Impl>(shape)) {}
121
123
124std::unique_ptr<TGeoOCCTSolidBridge> TGeoOCCTSolidBridge::FromSTEP(const std::string& name,
125 const std::string& path) {
126 STEPControl_Reader reader;
127 const IFSelect_ReturnStatus status = reader.ReadFile(path.c_str());
128 if (status != IFSelect_RetDone) {
129 throw std::runtime_error("TGeoOCCTSolidBridge::FromSTEP: failed to read STEP file \"" + path +
130 "\"");
131 }
132 if (reader.NbRootsForTransfer() == 0) {
133 throw std::runtime_error("TGeoOCCTSolidBridge::FromSTEP: STEP file \"" + path +
134 "\" contains no root shapes");
135 }
136 const Standard_Integer transferredRoots = reader.TransferRoots();
137 if (transferredRoots == 0) {
138 throw std::runtime_error(
139 "TGeoOCCTSolidBridge::FromSTEP: STEP transfer produced no shapes for \"" + path + "\"");
140 }
141 const TopoDS_Shape shape = reader.OneShape();
142 if (shape.IsNull()) {
143 throw std::runtime_error("TGeoOCCTSolidBridge::FromSTEP: STEP file \"" + path +
144 "\" yielded a null shape for \"" + name + "\"");
145 }
146 return std::make_unique<TGeoOCCTSolidBridge>(shape);
147}
148
149const TopoDS_Shape& TGeoOCCTSolidBridge::Shape() const { return fImpl->kernel.Shape(); }
150
151void TGeoOCCTSolidBridge::SetShape(const TopoDS_Shape& shape) { fImpl->kernel.SetShape(shape); }
152
154 const auto& bounds = fImpl->kernel.Bounds();
155 const G4ThreeVector center_mm = 0.5 * (bounds.min + bounds.max);
156 const G4ThreeVector half_mm = 0.5 * (bounds.max - bounds.min);
157 return BoundsCm{
158 .dx = half_mm.x() * kMmToCm,
159 .dy = half_mm.y() * kMmToCm,
160 .dz = half_mm.z() * kMmToCm,
161 .origin = {center_mm.x() * kMmToCm, center_mm.y() * kMmToCm, center_mm.z() * kMmToCm},
162 };
163}
164
165std::shared_ptr<const TGeoOCCTSolidBridge::DisplayMeshCm> TGeoOCCTSolidBridge::DisplayMesh() const {
166 for (;;) {
167 const std::uint64_t generationToBuild = fImpl->kernel.ShapeGeneration();
168 {
169 std::unique_lock<std::mutex> lock(fImpl->displayMeshMutex);
170 if (fImpl->displayMesh && fImpl->displayMeshGeneration == generationToBuild) {
171 return fImpl->displayMesh;
172 }
173 }
174
175 DisplayMeshCm mesh;
176 const auto& surfaceCache = fImpl->kernel.GetOrBuildSurfaceCache();
177 mesh.triangles.reserve(surfaceCache.triangles.size());
178 for (const auto& tri : surfaceCache.triangles) {
179 mesh.triangles.push_back(DisplayTriangleCm{
180 .p1 = {tri.p1.x() * kMmToCm, tri.p1.y() * kMmToCm, tri.p1.z() * kMmToCm},
181 .p2 = {tri.p2.x() * kMmToCm, tri.p2.y() * kMmToCm, tri.p2.z() * kMmToCm},
182 .p3 = {tri.p3.x() * kMmToCm, tri.p3.y() * kMmToCm, tri.p3.z() * kMmToCm},
183 });
184 }
185 auto builtMesh = std::make_shared<DisplayMeshCm>(std::move(mesh));
186
187 std::unique_lock<std::mutex> lock(fImpl->displayMeshMutex);
188 const std::uint64_t currentGeneration = fImpl->kernel.ShapeGeneration();
189 if (fImpl->displayMesh && fImpl->displayMeshGeneration == currentGeneration) {
190 return fImpl->displayMesh;
191 }
192 if (currentGeneration != generationToBuild) {
193 continue;
194 }
195 fImpl->displayMesh = std::move(builtMesh);
196 fImpl->displayMeshGeneration = currentGeneration;
197 return fImpl->displayMesh;
198 }
199}
200
202 return fImpl->kernel.ShapeGeneration();
203}
204
206 return fImpl->kernel.GetCubicVolume() * kMm3ToCm3;
207}
208
209double TGeoOCCTSolidBridge::SafetyCm(const double* point_cm, bool inside) const {
210 ThreadCaches& caches = fImpl->CachesForThisThread();
211 const G4ThreeVector point_mm = ToKernelPoint(point_cm);
212 const double safety_mm = inside ? fImpl->kernel.DistanceToOut(point_mm, caches.sphere)
213 : fImpl->kernel.DistanceToIn(point_mm, caches.classifier);
214 return safety_mm * kMmToCm;
215}
216
217double TGeoOCCTSolidBridge::DistFromOutsideCm(const double* point_cm, const double* dir,
218 double* safe_cm) const {
219 ThreadCaches& caches = fImpl->CachesForThisThread();
220 const G4ThreeVector point_mm = ToKernelPoint(point_cm);
221 if (safe_cm != nullptr) {
222 *safe_cm = fImpl->kernel.DistanceToIn(point_mm, caches.classifier) * kMmToCm;
223 }
224 const double dist_mm =
225 fImpl->kernel.DistanceToIn(point_mm, ToKernelDirection(dir), caches.intersector);
226 return dist_mm * kMmToCm;
227}
228
229double TGeoOCCTSolidBridge::DistFromInsideCm(const double* point_cm, const double* dir,
230 double* safe_cm, bool calcNorm, double* norm,
231 bool* validNorm) const {
232 ThreadCaches& caches = fImpl->CachesForThisThread();
233 const G4ThreeVector point_mm = ToKernelPoint(point_cm);
234 if (safe_cm != nullptr) {
235 *safe_cm = fImpl->kernel.DistanceToOut(point_mm, caches.sphere) * kMmToCm;
236 }
237
238 G4bool g4ValidNorm = false;
239 G4ThreeVector g4Norm;
240 const double dist_mm =
241 fImpl->kernel.DistanceToOut(point_mm, ToKernelDirection(dir), caches.intersector, calcNorm,
242 &g4ValidNorm, calcNorm ? &g4Norm : nullptr);
243 if (validNorm != nullptr) {
244 *validNorm = g4ValidNorm;
245 }
246 if (calcNorm && norm != nullptr && g4ValidNorm) {
247 WriteNormal(g4Norm, norm);
248 }
249 return dist_mm * kMmToCm;
250}
251
253TGeoOCCTSolidBridge::ClassifyCm(const double* point_cm) const {
254 ThreadCaches& caches = fImpl->CachesForThisThread();
255 return ToBridgePointClassification(fImpl->kernel.ClassifyPoint(
256 ToKernelPoint(point_cm), caches.classifier, caches.intersector, caches.sphere));
257}
258
259void TGeoOCCTSolidBridge::SurfaceNormalCm(const double* point_cm, double* norm) const {
260 WriteNormal(fImpl->kernel.SurfaceNormal(ToKernelPoint(point_cm)), norm);
261}
262
263} // namespace g4occt::detail
Shared OCCT-backed solid query kernel for adapter frontends.
std::unique_ptr< ThreadCaches > caches
G4OCCTSolidKernel::SphereCacheData sphere
G4OCCTSolidKernel::ClassifierCache classifier
G4OCCTSolidKernel::IntersectorCache intersector
std::weak_ptr< const void > lifetime
ROOT-independent bridge from TGeoOCCTSolid to the shared OCCT kernel.
Shared OCCT-backed query kernel used by adapter frontends.
PointClassification
Classification result for point-in-solid queries.
static G4double Infinity()
Return the Geant4 navigation infinity sentinel used by the kernel.
static std::unordered_map< std::uint64_t, ThreadCacheEntry > & ThreadLocalCaches()
std::shared_ptr< const DisplayMeshCm > displayMesh
double DistFromOutsideCm(const double *point_cm, const double *dir, double *safe_cm=nullptr) const
double SafetyCm(const double *point_cm, bool inside) const
void SurfaceNormalCm(const double *point_cm, double *norm) const
std::shared_ptr< const DisplayMeshCm > DisplayMesh() const
static std::unique_ptr< TGeoOCCTSolidBridge > FromSTEP(const std::string &name, const std::string &path)
PointClassification ClassifyCm(const double *point_cm) const
TGeoOCCTSolidBridge(const TopoDS_Shape &shape)
void SetShape(const TopoDS_Shape &shape)
double DistFromInsideCm(const double *point_cm, const double *dir, double *safe_cm=nullptr, bool calcNorm=false, double *norm=nullptr, bool *validNorm=nullptr) const