Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
REveGeomData.cxx
Go to the documentation of this file.
1// @(#)root/eve7:$Id$
2// Author: Sergey Linev, 14.12.2018
3
4/*************************************************************************
5 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#include <ROOT/REveGeomData.hxx>
13
14#include <ROOT/RBrowserRequest.hxx>
15#include <ROOT/RBrowserReply.hxx>
17#include <ROOT/REveUtil.hxx>
18#include <ROOT/RLogger.hxx>
19
20#include "TMath.h"
21#include "TColor.h"
22#include "TROOT.h"
23#include "TGeoNode.h"
24#include "TGeoVolume.h"
25#include "TGeoBBox.h"
26#include "TGeoManager.h"
27#include "TGeoMatrix.h"
28#include "TGeoMedium.h"
29#include "TGeoMaterial.h"
30#include "TGeoCompositeShape.h"
31#include "TBuffer3D.h"
32#include "TBufferJSON.h"
33
34#include <algorithm>
35
36using namespace std::string_literals;
37
38
39/** Base class for iterating of hierarchical structure */
40
41namespace ROOT {
42namespace Experimental {
43
44
45class RGeomBrowserIter {
46
47 REveGeomDescription &fDesc;
48 int fParentId{-1};
49 unsigned fChild{0};
50 int fNodeId{0};
51
52 std::vector<int> fStackParents;
53 std::vector<int> fStackChilds;
54
55public:
56
57 RGeomBrowserIter(REveGeomDescription &desc) : fDesc(desc) {}
58
59 const std::string &GetName() const { return fDesc.fDesc[fNodeId].name; }
60
61 bool IsValid() const { return fNodeId >= 0; }
62
63 int GetNodeId() const { return fNodeId; }
64
65 bool HasChilds() const { return (fNodeId < 0) ? true : fDesc.fDesc[fNodeId].chlds.size() > 0; }
66
67 int NumChilds() const { return (fNodeId < 0) ? 1 : fDesc.fDesc[fNodeId].chlds.size(); }
68
69 bool Enter()
70 {
71 if (fNodeId < 0) {
72 Reset();
73 fNodeId = 0;
74 return true;
75 }
76
77 if (fNodeId >= (int) fDesc.fDesc.size())
78 return false;
79
80 auto &node = fDesc.fDesc[fNodeId];
81 if (node.chlds.size() == 0) return false;
82 fStackParents.emplace_back(fParentId);
83 fStackChilds.emplace_back(fChild);
84 fParentId = fNodeId;
85 fChild = 0;
86 fNodeId = node.chlds[fChild];
87 return true;
88 }
89
90 bool Leave()
91 {
92 if (fStackParents.size() == 0) {
93 fNodeId = -1;
94 return false;
95 }
96 fParentId = fStackParents.back();
97 fChild = fStackChilds.back();
98
99 fStackParents.pop_back();
100 fStackChilds.pop_back();
101
102 if (fParentId < 0) {
103 fNodeId = 0;
104 } else {
105 fNodeId = fDesc.fDesc[fParentId].chlds[fChild];
106 }
107 return true;
108 }
109
110 bool Next()
111 {
112 // does not have parents
113 if ((fNodeId <= 0) || (fParentId < 0)) {
114 Reset();
115 return false;
116 }
117
118 auto &prnt = fDesc.fDesc[fParentId];
119 if (++fChild >= prnt.chlds.size()) {
120 fNodeId = -1; // not valid node, only Leave can be called
121 return false;
122 }
123
124 fNodeId = prnt.chlds[fChild];
125 return true;
126 }
127
128 bool Reset()
129 {
130 fParentId = -1;
131 fNodeId = -1;
132 fChild = 0;
133 fStackParents.clear();
134 fStackChilds.clear();
135
136 return true;
137 }
138
139 bool NextNode()
140 {
141 if (Enter()) return true;
142
143 if (Next()) return true;
144
145 while (Leave()) {
146 if (Next()) return true;
147 }
148
149 return false;
150 }
151
152 /** Navigate to specified path - path specified as string and should start with "/" */
153 bool Navigate(const std::string &path)
154 {
155 size_t pos = path.find("/");
156 if (pos != 0) return false;
157
158 Reset(); // set to the top of element
159
160 while (++pos < path.length()) {
161 auto last = pos;
162
163 pos = path.find("/", last);
164
165 if (pos == std::string::npos) pos = path.length();
166
167 std::string folder = path.substr(last, pos-last);
168
169 if (!Enter()) return false;
170
171 bool find = false;
172
173 do {
174 find = (folder.compare(GetName()) == 0);
175 } while (!find && Next());
176
177 if (!find) return false;
178 }
179
180 return true;
181 }
182
183 /** Navigate to specified path */
184 bool Navigate(const std::vector<std::string> &path)
185 {
186 Reset(); // set to the top of element
187
188 for (auto &folder : path) {
189
190 if (!Enter()) return false;
191
192 bool find = false;
193
194 do {
195 find = (folder.compare(GetName()) == 0);
196 } while (!find && Next());
197
198 if (!find) return false;
199 }
200
201 return true;
202 }
203
204
205 /// Returns array of ids to currently selected node
206 std::vector<int> CurrentIds() const
207 {
208 std::vector<int> res;
209 if (IsValid()) {
210 for (unsigned n=1;n<fStackParents.size();++n)
211 res.emplace_back(fStackParents[n]);
212 if (fParentId >= 0) res.emplace_back(fParentId);
213 res.emplace_back(fNodeId);
214 }
215 return res;
216 }
217
218};
219} // namespace Experimental
220} // namespace ROOT
221
222/////////////////////////////////////////////////////////////////////
223/// Pack matrix into vector, which can be send to client
224/// Following sizes can be used for vector:
225/// 0 - Identity matrix
226/// 3 - Translation
227/// 4 - Scale (last element always 1)
228/// 9 - Rotation
229/// 16 - Full size
230
232{
233 vect.clear();
234
235 if (!matr || matr->IsIdentity()) {
236 return;
237 }
238
239 auto trans = matr->GetTranslation();
240 auto scale = matr->GetScale();
241 auto rotate = matr->GetRotationMatrix();
242
243 bool is_translate = matr->IsA() == TGeoTranslation::Class(),
244 is_scale = matr->IsA() == TGeoScale::Class(),
245 is_rotate = matr->IsA() == TGeoRotation::Class();
246
247 if (!is_translate && !is_scale && !is_rotate) {
248 // check if trivial matrix
249
250 auto test = [](double val, double chk) { return (val==chk) || (TMath::Abs(val-chk) < 1e-20); };
251
252 bool no_scale = test(scale[0],1) && test(scale[1],1) && test(scale[2],1);
253 bool no_trans = test(trans[0],0) && test(trans[1],0) && test(trans[2],0);
254 bool no_rotate = test(rotate[0],1) && test(rotate[1],0) && test(rotate[2],0) &&
255 test(rotate[3],0) && test(rotate[4],1) && test(rotate[5],0) &&
256 test(rotate[6],0) && test(rotate[7],0) && test(rotate[8],1);
257
258 if (no_scale && no_trans && no_rotate)
259 return;
260
261 if (no_scale && no_trans && !no_rotate) {
262 is_rotate = true;
263 } else if (no_scale && !no_trans && no_rotate) {
264 is_translate = true;
265 } else if (!no_scale && no_trans && no_rotate) {
266 is_scale = true;
267 }
268 }
269
270 if (is_translate) {
271 vect.resize(3);
272 vect[0] = trans[0];
273 vect[1] = trans[1];
274 vect[2] = trans[2];
275 return;
276 }
277
278 if (is_scale) {
279 vect.resize(4);
280 vect[0] = scale[0];
281 vect[1] = scale[1];
282 vect[2] = scale[2];
283 vect[3] = 1;
284 return;
285 }
286
287 if (is_rotate) {
288 vect.resize(9);
289 for (int n=0;n<9;++n)
290 vect[n] = rotate[n];
291 return;
292 }
293
294 vect.resize(16);
295 vect[0] = rotate[0]; vect[4] = rotate[1]; vect[8] = rotate[2]; vect[12] = trans[0];
296 vect[1] = rotate[3]; vect[5] = rotate[4]; vect[9] = rotate[5]; vect[13] = trans[1];
297 vect[2] = rotate[6]; vect[6] = rotate[7]; vect[10] = rotate[8]; vect[14] = trans[2];
298 vect[3] = 0; vect[7] = 0; vect[11] = 0; vect[15] = 1;
299}
300
301/////////////////////////////////////////////////////////////////////
302/// Collect information about geometry hierarchy into flat list
303/// like it done JSROOT.GEO.ClonedNodes.prototype.CreateClones
304
306{
307 fDesc.clear();
308 fNodes.clear();
309 fSortMap.clear();
310 ClearDrawData();
311 fDrawIdCut = 0;
312
313 if (!mgr) return;
314
315 auto topnode = mgr->GetTopNode();
316 if (!volname.empty()) {
317 auto vol = mgr->GetVolume(volname.c_str());
318 if (vol) {
319 TGeoNode *node;
320 TGeoIterator next(mgr->GetTopVolume());
321 while ((node=next())) {
322 if (node->GetVolume() == vol) break;
323 }
324 if (node) { topnode = node; printf("Find node with volume\n"); }
325 }
326 }
327
328 // by top node visibility always enabled and harm logic
329 // later visibility can be controlled by other means
330 // mgr->GetTopNode()->GetVolume()->SetVisibility(kFALSE);
331
332 int maxnodes = mgr->GetMaxVisNodes();
333
334 SetNSegments(mgr->GetNsegments());
335 SetVisLevel(mgr->GetVisLevel());
336 SetMaxVisNodes(maxnodes);
337 SetMaxVisFaces( (maxnodes > 5000 ? 5000 : (maxnodes < 1000 ? 1000 : maxnodes)) * 100);
338
339 // vector to remember numbers
340 std::vector<int> numbers;
341 int offset = 1000000000;
342
343 // try to build flat list of all nodes
344 TGeoNode *snode = topnode;
345 TGeoIterator iter(topnode->GetVolume());
346 do {
347 // artificial offset, used as identifier
348 if (snode->GetNumber() >= offset) {
349 iter.Skip(); // no need to look inside
350 } else {
351 numbers.emplace_back(snode->GetNumber());
352 snode->SetNumber(offset + fNodes.size()); // use id with shift 1e9
353 fNodes.emplace_back(snode);
354 }
355 } while ((snode = iter()) != nullptr);
356
357 fDesc.reserve(fNodes.size());
358 numbers.reserve(fNodes.size());
359 fSortMap.reserve(fNodes.size());
360
361 // array for sorting
362 std::vector<REveGeomNode *> sortarr;
363 sortarr.reserve(fNodes.size());
364
365 // create vector of desc and childs
366 int cnt = 0;
367 for (auto &node: fNodes) {
368
369 fDesc.emplace_back(node->GetNumber() - offset);
370 auto &desc = fDesc[cnt++];
371
372 sortarr.emplace_back(&desc);
373
374 desc.name = node->GetName();
375
376 auto shape = dynamic_cast<TGeoBBox *>(node->GetVolume()->GetShape());
377 if (shape) {
378 desc.vol = shape->GetDX()*shape->GetDY()*shape->GetDZ();
379 desc.nfaces = 12; // TODO: get better value for each shape - excluding composite
380 }
381
382 CopyMaterialProperties(node->GetVolume(), desc);
383
384 auto chlds = node->GetNodes();
385
386 PackMatrix(desc.matr, node->GetMatrix());
387
388 if (chlds)
389 for (int n = 0; n <= chlds->GetLast(); ++n) {
390 auto chld = dynamic_cast<TGeoNode *> (chlds->At(n));
391 desc.chlds.emplace_back(chld->GetNumber()-offset);
392 }
393 }
394
395 // recover numbers
396 cnt = 0;
397 for (auto &node: fNodes)
398 node->SetNumber(numbers[cnt++]);
399
400 // sort in volume descent order
401 std::sort(sortarr.begin(), sortarr.end(), [](REveGeomNode *a, REveGeomNode * b) { return a->vol > b->vol; });
402
403 cnt = 0;
404 for (auto &elem: sortarr) {
405 fSortMap.emplace_back(elem->id);
406 elem->sortid = cnt++; // keep place in sorted array to correctly apply cut
407 }
408
409 MarkVisible(); // set visibility flags
410
411 ProduceIdShifts();
412}
413
414/////////////////////////////////////////////////////////////////////
415/// Set visibility flag for each nodes
416
418{
419 int res = 0, cnt = 0;
420 for (auto &node: fNodes) {
421 auto &desc = fDesc[cnt++];
422 desc.vis = 0;
423 desc.nochlds = false;
424
425 if (on_screen) {
426 if (node->IsOnScreen())
427 desc.vis = 99;
428 } else {
429 auto vol = node->GetVolume();
430
431 if (vol->IsVisible() && !vol->TestAttBit(TGeoAtt::kVisNone))
432 desc.vis = 99;
433
434 if (!node->IsVisDaughters())
435 desc.nochlds = true;
436
437 if ((desc.vis > 0) && (desc.chlds.size() > 0) && !desc.nochlds)
438 desc.vis = 1;
439 }
440
441 if (desc.IsVisible() && desc.CanDisplay()) res++;
442 }
443
444 return res;
445}
446
447/////////////////////////////////////////////////////////////////////
448/// Count total number of visible childs under each node
449
451{
452 for (auto &node : fDesc)
453 node.idshift = -1;
454
455 using ScanFunc_t = std::function<int(REveGeomNode &)>;
456
457 ScanFunc_t scan_func = [&, this](REveGeomNode &node) {
458 if (node.idshift < 0) {
459 node.idshift = 0;
460 for(auto id : node.chlds)
461 node.idshift += scan_func(fDesc[id]);
462 }
463
464 return node.idshift + 1;
465 };
466
467 if (fDesc.size() > 0)
468 scan_func(fDesc[0]);
469}
470
471/////////////////////////////////////////////////////////////////////
472/// Iterate over all nodes and call function for visible
473
475{
476 if (fDesc.empty()) return 0;
477
478 std::vector<int> stack;
479 stack.reserve(25); // reserve enough space for most use-cases
480 int counter{0};
481
482 using ScanFunc_t = std::function<int(int, int)>;
483
484 ScanFunc_t scan_func = [&, this](int nodeid, int lvl) {
485 auto &desc = fDesc[nodeid];
486 int res = 0;
487
488 if (desc.nochlds && (lvl > 0)) lvl = 0;
489
490 // same logic as in JSROOT.GEO.ClonedNodes.prototype.ScanVisible
491 bool is_visible = (lvl >= 0) && (desc.vis > lvl) && desc.CanDisplay();
492
493 if (is_visible || !only_visible)
494 if (func(desc, stack, is_visible, counter))
495 res++;
496
497 counter++; // count sequence id of current position in scan, will be used later for merging drawing lists
498
499 if ((desc.chlds.size() > 0) && ((lvl > 0) || !only_visible)) {
500 auto pos = stack.size();
501 stack.emplace_back(0);
502 for (unsigned k = 0; k < desc.chlds.size(); ++k) {
503 stack[pos] = k; // stack provides index in list of chdils
504 res += scan_func(desc.chlds[k], lvl - 1);
505 }
506 stack.pop_back();
507 } else {
508 counter += desc.idshift;
509 }
510
511 return res;
512 };
513
514 if (!maxlvl && (GetVisLevel() > 0)) maxlvl = GetVisLevel();
515 if (!maxlvl) maxlvl = 4;
516 if (maxlvl > 97) maxlvl = 97; // check while vis property of node is 99 normally
517
518 return scan_func(0, maxlvl);
519}
520
521/////////////////////////////////////////////////////////////////////
522/// Collect nodes which are used in visibles
523
525{
526 // TODO: for now reset all flags, later can be kept longer
527 for (auto &node : fDesc)
528 node.useflag = false;
529
530 drawing.cfg = &fCfg;
531
532 drawing.numnodes = fDesc.size();
533
534 for (auto &item : drawing.visibles) {
535 int nodeid{0};
536 for (auto &chindx : item.stack) {
537 auto &node = fDesc[nodeid];
538 if (!node.useflag) {
539 node.useflag = true;
540 drawing.nodes.emplace_back(&node);
541 }
542 if (chindx >= (int)node.chlds.size())
543 break;
544 nodeid = node.chlds[chindx];
545 }
546
547 auto &node = fDesc[nodeid];
548 if (!node.useflag) {
549 node.useflag = true;
550 drawing.nodes.emplace_back(&node);
551 }
552 }
553
554 printf("SELECT NODES %d\n", (int) drawing.nodes.size());
555}
556
557/////////////////////////////////////////////////////////////////////
558/// Find description object for requested shape
559/// If not exists - will be created
560
562{
563 std::string res;
564
565 auto request = TBufferJSON::FromJSON<RBrowserRequest>(msg);
566
567 if (msg.empty()) {
568 request = std::make_unique<RBrowserRequest>();
569 request->first = 0;
570 request->number = 100;
571 }
572
573 if (!request)
574 return res;
575
576 if (request->path.empty() && (request->first == 0) && (GetNumNodes() < (IsPreferredOffline() ? 1000000 : 1000))) {
577
578 std::vector<REveGeomNodeBase *> vect(fDesc.size(), nullptr);
579
580 int cnt = 0;
581 for (auto &item : fDesc)
582 vect[cnt++]= &item;
583
584 res = "DESCR:"s + TBufferJSON::ToJSON(&vect,GetJsonComp()).Data();
585
586 // example how iterator can be used
587 RGeomBrowserIter iter(*this);
588 int nelements = 0;
589 while (iter.NextNode())
590 nelements++;
591 printf("Total number of valid nodes %d\n", nelements);
592
593 } else {
594 std::vector<Browsable::RItem> temp_nodes;
595 bool toplevel = request->path.empty();
596
597 // create temporary object for the short time
598 RBrowserReply reply;
599 reply.path = request->path;
600 reply.first = request->first;
601
602 RGeomBrowserIter iter(*this);
603 if (iter.Navigate(request->path)) {
604
605 reply.nchilds = iter.NumChilds();
606 // scan childs of selected nodes
607 if (iter.Enter()) {
608
609 while ((request->first > 0) && iter.Next()) {
610 request->first--;
611 }
612
613 while (iter.IsValid() && (request->number > 0)) {
614 temp_nodes.emplace_back(iter.GetName(), iter.NumChilds());
615 if (toplevel) temp_nodes.back().SetExpanded(true);
616 request->number--;
617 if (!iter.Next()) break;
618 }
619 }
620 }
621
622 for (auto &n : temp_nodes)
623 reply.nodes.emplace_back(&n);
624
625 res = "BREPL:"s + TBufferJSON::ToJSON(&reply, GetJsonComp()).Data();
626 }
627
628 return res;
629}
630
631/////////////////////////////////////////////////////////////////////
632/// Find description object for requested shape
633/// If not exists - will be created
634
636{
637 for (auto &descr : fShapes)
638 if (descr.fShape == shape)
639 return descr;
640
641 fShapes.emplace_back(shape);
642 auto &elem = fShapes.back();
643 elem.id = fShapes.size() - 1;
644 return elem;
645}
646
647/////////////////////////////////////////////////////////////////////
648/// Find description object and create render information
649
652{
653 auto &elem = FindShapeDescr(shape);
654
655 if (elem.nfaces == 0) {
656
657 TGeoCompositeShape *comp = nullptr;
658
659 int boundary = 3; //
660 if (shape->IsComposite()) {
661 comp = dynamic_cast<TGeoCompositeShape *>(shape);
662 // composite is most complex for client, therefore by default build on server
663 boundary = 1;
664 } else if (!shape->IsCylType()) {
665 // simple box geometry is compact and can be delivered as raw
666 boundary = 2;
667 }
668
669 if (IsBuildShapes() < boundary) {
670 elem.nfaces = 1;
671 elem.fShapeInfo.shape = shape;
672 } else {
673
674 auto poly = std::make_unique<REveGeoPolyShape>();
675
676 if (comp) {
677 poly->BuildFromComposite(comp, GetNSegments());
678 } else {
679 poly->BuildFromShape(shape, GetNSegments());
680 }
681
683
684 poly->FillRenderData(rd);
685
686 elem.nfaces = poly->GetNumFaces();
687
688 elem.fRawInfo.raw.resize(rd.GetBinarySize());
689 rd.Write( reinterpret_cast<char *>(elem.fRawInfo.raw.data()), elem.fRawInfo.raw.size() );
690 elem.fRawInfo.sz[0] = rd.SizeV();
691 elem.fRawInfo.sz[1] = rd.SizeN();
692 elem.fRawInfo.sz[2] = rd.SizeI();
693
694 }
695 }
696
697 return elem;
698}
699
700/////////////////////////////////////////////////////////////////////
701/// Copy material properties
702
704{
705 if (!volume) return;
706
707 TColor *col{nullptr};
708
709 if ((volume->GetFillColor() > 1) && (volume->GetLineColor() == 1))
710 col = gROOT->GetColor(volume->GetFillColor());
711 else if (volume->GetLineColor() >= 0)
712 col = gROOT->GetColor(volume->GetLineColor());
713
714 if (volume->GetMedium() && (volume->GetMedium() != TGeoVolume::DummyMedium()) && volume->GetMedium()->GetMaterial()) {
715 auto material = volume->GetMedium()->GetMaterial();
716
717 auto fillstyle = material->GetFillStyle();
718 if ((fillstyle>=3000) && (fillstyle<=3100)) node.opacity = (3100 - fillstyle) / 100.;
719 if (!col) col = gROOT->GetColor(material->GetFillColor());
720 }
721
722 if (col) {
723 node.color = std::to_string((int)(col->GetRed()*255)) + "," +
724 std::to_string((int)(col->GetGreen()*255)) + "," +
725 std::to_string((int)(col->GetBlue()*255));
726 if (node.opacity == 1.)
727 node.opacity = col->GetAlpha();
728 } else {
729 node.color.clear();
730 }
731}
732
733/////////////////////////////////////////////////////////////////////
734/// Reset shape info, which used to pack binary data
735
737{
738 for (auto &s: fShapes)
739 s.reset();
740}
741
742/////////////////////////////////////////////////////////////////////
743/// Collect all information required to draw geometry on the client
744/// This includes list of each visible nodes, meshes and matrixes
745
747{
748 std::vector<int> viscnt(fDesc.size(), 0);
749
750 int level = GetVisLevel();
751
752 // first count how many times each individual node appears
753 int numnodes = ScanNodes(true, level, [&viscnt](REveGeomNode &node, std::vector<int> &, bool, int) {
754 viscnt[node.id]++;
755 return true;
756 });
757
758 if (GetMaxVisNodes() > 0) {
759 while ((numnodes > GetMaxVisNodes()) && (level > 1)) {
760 level--;
761 viscnt.assign(viscnt.size(), 0);
762 numnodes = ScanNodes(true, level, [&viscnt](REveGeomNode &node, std::vector<int> &, bool, int) {
763 viscnt[node.id]++;
764 return true;
765 });
766 }
767 }
768
769 fActualLevel = level;
770 fDrawIdCut = 0;
771
772 int totalnumfaces{0}, totalnumnodes{0};
773
774 //for (auto &node : fDesc)
775 // node.SetDisplayed(false);
776
777 // build all shapes in volume decreasing order
778 for (auto &sid: fSortMap) {
779 fDrawIdCut++; //
780 auto &desc = fDesc[sid];
781
782 if ((viscnt[sid] <= 0) || (desc.vol <= 0)) continue;
783
784 auto shape = fNodes[sid]->GetVolume()->GetShape();
785 if (!shape) continue;
786
787 // now we need to create TEveGeoPolyShape, which can provide all rendering data
788 auto &shape_descr = MakeShapeDescr(shape);
789
790 // should not happen, but just in case
791 if (shape_descr.nfaces <= 0) {
792 R__LOG_ERROR(EveLog()) << "No faces for the shape " << shape->GetName() << " class " << shape->ClassName();
793 continue;
794 }
795
796 // check how many faces are created
797 totalnumfaces += shape_descr.nfaces * viscnt[sid];
798 if ((GetMaxVisFaces() > 0) && (totalnumfaces > GetMaxVisFaces())) break;
799
800 // also avoid too many nodes
801 totalnumnodes += viscnt[sid];
802 if ((GetMaxVisNodes() > 0) && (totalnumnodes > GetMaxVisNodes())) break;
803
804 // desc.SetDisplayed(true);
805 }
806
807 // finally we should create data for streaming to the client
808 // it includes list of visible nodes and rawdata
809
810 REveGeomDrawing drawing;
811 ResetRndrInfos();
812 bool has_shape = false;
813
814 ScanNodes(true, level, [&, this](REveGeomNode &node, std::vector<int> &stack, bool, int seqid) {
815 if (node.sortid < fDrawIdCut) {
816 drawing.visibles.emplace_back(node.id, seqid, stack);
817
818 auto &item = drawing.visibles.back();
819 item.color = node.color;
820 item.opacity = node.opacity;
821
822 auto volume = fNodes[node.id]->GetVolume();
823
824 auto &sd = MakeShapeDescr(volume->GetShape());
825
826 item.ri = sd.rndr_info();
827 if (sd.has_shape()) has_shape = true;
828 }
829 return true;
830 });
831
832 CollectNodes(drawing);
833
834 fDrawJson = "GDRAW:"s + MakeDrawingJson(drawing, has_shape);
835
836 return true;
837}
838
839/////////////////////////////////////////////////////////////////////
840/// Clear raw data. Will be rebuild when next connection will be established
841
843{
844 fDrawJson.clear();
845}
846
847/////////////////////////////////////////////////////////////////////
848/// return true when node used in main geometry drawing and does not have childs
849/// for such nodes one could provide optimize toggling of visibility flags
850
852{
853 if ((nodeid < 0) || (nodeid >= (int)fDesc.size()))
854 return false;
855
856 auto &desc = fDesc[nodeid];
857
858 return (desc.sortid < fDrawIdCut) && desc.IsVisible() && desc.CanDisplay() && (desc.chlds.size()==0);
859}
860
861
862/////////////////////////////////////////////////////////////////////
863/// Search visible nodes for provided name
864/// If number of found elements less than 100, create description and shapes for them
865/// Returns number of match elements
866
867int ROOT::Experimental::REveGeomDescription::SearchVisibles(const std::string &find, std::string &hjson, std::string &json)
868{
869 hjson.clear();
870 json.clear();
871
872 if (find.empty()) {
873 hjson = "FOUND:RESET";
874 return 0;
875 }
876
877 std::vector<int> nodescnt(fDesc.size(), 0), viscnt(fDesc.size(), 0);
878
879 int nmatches{0};
880
881 auto match_func = [&find](REveGeomNode &node) {
882 return (node.vol > 0) && (node.name.compare(0, find.length(), find) == 0);
883 };
884
885 // first count how many times each individual node appears
886 ScanNodes(false, 0, [&nodescnt,&viscnt,&match_func,&nmatches](REveGeomNode &node, std::vector<int> &, bool is_vis, int) {
887
888 if (match_func(node)) {
889 nmatches++;
890 nodescnt[node.id]++;
891 if (is_vis) viscnt[node.id]++;
892 };
893 return true;
894 });
895
896 // do not send too much data, limit could be made configurable later
897 if (nmatches==0) {
898 hjson = "FOUND:NO";
899 return nmatches;
900 }
901
902 if (nmatches > 10 * GetMaxVisNodes()) {
903 hjson = "FOUND:Too many " + std::to_string(nmatches);
904 return nmatches;
905 }
906
907 // now build all necessary shapes and check number of faces - not too many
908
909 int totalnumfaces{0}, totalnumnodes{0}, scnt{0};
910 bool send_rawdata{true};
911
912 // build all shapes in volume decreasing order
913 for (auto &sid: fSortMap) {
914 if (scnt++ < fDrawIdCut) continue; // no need to send most significant shapes
915
916 if (viscnt[sid] == 0) continue; // this node is not used at all
917
918 auto &desc = fDesc[sid];
919 if ((viscnt[sid] <= 0) && (desc.vol <= 0)) continue;
920
921 auto shape = fNodes[sid]->GetVolume()->GetShape();
922 if (!shape) continue;
923
924 // create shape raw data
925 auto &shape_descr = MakeShapeDescr(shape);
926
927 // should not happen, but just in case
928 if (shape_descr.nfaces <= 0) {
929 R__LOG_ERROR(EveLog()) << "No faces for the shape " << shape->GetName() << " class " << shape->ClassName();
930 continue;
931 }
932
933 // check how many faces are created
934 totalnumfaces += shape_descr.nfaces * viscnt[sid];
935 if ((GetMaxVisFaces() > 0) && (totalnumfaces > GetMaxVisFaces())) { send_rawdata = false; break; }
936
937 // also avoid too many nodes
938 totalnumnodes += viscnt[sid];
939 if ((GetMaxVisNodes() > 0) && (totalnumnodes > GetMaxVisNodes())) { send_rawdata = false; break; }
940 }
941
942 // only for debug purposes - remove later
943 // send_rawdata = false;
944
945 // finally we should create data for streaming to the client
946 // it includes list of visible nodes and rawdata (if there is enough space)
947
948
949 std::vector<REveGeomNodeBase> found_desc; ///<! hierarchy of nodes, used for search
950 std::vector<int> found_map(fDesc.size(), -1); ///<! mapping between nodeid - > foundid
951
952 // these are only selected nodes to produce hierarchy
953
954 found_desc.emplace_back(0);
955 found_desc[0].vis = fDesc[0].vis;
956 found_desc[0].name = fDesc[0].name;
957 found_desc[0].color = fDesc[0].color;
958 found_map[0] = 0;
959
960 ResetRndrInfos();
961
962 REveGeomDrawing drawing;
963 bool has_shape = true;
964
965 ScanNodes(false, 0, [&, this](REveGeomNode &node, std::vector<int> &stack, bool is_vis, int seqid) {
966 // select only nodes which should match
967 if (!match_func(node))
968 return true;
969
970 // add entries into hierarchy of found elements
971 int prntid = 0;
972 for (auto &s : stack) {
973 int chldid = fDesc[prntid].chlds[s];
974 if (found_map[chldid] <= 0) {
975 int newid = found_desc.size();
976 found_desc.emplace_back(newid); // potentially original id can be used here
977 found_map[chldid] = newid; // re-map into reduced hierarchy
978
979 found_desc.back().vis = fDesc[chldid].vis;
980 found_desc.back().name = fDesc[chldid].name;
981 found_desc.back().color = fDesc[chldid].color;
982 }
983
984 auto pid = found_map[prntid];
985 auto cid = found_map[chldid];
986
987 // now add entry into childs lists
988 auto &pchlds = found_desc[pid].chlds;
989 if (std::find(pchlds.begin(), pchlds.end(), cid) == pchlds.end())
990 pchlds.emplace_back(cid);
991
992 prntid = chldid;
993 }
994
995 // no need to add visibles
996 if (!is_vis) return true;
997
998 drawing.visibles.emplace_back(node.id, seqid, stack);
999
1000 // no need to transfer shape if it provided with main drawing list
1001 // also no binary will be transported when too many matches are there
1002 if (!send_rawdata || (node.sortid < fDrawIdCut)) {
1003 // do not include render data
1004 return true;
1005 }
1006
1007 auto &item = drawing.visibles.back();
1008 auto volume = fNodes[node.id]->GetVolume();
1009
1010 item.color = node.color;
1011 item.opacity = node.opacity;
1012
1013 auto &sd = MakeShapeDescr(volume->GetShape());
1014
1015 item.ri = sd.rndr_info();
1016 if (sd.has_shape()) has_shape = true;
1017 return true;
1018 });
1019
1020 hjson = "FESCR:"s + TBufferJSON::ToJSON(&found_desc, GetJsonComp()).Data();
1021
1022 CollectNodes(drawing);
1023
1024 json = "FDRAW:"s + MakeDrawingJson(drawing, has_shape);
1025
1026 return nmatches;
1027}
1028
1029/////////////////////////////////////////////////////////////////////////////////
1030/// Returns nodeid for given stack array, returns -1 in case of failure
1031
1033{
1034 int nodeid{0};
1035
1036 for (auto &chindx: stack) {
1037 auto &node = fDesc[nodeid];
1038 if (chindx >= (int) node.chlds.size()) return -1;
1039 nodeid = node.chlds[chindx];
1040 }
1041
1042 return nodeid;
1043}
1044
1045/////////////////////////////////////////////////////////////////////////////////
1046/// Creates stack for given array of ids, first element always should be 0
1047
1048std::vector<int> ROOT::Experimental::REveGeomDescription::MakeStackByIds(const std::vector<int> &ids)
1049{
1050 std::vector<int> stack;
1051
1052 if (ids[0] != 0) {
1053 printf("Wrong first id\n");
1054 return stack;
1055 }
1056
1057 int nodeid = 0;
1058
1059 for (unsigned k = 1; k < ids.size(); ++k) {
1060
1061 int prntid = nodeid;
1062 nodeid = ids[k];
1063
1064 if (nodeid >= (int) fDesc.size()) {
1065 printf("Wrong node id %d\n", nodeid);
1066 stack.clear();
1067 return stack;
1068 }
1069 auto &chlds = fDesc[prntid].chlds;
1070 auto pos = std::find(chlds.begin(), chlds.end(), nodeid);
1071 if (pos == chlds.end()) {
1072 printf("Wrong id %d not a child of %d - fail to find stack num %d\n", nodeid, prntid, (int) chlds.size());
1073 stack.clear();
1074 return stack;
1075 }
1076
1077 stack.emplace_back(std::distance(chlds.begin(), pos));
1078 }
1079
1080 return stack;
1081}
1082
1083/////////////////////////////////////////////////////////////////////////////////
1084/// Produce stack based on string path
1085/// Used to highlight geo volumes by browser hover event
1086
1087std::vector<int> ROOT::Experimental::REveGeomDescription::MakeStackByPath(const std::vector<std::string> &path)
1088{
1089 std::vector<int> res;
1090
1091 RGeomBrowserIter iter(*this);
1092
1093 if (iter.Navigate(path))
1094 res = MakeStackByIds(iter.CurrentIds());
1095
1096 return res;
1097}
1098
1099/////////////////////////////////////////////////////////////////////////////////
1100/// Produce list of node ids for given stack
1101/// If found nodes preselected - use their ids
1102
1103std::vector<int> ROOT::Experimental::REveGeomDescription::MakeIdsByStack(const std::vector<int> &stack)
1104{
1105 std::vector<int> ids;
1106
1107 ids.emplace_back(0);
1108 int nodeid = 0;
1109 bool failure = false;
1110
1111 for (auto s : stack) {
1112 auto &chlds = fDesc[nodeid].chlds;
1113 if (s >= (int) chlds.size()) { failure = true; break; }
1114
1115 ids.emplace_back(chlds[s]);
1116
1117 nodeid = chlds[s];
1118 }
1119
1120 if (failure) {
1121 printf("Fail to convert stack into list of nodes\n");
1122 ids.clear();
1123 }
1124
1125 return ids;
1126}
1127
1128/////////////////////////////////////////////////////////////////////////////////
1129/// Returns path string for provided stack
1130
1131std::vector<std::string> ROOT::Experimental::REveGeomDescription::MakePathByStack(const std::vector<int> &stack)
1132{
1133 std::vector<std::string> path;
1134
1135 auto ids = MakeIdsByStack(stack);
1136 for (auto &id : ids)
1137 path.emplace_back(fDesc[id].name);
1138
1139 return path;
1140}
1141
1142
1143/////////////////////////////////////////////////////////////////////////////////
1144/// Return string with only part of nodes description which were modified
1145/// Checks also volume
1146
1148{
1149 std::vector<REveGeomNodeBase *> nodes;
1150 auto vol = fNodes[nodeid]->GetVolume();
1151
1152 // we take not only single node, but all there same volume is referenced
1153 // nodes.push_back(&fDesc[nodeid]);
1154
1155 int id{0};
1156 for (auto &desc : fDesc)
1157 if (fNodes[id++]->GetVolume() == vol)
1158 nodes.emplace_back(&desc);
1159
1160 return "MODIF:"s + TBufferJSON::ToJSON(&nodes, GetJsonComp()).Data();
1161}
1162
1163
1164/////////////////////////////////////////////////////////////////////////////////
1165/// Produce shape rendering data for given stack
1166/// All nodes, which are referencing same shape will be transferred
1167/// Returns true if new render information provided
1168
1169bool ROOT::Experimental::REveGeomDescription::ProduceDrawingFor(int nodeid, std::string &json, bool check_volume)
1170{
1171 // only this shape is interesting
1172
1173 TGeoVolume *vol = (nodeid < 0) ? nullptr : fNodes[nodeid]->GetVolume();
1174
1175 if (!vol || !vol->GetShape()) {
1176 json.append("NO");
1177 return false;
1178 }
1179
1180 REveGeomDrawing drawing;
1181
1182 ScanNodes(true, 0, [&, this](REveGeomNode &node, std::vector<int> &stack, bool, int seq_id) {
1183 // select only nodes which reference same shape
1184
1185 if (check_volume) {
1186 if (fNodes[node.id]->GetVolume() != vol) return true;
1187 } else {
1188 if (node.id != nodeid) return true;
1189 }
1190
1191 drawing.visibles.emplace_back(node.id, seq_id, stack);
1192
1193 auto &item = drawing.visibles.back();
1194
1195 item.color = node.color;
1196 item.opacity = node.opacity;
1197 return true;
1198 });
1199
1200 // no any visible nodes were done
1201 if (drawing.visibles.size()==0) {
1202 json.append("NO");
1203 return false;
1204 }
1205
1206 ResetRndrInfos();
1207
1208 bool has_shape = false, has_raw = false;
1209
1210 auto &sd = MakeShapeDescr(vol->GetShape());
1211
1212 // assign shape data
1213 for (auto &item : drawing.visibles) {
1214 item.ri = sd.rndr_info();
1215 if (sd.has_shape()) has_shape = true;
1216 if (sd.has_raw()) has_raw = true;
1217 }
1218
1219 CollectNodes(drawing);
1220
1221 json.append(MakeDrawingJson(drawing, has_shape));
1222
1223 return has_raw || has_shape;
1224}
1225
1226/////////////////////////////////////////////////////////////////////////////////
1227/// Produce JSON for the drawing
1228/// If TGeoShape appears in the drawing, one has to keep typeinfo
1229/// But in this case one can exclude several classes which are not interesting,
1230/// but appears very often
1231
1233{
1234 int comp = GetJsonComp();
1235
1236 if (!has_shapes || (comp < TBufferJSON::kSkipTypeInfo))
1237 return TBufferJSON::ToJSON(&drawing, comp).Data();
1238
1239 comp = comp % TBufferJSON::kSkipTypeInfo; // no typeingo skipping
1240
1241 TBufferJSON json;
1242 json.SetCompact(comp);
1243 json.SetSkipClassInfo(TClass::GetClass<REveGeomDrawing>());
1244 json.SetSkipClassInfo(TClass::GetClass<REveGeomNode>());
1245 json.SetSkipClassInfo(TClass::GetClass<REveGeomVisible>());
1246 json.SetSkipClassInfo(TClass::GetClass<RGeomShapeRenderInfo>());
1247 json.SetSkipClassInfo(TClass::GetClass<RGeomRawRenderInfo>());
1248
1249 return json.StoreObject(&drawing, TClass::GetClass<REveGeomDrawing>()).Data();
1250}
1251
1252/////////////////////////////////////////////////////////////////////////////////
1253/// Change visibility for specified element
1254/// Returns true if changes was performed
1255
1257{
1258 auto &dnode = fDesc[nodeid];
1259
1260 auto vol = fNodes[nodeid]->GetVolume();
1261
1262 // nothing changed
1263 if (vol->IsVisible() == selected)
1264 return false;
1265
1266 dnode.vis = selected ? 99 : 0;
1267 vol->SetVisibility(selected);
1268 if (dnode.chlds.size() > 0) {
1269 if (selected) dnode.vis = 1; // visibility disabled when any child
1270 vol->SetVisDaughters(selected);
1271 }
1272
1273 int id{0};
1274 for (auto &desc: fDesc)
1275 if (fNodes[id++]->GetVolume() == vol)
1276 desc.vis = dnode.vis;
1277
1278 ClearDrawData(); // after change raw data is no longer valid
1279
1280 return true;
1281}
1282
1283/////////////////////////////////////////////////////////////////////////////////
1284/// Change visibility for specified element
1285/// Returns true if changes was performed
1286
1287std::unique_ptr<ROOT::Experimental::REveGeomNodeInfo> ROOT::Experimental::REveGeomDescription::MakeNodeInfo(const std::vector<std::string> &path)
1288{
1289 std::unique_ptr<REveGeomNodeInfo> res;
1290
1291 RGeomBrowserIter iter(*this);
1292
1293 if (iter.Navigate(path)) {
1294
1295 auto node = fNodes[iter.GetNodeId()];
1296
1297 auto &desc = fDesc[iter.GetNodeId()];
1298
1299 res = std::make_unique<REveGeomNodeInfo>();
1300
1301 res->path = path;
1302 res->node_name = node->GetName();
1303 res->node_type = node->ClassName();
1304
1305 TGeoShape *shape = node->GetVolume() ? node->GetVolume()->GetShape() : nullptr;
1306
1307 if (shape) {
1308 res->shape_name = shape->GetName();
1309 res->shape_type = shape->ClassName();
1310 }
1311
1312 if (shape && desc.CanDisplay()) {
1313
1314 auto &shape_descr = MakeShapeDescr(shape);
1315
1316 res->ri = shape_descr.rndr_info(); // temporary pointer, can be used preserved for short time
1317 }
1318 }
1319
1320 return res;
1321}
1322
1323/////////////////////////////////////////////////////////////////////////////////
1324/// Change configuration by client
1325/// Returns true if any parameter was really changed
1326
1328{
1329 auto cfg = TBufferJSON::FromJSON<REveGeomConfig>(json);
1330 if (!cfg) return false;
1331
1332 auto json1 = TBufferJSON::ToJSON(cfg.get());
1333 auto json2 = TBufferJSON::ToJSON(&fCfg);
1334
1335 if (json1 == json2)
1336 return false;
1337
1338 fCfg = *cfg; // use assign
1339
1340 ClearDrawData();
1341
1342 return true;
1343}
1344
1345
#define R__LOG_ERROR(...)
Definition RLogger.hxx:362
#define b(i)
Definition RSha256.hxx:100
#define a(i)
Definition RSha256.hxx:99
#define e(i)
Definition RSha256.hxx:103
#define gROOT
Definition TROOT.h:406
void CollectNodes(REveGeomDrawing &drawing)
Collect nodes which are used in visibles.
std::vector< int > MakeStackByIds(const std::vector< int > &ids)
Creates stack for given array of ids, first element always should be 0.
ShapeDescr & MakeShapeDescr(TGeoShape *shape)
Find description object and create render information.
std::unique_ptr< REveGeomNodeInfo > MakeNodeInfo(const std::vector< std::string > &path)
Change visibility for specified element Returns true if changes was performed.
bool ProduceDrawingFor(int nodeid, std::string &json, bool check_volume=false)
Produce shape rendering data for given stack All nodes, which are referencing same shape will be tran...
std::vector< int > MakeStackByPath(const std::vector< std::string > &path)
Produce stack based on string path Used to highlight geo volumes by browser hover event.
std::string ProduceModifyReply(int nodeid)
Return string with only part of nodes description which were modified Checks also volume.
int SearchVisibles(const std::string &find, std::string &hjson, std::string &json)
Search visible nodes for provided name If number of found elements less than 100, create description ...
bool CollectVisibles()
Collect all information required to draw geometry on the client This includes list of each visible no...
void Build(TGeoManager *mgr, const std::string &volname="")
Collect information about geometry hierarchy into flat list like it done JSROOT.GEO....
bool ChangeConfiguration(const std::string &json)
Change configuration by client Returns true if any parameter was really changed.
int ScanNodes(bool only_visible, int maxlvl, REveGeomScanFunc_t func)
Iterate over all nodes and call function for visible.
void ClearDrawData()
Clear raw data. Will be rebuild when next connection will be established.
bool ChangeNodeVisibility(int nodeid, bool selected)
Change visibility for specified element Returns true if changes was performed.
void ProduceIdShifts()
Count total number of visible childs under each node.
std::string MakeDrawingJson(REveGeomDrawing &drawing, bool has_shapes=false)
Produce JSON for the drawing If TGeoShape appears in the drawing, one has to keep typeinfo But in thi...
void ResetRndrInfos()
Reset shape info, which used to pack binary data.
std::vector< std::string > MakePathByStack(const std::vector< int > &stack)
Returns path string for provided stack.
ShapeDescr & FindShapeDescr(TGeoShape *shape)
Find description object for requested shape If not exists - will be created.
bool IsPrincipalEndNode(int nodeid)
return true when node used in main geometry drawing and does not have childs for such nodes one could...
int FindNodeId(const std::vector< int > &stack)
Returns nodeid for given stack array, returns -1 in case of failure.
std::string ProcessBrowserRequest(const std::string &req="")
Find description object for requested shape If not exists - will be created.
void PackMatrix(std::vector< float > &arr, TGeoMatrix *matr)
Pack matrix into vector, which can be send to client Following sizes can be used for vector: 0 - Iden...
std::vector< int > MakeIdsByStack(const std::vector< int > &stack)
Produce list of node ids for given stack If found nodes preselected - use their ids.
void CopyMaterialProperties(TGeoVolume *vol, REveGeomNode &node)
Copy material properties.
int MarkVisible(bool on_screen=false)
Set visibility flag for each nodes.
Object with full description for drawing geometry It includes list of visible items and list of nodes...
std::vector< REveGeomNode * > nodes
all used nodes to display visible items and not known for client
std::vector< REveGeomVisible > visibles
all visible items
int numnodes
total number of nodes in description
REveGeomConfig * cfg
current configurations
int sortid
! place in sorted array, to check cuts, or id of original node when used search structures
std::string color
rgb code without rgb() prefix
int id
node id, index in array
Full node description including matrices and other attributes.
float opacity
! opacity of the color
int Write(char *msg, int maxlen)
Write render data to binary buffer.
virtual Color_t GetFillColor() const
Return the fill area color.
Definition TAttFill.h:30
virtual Style_t GetFillStyle() const
Return the fill area style.
Definition TAttFill.h:31
virtual Color_t GetLineColor() const
Return the line color.
Definition TAttLine.h:33
Class for serializing object to and from JavaScript Object Notation (JSON) format.
Definition TBufferJSON.h:30
TString StoreObject(const void *obj, const TClass *cl)
Store provided object as JSON structure Allows to configure different TBufferJSON properties before c...
static TString ToJSON(const T *obj, Int_t compact=0, const char *member_name=nullptr)
Definition TBufferJSON.h:75
void SetSkipClassInfo(const TClass *cl)
Specify class which typename will not be stored in JSON Several classes can be configured To exclude ...
void SetCompact(int level)
Set level of space/newline/array compression Lower digit of compact parameter define formatting rules...
The color creation and management class.
Definition TColor.h:19
static Int_t GetColor(const char *hexcolor)
Static method returning color number for color specified by hex color string of form: "#rrggbb",...
Definition TColor.cxx:1769
@ kVisNone
Definition TGeoAtt.h:26
Box class.
Definition TGeoBBox.h:18
virtual Double_t GetDX() const
Definition TGeoBBox.h:70
Class handling Boolean composition of shapes.
A geometry iterator.
Definition TGeoNode.h:245
void Skip()
Stop iterating the current branch.
The manager class for any TGeo geometry.
Definition TGeoManager.h:45
TGeoVolume * GetVolume(const char *name) const
Search for a named volume. All trailing blanks stripped.
Int_t GetMaxVisNodes() const
TGeoNode * GetTopNode() const
Int_t GetVisLevel() const
Returns current depth to which geometry is drawn.
Int_t GetNsegments() const
Get number of segments approximating circles.
TGeoVolume * GetTopVolume() const
Geometrical transformation package.
Definition TGeoMatrix.h:41
virtual const Double_t * GetTranslation() const =0
virtual const Double_t * GetScale() const =0
Bool_t IsIdentity() const
Definition TGeoMatrix.h:66
virtual const Double_t * GetRotationMatrix() const =0
TGeoMaterial * GetMaterial() const
Definition TGeoMedium.h:52
A node represent a volume positioned inside another.They store links to both volumes and to the TGeoM...
Definition TGeoNode.h:41
TGeoVolume * GetVolume() const
Definition TGeoNode.h:97
Int_t GetNumber() const
Definition TGeoNode.h:95
void SetNumber(Int_t number)
Definition TGeoNode.h:116
Base abstract class for all shapes.
Definition TGeoShape.h:26
virtual Bool_t IsComposite() const
Definition TGeoShape.h:130
virtual Bool_t IsCylType() const =0
virtual const char * GetName() const
Get the shape name.
TGeoVolume, TGeoVolumeMulti, TGeoVolumeAssembly are the volume classes.
Definition TGeoVolume.h:49
TGeoMedium * GetMedium() const
Definition TGeoVolume.h:174
static TGeoMedium * DummyMedium()
TGeoShape * GetShape() const
Definition TGeoVolume.h:189
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:130
const char * Data() const
Definition TString.h:369
const Int_t n
Definition legend1.C:16
RLogChannel & EveLog()
Log channel for Eve diagnostics.
Definition REveUtil.cxx:36
std::function< bool(REveGeomNode &, std::vector< int > &, bool, int)> REveGeomScanFunc_t
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
Short_t Abs(Short_t d)
Definition TMathBase.h:120
Definition test.py:1