Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
REveGeoOverlaps.cxx
Go to the documentation of this file.
1
7#include <ROOT/REveUtil.hxx>
8
9#include "TGeoBBox.h"
10#include "TGeoManager.h"
11#include "TGeoNode.h"
12#include "TGeoOverlap.h"
13#include "TGeoShape.h"
14#include "TGeoVolume.h"
15#include "TObjArray.h"
16#include "TPolyMarker3D.h"
17
18#include <algorithm>
19#include <cmath>
20#include <cstdio>
21#include <map>
22#include <set>
23#include <utility>
24
25using namespace ROOT::Experimental;
26
27namespace {
28
29/// Volume plus matrix is what ties a TGeoOverlap to a daughter, since it never
30/// names the node itself.
31bool SameMatrix(const TGeoMatrix *a, const TGeoMatrix *b)
32{
33 constexpr double tol = 1e-9;
34 if (!a || !b)
35 return false;
36 const Double_t *ta = a->GetTranslation(), *tb = b->GetTranslation();
37 for (int i = 0; i < 3; ++i)
38 if (std::fabs(ta[i] - tb[i]) > tol)
39 return false;
40 const Double_t *ra = a->GetRotationMatrix(), *rb = b->GetRotationMatrix();
41 for (int i = 0; i < 9; ++i)
42 if (std::fabs(ra[i] - rb[i]) > tol)
43 return false;
44 return true;
45}
46
47bool IsDaughter(TGeoVolume *mom, TGeoVolume *vol, const TGeoMatrix *matr)
48{
49 if (!mom || !vol || !matr)
50 return false;
51 for (int d = 0; d < mom->GetNdaughters(); ++d) {
52 TGeoNode *node = mom->GetNode(d);
53 if (node->GetVolume() == vol && SameMatrix(node->GetMatrix(), matr))
54 return true;
55 }
56 return false;
57}
58
59bool IsFinitePoint(const Float_t *p)
60{
61 return std::isfinite(p[0]) && std::isfinite(p[1]) && std::isfinite(p[2]);
62}
63
64bool InsideWorld(const Float_t *p, double limit)
65{
66 if (limit <= 0.)
67 return true;
68 return std::fabs(p[0]) <= limit && std::fabs(p[1]) <= limit && std::fabs(p[2]) <= limit;
69}
70
71/// A representative size for a volume's shape, for scaling a marker to fit
72/// next to it. TGeoBBox is the common ancestor of nearly every TGeoShape
73/// subclass -- tubes, cones, trapezoids all derive from it and fill in its
74/// dx/dy/dz via their own ComputeBBox() -- so this isn't limited to literal
75/// boxes. The *smallest* half-extent is what matters: a thin plate a metre
76/// wide and a millimetre thick is a millimetre-scale feature, not a metre-scale
77/// one, and a cross sized off the wrong dimension would stick out past it.
78double ShapeSize(TGeoVolume *vol)
79{
80 if (!vol || !vol->GetShape())
81 return 0.;
82 if (auto *bbox = dynamic_cast<TGeoBBox *>(vol->GetShape()))
83 return std::min({bbox->GetDX(), bbox->GetDY(), bbox->GetDZ()});
84 return 0.;
85}
86
87} // namespace
88
89////////////////////////////////////////////////////////////////////////////////
90
92{
93 fHolder = new REveElement("OverlapViz", "Shapes of the selected overlap");
94}
95
96////////////////////////////////////////////////////////////////////////////////
97/// Find every overlap and extrusion in the geometry and work out where each one is.
98///
99/// One pass over the top volume. CheckOverlaps() is recursive, so checking volumes
100/// one at a time would repeat the same work and cost seconds each time.
101
103{
104 fMgr = mgr;
105 fEntries.clear();
106 fSelected = -1;
107 fPrecision = precision;
108 ++fScanGen;
109
110 if (!mgr || !mgr->GetTopVolume())
111 return;
112
113 // CheckOverlaps() needs a navigator for whichever thread calls it -- its very
114 // first line is SetCheckingOverlaps(), which crashes on a null navigator. The
115 // initial scan runs on the main thread, which already has one from loading the
116 // geometry; a rescan from SetPrecision() runs on REveManager's MIR-exec thread,
117 // which does not.
118 if (!mgr->GetCurrentNavigator())
119 mgr->AddNavigator();
120
121 mgr->ClearOverlaps();
122 mgr->GetTopVolume()->CheckOverlaps(precision);
123
124 TObjArray *lst = mgr->GetListOfOverlaps();
125 if (!lst)
126 return;
127
128 // Mother candidates per volume. Only genuine overlaps need this -- there both
129 // sides are daughters of some common mother; an extrusion names its mother itself.
130 std::map<TGeoVolume *, std::vector<TGeoVolume *>> mothers;
131 bool mothersBuilt = false;
132 auto buildMothers = [&mothers, mgr]() {
133 TIter next(mgr->GetListOfVolumes());
134 while (auto *vol = static_cast<TGeoVolume *>(next()))
135 for (int d = 0; d < vol->GetNdaughters(); ++d)
136 mothers[vol->GetNode(d)->GetVolume()].emplace_back(vol);
137 };
138
139 // Every TGeoVolume* actually placed somewhere under top. A candidate mother
140 // can satisfy IsDaughter() below (right daughters, right matrices) and still
141 // be the wrong pick: some geometries (ALICE's does) carry two structurally
142 // identical TGeoVolume objects sharing one name, only one of which the real
143 // tree ever places -- the other is a registered-but-orphaned duplicate.
144 // IsDaughter() alone can't tell them apart since it only ever looks at a
145 // candidate's own local daughter list, never at whether the candidate itself
146 // is reachable, so that has to be checked separately.
147 std::set<TGeoVolume *> reachable;
148 bool reachableBuilt = false;
149 auto buildReachable = [&reachable, mgr]() {
150 TGeoVolume *top = mgr->GetTopVolume();
151 reachable.insert(top);
152 TGeoIterator iter(top);
153 TGeoNode *node = nullptr;
154 while ((node = iter.Next()))
155 reachable.insert(node->GetVolume());
156 };
157
158 // A coarse fuse against numeric garbage. TGeoOverlap's points are in the
159 // mother's own (small) local frame -- see LocalizeMarkers() -- so anything
160 // legitimate is nowhere near the size of the world; this only exists to catch
161 // outright nonsense (CMS has points out at 1e16) before it ever reaches a
162 // TGeoShape::Contains()/Safety() call. LocalizeMarkers() does the real,
163 // targeted filtering once the mother/daughters are known.
164 double worldLimit = 0.;
165 if (auto *wbox = dynamic_cast<TGeoBBox *>(mgr->GetTopVolume()->GetShape()))
166 worldLimit = 2. * std::max(wbox->GetDX(), std::max(wbox->GetDY(), wbox->GetDZ())) + 100.;
167
168 std::vector<std::vector<float>> rawMarkers; // mother-local points, kept until the mother is known
169
170 for (int i = 0; i < lst->GetEntriesFast(); ++i) {
171 auto *ovl = dynamic_cast<TGeoOverlap *>(lst->At(i));
172 if (!ovl)
173 continue;
174
175 Entry ent;
176 ent.fName = ovl->GetName() ? ovl->GetName() : "";
177 ent.fTitle = ovl->GetTitle() ? ovl->GetTitle() : "";
178 ent.fExtrusion = ovl->IsExtrusion();
179 ent.fValue = ovl->GetOverlap();
180 ent.fV1 = ovl->GetFirstVolume();
181 ent.fV2 = ovl->GetSecondVolume();
182 ent.fVol1 = ent.fV1 ? ent.fV1->GetName() : "";
183 ent.fVol2 = ent.fV2 ? ent.fV2->GetName() : "";
184 if (ovl->GetFirstMatrix())
185 ent.fM1 = *ovl->GetFirstMatrix();
186 if (ovl->GetSecondMatrix())
187 ent.fM2 = *ovl->GetSecondMatrix();
188
189 if (ent.fExtrusion) {
190 ent.fMother = ent.fV1;
191 } else {
192 if (!mothersBuilt) {
193 buildMothers();
194 mothersBuilt = true;
195 }
196 if (!reachableBuilt) {
197 buildReachable();
198 reachableBuilt = true;
199 }
200 auto it = mothers.find(ent.fV1);
201 if (it != mothers.end())
202 for (auto *cand : it->second)
203 if (reachable.count(cand) && IsDaughter(cand, ent.fV1, ovl->GetFirstMatrix()) &&
204 IsDaughter(cand, ent.fV2, ovl->GetSecondMatrix())) {
205 ent.fMother = cand;
206 break;
207 }
208 }
209
210 // Without a mother there is no frame to place it in. Drawing it anyway would
211 // put it somewhere arbitrary, which is worse than leaving it out.
212 if (!ent.fMother)
213 continue;
214
215 std::vector<float> pts;
216 if (TPolyMarker3D *pm = ovl->GetPolyMarker()) {
217 const Float_t *p = pm->GetP();
218 ent.fNumRawMarkers = pm->GetN();
219 pts.reserve(3 * pm->GetN());
220 for (int k = 0; k < pm->GetN(); ++k)
221 if (IsFinitePoint(p + 3 * k) && InsideWorld(p + 3 * k, worldLimit)) {
222 pts.push_back(p[3 * k]);
223 pts.push_back(p[3 * k + 1]);
224 pts.push_back(p[3 * k + 2]);
225 }
226 }
227
228 fEntries.emplace_back(std::move(ent));
229 rawMarkers.emplace_back(std::move(pts));
230 }
231
233
234 for (size_t i = 0; i < fEntries.size(); ++i)
235 LocalizeMarkers(fEntries[i], rawMarkers[i]);
236
237 BuildGroups();
238
240}
241
242////////////////////////////////////////////////////////////////////////////////
243/// Global matrix of every physical placement of each mother volume.
244///
245/// One walk of the geometry serves all entries; the set of mothers is small (four
246/// volumes for the CMS geometry) but finding them needs the full traversal anyway.
247
249{
250 std::set<TGeoVolume *> wanted;
251 for (auto &ent : fEntries)
252 wanted.insert(ent.fMother);
253
254 std::map<TGeoVolume *, std::vector<TGeoHMatrix>> found;
255
256 TGeoVolume *top = mgr->GetTopVolume();
257 if (wanted.count(top))
258 found[top].emplace_back(TGeoHMatrix()); // identity: the top volume is the world
259
260 TGeoIterator iter(top);
261 TGeoNode *node = nullptr;
262 while ((node = iter.Next())) {
263 TGeoVolume *vol = node->GetVolume();
264 if (!wanted.count(vol))
265 continue;
266 const TGeoMatrix *cur = iter.GetCurrentMatrix();
267 found[vol].emplace_back(cur ? TGeoHMatrix(*cur) : TGeoHMatrix());
268 }
269
270 for (auto &ent : fEntries) {
271 auto it = found.find(ent.fMother);
272 if (it != found.end())
273 ent.fGlobals = it->second;
274 }
275}
276
277////////////////////////////////////////////////////////////////////////////////
278/// Filter the marker points down to the ones that plausibly belong to this flaw.
279///
280/// TGeoOverlap's TPolyMarker3D is filled in the frame of whichever node
281/// CheckOverlaps() was examining -- i.e. already the mother's own local frame,
282/// the same one fM1/fM2 are expressed in. There is no placement to guess and no
283/// conversion to do: the points already are what they need to be.
284///
285/// What *is* still worth doing is dropping outliers: TGeoOverlap's own points
286/// can be finite and plausible-looking while sitting nowhere near either
287/// daughter, and for a flaw a fraction of a millimeter across that's glaring --
288/// the crude "inside the world" fuse applied before this function is far too
289/// loose to catch it (it exists only to stop outright numeric garbage, not to
290/// judge plausibility). So each point is tested against the two daughter
291/// shapes it should be between and dropped if it's near neither.
292///
293/// The shapes involved can be anything the geometry uses -- a tube, a
294/// composite, not just a box -- so the test goes through
295/// TGeoShape::Contains()/Safety() rather than assuming a TGeoBBox.
296
297void REveGeoOverlapTable::LocalizeMarkers(Entry &ent, const std::vector<float> &localPts)
298{
299 ent.fLocalMarkers.clear();
300 if (localPts.empty())
301 return;
302
303 const size_t npt = localPts.size() / 3;
304
305 // a little slack: the points sit on the offending surface, not strictly inside
306 const double margin = 1.0;
307 const double farAway = 1.e6; // stands in for "no daughter to test against"
308
309 auto daughterDistance = [](TGeoVolume *vol, const TGeoHMatrix &local, const Double_t *lp) {
310 if (!vol || !vol->GetShape())
311 return -1.; // no shape to test: not a vote against, just not informative
312 Double_t dl[3];
313 local.MasterToLocal(lp, dl); // lp is mother-local; bring it into the daughter's own frame
314 if (vol->GetShape()->Contains(dl))
315 return 0.;
316 return vol->GetShape()->Safety(dl, kFALSE);
317 };
318
319 // For an extrusion, fV1 *is* the mother -- testing a point against the whole
320 // mother would accept almost anything inside it, so only genuine daughters
321 // (not the mother itself) are tested.
322 bool testV1 = ent.fV1 && ent.fV1 != ent.fMother;
323 bool testV2 = ent.fV2 && ent.fV2 != ent.fMother;
324
325 // Distance from lp to the nearer of the two daughters, or 0 if neither is
326 // testable (nothing to judge the point against, so don't disqualify it).
327 auto distance = [&](const Double_t *lp) {
328 if (!testV1 && !testV2)
329 return 0.;
330 double d1 = testV1 ? daughterDistance(ent.fV1, ent.fM1, lp) : farAway;
331 double d2 = testV2 ? daughterDistance(ent.fV2, ent.fM2, lp) : farAway;
332 return std::min(d1 < 0. ? farAway : d1, d2 < 0. ? farAway : d2);
333 };
334
335 ent.fLocalMarkers.reserve(localPts.size());
336 for (size_t k = 0; k < npt; ++k) {
337 Double_t lp[3] = {localPts[3 * k], localPts[3 * k + 1], localPts[3 * k + 2]};
338 if (distance(lp) > margin)
339 continue;
340 ent.fLocalMarkers.push_back(localPts[3 * k]);
341 ent.fLocalMarkers.push_back(localPts[3 * k + 1]);
342 ent.fLocalMarkers.push_back(localPts[3 * k + 2]);
343 }
344}
345
346////////////////////////////////////////////////////////////////////////////////
347/// Text shown when the mouse is over one of the drawn pieces.
348///
349/// The client puts this straight into innerHTML, so <br> works and one element can
350/// carry the whole story: which flaw, how big, which two volumes, and -- the part
351/// that is otherwise impossible to tell by looking -- which placement out of how
352/// many this particular piece is.
353///
354/// \param which 0 = first volume, 1 = second volume, 2 = the marker points
355
356std::string REveGeoOverlapTable::MakeTip(const Entry &ent, int which, int placement, int inst, int ninst) const
357{
358 TString t;
359 t += TString::Format("<b>%s</b> &middot; %s &middot; <b>%.5f cm</b><br>", ent.fName.c_str(),
360 ent.fExtrusion ? "extrusion" : "overlap", ent.fValue);
361
362 // mark the volume this piece actually is
363 t += TString::Format("%sA %s%s<br>", which == 0 ? "<b>" : "", ent.fVol1.c_str(), which == 0 ? "</b>" : "");
364 t += TString::Format("%sB %s%s<br>", which == 1 ? "<b>" : "", ent.fVol2.c_str(), which == 1 ? "</b>" : "");
365
366 if (ent.fMother)
367 t += TString::Format("in %s<br>", ent.fMother->GetName());
368
369 if (ent.fGlobals.size() > 1)
370 t += TString::Format("placement %d of %d<br>", placement + 1, (int)ent.fGlobals.size());
371
372 if (ninst > 1)
373 t += TString::Format("instance %d of %d<br>", inst + 1, ninst);
374
375 if (which == 2) {
376 int kept = (int)(ent.fLocalMarkers.size() / 3);
377 int dropped = ent.fNumRawMarkers - kept;
378 if (dropped > 0)
379 t += TString::Format("%d points (%d dropped as out of range)", kept, dropped);
380 else
381 t += TString::Format("%d points", kept);
382 }
383
384 return std::string(t.Data());
385}
386
387////////////////////////////////////////////////////////////////////////////////
388/// Draw one placement of the selected overlap: the two shapes and the points.
389
390void REveGeoOverlapTable::AddPlacement(const Entry &ent, const TGeoHMatrix &global, int placement,
391 std::set<std::string> *seen, int inst, int ninst)
392{
393 auto addShape = [&](TGeoVolume *vol, const TGeoHMatrix &local, Color_t col, const char *tag, int which) {
394 if (!vol || !vol->GetShape())
395 return;
396 TGeoHMatrix m = global;
397 m.Multiply(&local);
398
399 // Drawing a whole group would otherwise stack 36 identical copies of the
400 // shared mother on top of each other -- same volume, same matrix, no extra
401 // information and a much heavier scene.
402 if (seen) {
403 const Double_t *t = m.GetTranslation(), *r = m.GetRotationMatrix();
404 char key[512];
405 int n = snprintf(key, sizeof(key), "%p|%.4f,%.4f,%.4f", (void *)vol, t[0], t[1], t[2]);
406 for (int i = 0; i < 9 && n < (int)sizeof(key); ++i)
407 n += snprintf(key + n, sizeof(key) - n, ",%.6f", r[i]);
408 if (!seen->insert(key).second)
409 return;
410 }
411 // REveGeoShape uses TGeoShape::UniqueID as a reference count and deletes the
412 // shape when it drops to zero. The shapes here belong to the TGeoManager and
413 // do not take part in that scheme, so handing one over directly means the
414 // geometry's own shape is deleted as soon as these elements are destroyed --
415 // which happens on the very next selection. Give each element a private clone.
416 TGeoShape *geoShape = nullptr;
417 {
419 geoShape = static_cast<TGeoShape *>(vol->GetShape()->Clone());
420 }
421 if (!geoShape)
422 return;
423 geoShape->SetUniqueID(0);
424
425 auto *shape = new REveGeoShape(TString::Format("%s_%s_%s", ent.fName.c_str(), tag, vol->GetName()).Data(),
426 MakeTip(ent, which, placement, inst, ninst).c_str());
427 shape->SetShape(geoShape);
428 shape->SetTransMatrix(m);
429 shape->SetMainColor(col);
430 shape->SetMainTransparency(50);
431 // fPickable defaults to false, and without it the element never enters the
432 // pick pass at all -- no highlight and no tooltip, with nothing to say why.
433 shape->SetPickable(kTRUE);
434 fHolder->AddElement(shape);
435 };
436
437 addShape(ent.fV1, ent.fM1, kRed, "A", 0);
438 addShape(ent.fV2, ent.fM2, kYellow, "B", 1);
439
440 if (ent.fLocalMarkers.empty())
441 return;
442
443 const size_t npt = ent.fLocalMarkers.size() / 3;
444 auto *ps = new REvePointSet(TString::Format("%s_points", ent.fName.c_str()).Data(),
445 MakeTip(ent, 2, placement, inst, ninst).c_str(), npt);
446 ps->SetMarkerColor(kCyan);
447 ps->SetMarkerSize(fMarkerSize);
448 ps->SetPickable(kTRUE);
449 ps->SetMarkerStyle(4);
450
451 // A GL point can vanish behind a transparent volume depending on draw order,
452 // so give every marker a second, harder-to-hide representation: a 3D cross,
453 // three lines through the point along x/y/z.
454 auto *cross = new REveStraightLineSet(TString::Format("%s_cross", ent.fName.c_str()).Data(),
455 MakeTip(ent, 2, placement, inst, ninst).c_str());
456 cross->SetMainColor(kCyan);
457 cross->SetLineWidth(2);
458 cross->SetPickable(kTRUE);
459
460 // Scaled to the smaller of the two daughters, not a flat constant: entries in
461 // this table range from a 33 cm overlap down to a 0.02 cm one, and a fixed
462 // arm length is either invisible against the big one or many times the size
463 // of the small one. fCrossSize remains the cap, in cm, it always was --
464 // still tunable via SetCrossSize() -- but the arm only uses that much of it
465 // when the smaller shape is actually big enough to warrant it; otherwise it
466 // scales down to a fraction of that shape instead of overshooting it.
467 constexpr double kCrossFraction = 0.3; // arm half-length as a fraction of the smaller daughter's thinnest dimension
468 double s1 = ShapeSize(ent.fV1), s2 = ShapeSize(ent.fV2);
469 double smallest = (s1 > 0. && s2 > 0.) ? std::min(s1, s2) : std::max(s1, s2);
470 double proportional = smallest > 0. ? kCrossFraction * smallest : (double)fCrossSize;
471 const float h = (float)std::min(proportional, (double)fCrossSize);
472
473 for (size_t k = 0; k < npt; ++k) {
474 Double_t lp[3] = {ent.fLocalMarkers[3 * k], ent.fLocalMarkers[3 * k + 1], ent.fLocalMarkers[3 * k + 2]}, gp[3];
475 global.LocalToMaster(lp, gp);
476 ps->SetNextPoint((float)gp[0], (float)gp[1], (float)gp[2]);
477
478 float x = (float)gp[0], y = (float)gp[1], z = (float)gp[2];
479 cross->AddLine(x - h, y, z, x + h, y, z);
480 cross->AddLine(x, y - h, z, x, y + h, z);
481 cross->AddLine(x, y, z - h, x, y, z + h);
482 }
483 fHolder->AddElement(ps);
484 fHolder->AddElement(cross);
485}
486
487////////////////////////////////////////////////////////////////////////////////
488/// Show one overlap and nothing else. Called from the client as a MIR when a row
489/// is picked; a negative index just clears the view.
490
492{
494
495 fSelected = ((idx >= 0) && (idx < (int)fEntries.size())) ? idx : -1;
496 fSelectedGroup = -1;
497
498 if (fSelected >= 0) {
499 const Entry &ent = fEntries[fSelected];
500 for (size_t p = 0; p < ent.fGlobals.size(); ++p)
501 AddPlacement(ent, ent.fGlobals[p], (int)p);
502 }
503
506}
507
508////////////////////////////////////////////////////////////////////////////////
509/// Collapse entries that are the same flaw repeated.
510///
511/// Two entries belong together when they are the same kind of problem between the
512/// same pair of logical volumes. The value is the same across a group in practice,
513/// but the largest is kept rather than assumed.
514
516{
517 fGroups.clear();
518 std::map<std::string, int> index;
519
520 for (size_t i = 0; i < fEntries.size(); ++i) {
521 const Entry &e = fEntries[i];
522 std::string key = e.fVol1 + "|" + e.fVol2 + "|" + (e.fExtrusion ? "e" : "o");
523 auto it = index.find(key);
524 if (it == index.end()) {
525 index[key] = (int)fGroups.size();
526 Group g;
527 g.fVol1 = e.fVol1;
528 g.fVol2 = e.fVol2;
529 g.fExtrusion = e.fExtrusion;
530 g.fValue = e.fValue;
531 g.fMembers.emplace_back((int)i);
532 fGroups.emplace_back(std::move(g));
533 } else {
534 Group &g = fGroups[it->second];
535 g.fMembers.emplace_back((int)i);
536 if (e.fValue > g.fValue)
537 g.fValue = e.fValue;
538 }
539 }
540}
541
542////////////////////////////////////////////////////////////////////////////////
543/// Show every instance of one kind of flaw at once.
544///
545/// Seeing all 36 supermodule extrusions together is the point of the grouped view:
546/// it says at a glance whether the problem is systematic or confined to a few
547/// placements. Shapes are de-duplicated, so the shared mother is drawn once.
548
550{
552 fSelected = -1;
553 fSelectedGroup = ((gidx >= 0) && (gidx < (int)fGroups.size())) ? gidx : -1;
554
555 if (fSelectedGroup >= 0) {
556 std::set<std::string> seen;
557 const auto &mem = fGroups[fSelectedGroup].fMembers;
558 for (size_t k = 0; k < mem.size(); ++k) {
559 const Entry &ent = fEntries[mem[k]];
560 for (size_t p = 0; p < ent.fGlobals.size(); ++p)
561 AddPlacement(ent, ent.fGlobals[p], (int)p, &seen, (int)k, (int)mem.size());
562 }
563 }
564
567}
568
569////////////////////////////////////////////////////////////////////////////////
570/// Re-run whichever selection is current, so a change of appearance takes effect
571/// without the client having to re-pick the row.
572
580
581////////////////////////////////////////////////////////////////////////////////
582/// Marker size for the overlap points. The points are the small thing in a scene
583/// full of large volumes, so this wants to be generous.
584
586{
587 if (size <= 0.f)
588 return;
590 Redraw();
591}
592
593////////////////////////////////////////////////////////////////////////////////
594/// Cap, in cm, on the half-length of each marker's 3D-cross arm. AddPlacement()
595/// scales the actual arm to a fraction of the smaller daughter's size, so this
596/// only bites when that would otherwise exceed it.
597
599{
600 if (size <= 0.f)
601 return;
603 Redraw();
604}
605
606////////////////////////////////////////////////////////////////////////////////
607/// Re-run the overlap check at a new precision and rebuild the table from
608/// scratch. Whatever was selected no longer means anything -- ScanOverlaps()
609/// rebuilds fEntries/fGroups in a new order with new indices -- so the 3D view
610/// is cleared rather than left showing a stale selection.
611
613{
614 if (precision <= 0. || !fMgr)
615 return;
616
618 fSelectedGroup = -1;
619 ScanOverlaps(fMgr, precision); // clears fSelected, resets fPrecision, stamps object props
620}
621
622////////////////////////////////////////////////////////////////////////////////
623/// Print one overlap's details to the server console: name, volumes, mother,
624/// TGeoOverlap's own title (usually the pair of node paths), every placement's
625/// global translation, and how many marker points survived filtering. For
626/// debugging from the client's row context menu -- this doesn't touch the 3D
627/// view or the selection.
628
630{
631
632 if ((idx < 0) || (idx >= (int)fEntries.size())) {
633 printf("REveGeoOverlapTable::PrintOverlap: index %d out of range (%d entries)\n", idx, (int)fEntries.size());
634 fflush(stdout);
635 return;
636 }
637
638 const Entry &e = fEntries[idx];
639 printf("=== %s : %s : %.6f cm ===\n", e.fName.c_str(), e.fExtrusion ? "extrusion" : "overlap", e.fValue);
640 printf(" A: %s\n", e.fVol1.c_str());
641 printf(" B: %s\n", e.fVol2.c_str());
642 printf(" mother: %s\n", e.fMother ? e.fMother->GetName() : "(none)");
643 if (!e.fTitle.empty())
644 printf(" title: %s\n", e.fTitle.c_str());
645 printf(" placements: %d\n", (int)e.fGlobals.size());
646 for (size_t p = 0; p < e.fGlobals.size(); ++p) {
647 const Double_t *t = e.fGlobals[p].GetTranslation();
648 printf(" [%d] translation = (%.4f, %.4f, %.4f)\n", (int)p, t[0], t[1], t[2]);
649 }
650 int kept = (int)(e.fLocalMarkers.size() / 3);
651 printf(" points: %d raw, %d kept (%d dropped)\n", e.fNumRawMarkers, kept, e.fNumRawMarkers - kept);
652 // stdout is fully buffered when it isn't a tty (the normal case for a
653 // long-running web server process), so without this the output just sits in
654 // the buffer indefinitely instead of reaching the terminal.
655 fflush(stdout);
656}
657
658////////////////////////////////////////////////////////////////////////////////
659/// Print a whole group -- every entry sharing this (vol1, vol2, extrusion) --
660/// to the server console, as one PrintOverlap() block per member.
661
663{
664 if ((gidx < 0) || (gidx >= (int)fGroups.size())) {
665 printf("REveGeoOverlapTable::PrintUniqueOverlap: index %d out of range (%d groups)\n", gidx, (int)fGroups.size());
666 fflush(stdout);
667 return;
668 }
669
670 const Group &g = fGroups[gidx];
671 printf("=== group: %s | %s | %s (%d members) ===\n", g.fVol1.c_str(), g.fVol2.c_str(),
672 g.fExtrusion ? "extrusion" : "overlap", (int)g.fMembers.size());
673 for (int ei : g.fMembers)
674 PrintOverlap(ei);
675}
676
677////////////////////////////////////////////////////////////////////////////////
678
679Int_t REveGeoOverlapTable::WriteCoreJson(nlohmann::json &j, Int_t rnr_offset)
680{
681 Int_t ret = REveElement::WriteCoreJson(j, rnr_offset);
682
683 j["fPrecision"] = fPrecision;
684 j["fScanGen"] = fScanGen;
685 j["fSelected"] = fSelected;
686
687 nlohmann::json arr = nlohmann::json::array();
688 for (size_t i = 0; i < fEntries.size(); ++i) {
689 const Entry &e = fEntries[i];
690 nlohmann::json o = nlohmann::json::object();
691 o["idx"] = (int)i;
692 o["name"] = e.fName;
693 o["kind"] = e.fExtrusion ? "extrusion" : "overlap";
694 o["value"] = e.fValue;
695 o["vol1"] = e.fVol1;
696 o["vol2"] = e.fVol2;
697 o["mother"] = e.fMother ? e.fMother->GetName() : "";
698 o["nplaced"] = (int)e.fGlobals.size();
699 o["npoints"] = (int)(e.fLocalMarkers.size() / 3);
700 o["ndropped"] = e.fNumRawMarkers - (int)(e.fLocalMarkers.size() / 3);
701 o["title"] = e.fTitle;
702 arr.emplace_back(std::move(o));
703 }
704 j["fOverlaps"] = std::move(arr);
705
706 nlohmann::json garr = nlohmann::json::array();
707 for (size_t i = 0; i < fGroups.size(); ++i) {
708 const Group &g = fGroups[i];
709 int npts = 0;
710 for (int ei : g.fMembers)
711 npts += (int)(fEntries[ei].fLocalMarkers.size() / 3);
712 nlohmann::json o = nlohmann::json::object();
713 o["idx"] = (int)i;
714 o["kind"] = g.fExtrusion ? "extrusion" : "overlap";
715 o["value"] = g.fValue;
716 o["vol1"] = g.fVol1;
717 o["vol2"] = g.fVol2;
718 o["ninst"] = (int)g.fMembers.size();
719 o["npoints"] = npts;
720 o["name"] = g.fMembers.empty() ? "" : fEntries[g.fMembers.front()].fName;
721 garr.emplace_back(std::move(o));
722 }
723 j["fGroups"] = std::move(garr);
724 j["fSelectedGroup"] = fSelectedGroup;
725 j["fMarkerSize"] = fMarkerSize;
726 j["fCrossSize"] = fCrossSize;
727
728 return ret;
729}
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define g(i)
Definition RSha256.hxx:105
#define a(i)
Definition RSha256.hxx:99
#define s1(x)
Definition RSha256.hxx:91
#define h(i)
Definition RSha256.hxx:106
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
short Color_t
Color number (short)
Definition RtypesCore.h:100
char Text_t
General string (char)
Definition RtypesCore.h:77
float Float_t
Float 4 bytes (float)
Definition RtypesCore.h:72
constexpr Bool_t kFALSE
Definition RtypesCore.h:109
constexpr Bool_t kTRUE
Definition RtypesCore.h:108
@ kRed
Definition Rtypes.h:66
@ kCyan
Definition Rtypes.h:66
@ kYellow
Definition Rtypes.h:66
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
virtual Int_t WriteCoreJson(nlohmann::json &cj, Int_t rnr_offset)
Write core json.
virtual void AddElement(REveElement *el)
Add el to the list of children.
virtual void DestroyElements()
Destroy all children of this element.
REveElement(const std::string &name="", const std::string &title="")
Default constructor.
REveGeoManagerHolder Exception-safe global variable holders.
Definition REveUtil.hxx:95
void ScanOverlaps(TGeoManager *mgr, double precision=0.001)
Find every overlap and extrusion in the geometry and work out where each one is.
void SelectOverlap(int idx)
Show one overlap and nothing else.
std::string MakeTip(const Entry &ent, int which, int placement, int inst, int ninst) const
Text shown when the mouse is over one of the drawn pieces.
float fCrossSize
cap, in cm, on the half-length of each marker's 3D-cross arm; AddPlacement() scales the actual arm to...
void AddPlacement(const Entry &ent, const TGeoHMatrix &global, int placement, std::set< std::string > *seen=nullptr, int inst=-1, int ninst=0)
Draw one placement of the selected overlap: the two shapes and the points.
void SetMarkerSize(float size)
Marker size for the overlap points.
REveElement * fHolder
! where the selected overlap is drawn; put this in a 3D scene
TGeoManager * fMgr
! kept from ScanOverlaps() so SetPrecision() can re-run it
void SetPrecision(double precision)
Re-run the overlap check at a new precision and rebuild the table from scratch.
REveGeoOverlapTable(const REveGeoOverlapTable &)=delete
void PrintOverlap(int idx)
Print one overlap's details to the server console: name, volumes, mother, TGeoOverlap's own title (us...
void Redraw()
Re-run whichever selection is current, so a change of appearance takes effect without the client havi...
void SelectUniqueOverlap(int gidx)
Show every instance of one kind of flaw at once.
void LocalizeMarkers(Entry &ent, const std::vector< float > &localPts)
Filter the marker points down to the ones that plausibly belong to this flaw.
int fScanGen
bumped by ScanOverlaps(); lets the client tell a rescan from an ordinary selection stamp
void PrintUniqueOverlap(int gidx)
Print a whole group – every entry sharing this (vol1, vol2, extrusion) – to the server console,...
void SetCrossSize(float size)
Cap, in cm, on the half-length of each marker's 3D-cross arm.
void CollectPlacements(TGeoManager *mgr)
Global matrix of every physical placement of each mother volume.
Int_t WriteCoreJson(nlohmann::json &j, Int_t rnr_offset) override
Write core json.
void BuildGroups()
Collapse entries that are the same flaw repeated.
static TGeoManager * GetGeoManager()
Return static geo-manager that is used internally to make shapes lead a happy life.
REveStraightLineSet Set of straight lines with optional markers along the lines.
Box class.
Definition TGeoBBox.h:18
Matrix class used for computing global transformations Should NOT be used for node definition.
Definition TGeoMatrix.h:459
A geometry iterator.
Definition TGeoNode.h:249
const TGeoMatrix * GetCurrentMatrix() const
Returns global matrix for current node.
TGeoNode * Next()
Returns next node.
The manager class for any TGeo geometry.
Definition TGeoManager.h:46
TGeoNavigator * AddNavigator()
Add a navigator in the list of navigators.
TObjArray * GetListOfOverlaps()
TObjArray * GetListOfVolumes() const
void ClearOverlaps()
Clear the list of overlaps.
TGeoNavigator * GetCurrentNavigator() const
Returns current navigator for the calling thread.
TGeoVolume * GetTopVolume() const
Geometrical transformation package.
Definition TGeoMatrix.h:39
virtual void LocalToMaster(const Double_t *local, Double_t *master) const
convert a point by multiplying its column vector (x, y, z, 1) to matrix inverse
A node represent a volume positioned inside another.They store links to both volumes and to the TGeoM...
Definition TGeoNode.h:39
TGeoVolume * GetVolume() const
Definition TGeoNode.h:100
virtual TGeoMatrix * GetMatrix() const =0
Base class describing geometry overlaps.
Definition TGeoOverlap.h:37
Base abstract class for all shapes.
Definition TGeoShape.h:25
virtual Double_t Safety(const Double_t *point, Bool_t in=kTRUE) const =0
virtual Bool_t Contains(const Double_t *point) const =0
TGeoVolume, TGeoVolumeMulti, TGeoVolumeAssembly are the volume classes.
Definition TGeoVolume.h:45
Int_t GetNdaughters() const
Definition TGeoVolume.h:371
TGeoNode * GetNode(const char *name) const
get the pointer to a daughter node
TGeoShape * GetShape() const
Definition TGeoVolume.h:193
void CheckOverlaps(Double_t ovlp=0.1, Option_t *option="")
Overlap checking tool. Check for illegal overlaps within a limit OVLP.
TObject * Clone(const char *newname="") const override
Make a clone of an object using the Streamer facility.
Definition TNamed.cxx:73
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
An array of TObjects.
Definition TObjArray.h:31
Int_t GetEntriesFast() const
Definition TObjArray.h:58
TObject * At(Int_t idx) const override
Definition TObjArray.h:170
virtual void SetUniqueID(UInt_t uid)
Set the unique object id.
Definition TObject.cxx:897
A 3D polymarker.
Basic string class.
Definition TString.h:137
const char * Data() const
Definition TString.h:385
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2460
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
Namespace for ROOT features in testing.
Definition TROOT.h:100
TGeoVolume * fV1
! first volume; the mother, for an extrusion
TGeoHMatrix fM2
! placements, in the mother's local frame
std::string fName
TGeoOverlap name, e.g. ov00035.
std::string fTitle
human-readable description
std::string fVol2
names of the two volumes
std::vector< TGeoHMatrix > fGlobals
! global matrix of every placement of the mother
bool fExtrusion
extrusion, as opposed to a real overlap
int fNumRawMarkers
points the overlap carried, before filtering
TGeoVolume * fMother
! volume both matrices are expressed in
double fValue
overlap/extrusion size, in cm
std::vector< float > fLocalMarkers
! marker points, in the mother's frame, finite only
One kind of flaw, with every placement of it that the geometry contains.
TMarker m
Definition textangle.C:8