12#include <BRepBndLib.hxx>
14#include <TopLoc_Location.hxx>
15#include <TopoDS_Shape.hxx>
20#include <IFSelect_ReturnStatus.hxx>
21#include <STEPCAFControl_Reader.hxx>
22#include <TDataStd_Name.hxx>
23#include <TDF_Label.hxx>
24#include <TDF_LabelSequence.hxx>
25#include <TDF_Tool.hxx>
26#include <TDocStd_Application.hxx>
27#include <TDocStd_Document.hxx>
28#include <XCAFDoc_DocumentTool.hxx>
29#include <XCAFDoc_Location.hxx>
30#include <XCAFDoc_MaterialTool.hxx>
31#include <XCAFDoc_ShapeTool.hxx>
34#include <G4Exception.hh>
35#include <G4RotationMatrix.hh>
36#include <G4ThreeVector.hh>
39#include <CLHEP/Vector/RotationInterfaces.h>
52std::pair<G4RotationMatrix, G4ThreeVector> TrsfToG4(
const gp_Trsf& trsf) {
55 G4RotationMatrix rot(CLHEP::HepRep3x3(trsf.Value(1, 1), trsf.Value(1, 2), trsf.Value(1, 3),
56 trsf.Value(2, 1), trsf.Value(2, 2), trsf.Value(2, 3),
57 trsf.Value(3, 1), trsf.Value(3, 2), trsf.Value(3, 3)));
58 G4ThreeVector trans(trsf.Value(1, 4), trsf.Value(2, 4), trsf.Value(3, 4));
68gp_Trsf LocationToTrsf(
const TopLoc_Location& loc) {
70 for (TopLoc_Location cursor = loc; !cursor.IsIdentity(); cursor = cursor.NextLocation()) {
71 gp_Trsf datum = cursor.FirstDatum()->Trsf();
72 Standard_Integer power = cursor.FirstPower();
78 for (Standard_Integer k = 0; k < power; ++k) {
81 result.Multiply(step);
87G4String GetLabelName(
const TDF_Label& label) {
88 Handle(TDataStd_Name) nameAttr;
89 if (label.FindAttribute(TDataStd_Name::GetID(), nameAttr)) {
90 TCollection_AsciiString ascii(nameAttr->Get());
91 return G4String(ascii.ToCString());
98G4String GetMaterialName(
const TDF_Label& label,
const Handle(XCAFDoc_MaterialTool) & matTool) {
99 Handle(TCollection_HAsciiString) matName;
100 Handle(TCollection_HAsciiString) matDescription;
101 Standard_Real density = 0.0;
102 Handle(TCollection_HAsciiString) densityName;
103 Handle(TCollection_HAsciiString) densityValType;
104 if (matTool->GetMaterial(label, matName, matDescription, density, densityName, densityValType)) {
105 if (!matName.IsNull()) {
106 return G4String(matName->ToCString());
119TopoDS_Shape RecenterShape(
const TopoDS_Shape& shape, gp_Vec& centroid) {
121 BRepBndLib::AddOptimal(shape, bbox, Standard_False);
123 centroid = gp_Vec(0.0, 0.0, 0.0);
126 Standard_Real xMin = 0.0, yMin = 0.0, zMin = 0.0;
127 Standard_Real xMax = 0.0, yMax = 0.0, zMax = 0.0;
128 bbox.Get(xMin, yMin, zMin, xMax, yMax, zMax);
130 centroid = gp_Vec(0.5 * (xMin + xMax), 0.5 * (yMin + yMax), 0.5 * (zMin + zMax));
133 centerTrsf.SetTranslation(gp_Vec(-centroid.X(), -centroid.Y(), -centroid.Z()));
134 return shape.Moved(TopLoc_Location(centerTrsf));
141G4String MakeUniqueName(
const G4String& name, std::map<G4String, int>& usedNames) {
142 auto [it, inserted] = usedNames.emplace(name, 0);
150 candidate = name +
"_" + std::to_string(it->second);
151 }
while (usedNames.count(candidate) > 0);
152 usedNames.emplace(candidate, 0);
161std::string LabelKey(
const TDF_Label& label) {
162 TCollection_AsciiString entry;
163 TDF_Tool::Entry(label, entry);
164 return std::string(entry.ToCString());
183 std::map<std::string, std::pair<G4OCCTLogicalVolume*, gp_Vec>>
prototypeMap;
192void G4OCCTAssemblyVolume::ImportLabel(
const TDF_Label& label, G4AssemblyVolume* parentAssembly,
193 const gp_Trsf& composedTrsf, BuildContext& ctx) {
194 const Handle(XCAFDoc_ShapeTool) & shapeTool = ctx.shapeTool;
197 if (shapeTool->IsAssembly(label)) {
198 TDF_LabelSequence components;
199 shapeTool->GetComponents(label, components, Standard_False);
201 for (Standard_Integer i = 1; i <= components.Length(); ++i) {
202 const TDF_Label& comp = components.Value(i);
207 TopLoc_Location compLoc;
208 Handle(XCAFDoc_Location) locAttr;
209 if (comp.FindAttribute(XCAFDoc_Location::GetID(), locAttr)) {
210 compLoc = locAttr->Get();
212 gp_Trsf compTrsf = LocationToTrsf(compLoc);
215 gp_Trsf childTrsf = composedTrsf;
216 childTrsf.Multiply(compTrsf);
220 if (shapeTool->GetReferredShape(comp, referred)) {
221 ImportLabel(referred, parentAssembly, childTrsf, ctx);
223 G4Exception(
"G4OCCTAssemblyVolume::ImportLabel",
"G4OCCT_Asm001", JustWarning,
224 "Assembly component has no referred shape; skipping.");
231 if (shapeTool->IsSimpleShape(label)) {
232 const std::string labelKey = LabelKey(label);
235 auto protoIt = ctx.prototypeMap.find(labelKey);
238 if (protoIt != ctx.prototypeMap.end()) {
241 lv = protoIt->second.first;
242 centroid = protoIt->second.second;
245 TopoDS_Shape rawShape = shapeTool->GetShape(label);
246 if (rawShape.IsNull()) {
247 G4Exception(
"G4OCCTAssemblyVolume::ImportLabel",
"G4OCCT_Asm002", JustWarning,
248 "Simple-shape label has a null OCCT shape; skipping.");
255 G4String matKey = GetMaterialName(label, ctx.matTool);
256 if (matKey.empty()) {
257 matKey = GetLabelName(label);
259 if (matKey.empty()) {
260 G4Exception(
"G4OCCTAssemblyVolume::ImportLabel",
"G4OCCT_Asm003", FatalException,
261 "Simple-shape label carries neither a STEP material attribute nor a part name. "
262 "All shapes must have materials registered in G4OCCTMaterialMap.");
265 G4Material* material = ctx.materialMap.Resolve(matKey);
268 G4String rawName = GetLabelName(label);
269 if (rawName.empty()) {
272 G4String uniqueName = MakeUniqueName(rawName, ctx.usedNames);
277 TopoDS_Shape centeredShape = RecenterShape(rawShape, centroid);
279 auto* solid =
new G4OCCTSolid(uniqueName +
"_solid", centeredShape);
284 ctx.prototypeMap.emplace(labelKey, std::make_pair(lv, centroid));
285 if (ctx.logicalVolumes) {
286 (*ctx.logicalVolumes)[uniqueName] = lv;
298 gp_Trsf recenterComp;
299 recenterComp.SetTranslation(centroid);
300 const gp_Trsf effectiveTrsf = composedTrsf.Multiplied(recenterComp);
302 auto [rot, trans] = TrsfToG4(effectiveTrsf);
303 parentAssembly->AddPlacedVolume(lv, trans,
new G4RotationMatrix(rot));
315 Handle(TDocStd_Application) app =
new TDocStd_Application;
316 Handle(TDocStd_Document) doc;
317 app->NewDocument(
"MDTV-CAF", doc);
319 STEPCAFControl_Reader cafReader;
320 cafReader.SetNameMode(Standard_True);
321 cafReader.SetMatMode(Standard_True);
322 cafReader.SetColorMode(Standard_True);
324 if (cafReader.ReadFile(path.c_str()) != IFSelect_RetDone) {
325 throw std::runtime_error(
"G4OCCTAssemblyVolume::FromSTEP: failed to read STEP file: " + path);
327 if (!cafReader.Transfer(doc)) {
328 throw std::runtime_error(
"G4OCCTAssemblyVolume::FromSTEP: failed to transfer STEP document: " +
332 Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(doc->Main());
333 Handle(XCAFDoc_MaterialTool) matTool = XCAFDoc_DocumentTool::MaterialTool(doc->Main());
336 TDF_LabelSequence freeShapes;
337 shapeTool->GetFreeShapes(freeShapes);
338 if (freeShapes.IsEmpty()) {
339 throw std::runtime_error(
340 "G4OCCTAssemblyVolume::FromSTEP: STEP file contains no top-level shapes: " + path);
347 .shapeTool = shapeTool,
350 .logicalVolumes = &result->fLogicalVolumes,
353 const gp_Trsf identity;
354 for (Standard_Integer i = 1; i <= freeShapes.Length(); ++i) {
355 ImportLabel(freeShapes.Value(i), result, identity, ctx);
Declaration of G4OCCTAssemblyVolume.
Declaration of G4OCCTSolid.
Extends Geant4's G4AssemblyVolume with an OCCT XDE label reference.
G4OCCTAssemblyVolume()=default
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.
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.