11#include "G4BooleanSolid.hh"
17#include <QColorDialog>
19#include <QDialogButtonBox>
21#include <QSignalBlocker>
33#include "G4VisAttributes.hh"
34#include "G4Material.hh"
35#include "G4VisManager.hh"
36#include "G4VViewer.hh"
37#include "G4VSceneHandler.hh"
38#include "G4VGraphicsSystem.hh"
39#include "G4UImanager.hh"
45std::vector<std::string> splitParams(
const std::string& s) {
46 std::vector<std::string> result;
47 std::istringstream ss(s);
49 while (std::getline(ss, tok,
',')) {
50 const auto a = tok.find_first_not_of(
" \t");
51 const auto b = tok.find_last_not_of(
" \t");
52 if (a != std::string::npos)
53 result.push_back(tok.substr(a, b - a + 1));
58using PDesc = std::vector<const char*>;
60const PDesc& solidParamDescs(
const std::string& solid, std::size_t n) {
61 static const std::unordered_map<std::string, PDesc> kFixed = {
62 {
"G4Box", {
"dx: half length in x",
63 "dy: half length in y",
64 "dz: half length in z"}},
65 {
"G4Tubs", {
"rin: inner radius",
67 "length: half length in z",
68 "phi start: starting phi angle",
69 "phi total: total phi angle"}},
70 {
"G4Cons", {
"rin1: inner radius at -dz",
71 "rout1: outer radius at -dz",
72 "rin2: inner radius at +dz",
73 "rout2: outer radius at +dz",
74 "length: half length in z",
75 "phi start: starting phi angle",
76 "phi total: total phi angle"}},
77 {
"G4Trd", {
"dx1: half length in x at -dz",
78 "dx2: half length in x at +dz",
79 "dy1: half length in y at -dz",
80 "dy2: half length in y at +dz",
81 "z: half length in z"}},
82 {
"G4Sphere", {
"rmin: inner radius",
84 "sphi: starting phi angle",
85 "dphi: delta phi angle",
86 "stheta: starting theta angle",
87 "dtheta: delta theta angle"}},
88 {
"G4Paraboloid", {
"dz: half length in z",
90 "r2: radius at +dz"}},
91 {
"G4EllipticalTube", {
"dx: half length in x",
92 "dy: half length in y",
93 "dz: half length in z"}},
95 static const std::unordered_map<std::size_t, PDesc> kTrap = {
96 {4, {
"pz: length along Z",
98 "px: length along X (wider side)",
99 "pltx: length along X (narrower side)"}},
100 {11, {
"pDz: half Z length",
101 "pTheta: polar angle of line joining base centres",
102 "pPhi: azimuthal angle of line joining base centres",
103 "pDy1: half Y length at -dz",
104 "pDx1: half X length at smaller Y, base at -dz",
105 "pDx2: half X length at bigger Y, base at -dz",
106 "pAlp1: angle between Y-axis and centre line at -dz",
107 "pDy2: half Y length at +dz",
108 "pDx3: half X length at smaller Y, base at +dz",
109 "pDx4: half X length at bigger Y, base at +dz",
110 "pAlp2: angle between Y-axis and centre line at +dz"}},
112 static const PDesc kEmpty;
114 if (solid ==
"G4Trap") {
115 const auto it = kTrap.find(n);
116 return it != kTrap.end() ? it->second : kEmpty;
118 const auto it = kFixed.find(solid);
119 return it != kFixed.end() ? it->second : kEmpty;
124static QString formatVal(
const std::string& token) {
125 const auto star = token.find(
'*');
126 const std::string numPart = (star == std::string::npos) ? token : token.substr(0, star);
127 const std::string unit = (star == std::string::npos) ?
"" : token.substr(star);
130 double v = std::stod(numPart, &pos);
131 if (pos != numPart.size())
return QString::fromStdString(token);
132 if (std::abs(v) < 1e-7) v = 0.0;
133 QString s = QString::number(v,
'f', 3);
134 while (s.endsWith(
'0') && s.contains(
'.')) s.chop(1);
135 if (s.endsWith(
'.')) s.chop(1);
136 return s + QString::fromStdString(unit);
138 return QString::fromStdString(token);
142QString formatParameters(
const std::string& solid,
const std::string& paramsStr) {
143 if (paramsStr.empty())
return {};
144 const auto vals = splitParams(paramsStr);
145 if (vals.empty())
return {};
147 QString html =
"Parameters:";
150 if (solid ==
"G4Polycone" && vals.size() >= 3) {
151 const char* kPcFixed[3] = {
152 "phi start: starting phi angle",
153 "phi total: total phi angle",
154 "nplanes: number of planes"
156 for (
int i = 0; i < 3 && i < (int)vals.size(); ++i)
157 html += QString(
"<br> <i>%1</i>: %2").arg(kPcFixed[i], formatVal(vals[i]));
159 try { nplanes = std::stoi(vals[2]); }
catch (...) {}
161 for (
int p = 0; p < nplanes && idx < (int)vals.size(); ++p, ++idx)
162 html += QString(
"<br> <i>z[%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>rin[%1]</i>: %2").arg(p).arg(formatVal(vals[idx]));
165 for (
int p = 0; p < nplanes && idx < (int)vals.size(); ++p, ++idx)
166 html += QString(
"<br> <i>rout[%1]</i>: %2").arg(p).arg(formatVal(vals[idx]));
171 if (solid ==
"G4Trap" && vals.size() == 24) {
172 const char* kAxes[3] = {
"x",
"y",
"z"};
173 for (
int v = 0; v < 8; ++v)
174 for (
int c = 0; c < 3; ++c)
175 html += QString(
"<br> <i>v%1%2</i>: %3")
176 .arg(v + 1).arg(kAxes[c]).arg(formatVal(vals[v * 3 + c]));
181 const auto& descs = solidParamDescs(solid, vals.size());
182 for (std::size_t i = 0; i < vals.size(); ++i) {
183 const QString desc = i < descs.size()
184 ? QString::fromStdString(descs[i])
185 : QString(
"missing parameters description");
186 html += QString(
"<br> <i>%1</i>: %2").arg(desc, formatVal(vals[i]));
191std::string resolveBooleanOperandName(
const std::string& systemName,
192 const std::string& operand,
193 const std::unordered_map<std::string, const GVolume*>& gvolumes) {
194 if (gvolumes.find(operand) != gvolumes.end())
return operand;
196 const std::string qualified = systemName.empty() ? operand : systemName +
"/" + operand;
197 if (gvolumes.find(qualified) != gvolumes.end())
return qualified;
202std::string leafName(
const std::string& volumeName) {
203 const auto slash = volumeName.find_last_of(
'/');
204 return slash == std::string::npos ? volumeName : volumeName.substr(slash + 1);
207QString joinBooleanTokens(
const std::vector<std::string>& tokens) {
209 for (
const auto& token : tokens) {
210 parts << QString::fromStdString(token).toHtmlEscaped();
212 return parts.join(QStringLiteral(
" "));
215QString formatOperationLine(
const std::vector<std::string>& tokens,
216 const std::string& systemName,
217 const std::unordered_map<std::string, const GVolume*>& gvolumes) {
218 if (tokens.size() != 3)
return joinBooleanTokens(tokens);
220 const auto left = leafName(resolveBooleanOperandName(systemName, tokens[0], gvolumes));
221 const auto right = leafName(resolveBooleanOperandName(systemName, tokens[2], gvolumes));
222 return QString(
"%1 %2 %3").arg(QString::fromStdString(left).toHtmlEscaped(),
223 QString::fromStdString(tokens[1]).toHtmlEscaped(),
224 QString::fromStdString(right).toHtmlEscaped());
227void appendNestedBooleanOperationLines(QString& html,
228 const std::string& volumeName,
229 const std::string& systemName,
230 const std::unordered_map<std::string, const GVolume*>& gvolumes,
231 std::set<std::string>& visited) {
232 const auto fullName = resolveBooleanOperandName(systemName, volumeName, gvolumes);
233 if (!visited.insert(fullName).second)
return;
235 const auto gvolIt = gvolumes.find(fullName);
236 if (gvolIt == gvolumes.end())
return;
238 const auto solidsOpr = gvolIt->second->getSolidsOpr();
239 if (!solidsOpr)
return;
242 if (tokens.size() != 3)
return;
244 html += QString(
"<br> %1 = %2")
245 .arg(QString::fromStdString(leafName(fullName)).toHtmlEscaped(),
246 formatOperationLine(tokens, systemName, gvolumes));
248 appendNestedBooleanOperationLines(html, tokens[0], systemName, gvolumes, visited);
249 appendNestedBooleanOperationLines(html, tokens[2], systemName, gvolumes, visited);
252QString formatBooleanOperationDescription(
const std::string& fullName,
253 const std::string& solidsOpr,
254 const std::unordered_map<std::string, const GVolume*>& gvolumes) {
258 if (tokens.size() != 3) {
259 return QObject::tr(
"Boolean Operation: %1").arg(QString::fromStdString(solidsOpr).toHtmlEscaped());
262 const auto slash = fullName.find_last_of(
'/');
263 const std::string systemName = slash == std::string::npos ?
"" : fullName.substr(0, slash);
264 QString html = QObject::tr(
"Boolean Operation: %1").arg(formatOperationLine(tokens, systemName, gvolumes));
266 std::set<std::string> visited{fullName};
267 appendNestedBooleanOperationLines(html, tokens[0], systemName, gvolumes, visited);
268 appendNestedBooleanOperationLines(html, tokens[2], systemName, gvolumes, visited);
272QString formatMirrorVector(
const std::vector<double>& values,
double scale = 1.0,
const QString& unit = {}) {
273 if (values.empty())
return QObject::tr(
"Not set");
275 QStringList formatted;
276 for (
const double value : values) {
277 QString entry = QString::number(value / scale,
'g', 10);
278 if (!unit.isEmpty()) entry += QStringLiteral(
" ") + unit;
281 return formatted.join(QStringLiteral(
", "));
284QString formatMirrorProperties(
const GMirror& mirror) {
285 QString html = QStringLiteral(
"<table cellspacing=\"5\">");
286 const auto addRow = [&html](
const QString& label, QString value) {
287 if (value.isEmpty()) value = QObject::tr(
"Not set");
288 value = value.toHtmlEscaped().replace(QStringLiteral(
"\n"), QStringLiteral(
"<br>"));
289 html += QStringLiteral(
"<tr><td valign=\"top\"><b>%1:</b></td><td>%2</td></tr>")
290 .arg(label.toHtmlEscaped(), value);
293 addRow(QObject::tr(
"System"), QString::fromStdString(mirror.
getSystem()));
294 addRow(QObject::tr(
"Name"), QString::fromStdString(mirror.
getName()));
295 addRow(QObject::tr(
"Description"), QString::fromStdString(mirror.
getDescription()));
296 addRow(QObject::tr(
"Type"), QString::fromStdString(mirror.
getType()));
297 addRow(QObject::tr(
"Finish"), QString::fromStdString(mirror.
getFinish()));
298 addRow(QObject::tr(
"Model"), QString::fromStdString(mirror.
getModel()));
299 addRow(QObject::tr(
"Border"), QString::fromStdString(mirror.
getBorder()));
300 addRow(QObject::tr(
"Material optical properties"), QString::fromStdString(mirror.
getMatOptProps()));
301 addRow(QObject::tr(
"Photon energy"), formatMirrorVector(mirror.
getPhotonEnergy(), CLHEP::eV,
"eV"));
303 addRow(QObject::tr(
"Reflectivity"), formatMirrorVector(mirror.
getReflectivity()));
304 addRow(QObject::tr(
"Efficiency"), formatMirrorVector(mirror.
getEfficiency()));
305 addRow(QObject::tr(
"Specular lobe"), formatMirrorVector(mirror.
getSpecularLobe()));
306 addRow(QObject::tr(
"Specular spike"), formatMirrorVector(mirror.
getSpecularSpike()));
307 addRow(QObject::tr(
"Backscatter"), formatMirrorVector(mirror.
getBackscatter()));
308 addRow(QObject::tr(
"Transmittance"), formatMirrorVector(mirror.
getTransmittance()));
309 addRow(QObject::tr(
"Sigma alpha"),
312 return html + QStringLiteral(
"</table>");
320 if (g4volume ==
nullptr || g4volume->
getLogical() ==
nullptr || g4volume->
getSolid() ==
nullptr) {
322 material =
"unavailable";
323 color = QColor::fromRgbF(1.0, 1.0, 1.0);
331 auto svolume = g4volume->
getSolid();
333 std::string lname = lvolume->GetName();
335 auto mlvolume = pvolume !=
nullptr ? pvolume->GetMotherLogical() :
nullptr;
337 material = lvolume->GetMaterial()->GetName();
341 material =
"G4_Galactic";
345 auto visAttributes = lvolume->GetVisAttributes();
346 if (visAttributes !=
nullptr) {
347 auto gcolor = visAttributes->GetColour();
348 color = QColor::fromRgbF(gcolor.GetRed(), gcolor.GetGreen(), gcolor.GetBlue());
349 opacity = gcolor.GetAlpha();
350 is_visible = visAttributes->IsVisible();
354 color = QColor::fromRgbF(1.0, 1.0, 1.0);
361 logical_ptr = lvolume;
365 solidType = gvolume->
getType();
367 position = gvolume->
getPos();
368 rotation = gvolume->
getRot();
372 mirrorName = gvolume->
getMirror().value_or(
"");
382void G4Ttree_item::compute_physics_quantities()
const {
383 if (physics_computed)
return;
384 physics_computed =
true;
385 if (logical_ptr ==
nullptr || solid_ptr ==
nullptr)
return;
387 if (
dynamic_cast<G4BooleanSolid*
>(solid_ptr) !=
nullptr) {
388 volume = solid_ptr->EstimateCubicVolume(100000, 0.001) / CLHEP::cm3;
389 density = logical_ptr->GetMaterial()->GetDensity() / (CLHEP::g / CLHEP::cm3);
390 mass = volume * density;
393 volume = solid_ptr->GetCubicVolume() / CLHEP::cm3;
394 mass = logical_ptr->GetMass(
false,
false) / CLHEP::g;
395 density = volume > 0 ? mass / volume : 0.0;
403 return v4name.substr(v4name.find_last_of(
'/') + 1);
409 return v4name.substr(0, v4name.find_last_of(
'/'));
415 std::unordered_map<std::string, G4Volume*> g4volumes_map,
416 std::unordered_map<std::string, const GVolume*> gvolumes_map_in,
418 std::unordered_map<std::string, const GMirror*> gmirrors_map_in) :
421 gvolumes_map(std::move(gvolumes_map_in)),
422 gmirrors_map(std::move(gmirrors_map_in)) {
424 build_tree(g4volumes_map);
427 treeWidget =
new QTreeWidget(
this);
428 treeWidget->setColumnCount(3);
430 headers <<
"Visibility" <<
"Color" <<
"Name";
431 treeWidget->setHeaderLabels(headers);
432 treeWidget->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
433 treeWidget->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
434 treeWidget->header()->setSectionResizeMode(2, QHeaderView::Stretch);
435 treeWidget->setRootIsDecorated(
true);
436 treeWidget->setAlternatingRowColors(
true);
438 auto* mainLayout =
new QHBoxLayout(
this);
439 mainLayout->addWidget(treeWidget, 3);
442 rightPanel = right_widget();
443 mainLayout->addWidget(rightPanel, 3);
445 setLayout(mainLayout);
451 connect(treeWidget, &QTreeWidget::itemChanged,
452 this, >ree::onItemChanged);
456 connect(treeWidget, &QTreeWidget::itemClicked,
457 this, >ree::onTreeItemClicked);
459 connect(treeWidget, &QTreeWidget::currentItemChanged,
460 this, >ree::onCurrentItemChanged);
463 connect(styleButtons->buttonsWidget,
464 SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem*)),
465 this, SLOT(changeStyle()));
469 connect(opacitySlider, &QSlider::valueChanged,
470 this, >ree::onOpacitySliderChanged);
477void GTree::populateTree() {
479 for (
auto& [systemName, volMap] : g4_systems_tree) {
481 auto* systemItem =
new QTreeWidgetItem(treeWidget);
482 systemItem->setText(2, QString::fromStdString(systemName));
483 systemItem->setFlags(systemItem->flags() | Qt::ItemIsUserCheckable);
484 systemItem->setCheckState(0, Qt::Checked);
487 std::map<std::string, QTreeWidgetItem*> itemLookup;
492 for (
auto& [volName, vptr] : volMap) {
495 auto* item =
new QTreeWidgetItem;
497 item->setData(2, Qt::UserRole, QString::fromStdString(volName));
500 item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
501 item->setCheckState(0, vitem->
get_visibility() ? Qt::Checked : Qt::Unchecked);
503 itemLookup[volName] = item;
509 for (
auto& [volName, vptr] : volMap) {
510 const G4Ttree_item* vitem = vptr.get();
512 auto* thisItem = itemLookup[volName];
514 QTreeWidgetItem* parentItem = systemItem;
517 if (!mother.empty() && mother !=
"root") {
518 auto itM = itemLookup.find(mother);
519 if (itM != itemLookup.end()) {
520 parentItem = itM->second;
524 parentItem->addChild(thisItem);
530 for (
auto& [volName, vptr] : volMap) {
531 const G4Ttree_item* vitem = vptr.get();
532 QTreeWidgetItem* item = itemLookup[volName];
534 auto* colorBtn =
new QPushButton(treeWidget);
536 colorBtn->setFixedSize(20, 20);
537 colorBtn->setFlat(
true);
538 colorBtn->setText(QString());
540 colorBtn->setStyleSheet(
541 QString(
"QPushButton { background-color: %1; border: 1px solid %2; }")
542 .arg(c.name(), palette().color(QPalette::Mid).name())
546 colorBtn->setProperty(
"volumeName", QString::fromStdString(volName));
547 connect(colorBtn, &QPushButton::clicked,
this, >ree::onColorButtonClicked);
549 treeWidget->setItemWidget(item, 1, colorBtn);
553 treeWidget->expandAll();
558void GTree::build_tree(std::unordered_map<std::string, G4Volume*> g4volumes_map) {
560 for (
auto [name, g4volume] : g4volumes_map) {
561 if (g4volume ==
nullptr || g4volume->getLogical() ==
nullptr ||
562 g4volume->getSolid() ==
nullptr || g4volume->getPhysical() ==
nullptr) {
563 log->info(2,
"Skipping non-placed helper volume <", name,
"> from tree");
578 auto& system_tree = g4_systems_tree[system_name];
579 const GVolume* gvol =
nullptr;
580 auto it = gvolumes_map.find(name);
581 if (it != gvolumes_map.end()) gvol = it->second;
582 system_tree[name] = std::make_unique<G4Ttree_item>(g4volume, gvol);
586 log->info(2,
"Adding ", name,
" to tree, system_name is ", system_name);
591void GTree::onItemChanged(QTreeWidgetItem* item,
int column) {
592 if (column != 0)
return;
595 QVariant v = item->data(2, Qt::UserRole);
601 bool visible = (item->checkState(0) == Qt::Checked);
603 QSignalBlocker blocker(treeWidget);
605 const int nChildren = item->childCount();
606 for (
int i = 0; i < nChildren; ++i) {
607 QTreeWidgetItem* child = item->child(i);
608 QVariant cv = child->data(2, Qt::UserRole);
612 const QString fullName = cv.toString();
615 child->setCheckState(0, visible ? Qt::Checked : Qt::Unchecked);
618 set_visibility(fullName.toStdString(), visible);
627 const QString fullName = v.toString();
628 bool visible = (item->checkState(0) == Qt::Checked);
630 QSignalBlocker blocker(treeWidget);
633 set_visibility(fullName.toStdString(), visible);
636 const int nChildren = item->childCount();
637 for (
int i = 0; i < nChildren; ++i) {
638 QTreeWidgetItem* child = item->child(i);
639 QVariant cv = child->data(2, Qt::UserRole);
643 const QString childName = cv.toString();
646 child->setCheckState(0, visible ? Qt::Checked : Qt::Unchecked);
649 set_visibility(childName.toStdString(), visible);
655void GTree::onColorButtonClicked() {
656 auto* btn = qobject_cast<QPushButton*>(sender());
660 const QString volName = btn->property(
"volumeName").toString();
661 if (volName.isEmpty())
664 QColor initial = Qt::white;
666 QColor c = QColorDialog::getColor(initial,
this, tr(
"Select color"));
672 QString(
"QPushButton { background-color: %1; border: 1px solid %2; }")
673 .arg(c.name(), palette().color(QPalette::Mid).name())
677 set_color(volName.toStdString(), c);
682void GTree::set_visibility(
const std::string& volumeName,
bool visible) {
683 std::string vis_int = visible ?
"1" :
"0";
685 std::string command =
"/vis/geometry/set/visibility " + volumeName +
" -1 " + vis_int;
689 command =
"/vis/geometry/set/visibility " + volumeName +
" 0 " + vis_int;
696void GTree::set_color(
const std::string& volumeName,
const QColor& c) {
697 G4Ttree_item* item = findTreeItem(volumeName);
698 double currentOpacity = item ? item->
get_opacity() : 1.0;
701 std::string command =
"/vis/geometry/set/colour " + volumeName +
" 0 "
702 + std::to_string(c.redF()) +
" "
703 + std::to_string(c.greenF()) +
" "
704 + std::to_string(c.blueF()) +
" "
705 + std::to_string(currentOpacity);
711int GTree::get_ndaughters(QTreeWidgetItem* item)
const {
713 return item->childCount();
717G4Ttree_item* GTree::findTreeItem(
const std::string& fullName) {
718 for (
const auto& [systemName, volMap] : g4_systems_tree) {
719 auto it = volMap.find(fullName);
720 if (it != volMap.end()) {
721 return it->second.get();
729void GTree::onTreeItemClicked(QTreeWidgetItem* item,
int ) {
734 bottomPanel->setVisible(
false);
735 current_volume_name.clear();
739 bottomPanel->setVisible(
true);
742 QVariant v = item->data(2, Qt::UserRole);
743 bool isVolume = v.isValid();
747 typeLabel->setText(QStringLiteral(
"<b>G4 Volume</b>"));
748 current_volume_name = v.toString().toStdString();
751 typeLabel->setText(QStringLiteral(
"<b>System</b>"));
752 current_volume_name.clear();
756 int nd = get_ndaughters(item);
757 daughtersLabel->setText(tr(
"Daughters: %1").arg(nd));
760 QString itemName = item->text(2);
761 nameLabel->setText(tr(
"Name: %1").arg(itemName));
765 styleButtons->setVisible(
true);
766 if (mirrorButton) mirrorButton->setVisible(
false);
768 const std::string fullName = v.toString().toStdString();
769 const G4Ttree_item* titem = findTreeItem(fullName);
772 materialLabel->setText(
773 tr(
"Material: %1").arg(QString::fromStdString(titem->
get_material()))
777 massLabel->setText(tr(
"Mass: %1 g").arg(mass));
780 massLabel->setText(tr(
"Mass: %1 kg").arg(mass / 1000));
783 if (volume < 1000000) {
784 volumeLabel->setText(tr(
"Volume: %1 cm3").arg(volume));
787 volumeLabel->setText(tr(
"Volume: %1 m3").arg(volume / 1000000));
790 densityLabel->setText(
791 tr(
"Density: %1 g / cm3").arg(titem->
get_density())
796 solidTypeLabel->setText(solidT.empty() ? QString()
797 : tr(
"Solid: %1").arg(QString::fromStdString(solidT)));
799 const bool hasParams = !params.empty();
800 parametersLabel->setVisible(hasParams);
801 if (hasParams) parametersLabel->setHtml(formatParameters(solidT, params));
803 positionLabel->setText(pos.empty() ? QString()
804 : tr(
"Position: %1").arg(QString::fromStdString(pos)));
806 rotationLabel->setText(rot.empty() ? QString()
807 : tr(
"Rotation: %1").arg(QString::fromStdString(rot)));
809 motherLabel->setText(mom.empty() ? QString() : tr(
"Mother: %1").arg(QString::fromStdString(mom)));
810 const auto boolDesc =
811 formatBooleanOperationDescription(fullName, titem->
get_solidsOpr(), gvolumes_map);
812 if (!boolDesc.isEmpty()) {
813 descriptionLabel->setText(boolDesc);
817 descriptionLabel->setText(desc.empty() ? QString()
818 : tr(
"Description: %1")
819 .arg(QString::fromStdString(desc)));
824 mirrorName !=
"none";
825 if (mirrorButton && hasMirror) {
826 const auto slash = fullName.find_last_of(
'/');
827 const std::string systemName = slash == std::string::npos ?
"" : fullName.substr(0, slash);
828 const std::string mirrorKey = systemName +
"/" + mirrorName;
829 if (gmirrors_map.find(mirrorKey) != gmirrors_map.end()) {
830 mirrorButton->setText(tr(
"Mirror: %1").arg(QString::fromStdString(mirrorName)));
831 mirrorButton->setProperty(
"mirrorKey", QString::fromStdString(mirrorKey));
832 mirrorButton->setVisible(
true);
838 int sliderVal =
static_cast<int>(op * 100.0 + 0.5);
840 QSignalBlocker blocker(opacitySlider);
841 opacitySlider->setValue(sliderVal);
843 opacityLabel->setText(QString::number(op,
'f', 2));
844 opacitySlider->setVisible(
true);
850 inspectButton->setText(tr(
"Inspect %1").arg(leaf));
851 inspectButton->setVisible(
true);
853 if (drawOverlapsButton) {
855 drawOverlapsButton->setText(tr(
"Draw Logical Overlaps %1").arg(leaf));
856 drawOverlapsButton->setVisible(
true);
860 styleButtons->setVisible(
false);
861 opacitySlider->setVisible(
false);
862 if (mirrorButton) mirrorButton->setVisible(
false);
863 if (inspectButton) inspectButton->setVisible(
false);
864 if (drawOverlapsButton) drawOverlapsButton->setVisible(
false);
866 materialLabel->setText(tr(
""));
867 massLabel->setText(tr(
""));
868 volumeLabel->setText(tr(
""));
869 densityLabel->setText(tr(
""));
870 solidTypeLabel->setText(tr(
""));
871 parametersLabel->clear();
872 parametersLabel->setVisible(
false);
873 positionLabel->setText(tr(
""));
874 rotationLabel->setText(tr(
""));
875 motherLabel->setText(tr(
""));
876 descriptionLabel->setText(tr(
""));
881void GTree::onCurrentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous) {
887 styleButtons->reset_buttons();
890 onTreeItemClicked(current, 0);
894void GTree::changeStyle() {
897 if (current_volume_name.empty())
900 int button_index = styleButtons->button_pressed();
902 if (button_index == 3) {
905 QSignalBlocker blocker(styleButtons->buttonsWidget);
906 styleButtons->reset_buttons();
914 if (button_index == 0) {
915 command =
"/vis/geometry/set/forceWireframe " + current_volume_name +
" 0 1 ";
917 else if (button_index == 1) {
918 command =
"/vis/geometry/set/forceSolid " + current_volume_name +
" 0 1 ";
920 else if (button_index == 2) {
921 command =
"/vis/geometry/set/forceCloud " + current_volume_name +
" 0 1 ";
933void GTree::centreTwinkle() {
934 G4Ttree_item* item = findTreeItem(current_volume_name);
937 twinkleVolumeName = current_volume_name;
945 twinkleTimer =
new QTimer(
this);
946 connect(twinkleTimer, &QTimer::timeout,
this, >ree::onTwinkleStep);
948 if (twinkleTimer->isActive())
949 twinkleTimer->stop();
951 twinkleTimer->start(180);
956void GTree::onTwinkleStep() {
957 constexpr int kSteps = 5;
958 static const QColor kFlash[kSteps] = {
961 QColor( 50, 255, 50),
962 QColor( 0, 220, 255),
963 QColor(220, 50, 255),
966 if (twinkleTick < kSteps) {
967 set_color(twinkleVolumeName, kFlash[twinkleTick]);
970 twinkleTimer->stop();
971 G4Ttree_item* item = findTreeItem(twinkleVolumeName);
976 const double r = twinkleSavedColor.redF();
977 const double g = twinkleSavedColor.greenF();
978 const double b = twinkleSavedColor.blueF();
979 const std::string cmd =
"/vis/geometry/set/colour " + twinkleVolumeName +
" 0 "
980 + std::to_string(r) +
" "
981 + std::to_string(g) +
" "
982 + std::to_string(b) +
" "
983 + std::to_string(twinkleSavedOpacity);
990void GTree::inspectVolume() {
991 if (current_volume_name.empty())
return;
993 G4UImanager* uim = G4UImanager::GetUIpointer();
998 std::string driverName =
"OGLSQt";
999 std::string originalViewerName;
1000 auto* vm = G4VisManager::GetInstance();
1002 const auto* viewer = vm->GetCurrentViewer();
1004 originalViewerName = viewer->GetName();
1005 const auto* sh = viewer->GetSceneHandler();
1007 const auto* gs = sh->GetGraphicsSystem();
1008 if (gs) driverName = gs->GetNickname();
1020 uim->ApplyCommand(
"/vis/open " + driverName);
1021 uim->ApplyCommand(
"/vis/scene/create");
1022 uim->ApplyCommand(
"/vis/sceneHandler/attach");
1023 uim->ApplyCommand(
"/vis/scene/add/volume " + current_volume_name +
" -1");
1024 uim->ApplyCommand(
"/vis/viewer/set/background 1 1 1 1");
1025 uim->ApplyCommand(
"/vis/viewer/set/lineSegmentsPerCircle 100");
1028 uim->ApplyCommand(
"/vis/set/textColour black");
1029 uim->ApplyCommand(
"/vis/set/textLayout centre");
1030 uim->ApplyCommand(
"/vis/scene/add/text2D 0 0.85 36 ! ! " + leafName);
1031 uim->ApplyCommand(
"/vis/viewer/flush");
1035 if (!originalViewerName.empty())
1036 uim->ApplyCommand(
"/vis/viewer/select " + originalViewerName);
1040void GTree::showMirrorProperties() {
1041 if (!mirrorButton)
return;
1043 const std::string mirrorKey = mirrorButton->property(
"mirrorKey").toString().toStdString();
1044 const auto mirrorIt = gmirrors_map.find(mirrorKey);
1045 if (mirrorIt == gmirrors_map.end() || mirrorIt->second ==
nullptr)
return;
1047 const GMirror& mirror = *mirrorIt->second;
1048 QDialog dialog(
this);
1049 dialog.setWindowTitle(tr(
"Mirror: %1").arg(QString::fromStdString(mirror.
getName())));
1050 dialog.setModal(
true);
1052 auto* layout =
new QVBoxLayout(&dialog);
1053 auto* properties =
new QTextEdit(&dialog);
1054 properties->setReadOnly(
true);
1055 properties->setHtml(formatMirrorProperties(mirror));
1056 layout->addWidget(properties);
1058 auto* buttons =
new QDialogButtonBox(QDialogButtonBox::Close, &dialog);
1059 connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
1060 layout->addWidget(buttons);
1062 dialog.resize(650, 520);
1068void GTree::drawOverlapsWarning() {
1069 QMessageBox::warning(
this,
1070 tr(
"Not yet implemented"),
1071 tr(
"Draw Logical Overlaps will be implemented when Geant4 fixes the\n"
1072 "G4ToolsSGSceneHandler::GetOrCreateNode \"World mis-match\" crash\n"
1073 "triggered by /vis/drawLogicalVolume in Geant4 11.4.2."));
1078void GTree::onOpacitySliderChanged(
int value) {
1079 if (current_volume_name.empty())
1082 double opacity = value / 100.0;
1085 opacityLabel->setText(QString::number(opacity,
'f', 2));
1088 set_opacity(current_volume_name, opacity);
1092void GTree::set_opacity(
const std::string& volumeName,
double opacity) {
1095 G4Ttree_item* item = findTreeItem(volumeName);
1099 double r = c.redF();
1100 double g = c.greenF();
1101 double b = c.blueF();
1104 std::string command =
"/vis/geometry/set/colour " + volumeName +
" 0 "
1105 + std::to_string(r) +
" "
1106 + std::to_string(g) +
" "
1107 + std::to_string(b) +
" "
1108 + std::to_string(opacity);
Lightweight per-volume record used by GTree to populate the UI.
std::string get_mirrorName() const
Return the associated mirror name.
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
std::vector< double > getReflectivity() const
std::vector< double > getSpecularSpike() const
std::vector< double > getEfficiency() const
bool hasSigmaAlpha() const
std::string getName() const
std::vector< double > getPhotonEnergy() const
std::string getMatOptProps() const
std::vector< double > getBackscatter() const
std::vector< double > getTransmittance() const
std::string getDescription() const
double getSigmaAlpha() const
std::string getType() const
std::vector< double > getSpecularLobe() const
std::string getFinish() const
std::string getBorder() const
std::string getSystem() const
std::string getModel() const
std::vector< double > getIndexOfRefraction() const
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, std::unordered_map< std::string, const GMirror * > gmirrors_map={})
Construct the geometry tree widget.
std::string getDescription() const
std::string getRot() const
const std::optional< std::string > & getSolidsOpr() const
const std::optional< std::string > & getParameters() const
std::string getPos() const
std::string getType() const
const std::optional< std::string > & getMirror() const
std::string getMotherName() const
Option-set definition entry point for the GTree module.
constexpr const char * GTREE_LOGGER
constexpr char MOTHEROFUSALL[]
constexpr char ROOTWORLDGVOLUMENAME[]
bool is_unset(std::string_view s)
void apply_uimanager_commands(const std::string &commands)
vector< std::string > getStringVectorFromString(const std::string &input)