11#include "G4BooleanSolid.hh"
17#include <QColorDialog>
19#include <QSignalBlocker>
31#include "G4VisAttributes.hh"
32#include "G4Material.hh"
33#include "G4VisManager.hh"
34#include "G4VViewer.hh"
35#include "G4VSceneHandler.hh"
36#include "G4VGraphicsSystem.hh"
37#include "G4UImanager.hh"
43std::vector<std::string> splitParams(
const std::string& s) {
44 std::vector<std::string> result;
45 std::istringstream ss(s);
47 while (std::getline(ss, tok,
',')) {
48 const auto a = tok.find_first_not_of(
" \t");
49 const auto b = tok.find_last_not_of(
" \t");
50 if (a != std::string::npos)
51 result.push_back(tok.substr(a, b - a + 1));
56using PDesc = std::vector<const char*>;
58const PDesc& solidParamDescs(
const std::string& solid, std::size_t n) {
59 static const std::unordered_map<std::string, PDesc> kFixed = {
60 {
"G4Box", {
"dx: half length in x",
61 "dy: half length in y",
62 "dz: half length in z"}},
63 {
"G4Tubs", {
"rin: inner radius",
65 "length: half length in z",
66 "phi start: starting phi angle",
67 "phi total: total phi angle"}},
68 {
"G4Cons", {
"rin1: inner radius at -dz",
69 "rout1: outer radius at -dz",
70 "rin2: inner radius at +dz",
71 "rout2: outer radius at +dz",
72 "length: half length in z",
73 "phi start: starting phi angle",
74 "phi total: total phi angle"}},
75 {
"G4Trd", {
"dx1: half length in x at -dz",
76 "dx2: half length in x at +dz",
77 "dy1: half length in y at -dz",
78 "dy2: half length in y at +dz",
79 "z: half length in z"}},
80 {
"G4Sphere", {
"rmin: inner radius",
82 "sphi: starting phi angle",
83 "dphi: delta phi angle",
84 "stheta: starting theta angle",
85 "dtheta: delta theta angle"}},
86 {
"G4Paraboloid", {
"dz: half length in z",
88 "r2: radius at +dz"}},
89 {
"G4EllipticalTube", {
"dx: half length in x",
90 "dy: half length in y",
91 "dz: half length in z"}},
93 static const std::unordered_map<std::size_t, PDesc> kTrap = {
94 {4, {
"pz: length along Z",
96 "px: length along X (wider side)",
97 "pltx: length along X (narrower side)"}},
98 {11, {
"pDz: half Z length",
99 "pTheta: polar angle of line joining base centres",
100 "pPhi: azimuthal angle of line joining base centres",
101 "pDy1: half Y length at -dz",
102 "pDx1: half X length at smaller Y, base at -dz",
103 "pDx2: half X length at bigger Y, base at -dz",
104 "pAlp1: angle between Y-axis and centre line at -dz",
105 "pDy2: half Y length at +dz",
106 "pDx3: half X length at smaller Y, base at +dz",
107 "pDx4: half X length at bigger Y, base at +dz",
108 "pAlp2: angle between Y-axis and centre line at +dz"}},
110 static const PDesc kEmpty;
112 if (solid ==
"G4Trap") {
113 const auto it = kTrap.find(n);
114 return it != kTrap.end() ? it->second : kEmpty;
116 const auto it = kFixed.find(solid);
117 return it != kFixed.end() ? it->second : kEmpty;
122static QString formatVal(
const std::string& token) {
123 const auto star = token.find(
'*');
124 const std::string numPart = (star == std::string::npos) ? token : token.substr(0, star);
125 const std::string unit = (star == std::string::npos) ?
"" : token.substr(star);
128 double v = std::stod(numPart, &pos);
129 if (pos != numPart.size())
return QString::fromStdString(token);
130 if (std::abs(v) < 1e-7) v = 0.0;
131 QString s = QString::number(v,
'f', 3);
132 while (s.endsWith(
'0') && s.contains(
'.')) s.chop(1);
133 if (s.endsWith(
'.')) s.chop(1);
134 return s + QString::fromStdString(unit);
136 return QString::fromStdString(token);
140QString formatParameters(
const std::string& solid,
const std::string& paramsStr) {
141 if (paramsStr.empty())
return {};
142 const auto vals = splitParams(paramsStr);
143 if (vals.empty())
return {};
145 QString html =
"Parameters:";
148 if (solid ==
"G4Polycone" && vals.size() >= 3) {
149 const char* kPcFixed[3] = {
150 "phi start: starting phi angle",
151 "phi total: total phi angle",
152 "nplanes: number of planes"
154 for (
int i = 0; i < 3 && i < (int)vals.size(); ++i)
155 html += QString(
"<br> <i>%1</i>: %2").arg(kPcFixed[i], formatVal(vals[i]));
157 try { nplanes = std::stoi(vals[2]); }
catch (...) {}
159 for (
int p = 0; p < nplanes && idx < (int)vals.size(); ++p, ++idx)
160 html += QString(
"<br> <i>z[%1]</i>: %2").arg(p).arg(formatVal(vals[idx]));
161 for (
int p = 0; p < nplanes && idx < (int)vals.size(); ++p, ++idx)
162 html += QString(
"<br> <i>rin[%1]</i>: %2").arg(p).arg(formatVal(vals[idx]));
163 for (
int p = 0; p < nplanes && idx < (int)vals.size(); ++p, ++idx)
164 html += QString(
"<br> <i>rout[%1]</i>: %2").arg(p).arg(formatVal(vals[idx]));
169 if (solid ==
"G4Trap" && vals.size() == 24) {
170 const char* kAxes[3] = {
"x",
"y",
"z"};
171 for (
int v = 0; v < 8; ++v)
172 for (
int c = 0; c < 3; ++c)
173 html += QString(
"<br> <i>v%1%2</i>: %3")
174 .arg(v + 1).arg(kAxes[c]).arg(formatVal(vals[v * 3 + c]));
179 const auto& descs = solidParamDescs(solid, vals.size());
180 for (std::size_t i = 0; i < vals.size(); ++i) {
181 const QString desc = i < descs.size()
182 ? QString::fromStdString(descs[i])
183 : QString(
"missing parameters description");
184 html += QString(
"<br> <i>%1</i>: %2").arg(desc, formatVal(vals[i]));
189std::string resolveBooleanOperandName(
const std::string& systemName,
190 const std::string& operand,
191 const std::unordered_map<std::string, const GVolume*>& gvolumes) {
192 if (gvolumes.find(operand) != gvolumes.end())
return operand;
194 const std::string qualified = systemName.empty() ? operand : systemName +
"/" + operand;
195 if (gvolumes.find(qualified) != gvolumes.end())
return qualified;
200std::string leafName(
const std::string& volumeName) {
201 const auto slash = volumeName.find_last_of(
'/');
202 return slash == std::string::npos ? volumeName : volumeName.substr(slash + 1);
205QString joinBooleanTokens(
const std::vector<std::string>& tokens) {
207 for (
const auto& token : tokens) {
208 parts << QString::fromStdString(token).toHtmlEscaped();
210 return parts.join(QStringLiteral(
" "));
213QString formatOperationLine(
const std::vector<std::string>& tokens,
214 const std::string& systemName,
215 const std::unordered_map<std::string, const GVolume*>& gvolumes) {
216 if (tokens.size() != 3)
return joinBooleanTokens(tokens);
218 const auto left = leafName(resolveBooleanOperandName(systemName, tokens[0], gvolumes));
219 const auto right = leafName(resolveBooleanOperandName(systemName, tokens[2], gvolumes));
220 return QString(
"%1 %2 %3").arg(QString::fromStdString(left).toHtmlEscaped(),
221 QString::fromStdString(tokens[1]).toHtmlEscaped(),
222 QString::fromStdString(right).toHtmlEscaped());
225void appendNestedBooleanOperationLines(QString& html,
226 const std::string& volumeName,
227 const std::string& systemName,
228 const std::unordered_map<std::string, const GVolume*>& gvolumes,
229 std::set<std::string>& visited) {
230 const auto fullName = resolveBooleanOperandName(systemName, volumeName, gvolumes);
231 if (!visited.insert(fullName).second)
return;
233 const auto gvolIt = gvolumes.find(fullName);
234 if (gvolIt == gvolumes.end())
return;
236 const auto solidsOpr = gvolIt->second->getSolidsOpr();
240 if (tokens.size() != 3)
return;
242 html += QString(
"<br> %1 = %2")
243 .arg(QString::fromStdString(leafName(fullName)).toHtmlEscaped(),
244 formatOperationLine(tokens, systemName, gvolumes));
246 appendNestedBooleanOperationLines(html, tokens[0], systemName, gvolumes, visited);
247 appendNestedBooleanOperationLines(html, tokens[2], systemName, gvolumes, visited);
250QString formatBooleanOperationDescription(
const std::string& fullName,
251 const std::string& solidsOpr,
252 const std::unordered_map<std::string, const GVolume*>& gvolumes) {
256 if (tokens.size() != 3) {
257 return QObject::tr(
"Boolean Operation: %1").arg(QString::fromStdString(solidsOpr).toHtmlEscaped());
260 const auto slash = fullName.find_last_of(
'/');
261 const std::string systemName = slash == std::string::npos ?
"" : fullName.substr(0, slash);
262 QString html = QObject::tr(
"Boolean Operation: %1").arg(formatOperationLine(tokens, systemName, gvolumes));
264 std::set<std::string> visited{fullName};
265 appendNestedBooleanOperationLines(html, tokens[0], systemName, gvolumes, visited);
266 appendNestedBooleanOperationLines(html, tokens[2], systemName, gvolumes, visited);
275 if (g4volume ==
nullptr || g4volume->
getLogical() ==
nullptr || g4volume->
getSolid() ==
nullptr) {
277 material =
"unavailable";
278 color = QColor::fromRgbF(1.0, 1.0, 1.0);
286 auto svolume = g4volume->
getSolid();
288 std::string lname = lvolume->GetName();
290 auto mlvolume = pvolume !=
nullptr ? pvolume->GetMotherLogical() :
nullptr;
291 mother = mlvolume !=
nullptr ? mlvolume->GetName() :
MOTHEROFUSALL;
292 material = lvolume->GetMaterial()->GetName();
296 material =
"G4_Galactic";
300 auto visAttributes = lvolume->GetVisAttributes();
301 if (visAttributes !=
nullptr) {
302 auto gcolor = visAttributes->GetColour();
303 color = QColor::fromRgbF(gcolor.GetRed(), gcolor.GetGreen(), gcolor.GetBlue());
304 opacity = gcolor.GetAlpha();
305 is_visible = visAttributes->IsVisible();
309 color = QColor::fromRgbF(1.0, 1.0, 1.0);
316 logical_ptr = lvolume;
320 solidType = gvolume->
getType();
322 position = gvolume->
getPos();
323 rotation = gvolume->
getRot();
336void G4Ttree_item::compute_physics_quantities()
const {
337 if (physics_computed)
return;
338 physics_computed =
true;
339 if (logical_ptr ==
nullptr || solid_ptr ==
nullptr)
return;
341 if (
dynamic_cast<G4BooleanSolid*
>(solid_ptr) !=
nullptr) {
342 volume = solid_ptr->EstimateCubicVolume(100000, 0.001) / CLHEP::cm3;
343 density = logical_ptr->GetMaterial()->GetDensity() / (CLHEP::g / CLHEP::cm3);
344 mass = volume * density;
347 volume = solid_ptr->GetCubicVolume() / CLHEP::cm3;
348 mass = logical_ptr->GetMass(
false,
false) / CLHEP::g;
349 density = volume > 0 ? mass / volume : 0.0;
357 return v4name.substr(v4name.find_last_of(
'/') + 1);
363 return v4name.substr(0, v4name.find_last_of(
'/'));
369 std::unordered_map<std::string, G4Volume*> g4volumes_map,
370 std::unordered_map<std::string, const GVolume*> gvolumes_map_in,
374 gvolumes_map(std::move(gvolumes_map_in)) {
376 build_tree(g4volumes_map);
379 treeWidget =
new QTreeWidget(
this);
380 treeWidget->setColumnCount(3);
382 headers <<
"Visibility" <<
"Color" <<
"Name";
383 treeWidget->setHeaderLabels(headers);
384 treeWidget->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
385 treeWidget->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
386 treeWidget->header()->setSectionResizeMode(2, QHeaderView::Stretch);
387 treeWidget->setRootIsDecorated(
true);
388 treeWidget->setAlternatingRowColors(
true);
390 auto* mainLayout =
new QHBoxLayout(
this);
391 mainLayout->addWidget(treeWidget, 3);
394 rightPanel = right_widget();
395 mainLayout->addWidget(rightPanel, 3);
397 setLayout(mainLayout);
403 connect(treeWidget, &QTreeWidget::itemChanged,
404 this, >ree::onItemChanged);
408 connect(treeWidget, &QTreeWidget::itemClicked,
409 this, >ree::onTreeItemClicked);
411 connect(treeWidget, &QTreeWidget::currentItemChanged,
412 this, >ree::onCurrentItemChanged);
415 connect(styleButtons->buttonsWidget,
416 SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem*)),
417 this, SLOT(changeStyle()));
421 connect(opacitySlider, &QSlider::valueChanged,
422 this, >ree::onOpacitySliderChanged);
429void GTree::populateTree() {
431 for (
auto& [systemName, volMap] : g4_systems_tree) {
433 auto* systemItem =
new QTreeWidgetItem(treeWidget);
434 systemItem->setText(2, QString::fromStdString(systemName));
435 systemItem->setFlags(systemItem->flags() | Qt::ItemIsUserCheckable);
436 systemItem->setCheckState(0, Qt::Checked);
439 std::map<std::string, QTreeWidgetItem*> itemLookup;
444 for (
auto& [volName, vptr] : volMap) {
447 auto* item =
new QTreeWidgetItem;
449 item->setData(2, Qt::UserRole, QString::fromStdString(volName));
452 item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
453 item->setCheckState(0, vitem->
get_visibility() ? Qt::Checked : Qt::Unchecked);
455 itemLookup[volName] = item;
461 for (
auto& [volName, vptr] : volMap) {
462 const G4Ttree_item* vitem = vptr.get();
464 auto* thisItem = itemLookup[volName];
466 QTreeWidgetItem* parentItem = systemItem;
469 if (!mother.empty() && mother !=
"root") {
470 auto itM = itemLookup.find(mother);
471 if (itM != itemLookup.end()) {
472 parentItem = itM->second;
476 parentItem->addChild(thisItem);
482 for (
auto& [volName, vptr] : volMap) {
483 const G4Ttree_item* vitem = vptr.get();
484 QTreeWidgetItem* item = itemLookup[volName];
486 auto* colorBtn =
new QPushButton(treeWidget);
488 colorBtn->setFixedSize(20, 20);
489 colorBtn->setFlat(
true);
490 colorBtn->setText(QString());
492 colorBtn->setStyleSheet(
493 QString(
"QPushButton { background-color: %1; border: 1px solid %2; }")
494 .arg(c.name(), palette().color(QPalette::Mid).name())
498 colorBtn->setProperty(
"volumeName", QString::fromStdString(volName));
499 connect(colorBtn, &QPushButton::clicked,
this, >ree::onColorButtonClicked);
501 treeWidget->setItemWidget(item, 1, colorBtn);
505 treeWidget->expandAll();
510void GTree::build_tree(std::unordered_map<std::string, G4Volume*> g4volumes_map) {
512 for (
auto [name, g4volume] : g4volumes_map) {
513 if (g4volume ==
nullptr || g4volume->getLogical() ==
nullptr ||
514 g4volume->getSolid() ==
nullptr || g4volume->getPhysical() ==
nullptr) {
515 log->info(2,
"Skipping non-placed helper volume <", name,
"> from tree");
530 auto& system_tree = g4_systems_tree[system_name];
531 const GVolume* gvol =
nullptr;
532 auto it = gvolumes_map.find(name);
533 if (it != gvolumes_map.end()) gvol = it->second;
534 system_tree[name] = std::make_unique<G4Ttree_item>(g4volume, gvol);
538 log->info(2,
"Adding ", name,
" to tree, system_name is ", system_name);
543void GTree::onItemChanged(QTreeWidgetItem* item,
int column) {
544 if (column != 0)
return;
547 QVariant v = item->data(2, Qt::UserRole);
553 bool visible = (item->checkState(0) == Qt::Checked);
555 QSignalBlocker blocker(treeWidget);
557 const int nChildren = item->childCount();
558 for (
int i = 0; i < nChildren; ++i) {
559 QTreeWidgetItem* child = item->child(i);
560 QVariant cv = child->data(2, Qt::UserRole);
564 const QString fullName = cv.toString();
567 child->setCheckState(0, visible ? Qt::Checked : Qt::Unchecked);
570 set_visibility(fullName.toStdString(), visible);
579 const QString fullName = v.toString();
580 bool visible = (item->checkState(0) == Qt::Checked);
582 QSignalBlocker blocker(treeWidget);
585 set_visibility(fullName.toStdString(), visible);
588 const int nChildren = item->childCount();
589 for (
int i = 0; i < nChildren; ++i) {
590 QTreeWidgetItem* child = item->child(i);
591 QVariant cv = child->data(2, Qt::UserRole);
595 const QString childName = cv.toString();
598 child->setCheckState(0, visible ? Qt::Checked : Qt::Unchecked);
601 set_visibility(childName.toStdString(), visible);
607void GTree::onColorButtonClicked() {
608 auto* btn = qobject_cast<QPushButton*>(sender());
612 const QString volName = btn->property(
"volumeName").toString();
613 if (volName.isEmpty())
616 QColor initial = Qt::white;
618 QColor c = QColorDialog::getColor(initial,
this, tr(
"Select color"));
624 QString(
"QPushButton { background-color: %1; border: 1px solid %2; }")
625 .arg(c.name(), palette().color(QPalette::Mid).name())
629 set_color(volName.toStdString(), c);
634void GTree::set_visibility(
const std::string& volumeName,
bool visible) {
635 std::string vis_int = visible ?
"1" :
"0";
637 std::string command =
"/vis/geometry/set/visibility " + volumeName +
" -1 " + vis_int;
640 if (volumeName == ROOTWORLDGVOLUMENAME) {
641 command =
"/vis/geometry/set/visibility " + volumeName +
" 0 " + vis_int;
648void GTree::set_color(
const std::string& volumeName,
const QColor& c) {
649 G4Ttree_item* item = findTreeItem(volumeName);
650 double currentOpacity = item ? item->
get_opacity() : 1.0;
653 std::string command =
"/vis/geometry/set/colour " + volumeName +
" 0 "
654 + std::to_string(c.redF()) +
" "
655 + std::to_string(c.greenF()) +
" "
656 + std::to_string(c.blueF()) +
" "
657 + std::to_string(currentOpacity);
663int GTree::get_ndaughters(QTreeWidgetItem* item)
const {
665 return item->childCount();
669G4Ttree_item* GTree::findTreeItem(
const std::string& fullName) {
670 for (
const auto& [systemName, volMap] : g4_systems_tree) {
671 auto it = volMap.find(fullName);
672 if (it != volMap.end()) {
673 return it->second.get();
681void GTree::onTreeItemClicked(QTreeWidgetItem* item,
int ) {
686 bottomPanel->setVisible(
false);
687 current_volume_name.clear();
691 bottomPanel->setVisible(
true);
694 QVariant v = item->data(2, Qt::UserRole);
695 bool isVolume = v.isValid();
699 typeLabel->setText(QStringLiteral(
"<b>G4 Volume</b>"));
700 current_volume_name = v.toString().toStdString();
703 typeLabel->setText(QStringLiteral(
"<b>System</b>"));
704 current_volume_name.clear();
708 int nd = get_ndaughters(item);
709 daughtersLabel->setText(tr(
"Daughters: %1").arg(nd));
712 QString itemName = item->text(2);
713 nameLabel->setText(tr(
"Name: %1").arg(itemName));
717 styleButtons->setVisible(
true);
719 const std::string fullName = v.toString().toStdString();
720 const G4Ttree_item* titem = findTreeItem(fullName);
723 materialLabel->setText(
724 tr(
"Material: %1").arg(QString::fromStdString(titem->
get_material()))
728 massLabel->setText(tr(
"Mass: %1 g").arg(mass));
731 massLabel->setText(tr(
"Mass: %1 kg").arg(mass / 1000));
734 if (volume < 1000000) {
735 volumeLabel->setText(tr(
"Volume: %1 cm3").arg(volume));
738 volumeLabel->setText(tr(
"Volume: %1 m3").arg(volume / 1000000));
741 densityLabel->setText(
742 tr(
"Density: %1 g / cm3").arg(titem->
get_density())
747 solidTypeLabel->setText(solidT.empty() ? QString()
748 : tr(
"Solid: %1").arg(QString::fromStdString(solidT)));
750 const bool hasParams = !params.empty();
751 parametersLabel->setVisible(hasParams);
752 if (hasParams) parametersLabel->setHtml(formatParameters(solidT, params));
754 positionLabel->setText(pos.empty() ? QString()
755 : tr(
"Position: %1").arg(QString::fromStdString(pos)));
757 rotationLabel->setText(rot.empty() ? QString()
758 : tr(
"Rotation: %1").arg(QString::fromStdString(rot)));
760 motherLabel->setText(mom.empty() ? QString() : tr(
"Mother: %1").arg(QString::fromStdString(mom)));
761 const auto boolDesc =
762 formatBooleanOperationDescription(fullName, titem->
get_solidsOpr(), gvolumes_map);
763 if (!boolDesc.isEmpty()) {
764 descriptionLabel->setText(boolDesc);
768 descriptionLabel->setText(desc.empty() ? QString()
769 : tr(
"Description: %1")
770 .arg(QString::fromStdString(desc)));
775 int sliderVal =
static_cast<int>(op * 100.0 + 0.5);
777 QSignalBlocker blocker(opacitySlider);
778 opacitySlider->setValue(sliderVal);
780 opacityLabel->setText(QString::number(op,
'f', 2));
781 opacitySlider->setVisible(
true);
787 inspectButton->setText(tr(
"Inspect %1").arg(leaf));
788 inspectButton->setVisible(
true);
790 if (drawOverlapsButton) {
792 drawOverlapsButton->setText(tr(
"Draw Logical Overlaps %1").arg(leaf));
793 drawOverlapsButton->setVisible(
true);
797 styleButtons->setVisible(
false);
798 opacitySlider->setVisible(
false);
799 if (inspectButton) inspectButton->setVisible(
false);
800 if (drawOverlapsButton) drawOverlapsButton->setVisible(
false);
802 materialLabel->setText(tr(
""));
803 massLabel->setText(tr(
""));
804 volumeLabel->setText(tr(
""));
805 densityLabel->setText(tr(
""));
806 solidTypeLabel->setText(tr(
""));
807 parametersLabel->clear();
808 parametersLabel->setVisible(
false);
809 positionLabel->setText(tr(
""));
810 rotationLabel->setText(tr(
""));
811 motherLabel->setText(tr(
""));
812 descriptionLabel->setText(tr(
""));
817void GTree::onCurrentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous) {
823 styleButtons->reset_buttons();
826 onTreeItemClicked(current, 0);
830void GTree::changeStyle() {
833 if (current_volume_name.empty())
836 int button_index = styleButtons->button_pressed();
838 if (button_index == 3) {
841 QSignalBlocker blocker(styleButtons->buttonsWidget);
842 styleButtons->reset_buttons();
850 if (button_index == 0) {
851 command =
"/vis/geometry/set/forceWireframe " + current_volume_name +
" 0 1 ";
853 else if (button_index == 1) {
854 command =
"/vis/geometry/set/forceSolid " + current_volume_name +
" 0 1 ";
856 else if (button_index == 2) {
857 command =
"/vis/geometry/set/forceCloud " + current_volume_name +
" 0 1 ";
869void GTree::centreTwinkle() {
870 G4Ttree_item* item = findTreeItem(current_volume_name);
873 twinkleVolumeName = current_volume_name;
881 twinkleTimer =
new QTimer(
this);
882 connect(twinkleTimer, &QTimer::timeout,
this, >ree::onTwinkleStep);
884 if (twinkleTimer->isActive())
885 twinkleTimer->stop();
887 twinkleTimer->start(180);
892void GTree::onTwinkleStep() {
893 constexpr int kSteps = 5;
894 static const QColor kFlash[kSteps] = {
897 QColor( 50, 255, 50),
898 QColor( 0, 220, 255),
899 QColor(220, 50, 255),
902 if (twinkleTick < kSteps) {
903 set_color(twinkleVolumeName, kFlash[twinkleTick]);
906 twinkleTimer->stop();
907 G4Ttree_item* item = findTreeItem(twinkleVolumeName);
912 const double r = twinkleSavedColor.redF();
913 const double g = twinkleSavedColor.greenF();
914 const double b = twinkleSavedColor.blueF();
915 const std::string cmd =
"/vis/geometry/set/colour " + twinkleVolumeName +
" 0 "
916 + std::to_string(r) +
" "
917 + std::to_string(g) +
" "
918 + std::to_string(b) +
" "
919 + std::to_string(twinkleSavedOpacity);
926void GTree::inspectVolume() {
927 if (current_volume_name.empty())
return;
929 G4UImanager* uim = G4UImanager::GetUIpointer();
934 std::string driverName =
"OGLSQt";
935 std::string originalViewerName;
936 auto* vm = G4VisManager::GetInstance();
938 const auto* viewer = vm->GetCurrentViewer();
940 originalViewerName = viewer->GetName();
941 const auto* sh = viewer->GetSceneHandler();
943 const auto* gs = sh->GetGraphicsSystem();
944 if (gs) driverName = gs->GetNickname();
956 uim->ApplyCommand(
"/vis/open " + driverName);
957 uim->ApplyCommand(
"/vis/scene/create");
958 uim->ApplyCommand(
"/vis/sceneHandler/attach");
959 uim->ApplyCommand(
"/vis/scene/add/volume " + current_volume_name +
" -1");
960 uim->ApplyCommand(
"/vis/viewer/set/background 1 1 1 1");
961 uim->ApplyCommand(
"/vis/viewer/set/lineSegmentsPerCircle 100");
964 uim->ApplyCommand(
"/vis/set/textColour black");
965 uim->ApplyCommand(
"/vis/set/textLayout centre");
966 uim->ApplyCommand(
"/vis/scene/add/text2D 0 0.85 36 ! ! " + leafName);
967 uim->ApplyCommand(
"/vis/viewer/flush");
971 if (!originalViewerName.empty())
972 uim->ApplyCommand(
"/vis/viewer/select " + originalViewerName);
977void GTree::drawOverlapsWarning() {
978 QMessageBox::warning(
this,
979 tr(
"Not yet implemented"),
980 tr(
"Draw Logical Overlaps will be implemented when Geant4 fixes the\n"
981 "G4ToolsSGSceneHandler::GetOrCreateNode \"World mis-match\" crash\n"
982 "triggered by /vis/drawLogicalVolume in Geant4 11.4.2."));
987void GTree::onOpacitySliderChanged(
int value) {
988 if (current_volume_name.empty())
991 double opacity = value / 100.0;
994 opacityLabel->setText(QString::number(opacity,
'f', 2));
997 set_opacity(current_volume_name, opacity);
1001void GTree::set_opacity(
const std::string& volumeName,
double opacity) {
1004 G4Ttree_item* item = findTreeItem(volumeName);
1008 double r = c.redF();
1009 double g = c.greenF();
1010 double b = c.blueF();
1013 std::string command =
"/vis/geometry/set/colour " + volumeName +
" 0 "
1014 + std::to_string(r) +
" "
1015 + std::to_string(g) +
" "
1016 + std::to_string(b) +
" "
1017 + std::to_string(opacity);
Lightweight per-volume record used by GTree to populate the UI.
std::string get_solidsOpr() const
Return the boolean solid operation descriptor.
std::string get_motherVolume() const
Return the mother volume name.
static std::string vname_from_v4name(std::string v4name)
Extract the "leaf" volume name from a full volume name.
double get_opacity() const
Return the cached opacity (alpha) in [0,1].
std::string get_volDescription() const
Return the volume description.
std::string get_rotation() const
Return the placement rotation string with units.
double get_density() const
Return the density, computing it on first request.
void set_color(const QColor &c)
Update the cached color.
void set_opacity(double opacity)
Update the cached opacity.
double get_volume() const
Return the volume, computing it on first request.
bool get_visibility() const
Return the cached visibility state.
double get_mass() const
Return the mass, computing it on first request.
QColor get_color() const
Return the cached RGB color.
std::string get_solidType() const
Return the solid type string (e.g. "G4Box").
static std::string system_from_v4name(std::string v4name)
Extract the system name from a full volume name.
std::string get_position() const
Return the placement position string with units.
std::string get_parameters() const
Return the solid parameters string with units.
std::string get_material() const
Return the cached material name.
G4Ttree_item(G4Volume *g4volume, const GVolume *gvolume=nullptr)
Construct a cached record for a single geometry volume.
std::string get_mother() const
Return the cached mother name.
G4VSolid * getSolid() const noexcept
G4VPhysicalVolume * getPhysical() const noexcept
G4LogicalVolume * getLogical() const noexcept
GBase(const std::shared_ptr< GOptions > &gopt, std::string logger_name="")
std::shared_ptr< GLogger > log
GTree(const std::shared_ptr< GOptions > &gopt, std::unordered_map< std::string, G4Volume * > g4volumes_map, std::unordered_map< std::string, const GVolume * > gvolumes_map={}, QWidget *parent=nullptr)
Construct the geometry tree widget.
std::string getDescription() const
std::string getRot() const
std::string getPos() const
std::string getParameters() const
std::string getType() const
std::string getMotherName() const
std::string getSolidsOpr() const
#define ROOTWORLDGVOLUMENAME
Option-set definition entry point for the GTree module.
constexpr const char * GTREE_LOGGER
#define UNINITIALIZEDSTRINGQUANTITY
void apply_uimanager_commands(const std::string &commands)
vector< std::string > getStringVectorFromString(const std::string &input)