8#include <IFSelect_ReturnStatus.hxx>
9#include <STEPControl_Reader.hxx>
11#include <G4ThreeVector.hh>
19#include <unordered_map>
25 constexpr double kCmToMm = 10.0;
26 constexpr double kMmToCm = 0.1;
27 constexpr double kMm3ToCm3 = 0.001;
29 G4ThreeVector ToKernelPoint(
const double* point_cm) {
30 return G4ThreeVector(point_cm[0] * kCmToMm, point_cm[1] * kCmToMm, point_cm[2] * kCmToMm);
33 G4ThreeVector ToKernelDirection(
const double* dir) {
34 return G4ThreeVector(dir[0], dir[1], dir[2]);
37 void WriteNormal(
const G4ThreeVector& normal,
double* out) {
45 switch (classification) {
53 throw std::runtime_error(
"TGeoOCCTSolidBridge::ClassifyCm: unknown point classification");
59 G4OCCTSolidKernel::SphereCacheData
sphere;
62 struct ThreadCacheEntry {
64 std::unique_ptr<ThreadCaches>
caches;
71 explicit Impl(
const TopoDS_Shape& shape)
80 if (existing !=
caches.end()) {
81 return *existing->second.caches;
85 if (it->second.lifetime.expired()) {
94 std::make_unique<ThreadCaches>()})
96 return *it->second.caches;
100 static thread_local std::unordered_map<std::uint64_t, ThreadCacheEntry>
caches;
105 static std::atomic<std::uint64_t> nextKey{1};
106 return nextKey.fetch_add(1, std::memory_order_relaxed);
120 : fImpl(std::make_unique<
Impl>(shape)) {}
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 +
132 if (reader.NbRootsForTransfer() == 0) {
133 throw std::runtime_error(
"TGeoOCCTSolidBridge::FromSTEP: STEP file \"" + path +
134 "\" contains no root shapes");
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 +
"\"");
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 +
"\"");
146 return std::make_unique<TGeoOCCTSolidBridge>(shape);
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);
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},
167 const std::uint64_t generationToBuild = fImpl->kernel.ShapeGeneration();
169 std::unique_lock<std::mutex> lock(fImpl->displayMeshMutex);
170 if (fImpl->displayMesh && fImpl->displayMeshGeneration == generationToBuild) {
171 return fImpl->displayMesh;
176 const auto& surfaceCache = fImpl->kernel.GetOrBuildSurfaceCache();
177 mesh.
triangles.reserve(surfaceCache.triangles.size());
178 for (
const auto& tri : surfaceCache.triangles) {
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},
185 auto builtMesh = std::make_shared<DisplayMeshCm>(std::move(mesh));
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;
192 if (currentGeneration != generationToBuild) {
195 fImpl->displayMesh = std::move(builtMesh);
196 fImpl->displayMeshGeneration = currentGeneration;
197 return fImpl->displayMesh;
202 return fImpl->kernel.ShapeGeneration();
206 return fImpl->kernel.GetCubicVolume() * kMm3ToCm3;
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;
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;
224 const double dist_mm =
225 fImpl->kernel.DistanceToIn(point_mm, ToKernelDirection(dir),
caches.intersector);
226 return dist_mm * kMmToCm;
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;
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;
246 if (calcNorm && norm !=
nullptr && g4ValidNorm) {
247 WriteNormal(g4Norm, norm);
249 return dist_mm * kMmToCm;
254 ThreadCaches&
caches = fImpl->CachesForThisThread();
255 return ToBridgePointClassification(fImpl->kernel.ClassifyPoint(
260 WriteNormal(fImpl->kernel.SurfaceNormal(ToKernelPoint(point_cm)), norm);
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.
const std::uint64_t cacheKey
static std::unordered_map< std::uint64_t, ThreadCacheEntry > & ThreadLocalCaches()
std::uint64_t displayMeshGeneration
std::shared_ptr< const void > cacheLifetime
Impl(const TopoDS_Shape &shape)
std::mutex displayMeshMutex
ThreadCaches & CachesForThisThread() const
std::shared_ptr< const DisplayMeshCm > displayMesh
static std::uint64_t NextCacheKey()
std::uint64_t ShapeGeneration() const
double DistFromOutsideCm(const double *point_cm, const double *dir, double *safe_cm=nullptr) const
double SafetyCm(const double *point_cm, bool inside) const
const TopoDS_Shape & Shape() 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)
static double InfinityCm()
PointClassification ClassifyCm(const double *point_cm) const
TGeoOCCTSolidBridge(const TopoDS_Shape &shape)
void SetShape(const TopoDS_Shape &shape)
double CapacityCm3() const
double DistFromInsideCm(const double *point_cm, const double *dir, double *safe_cm=nullptr, bool calcNorm=false, double *norm=nullptr, bool *validNorm=nullptr) const
std::vector< DisplayTriangleCm > triangles
std::array< double, 3 > p1