13#include <BRepBndLib.hxx>
15#include <TopLoc_Location.hxx>
16#include <TopoDS_Shape.hxx>
21#include <IFSelect_ReturnStatus.hxx>
22#include <STEPCAFControl_Reader.hxx>
23#include <TDataStd_Name.hxx>
24#include <TDF_Label.hxx>
25#include <TDF_LabelSequence.hxx>
26#include <TDF_Tool.hxx>
27#include <TDocStd_Application.hxx>
28#include <TDocStd_Document.hxx>
29#include <XCAFDoc_DocumentTool.hxx>
30#include <XCAFDoc_Location.hxx>
31#include <XCAFDoc_MaterialTool.hxx>
32#include <XCAFDoc_ShapeTool.hxx>
35#include <G4Exception.hh>
36#include <G4RotationMatrix.hh>
37#include <G4ThreeVector.hh>
40#include <CLHEP/Vector/RotationInterfaces.h>
53std::pair<G4RotationMatrix, G4ThreeVector> TrsfToG4(
const gp_Trsf& trsf) {
56 G4RotationMatrix rot(CLHEP::HepRep3x3(trsf.Value(1, 1), trsf.Value(1, 2), trsf.Value(1, 3),
57 trsf.Value(2, 1), trsf.Value(2, 2), trsf.Value(2, 3),
58 trsf.Value(3, 1), trsf.Value(3, 2), trsf.Value(3, 3)));
59 G4ThreeVector trans(trsf.Value(1, 4), trsf.Value(2, 4), trsf.Value(3, 4));
69gp_Trsf LocationToTrsf(
const TopLoc_Location& loc) {
71 for (TopLoc_Location cursor = loc; !cursor.IsIdentity(); cursor = cursor.NextLocation()) {
72 gp_Trsf datum = cursor.FirstDatum()->Trsf();
73 Standard_Integer power = cursor.FirstPower();
79 for (Standard_Integer k = 0; k < power; ++k) {
82 result.Multiply(step);
88G4String GetLabelName(
const TDF_Label& label) {
89 Handle(TDataStd_Name) nameAttr;
90 if (label.FindAttribute(TDataStd_Name::GetID(), nameAttr)) {
91 TCollection_AsciiString ascii(nameAttr->Get());
92 return G4String(ascii.ToCString());
99G4String GetMaterialName(
const TDF_Label& label,
const Handle(XCAFDoc_MaterialTool) & matTool) {
100 Handle(TCollection_HAsciiString) matName;
101 Handle(TCollection_HAsciiString) matDescription;
102 Standard_Real density = 0.0;
103 Handle(TCollection_HAsciiString) densityName;
104 Handle(TCollection_HAsciiString) densityValType;
105 if (matTool->GetMaterial(label, matName, matDescription, density, densityName, densityValType)) {
106 if (!matName.IsNull()) {
107 return G4String(matName->ToCString());
120TopoDS_Shape RecenterShape(
const TopoDS_Shape& shape, gp_Vec& centroid) {
122 BRepBndLib::AddOptimal(shape, bbox, Standard_False);
124 centroid = gp_Vec(0.0, 0.0, 0.0);
127 Standard_Real xMin = 0.0, yMin = 0.0, zMin = 0.0;
128 Standard_Real xMax = 0.0, yMax = 0.0, zMax = 0.0;
129 bbox.Get(xMin, yMin, zMin, xMax, yMax, zMax);
131 centroid = gp_Vec(0.5 * (xMin + xMax), 0.5 * (yMin + yMax), 0.5 * (zMin + zMax));
134 centerTrsf.SetTranslation(gp_Vec(-centroid.X(), -centroid.Y(), -centroid.Z()));
135 return shape.Moved(TopLoc_Location(centerTrsf));
142G4String MakeUniqueName(
const G4String& name, std::map<G4String, int>& usedNames) {
143 auto [it, inserted] = usedNames.emplace(name, 0);
151 candidate = name +
"_" + std::to_string(it->second);
152 }
while (usedNames.count(candidate) > 0);
153 usedNames.emplace(candidate, 0);
162std::string LabelKey(
const TDF_Label& label) {
163 TCollection_AsciiString entry;
164 TDF_Tool::Entry(label, entry);
165 return std::string(entry.ToCString());
184 std::map<std::string, std::pair<G4OCCTLogicalVolume*, gp_Vec>>
prototypeMap;
193void G4OCCTAssemblyVolume::ImportLabel(
const TDF_Label& label, G4AssemblyVolume* parentAssembly,
194 const gp_Trsf& composedTrsf, BuildContext& ctx) {
195 const Handle(XCAFDoc_ShapeTool) & shapeTool = ctx.shapeTool;
198 if (shapeTool->IsAssembly(label)) {
199 TDF_LabelSequence components;
200 shapeTool->GetComponents(label, components, Standard_False);
202 for (Standard_Integer i = 1; i <= components.Length(); ++i) {
203 const TDF_Label& comp = components.Value(i);
208 TopLoc_Location compLoc;
209 Handle(XCAFDoc_Location) locAttr;
210 if (comp.FindAttribute(XCAFDoc_Location::GetID(), locAttr)) {
211 compLoc = locAttr->Get();
213 gp_Trsf compTrsf = LocationToTrsf(compLoc);
216 gp_Trsf childTrsf = composedTrsf;
217 childTrsf.Multiply(compTrsf);
221 if (shapeTool->GetReferredShape(comp, referred)) {
222 ImportLabel(referred, parentAssembly, childTrsf, ctx);
224 G4Exception(
"G4OCCTAssemblyVolume::ImportLabel",
"G4OCCT_Asm001", JustWarning,
225 "Assembly component has no referred shape; skipping.");
232 if (shapeTool->IsSimpleShape(label)) {
233 const std::string labelKey = LabelKey(label);
236 auto protoIt = ctx.prototypeMap.find(labelKey);
239 if (protoIt != ctx.prototypeMap.end()) {
242 lv = protoIt->second.first;
243 centroid = protoIt->second.second;
246 TopoDS_Shape rawShape = shapeTool->GetShape(label);
247 if (rawShape.IsNull()) {
248 G4Exception(
"G4OCCTAssemblyVolume::ImportLabel",
"G4OCCT_Asm002", JustWarning,
249 "Simple-shape label has a null OCCT shape; skipping.");
256 G4String matKey = GetMaterialName(label, ctx.matTool);
257 if (matKey.empty()) {
258 matKey = GetLabelName(label);
260 if (matKey.empty()) {
261 G4Exception(
"G4OCCTAssemblyVolume::ImportLabel",
"G4OCCT_Asm003", FatalException,
262 "Simple-shape label carries neither a STEP material attribute nor a part name. "
263 "All shapes must have materials registered in G4OCCTMaterialMap.");
266 G4Material* material = ctx.materialMap.Resolve(matKey);
269 G4String rawName = GetLabelName(label);
270 if (rawName.empty()) {
273 G4String uniqueName = MakeUniqueName(rawName, ctx.usedNames);
278 TopoDS_Shape centeredShape = RecenterShape(rawShape, centroid);
280 auto* solid =
new G4OCCTSolid(uniqueName +
"_solid", centeredShape);
285 ctx.prototypeMap.emplace(labelKey, std::make_pair(lv, centroid));
286 if (ctx.logicalVolumes) {
287 (*ctx.logicalVolumes)[uniqueName] = lv;
299 gp_Trsf recenterComp;
300 recenterComp.SetTranslation(centroid);
301 const gp_Trsf effectiveTrsf = composedTrsf.Multiplied(recenterComp);
303 auto [rot, trans] = TrsfToG4(effectiveTrsf);
304 parentAssembly->AddPlacedVolume(lv, trans, &rot);
316 Handle(TDocStd_Application) app =
new TDocStd_Application;
317 Handle(TDocStd_Document) doc;
318 app->NewDocument(
"MDTV-CAF", doc);
320 STEPCAFControl_Reader cafReader;
321 cafReader.SetNameMode(Standard_True);
322 cafReader.SetMatMode(Standard_True);
323 cafReader.SetColorMode(Standard_True);
325 if (cafReader.ReadFile(path.c_str()) != IFSelect_RetDone) {
326 throw std::runtime_error(
"G4OCCTAssemblyVolume::FromSTEP: failed to read STEP file: " + path);
328 if (!cafReader.Transfer(doc)) {
329 throw std::runtime_error(
"G4OCCTAssemblyVolume::FromSTEP: failed to transfer STEP document: " +
333 Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(doc->Main());
334 Handle(XCAFDoc_MaterialTool) matTool = XCAFDoc_DocumentTool::MaterialTool(doc->Main());
337 TDF_LabelSequence freeShapes;
338 shapeTool->GetFreeShapes(freeShapes);
339 if (freeShapes.IsEmpty()) {
340 throw std::runtime_error(
341 "G4OCCTAssemblyVolume::FromSTEP: STEP file contains no top-level shapes: " + path);
348 .shapeTool = shapeTool,
351 .logicalVolumes = &result->fLogicalVolumes,
354 const gp_Trsf identity;
355 for (Standard_Integer i = 1; i <= freeShapes.Length(); ++i) {
356 ImportLabel(freeShapes.Value(i), result, identity, ctx);
365 std::size_t count = 0;
366 for (
auto& [name, lv] : fLogicalVolumes) {
367 G4VSensitiveDetector* sd = sdMap.
Resolve(name);
369 lv->SetSensitiveDetector(sd);
Declaration of G4OCCTAssemblyVolume.
Declaration of G4OCCTSensitiveDetectorMap.
Declaration of G4OCCTSolid.
Extends Geant4's G4AssemblyVolume with an OCCT XDE label reference.
G4OCCTAssemblyVolume()=default
std::size_t ApplySDMap(const G4OCCTSensitiveDetectorMap &sdMap)
static G4OCCTAssemblyVolume * FromSTEP(const std::string &path, const G4OCCTMaterialMap &materialMap)
Extends Geant4's G4LogicalVolume with an associated OCCT shape.
Maps STEP material names to Geant4 G4Material objects.
Maps volume name patterns to Geant4 G4VSensitiveDetector objects.
G4VSensitiveDetector * Resolve(const G4String &volumeName) const
Geant4 solid wrapping an Open CASCADE Technology (OCCT) TopoDS_Shape.
State threaded through the recursive XDE label traversal.
std::map< G4String, G4OCCTLogicalVolume * > * logicalVolumes
Flat collection of all created logical volumes (output to caller).
std::map< G4String, int > usedNames
Names already used by logical volumes; used to disambiguate duplicates.
Handle(XCAFDoc_ShapeTool) shapeTool
OCCT shape tool for the XDE document.
Handle(XCAFDoc_MaterialTool) matTool
OCCT material tool for the XDE document.
std::map< std::string, std::pair< G4OCCTLogicalVolume *, gp_Vec > > prototypeMap
const G4OCCTMaterialMap & materialMap
User-supplied material map.