11 #include <geometry/modules/vrmlWriter/VRMLWriterModule.h>
12 #include <geometry/GeometryManager.h>
14 #include "G4PhysicalVolumeStore.hh"
15 #include "G4LogicalVolumeStore.hh"
16 #include "G4SolidStore.hh"
17 #include "G4VPhysicalVolume.hh"
18 #include "G4LogicalVolume.hh"
19 #include "G4VSolid.hh"
20 #include "G4VisAttributes.hh"
21 #include "G4VisExtent.hh"
22 #include "G4Material.hh"
23 #include "G4ThreeVector.hh"
24 #include "G4RotationMatrix.hh"
25 #include "G4Transform3D.hh"
26 #include "G4VPVParameterisation.hh"
29 #include <G4Polyhedron.hh>
30 #include <G4DisplacedSolid.hh>
41 setDescription(
"Write the detector geometry in a hierarchical VRML format.");
58 B2ERROR(
"No geometry found: add the Geometry module to the path before the VRMLWriter module.");
64 G4PhysicalVolumeStore* pvStore = G4PhysicalVolumeStore::GetInstance();
65 G4LogicalVolumeStore* lvStore = G4LogicalVolumeStore::GetInstance();
66 G4SolidStore* solidStore = G4SolidStore::GetInstance();
68 m_PVName =
new std::vector<std::string>(pvStore->size(),
"");
69 m_LVName =
new std::vector<std::string>(lvStore->size(),
"");
70 m_SolidName =
new std::vector<std::string>(solidStore->size(),
"");
73 for (G4VPhysicalVolume* physVol : *pvStore) {
74 int pvIndex = std::find(pvStore->begin(), pvStore->end(), physVol) - pvStore->begin();
75 if ((*
m_PVName)[pvIndex].length() == 0)
77 G4LogicalVolume* logVol = physVol->GetLogicalVolume();
78 int lvIndex = std::find(lvStore->begin(), lvStore->end(), logVol) - lvStore->begin();
79 if ((*
m_LVName)[lvIndex].length() == 0)
81 G4VSolid* solid = logVol->GetSolid();
82 int solidIndex = std::find(solidStore->begin(), solidStore->end(), solid) - solidStore->begin();
89 m_IsCylinder =
new std::vector<bool>(solidStore->size(),
false);
90 for (
unsigned int solidIndex = 0; solidIndex < solidStore->size(); ++solidIndex) {
92 if ((*solidStore)[solidIndex]->GetEntityType() ==
"G4Tubs") {
93 auto* tube = (G4Tubs*)((*solidStore)[solidIndex]);
94 (*m_IsCylinder)[solidIndex] = ((tube->GetInnerRadius() == 0.0) && (tube->GetDeltaPhiAngle() == 2.0 * M_PI));
103 m_PVIndex =
new std::vector<std::vector<int> >(lvStore->size(), std::vector<int>());
104 m_LVWritten =
new std::vector<bool>(lvStore->size(),
false);
105 m_PVWritten =
new std::vector<bool>(pvStore->size(),
false);
109 int pvIndex = std::find(pvStore->begin(), pvStore->end(), topVol) - pvStore->begin();
110 int lvIndex = std::find(lvStore->begin(), lvStore->end(), topVol->GetLogicalVolume()) - lvStore->begin();
111 m_File <<
"# PhysicalVolume " << topVol->GetName() << std::endl <<
112 "DEF " << (*m_PVName)[pvIndex] <<
" Transform {" << std::endl <<
113 " children lv_" << (*m_LVName)[lvIndex] <<
"{}" << std::endl <<
130 G4SolidStore* solidStore = G4SolidStore::GetInstance();
131 G4LogicalVolumeStore* lvStore = G4LogicalVolumeStore::GetInstance();
132 G4PhysicalVolumeStore* pvStore = G4PhysicalVolumeStore::GetInstance();
134 G4String name = originalName;
135 if (name.length() == 0) { name =
"anonymous"; }
137 for (
char c :
" .,:;?'\"*+-=|^!/@#$\\%{}[]()<>") std::replace(name.begin(), name.end(), c,
'_');
139 for (
int j = (
int)index - 1; j >= 0; --j) {
140 if ((*names)[j].length() == 0)
continue;
144 match = (*pvStore)[j]->GetName().compare((*pvStore)[index]->GetName());
break;
146 match = (*lvStore)[j]->GetName().compare((*lvStore)[index]->GetName());
break;
148 match = (*solidStore)[j]->GetName().compare((*solidStore)[index]->GetName());
break;
151 if (name.length() == (*names)[j].length()) {
152 (*names)[j].append(
"_1");
154 int n = std::stoi((*names)[j].substr(name.length() + 1),
nullptr);
156 name.append(std::to_string(n + 1));
160 (*names)[index] = name;
165 m_File <<
"# Solid " << solid->GetName() <<
" of type " << solid->GetEntityType() << std::endl;
167 auto* tube = (G4Tubs*)solid;
169 m_File <<
"PROTO solid_" << solidName <<
" [ ] {" << std::endl <<
170 " Cylinder {" << std::endl << std::setprecision(10) <<
171 " radius " << tube->GetOuterRadius() << std::endl <<
172 " height " << tube->GetZHalfLength() * 2.0 << std::endl <<
175 }
else if (solid->GetEntityType() ==
"G4Box") {
176 auto* box = (G4Box*)solid;
177 m_File <<
"PROTO solid_" << solidName <<
" [ ] {" << std::endl <<
178 " Box {" << std::endl << std::setprecision(10) <<
179 " size " << box->GetXHalfLength() * 2.0 <<
" " <<
180 box->GetYHalfLength() * 2.0 <<
" " <<
181 box->GetZHalfLength() * 2.0 << std::endl <<
184 }
else if ((solid->GetEntityType() ==
"G4IntersectionSolid") ||
185 (solid->GetEntityType() ==
"G4UnionSolid") ||
186 (solid->GetEntityType() ==
"G4SubtractionSolid") ||
187 (solid->GetEntityType() ==
"G4BooleanSolid")) {
189 auto* g4polyhedron =
new G4Polyhedron(*polyhedron);
201 G4LogicalVolumeStore* lvStore = G4LogicalVolumeStore::GetInstance();
202 int lvIndex = std::find(lvStore->begin(), lvStore->end(), logVol) - lvStore->begin();
204 (*m_LVWritten)[lvIndex] =
true;
205 G4Color color(0.0, 1.0, 0.0, 0.5);
206 if ((lvName.compare(0, 23,
"eclBarrelCrystalLogical") == 0) ||
207 (lvName.compare(0, 20,
"eclFwdCrystalLogical") == 0) ||
208 (lvName.compare(0, 20,
"eclBwdCrystalLogical") == 0)) {
209 color = G4Color(1.0, 0.25, 0.0, 0.7);
211 std::string visible =
"";
212 G4String materialName = logVol->GetMaterial()->GetName();
214 if (materialName ==
"Vacuum") visible =
"#";
215 if (materialName ==
"G4_AIR") visible =
"#";
216 if (materialName ==
"CDCGas") visible =
"#";
217 if (materialName ==
"ColdAir") visible =
"#";
218 if (materialName ==
"STR-DryAir") visible =
"#";
219 if (materialName ==
"TOPAir") visible =
"#";
220 if (materialName ==
"TOPVacuum") visible =
"#";
221 const G4VisAttributes* visAttr = logVol->GetVisAttributes();
223 color =
const_cast<G4Color&
>(logVol->GetVisAttributes()->GetColor());
224 if (!(visAttr->IsVisible())) visible =
"#";
228 bool hasDaughters = (visible.length() == 0) || ((*
m_PVIndex)[lvIndex].size() > 0);
229 if (logVol->GetSensitiveDetector() !=
nullptr) visible =
"";
230 m_File <<
"# LogicalVolume " << logVol->GetName() <<
" containing " << materialName << std::endl <<
231 "PROTO lv_" << lvName <<
" [ ] {" << std::endl <<
232 " Group {" << std::endl <<
233 " " << (hasDaughters ?
"" :
"#") <<
"children [" << std::endl;
236 m_File <<
" " << visible <<
"Transform {" << std::endl <<
237 " " << visible <<
" rotation 1 0 0 " << M_PI_2 << std::endl <<
238 " " << visible <<
" children [" << std::endl;
241 m_File <<
" " << visible <<
"Shape {" << std::endl <<
242 " " << visible <<
" appearance Appearance {" << std::endl <<
243 " " << visible <<
" material Material {" << std::endl <<
244 " " << visible <<
" diffuseColor " << color.GetRed() <<
" " <<
245 color.GetGreen() <<
" " <<
246 color.GetBlue() << std::endl <<
247 " " << visible <<
" " << (color.GetAlpha() == 1.0 ?
"#" :
"") <<
248 "transparency " << 1.0 - color.GetAlpha() << std::endl <<
249 " " << visible <<
" }" << std::endl <<
250 " " << visible <<
" }" << std::endl <<
251 " " << visible <<
" geometry solid_" << solidName <<
"{}" << std::endl <<
252 " " << visible <<
"}" << std::endl;
254 visible.resize(visible.length() - 2);
255 m_File <<
" " << visible <<
" ]" << std::endl <<
256 " " << visible <<
"}" << std::endl;
258 G4PhysicalVolumeStore* pvStore = G4PhysicalVolumeStore::GetInstance();
259 for (
int daughter : (*
m_PVIndex)[lvIndex]) {
260 G4VPhysicalVolume* physVol = (*pvStore)[daughter];
261 std::string pvNameDaughter = (*m_PVName)[daughter];
262 int lvDaughter = std::find(lvStore->begin(), lvStore->end(), physVol->GetLogicalVolume()) - lvStore->begin();
263 std::string lvNameDaughter = (*m_LVName)[lvDaughter];
264 if (physVol->IsReplicated()) {
265 G4VSolid* solid = logVol->GetSolid();
271 physVol->GetReplicationData(axis, nReplicas, width, offset, consuming);
272 G4VPVParameterisation* physParameterisation = physVol->GetParameterisation();
273 if (physParameterisation) {
274 for (
int replica = 0; replica < nReplicas; ++replica) {
275 std::string pvNameReplica = pvNameDaughter;
276 std::string lvNameReplica = lvNameDaughter;
277 pvNameReplica.append(
"_");
278 pvNameReplica.append(std::to_string(replica));
279 physVol->SetCopyNo(replica);
280 physParameterisation->ComputeTransformation(replica, physVol);
281 G4VSolid* solidReplica = physParameterisation->ComputeSolid(replica, physVol);
282 if (solidReplica != solid) {
283 lvNameReplica.append(
"_");
284 lvNameReplica.append(std::to_string(replica));
289 G4ThreeVector originalTranslation = physVol->GetTranslation();
290 G4RotationMatrix* originalRotation = physVol->GetRotation();
291 for (
int replica = 0; replica < nReplicas; ++replica) {
292 G4ThreeVector translation;
293 G4RotationMatrix rotation;
294 std::string pvNameReplica = pvNameDaughter;
295 std::string lvNameReplica = lvNameDaughter;
296 pvNameReplica.append(
"_");
297 pvNameReplica.append(std::to_string(replica));
298 physVol->SetCopyNo(replica);
302 translation.setX(width * (replica - 0.5 * (nReplicas - 1)));
303 physVol->SetTranslation(translation);
306 translation.setY(width * (replica - 0.5 * (nReplicas - 1)));
307 physVol->SetTranslation(translation);
310 translation.setZ(width * (replica - 0.5 * (nReplicas - 1)));
311 physVol->SetTranslation(translation);
314 if (solid->GetEntityType() ==
"G4Tubs") {
315 lvNameReplica.append(
"_");
316 lvNameReplica.append(std::to_string(replica));
318 B2WARNING(
"Built-in volumes replicated along radius for " << solid->GetEntityType() <<
319 " (solid " << solid->GetName() <<
") are not visualisable.");
323 physVol->SetRotation(&(rotation.rotateZ(-(offset + (replica + 0.5) * width))));
329 physVol->SetTranslation(originalTranslation);
330 physVol->SetRotation(originalRotation);
335 (*m_PVWritten)[daughter] =
true;
337 G4VisExtent extent = logVol->GetSolid()->GetExtent();
338 double xMin = extent.GetXmin();
339 double xMax = extent.GetXmax();
340 double yMin = extent.GetYmin();
341 double yMax = extent.GetYmax();
342 double zMin = extent.GetZmin();
343 double zMax = extent.GetZmax();
344 double xCenter = 0.5 * (xMin + xMax);
345 double yCenter = 0.5 * (yMin + yMax);
346 double zCenter = 0.5 * (zMin + zMax);
347 bool atOrigin = (std::fabs(xCenter) + std::fabs(yCenter) + std::fabs(zCenter) > 1.0e-12);
348 m_File <<
" " << (hasDaughters ?
"" :
"#") <<
"]" << std::endl <<
349 " " << (atOrigin ?
"" :
"#") <<
350 "bboxCenter " << xCenter <<
" " << yCenter <<
" " << zCenter << std::endl <<
351 " bboxSize " << xMax - xMin <<
" " <<
352 (isCylinder ? zMax - zMin : yMax - yMin) <<
" " <<
353 (isCylinder ? yMax - yMin : zMax - zMin) << std::endl <<
355 "} # end of " << lvName << std::endl;
360 G4LogicalVolume* logVol = physVol->GetLogicalVolume();
361 G4VSolid* solid = logVol->GetSolid();
362 G4LogicalVolumeStore* lvStore = G4LogicalVolumeStore::GetInstance();
363 int lvIndex = std::find(lvStore->begin(), lvStore->end(), logVol) - lvStore->begin();
364 std::string lvName = (*m_LVName)[lvIndex];
365 if (physVol->IsReplicated()) {
366 G4SolidStore* solidStore = G4SolidStore::GetInstance();
367 int solidIndex = std::find(solidStore->begin(), solidStore->end(), solid) - solidStore->begin();
368 std::string solidName = (*m_SolidName)[solidIndex];
374 physVol->GetReplicationData(axis, nReplicas, width, offset, consuming);
375 G4VPVParameterisation* physParameterisation = physVol->GetParameterisation();
376 if (physParameterisation) {
377 for (
int replica = 0; replica < nReplicas; ++replica) {
378 std::string lvNameReplica = lvName;
379 G4VSolid* solidReplica = physParameterisation->ComputeSolid(replica, physVol);
380 if (solidReplica != solid) {
381 solidReplica->ComputeDimensions(physParameterisation, replica, physVol);
382 std::string solidNameReplica = solidName;
383 solidNameReplica.append(
"_");
384 solidNameReplica.append(std::to_string(replica));
385 lvNameReplica.append(
"_");
386 lvNameReplica.append(std::to_string(replica));
393 for (
int replica = 0; replica < nReplicas; ++replica) {
395 if (solid->GetEntityType() ==
"G4Tubs") {
396 double originalRMin = ((G4Tubs*)solid)->GetInnerRadius();
397 double originalRMax = ((G4Tubs*)solid)->GetOuterRadius();
398 ((G4Tubs*)solid)->SetInnerRadius(offset + width * replica);
399 ((G4Tubs*)solid)->SetOuterRadius(offset + width * (replica + 1));
400 std::string solidNameReplica = solidName;
401 solidNameReplica.append(
"_");
402 solidNameReplica.append(std::to_string(replica));
403 std::string lvNameReplica = lvName;
404 lvNameReplica.append(
"_");
405 lvNameReplica.append(std::to_string(replica));
407 ((G4Tubs*)solid)->SetInnerRadius(originalRMin);
408 ((G4Tubs*)solid)->SetOuterRadius(originalRMax);
412 B2WARNING(
"Built-in volumes replicated along radius for " << solid->GetEntityType() <<
413 " (solid " << solid->GetName() <<
") are not visualisable.");
427 G4PhysicalVolumeStore* pvStore = G4PhysicalVolumeStore::GetInstance();
428 G4LogicalVolumeStore* lvStore = G4LogicalVolumeStore::GetInstance();
429 G4SolidStore* solidStore = G4SolidStore::GetInstance();
431 int pvIndex = std::find(pvStore->begin(), pvStore->end(), physVol) - pvStore->begin();
435 G4LogicalVolume* logVol = physVol->GetLogicalVolume();
436 int lvIndex = std::find(lvStore->begin(), lvStore->end(), logVol) - lvStore->begin();
437 for (
int daughter = 0; daughter < logVol->GetNoDaughters(); ++daughter) {
438 G4VPhysicalVolume* dPhysVol = logVol->GetDaughter(daughter);
439 (*m_PVIndex)[lvIndex].push_back(std::find(pvStore->begin(), pvStore->end(), dPhysVol) - pvStore->begin());
443 int solidIndex = std::find(solidStore->begin(), solidStore->end(), logVol->GetSolid()) - solidStore->begin();
449 m_File <<
"#VRML V2.0 utf8" << std::endl << std::endl <<
450 "WorldInfo {" << std::endl <<
451 " info [ \"(c) The Belle II Collaboration\" ]" << std::endl <<
452 " title \"The Belle II Detector\"" << std::endl <<
454 "Viewpoint {" << std::endl <<
455 " position 0 0 15000" << std::endl <<
456 " description \"front\"" << std::endl <<
458 "Viewpoint {" << std::endl <<
459 " position 0 0 -15000" << std::endl <<
460 " orientation 0 1 0 3.141593" << std::endl <<
461 " description \"back\"" << std::endl <<
463 "Viewpoint {" << std::endl <<
464 " position 0 15000 0" << std::endl <<
465 " orientation 1 0 0 -1.570796" << std::endl <<
466 " description \"top\"" << std::endl <<
468 "Viewpoint {" << std::endl <<
469 " position 0 -15000 0" << std::endl <<
470 " orientation 1 0 0 1.570796" << std::endl <<
471 " description \"bottom\"" << std::endl <<
473 "Viewpoint {" << std::endl <<
474 " position 15000 0 0" << std::endl <<
475 " orientation 0 1 0 1.570796" << std::endl <<
476 " description \"right\"" << std::endl <<
478 "Viewpoint {" << std::endl <<
479 " position -15000 0 0" << std::endl <<
480 " orientation 0 1 0 -1.570796" << std::endl <<
481 " description \"left\"" << std::endl <<
488 m_File <<
"PROTO solid_" << name <<
" [ ] {" << std::endl <<
489 " IndexedFaceSet {" << std::endl <<
490 " coord Coordinate {" << std::endl <<
491 " point [" << std::endl;
492 for (
int j = 1; j <= polyhedron->GetNoVertices(); ++j) {
493 m_File <<
" " << polyhedron->GetVertex(j).x() <<
" " <<
494 polyhedron->GetVertex(j).y() <<
" " <<
495 polyhedron->GetVertex(j).z() << std::endl;
497 m_File <<
" ]" << std::endl <<
499 " coordIndex [" << std::endl;
500 for (
int j = 0; j < polyhedron->GetNoFacets(); ++j) {
501 G4bool notLastEdge =
true;
502 G4int ndx = -1, edgeFlag = 1;
505 notLastEdge = polyhedron->GetNextVertexIndex(ndx, edgeFlag);
507 }
while (notLastEdge);
508 m_File <<
" -1" << std::endl;
510 m_File <<
" ]" << std::endl <<
514 B2INFO(
"Polyhedron representation of solid " << name <<
" cannot be created");
522 m_File <<
" USE " << pvName << std::endl;
524 m_File <<
" # PhysicalVolume " << physVol->GetName() <<
" copy " << physVol->GetCopyNo() << std::endl <<
525 " DEF " << pvName <<
" Transform {" << std::endl;
526 G4RotationMatrix* rot = physVol->GetObjectRotation();
527 G4ThreeVector move = physVol->GetObjectTranslation();
528 double yaw = std::atan2(rot->yx(), rot->xx());
529 double pitch = -std::asin(rot->zx());
530 double roll = std::atan2(rot->zy(), rot->zz());
531 std::cout << physVol->GetName() <<
" translation: " << move.x() <<
"," << move.y() <<
"," << move.z() << std::endl;
532 std::cout << physVol->GetName() <<
" rotation: " << yaw * 180.0 / M_PI <<
"," << pitch * 180.0 / M_PI <<
"," << roll * 180.0 / M_PI
534 if (move.mag2() != 0.0) {
535 m_File <<
" translation " << std::setprecision(10) <<
536 move.x() <<
" " << move.y() <<
" " << move.z() << std::endl;
538 if (!(rot->isIdentity())) {
539 double trace = std::max(-1.0, std::min(3.0, rot->xx() + rot->yy() + rot->zz()));
540 double angle = std::acos(0.5 * (trace - 1.0));
541 G4ThreeVector rotA(rot->zy() - rot->yz(), rot->xz() - rot->zx(), rot->yx() - rot->xy());
542 if ((rotA.x() == 0.0) && (rotA.y() == 0.0) && (rotA.z() == 0.0)) {
545 if (rot->xx() > 0.0) {
546 rotA.setX((rot->yy() - 1.0) * (rot->zz() - 1.0) - rot->yz() * rot->zy());
547 rotA.setY(rot->yz() * rot->zx() - rot->yx() * (rot->zz() - 1.0));
548 rotA.setZ(rot->yx() * rot->zy() - rot->zx() * (rot->yy() - 1.0));
549 }
else if (rot->yy() > 0.0) {
550 rotA.setX(rot->xy() * (rot->zz() - 1.0) - rot->xz() * rot->zy());
551 rotA.setY(rot->xz() * rot->zx() - (rot->xx() - 1.0) * (rot->zz() - 1.0));
552 rotA.setZ((rot->xx() - 1.0) * rot->zy() - rot->xy() * rot->zx());
554 rotA.setX(rot->xy() * rot->yz() - rot->xz() * (rot->yy() - 1.0));
555 rotA.setY(rot->xz() * rot->yx() - rot->yz() * (rot->xx() - 1.0));
556 rotA.setZ((rot->xx() - 1.0) * (rot->yy() - 1.0) - rot->xy() * rot->yx());
560 m_File <<
" rotation " << std::setprecision(10) <<
561 rotA.x() <<
" " << rotA.y() <<
" " << rotA.z() <<
" " << angle << std::endl;
563 m_File <<
" children lv_" << lvName <<
"{}" << std::endl <<
572 G4VSolid* solidA = solid->GetConstituentSolid(0);
573 G4VSolid* solidB = solid->GetConstituentSolid(1);
574 HepPolyhedron* polyhedronA =
nullptr;
575 if ((solidA->GetEntityType() ==
"G4IntersectionSolid") ||
576 (solidA->GetEntityType() ==
"G4UnionSolid") ||
577 (solidA->GetEntityType() ==
"G4SubtractionSolid") ||
578 (solidA->GetEntityType() ==
"G4BooleanSolid")) {
581 polyhedronA =
new HepPolyhedron(*(solidA->GetPolyhedron()));
583 HepPolyhedron* polyhedronB =
nullptr;
584 G4VSolid* solidB2 = solidB;
585 if (solidB->GetEntityType() ==
"G4DisplacedSolid") {
586 solidB2 = ((G4DisplacedSolid*)solidB)->GetConstituentMovedSolid();
588 if ((solidB2->GetEntityType() ==
"G4IntersectionSolid") ||
589 (solidB2->GetEntityType() ==
"G4UnionSolid") ||
590 (solidB2->GetEntityType() ==
"G4SubtractionSolid") ||
591 (solidB2->GetEntityType() ==
"G4BooleanSolid")) {
593 if (solidB != solidB2) {
594 polyhedronB->Transform(G4Transform3D(
595 ((G4DisplacedSolid*)solidB)->GetObjectRotation(),
596 ((G4DisplacedSolid*)solidB)->GetObjectTranslation()));
599 polyhedronB =
new HepPolyhedron(*(solidB->GetPolyhedron()));
601 auto* result =
new HepPolyhedron();
602 if (solid->GetEntityType() ==
"G4UnionSolid") {
603 *result = polyhedronA->add(*polyhedronB);
604 }
else if (solid->GetEntityType() ==
"G4SubtractionSolid") {
605 *result = polyhedronA->subtract(*polyhedronB);
606 }
else if (solid->GetEntityType() ==
"G4IntersectionSolid") {
607 *result = polyhedronA->intersect(*polyhedronB);
609 B2WARNING(
"getBooleanSolidPolyhedron(): Unrecognized boolean solid " << solid->GetName() <<
610 " of type " << solid->GetEntityType());