Logo ROOT  
Reference Guide
REveManager.cxx
Go to the documentation of this file.
1// @(#)root/eve7:$Id$
2// Authors: Matevz Tadel & Alja Mrak-Tadel: 2006, 2007
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/REveManager.hxx>
13
14#include <ROOT/REveUtil.hxx>
16#include <ROOT/REveViewer.hxx>
17#include <ROOT/REveScene.hxx>
18#include <ROOT/REveClient.hxx>
20#include <ROOT/RWebWindow.hxx>
21#include <ROOT/RFileDialog.hxx>
22#include <ROOT/RLogger.hxx>
23#include <ROOT/REveSystem.hxx>
24
25#include "TGeoManager.h"
26#include "TGeoMatrix.h"
27#include "TObjString.h"
28#include "TROOT.h"
29#include "TSystem.h"
30#include "TFile.h"
31#include "TMap.h"
32#include "TExMap.h"
33#include "TEnv.h"
34#include "TColor.h"
35#include "TPRegexp.h"
36#include "TClass.h"
37#include "TMethod.h"
38#include "TMethodCall.h"
39#include "THttpServer.h"
40#include "TTimer.h"
41#include "TApplication.h"
42
43#include <fstream>
44#include <sstream>
45#include <iostream>
46#include <regex>
47
48#include <nlohmann/json.hpp>
49
50using namespace ROOT::Experimental;
51namespace REX = ROOT::Experimental;
52
53REveManager *REX::gEve = nullptr;
54
55thread_local std::vector<RLogEntry> gEveLogEntries;
56/** \class REveManager
57\ingroup REve
58Central application manager for Eve.
59Manages elements, GUI, GL scenes and GL viewers.
60
61Following parameters can be specified in .rootrc file
62
63WebEve.GLViewer: Three # kind of GLViewer, either Three, JSRoot or RCore
64WebEve.DisableShow: 1 # do not start new web browser when REveManager::Show is called
65WebEve.HTimeout: 200 # timeout in ms for elements highlight
66WebEve.DblClick: Off # mouse double click handling in GL viewer: Off or Reset
67WebEve.TableRowHeight: 33 # size of each row in pixels in the Table view, can be used to make design more compact
68*/
69
70////////////////////////////////////////////////////////////////////////////////
71
73 : // (Bool_t map_window, Option_t* opt) :
74 fExcHandler(nullptr), fVizDB(nullptr), fVizDBReplace(kTRUE), fVizDBUpdate(kTRUE), fGeometries(nullptr),
75 fGeometryAliases(nullptr),
76 fKeepEmptyCont(kFALSE)
77{
78 // Constructor.
79
80 static const REveException eh("REveManager::REveManager ");
81
82 if (REX::gEve)
83 throw eh + "There can be only one REve!";
84
85 REX::gEve = this;
86
88 fServerStatus.fTStart = std::time(nullptr);
89
91
92 fGeometries = new TMap;
96 fVizDB = new TMap;
98
99 fElementIdMap[0] = nullptr; // do not increase count for null element.
100
101 fWorld = new REveScene("EveWorld", "Top-level Eve Scene");
104
105 fSelectionList = new REveElement("Selection List");
106 fSelectionList->SetChildClass(TClass::GetClass<REveSelection>());
109 fSelection = new REveSelection("Global Selection", "", kRed, kViolet);
112 fHighlight = new REveSelection("Global Highlight", "", kGreen, kCyan);
116
117 fViewers = new REveViewerList("Viewers");
120
121 fScenes = new REveSceneList("Scenes");
124
125 fGlobalScene = new REveScene("Geometry scene");
128
129 fEventScene = new REveScene("Event scene");
132
133 {
134 REveViewer *v = SpawnNewViewer("Default Viewer");
135 v->AddScene(fGlobalScene);
136 v->AddScene(fEventScene);
137 }
138
139 // !!! AMT increase threshold to enable color pick on client
141
143 fWebWindow->UseServerThreads();
144 fWebWindow->SetDefaultPage("file:rootui5sys/eve7/index.html");
145
146 const char *gl_viewer = gEnv->GetValue("WebEve.GLViewer", "Three");
147 const char *gl_dblclick = gEnv->GetValue("WebEve.DblClick", "Off");
148 Int_t htimeout = gEnv->GetValue("WebEve.HTimeout", 250);
149 Int_t table_row_height = gEnv->GetValue("WebEve.TableRowHeight", 0);
150 fWebWindow->SetUserArgs(Form("{ GLViewer: \"%s\", DblClick: \"%s\", HTimeout: %d, TableRowHeight: %d }", gl_viewer,
151 gl_dblclick, htimeout, table_row_height));
152
153 // this is call-back, invoked when message received via websocket
154 fWebWindow->SetCallBacks([this](unsigned connid) { WindowConnect(connid); },
155 [this](unsigned connid, const std::string &arg) { WindowData(connid, arg); },
156 [this](unsigned connid) { WindowDisconnect(connid); });
157 fWebWindow->SetGeometry(900, 700); // configure predefined window geometry
158 fWebWindow->SetConnLimit(100); // maximal number of connections
159 fWebWindow->SetMaxQueueLength(30); // number of allowed entries in the window queue
160
161 fMIRExecThread = std::thread{[this] { MIRExecThread(); }};
162}
163
164////////////////////////////////////////////////////////////////////////////////
165/// Destructor.
166
168{
169 fMIRExecThread.join();
170
171 // QQQQ How do we stop THttpServer / fWebWindow?
172
177 // Not needed - no more top-items: fScenes->Destroy();
178 fScenes = nullptr;
179
182 // Not needed - no more top-items: fViewers->Destroy();
183 fViewers = nullptr;
184
185 // fWindowManager->DestroyWindows();
186 // fWindowManager->DecDenyDestroy();
187 // fWindowManager->Destroy();
188 // fWindowManager = 0;
189
192
193 delete fGeometryAliases;
194 delete fGeometries;
195 delete fVizDB;
196 delete fExcHandler;
197}
198
199////////////////////////////////////////////////////////////////////////////////
200/// Create a new GL viewer.
201
202REveViewer *REveManager::SpawnNewViewer(const char *name, const char *title)
203{
204 REveViewer *v = new REveViewer(name, title);
206 return v;
207}
208
209////////////////////////////////////////////////////////////////////////////////
210/// Create a new scene.
211
212REveScene *REveManager::SpawnNewScene(const char *name, const char *title)
213{
214 REveScene *s = new REveScene(name, title);
216 return s;
217}
218
220{
221 printf("REveManager::RegisterRedraw3D() obsolete\n");
222}
223
224////////////////////////////////////////////////////////////////////////////////
225/// Perform 3D redraw of scenes and viewers whose contents has
226/// changed.
227
229{
230 printf("REveManager::DoRedraw3D() obsolete\n");
231}
232
233////////////////////////////////////////////////////////////////////////////////
234/// Perform 3D redraw of all scenes and viewers.
235
236void REveManager::FullRedraw3D(Bool_t /*resetCameras*/, Bool_t /*dropLogicals*/)
237{
238 printf("REveManager::FullRedraw3D() obsolete\n");
239}
240
241////////////////////////////////////////////////////////////////////////////////
242/// Clear all selection objects. Can make things easier for EVE when going to
243/// the next event. Still, destruction os selected object should still work
244/// correctly as long as it is executed within a change cycle.
245
247{
248 for (auto el : fSelectionList->fChildren) {
249 dynamic_cast<REveSelection *>(el)->ClearSelection();
250 }
251}
252
253////////////////////////////////////////////////////////////////////////////////
254/// Add an element. If parent is not specified it is added into
255/// current event (which is created if does not exist).
256
258{
259 if (parent == nullptr) {
260 // XXXX
261 }
262
263 parent->AddElement(element);
264}
265
266////////////////////////////////////////////////////////////////////////////////
267/// Add a global element, i.e. one that does not change on each
268/// event, like geometry or projection manager.
269/// If parent is not specified it is added to a global scene.
270
272{
273 if (!parent)
274 parent = fGlobalScene;
275
276 parent->AddElement(element);
277}
278
279////////////////////////////////////////////////////////////////////////////////
280/// Remove element from parent.
281
283{
284 parent->RemoveElement(element);
285}
286
287////////////////////////////////////////////////////////////////////////////////
288/// Lookup ElementId in element map and return corresponding REveElement*.
289/// Returns nullptr if the id is not found
290
292{
293 auto it = fElementIdMap.find(id);
294 return (it != fElementIdMap.end()) ? it->second : nullptr;
295}
296
297////////////////////////////////////////////////////////////////////////////////
298/// Assign a unique ElementId to given element.
299
301{
302 static const REveException eh("REveManager::AssignElementId ");
303
305 throw eh + "ElementId map is full.";
306
307next_free_id:
308 while (fElementIdMap.find(++fLastElementId) != fElementIdMap.end())
309 ;
310 if (fLastElementId == 0)
311 goto next_free_id;
312 // MT - alternatively, we could spawn a thread to find next thousand or so ids and
313 // put them in a vector of ranges. Or collect them when they are freed.
314 // Don't think this won't happen ... online event display can run for months
315 // and easily produce 100000 objects per minute -- about a month to use up all id space!
316
317 element->fElementId = fLastElementId;
318 fElementIdMap.insert(std::make_pair(fLastElementId, element));
320}
321
322////////////////////////////////////////////////////////////////////////////////
323/// Activate EVE browser (summary view) for specified element id
324
326{
327 nlohmann::json msg = {};
328 msg["content"] = "BrowseElement";
329 msg["id"] = id;
330
331 fWebWindow->Send(0, msg.dump());
332}
333
334////////////////////////////////////////////////////////////////////////////////
335/// Called from REveElement prior to its destruction so the
336/// framework components (like object editor) can unreference it.
337
339{
340 if (el->fImpliedSelected > 0) {
341 for (auto slc : fSelectionList->fChildren) {
342 REveSelection *sel = dynamic_cast<REveSelection *>(slc);
344 }
345
346 if (el->fImpliedSelected != 0)
347 Error("REveManager::PreDeleteElement", "ImpliedSelected not zero (%d) after cleanup of selections.",
348 el->fImpliedSelected);
349 }
350 // Primary selection deregistration is handled through Niece removal from Aunts.
351
352 if (el->fElementId != 0) {
353 auto it = fElementIdMap.find(el->fElementId);
354 if (it != fElementIdMap.end()) {
355 if (it->second == el) {
356 fElementIdMap.erase(it);
358 } else
359 Error("PreDeleteElement", "element ptr in ElementIdMap does not match the argument element.");
360 } else
361 Error("PreDeleteElement", "element id %u was not registered in ElementIdMap.", el->fElementId);
362 } else
363 Error("PreDeleteElement", "element with 0 ElementId passed in.");
364}
365
366////////////////////////////////////////////////////////////////////////////////
367/// Insert a new visualization-parameter database entry. Returns
368/// true if the element is inserted successfully.
369/// If entry with the same key already exists the behaviour depends on the
370/// 'replace' flag:
371/// - true - The old model is deleted and new one is inserted (default).
372/// Clients of the old model are transferred to the new one and
373/// if 'update' flag is true (default), the new model's parameters
374/// are assigned to all clients.
375/// - false - The old model is kept, false is returned.
376///
377/// If insert is successful, the ownership of the model-element is
378/// transferred to the manager.
379
381{
382 TPair *pair = (TPair *)fVizDB->FindObject(tag);
383 if (pair) {
384 if (replace) {
385 model->IncDenyDestroy();
386 model->SetRnrChildren(kFALSE);
387
388 REveElement *old_model = dynamic_cast<REveElement *>(pair->Value());
389 if (old_model) {
390 while (old_model->HasChildren()) {
391 REveElement *el = old_model->FirstChild();
392 el->SetVizModel(model);
393 if (update) {
394 el->CopyVizParams(model);
396 }
397 }
398 old_model->DecDenyDestroy();
399 }
400 pair->SetValue(dynamic_cast<TObject *>(model));
401 return kTRUE;
402 } else {
403 return kFALSE;
404 }
405 } else {
406 model->IncDenyDestroy();
407 model->SetRnrChildren(kFALSE);
408 fVizDB->Add(new TObjString(tag), dynamic_cast<TObject *>(model));
409 return kTRUE;
410 }
411}
412
413////////////////////////////////////////////////////////////////////////////////
414/// Insert a new visualization-parameter database entry with the default
415/// parameters for replace and update, as specified by members
416/// fVizDBReplace(default=kTRUE) and fVizDBUpdate(default=kTRUE).
417/// See docs of the above function.
418
420{
421 return InsertVizDBEntry(tag, model, fVizDBReplace, fVizDBUpdate);
422}
423
424////////////////////////////////////////////////////////////////////////////////
425/// Find a visualization-parameter database entry corresponding to tag.
426/// If the entry is not found 0 is returned.
427
429{
430 return dynamic_cast<REveElement *>(fVizDB->GetValue(tag));
431}
432
433////////////////////////////////////////////////////////////////////////////////
434/// Load visualization-parameter database from file filename. The
435/// replace, update arguments replace the values of fVizDBReplace
436/// and fVizDBUpdate members for the duration of the macro
437/// execution.
438
439void REveManager::LoadVizDB(const TString &filename, Bool_t replace, Bool_t update)
440{
441 Bool_t ex_replace = fVizDBReplace;
442 Bool_t ex_update = fVizDBUpdate;
443 fVizDBReplace = replace;
445
446 LoadVizDB(filename);
447
448 fVizDBReplace = ex_replace;
449 fVizDBUpdate = ex_update;
450}
451
452////////////////////////////////////////////////////////////////////////////////
453/// Load visualization-parameter database from file filename.
454/// State of data-members fVizDBReplace and fVizDBUpdate determine
455/// how the registered entries are handled.
456
457void REveManager::LoadVizDB(const TString &filename)
458{
459 REveUtil::Macro(filename);
460 Redraw3D();
461}
462
463////////////////////////////////////////////////////////////////////////////////
464/// Save visualization-parameter database to file filename.
465
466void REveManager::SaveVizDB(const TString &filename)
467{
468 TPMERegexp re("(.+)\\.\\w+");
469 if (re.Match(filename) != 2) {
470 Error("SaveVizDB", "filename does not match required format '(.+)\\.\\w+'.");
471 return;
472 }
473
474 TString exp_filename(filename);
475 gSystem->ExpandPathName(exp_filename);
476
477 std::ofstream out(exp_filename, std::ios::out | std::ios::trunc);
478 out << "void " << re[1] << "()\n";
479 out << "{\n";
480 out << " REveManager::Create();\n";
481
483
484 Int_t var_id = 0;
485 TString var_name;
486 TIter next(fVizDB);
487 TObjString *key;
488 while ((key = (TObjString *)next())) {
489 REveElement *mdl = dynamic_cast<REveElement *>(fVizDB->GetValue(key));
490 if (mdl) {
491 var_name.Form("x%03d", var_id++);
492 mdl->SaveVizParams(out, key->String(), var_name);
493 } else {
494 Warning("SaveVizDB", "Saving failed for key '%s'.", key->String().Data());
495 }
496 }
497
498 out << "}\n";
499 out.close();
500}
501
502////////////////////////////////////////////////////////////////////////////////
503/// Get geometry with given filename.
504/// This is cached internally so the second time this function is
505/// called with the same argument the same geo-manager is returned.
506/// gGeoManager is set to the return value.
507
509{
510 static const REveException eh("REveManager::GetGeometry ");
511
512 TString exp_filename = filename;
513 gSystem->ExpandPathName(exp_filename);
514 printf("REveManager::GetGeometry loading: '%s' -> '%s'.\n", filename.Data(), exp_filename.Data());
515
517 if (gGeoManager) {
519 } else {
520 Bool_t locked = TGeoManager::IsLocked();
521 if (locked) {
522 Warning("REveManager::GetGeometry", "TGeoManager is locked ... unlocking it.");
524 }
525 if (TGeoManager::Import(filename) == 0) {
526 throw eh + "TGeoManager::Import() failed for '" + exp_filename + "'.";
527 }
528 if (locked) {
530 }
531
533
534 // Import colors exported by Gled, if they exist.
535 {
536 TFile f(exp_filename, "READ");
537 TObjArray *collist = (TObjArray *)f.Get("ColorList");
538 f.Close();
539 if (collist) {
541 TGeoVolume *vol;
542 while ((vol = (TGeoVolume *)next()) != nullptr) {
543 Int_t oldID = vol->GetLineColor();
544 TColor *col = (TColor *)collist->At(oldID);
545 Float_t r, g, b;
546 col->GetRGB(r, g, b);
547 Int_t newID = TColor::GetColor(r, g, b);
548 vol->SetLineColor(newID);
549 }
550 }
551 }
552
553 fGeometries->Add(new TObjString(filename), gGeoManager);
554 }
555 return gGeoManager;
556}
557
558////////////////////////////////////////////////////////////////////////////////
559/// Get geometry with given alias.
560/// The alias must be registered via RegisterGeometryAlias().
561
563{
564 static const REveException eh("REveManager::GetGeometry ");
565
566 TObjString *full_name = (TObjString *)fGeometryAliases->GetValue(alias);
567 if (!full_name)
568 throw eh + "geometry alias '" + alias + "' not registered.";
569 return GetGeometry(full_name->String());
570}
571
572////////////////////////////////////////////////////////////////////////////////
573/// Get the default geometry.
574/// It should be registered via RegisterGeometryName("Default", <URL>).
575
577{
578 return GetGeometryByAlias("Default");
579}
580
581////////////////////////////////////////////////////////////////////////////////
582/// Register 'name' as an alias for geometry file 'filename'.
583/// The old aliases are silently overwritten.
584/// After that the geometry can be retrieved also by calling:
585/// REX::gEve->GetGeometryByName(name);
586
587void REveManager::RegisterGeometryAlias(const TString &alias, const TString &filename)
588{
589 fGeometryAliases->Add(new TObjString(alias), new TObjString(filename));
590}
591
592////////////////////////////////////////////////////////////////////////////////
593/// Work-around uber ugly hack used in SavePrimitive and co.
594
596{
597 TIter nextcl(gROOT->GetListOfClasses());
598 TClass *cls;
599 while ((cls = (TClass *)nextcl())) {
601 }
602}
603
604////////////////////////////////////////////////////////////////////////////////
605/// Register new directory to THttpServer
606// For example: AddLocation("mydir/", "/test/EveWebApp/ui5");
607//
608void REveManager::AddLocation(const std::string &locationName, const std::string &path)
609{
610 fWebWindow->GetServer()->AddLocation(locationName.c_str(), path.c_str());
611}
612
613////////////////////////////////////////////////////////////////////////////////
614/// Set content of default window HTML page
615// Got example: SetDefaultHtmlPage("file:currentdir/test.html")
616//
617void REveManager::SetDefaultHtmlPage(const std::string &path)
618{
619 fWebWindow->SetDefaultPage(path.c_str());
620}
621
622////////////////////////////////////////////////////////////////////////////////
623/// Set client version, used as prefix in scripts URL
624/// When changed, web browser will reload all related JS files while full URL will be different
625/// Default is empty value - no extra string in URL
626/// Version should be string like "1.2" or "ver1.subv2" and not contain any special symbols
627void REveManager::SetClientVersion(const std::string &version)
628{
629 fWebWindow->SetClientVersion(version);
630}
631
632////////////////////////////////////////////////////////////////////////////////
633/// If global REveManager* REX::gEve is not set initialize it.
634/// Returns REX::gEve.
635
637{
638 static const REveException eh("REveManager::Create ");
639
640 if (!REX::gEve) {
641 // XXXX Initialize some server stuff ???
642
643 REX::gEve = new REveManager();
644 }
645 return REX::gEve;
646}
647
648////////////////////////////////////////////////////////////////////////////////
649/// Properly terminate global REveManager.
650
652{
653 if (!REX::gEve)
654 return;
655
656 delete REX::gEve;
657 REX::gEve = nullptr;
658}
659
661{
662 class XThreadTimer : public TTimer {
663 std::function<void()> foo_;
664 public:
665 XThreadTimer(std::function<void()> f) : foo_(f)
666 {
667 SetTime(0);
669 gSystem->AddTimer(this);
670 }
671 Bool_t Notify() override
672 {
673 foo_();
674 gSystem->RemoveTimer(this);
675 delete this;
676 return kTRUE;
677 }
678 };
679
680 new XThreadTimer(func);
681}
682
684{
686 // QQQQ Should call Terminate() but it needs to:
687 // - properly stop MIRExecThread;
688 // - shutdown civet/THttp/RWebWindow
690 });
691}
692
693////////////////////////////////////////////////////////////////////////////////
694/// Process new connection from web window
695
696void REveManager::WindowConnect(unsigned connid)
697{
698 std::unique_lock<std::mutex> lock(fServerState.fMutex);
699
701 {
702 fServerState.fCV.wait(lock);
703 }
704
705 fConnList.emplace_back(connid);
706 printf("connection established %u\n", connid);
707
708 // QQQQ do we want mir-time here as well? maybe set it at the end of function?
709 // Note, this is all under lock, so nobody will get state out in between.
710 fServerStatus.fTLastMir = fServerStatus.fTLastConnect = std::time(nullptr);
712
713 // This prepares core and render data buffers.
714 printf("\nEVEMNG ............. streaming the world scene.\n");
715
716 fWorld->AddSubscriber(std::make_unique<REveClient>(connid, fWebWindow));
718
719 printf(" sending json, len = %d\n", (int)fWorld->fOutputJson.size());
720 Send(connid, fWorld->fOutputJson);
721 printf(" for now assume world-scene has no render data, binary-size=%d\n", fWorld->fTotalBinarySize);
722 assert(fWorld->fTotalBinarySize == 0);
723
724 for (auto &c : fScenes->RefChildren()) {
725 REveScene *scene = dynamic_cast<REveScene *>(c);
726
727 scene->AddSubscriber(std::make_unique<REveClient>(connid, fWebWindow));
728 printf("\nEVEMNG ............. streaming scene %s [%s]\n", scene->GetCTitle(), scene->GetCName());
729
730 // This prepares core and render data buffers.
731 scene->StreamElements();
732
733 printf(" sending json, len = %d\n", (int)scene->fOutputJson.size());
734 Send(connid, scene->fOutputJson);
735
736 if (scene->fTotalBinarySize > 0) {
737 printf(" sending binary, len = %d\n", scene->fTotalBinarySize);
738 SendBinary(connid, &scene->fOutputBinary[0], scene->fTotalBinarySize);
739 } else {
740 printf(" NOT sending binary, len = %d\n", scene->fTotalBinarySize);
741 }
742 }
743
744 fServerState.fCV.notify_all();
745}
746
747////////////////////////////////////////////////////////////////////////////////
748/// Process disconnect of web window
749
750void REveManager::WindowDisconnect(unsigned connid)
751{
752 std::unique_lock<std::mutex> lock(fServerState.fMutex);
754 {
755 fServerState.fCV.wait(lock);
756 }
757 auto conn = fConnList.end();
758 for (auto i = fConnList.begin(); i != fConnList.end(); ++i) {
759 if (i->fId == connid) {
760 conn = i;
761 break;
762 }
763 }
764 // this should not happen, just check
765 if (conn == fConnList.end()) {
766 printf("error, connection not found!");
767 } else {
768 printf("connection closed %u\n", connid);
769 fConnList.erase(conn);
770 for (auto &c : fScenes->RefChildren()) {
771 REveScene *scene = dynamic_cast<REveScene *>(c);
772 scene->RemoveSubscriber(connid);
773 }
774 fWorld->RemoveSubscriber(connid);
775 }
776
777 fServerStatus.fTLastDisconnect = std::time(nullptr);
779
780 fServerState.fCV.notify_all();
781}
782
783////////////////////////////////////////////////////////////////////////////////
784/// Process data from web window
785
786void REveManager::WindowData(unsigned connid, const std::string &arg)
787{
788 static const REveException eh("REveManager::WindowData ");
789
790 // find connection object
791 bool found = false;
792 for (auto &conn : fConnList) {
793 if (conn.fId == connid) {
794 found = true;
795 break;
796 }
797 }
798
799 // this should not happen, just check
800 if (!found) {
801 R__LOG_ERROR(REveLog()) << "Internal error - no connection with id " << connid << " found";
802 return;
803 }
804 // client status data
805 if (arg.compare("__REveDoneChanges") == 0)
806 {
807 std::unique_lock<std::mutex> lock(fServerState.fMutex);
808
809 for (auto &conn : fConnList) {
810 if (conn.fId == connid) {
811 conn.fState = Conn::Free;
812 break;
813 }
814 }
815
816 if (ClientConnectionsFree()) {
818 fServerState.fCV.notify_all();
819 }
820
821 return;
822 }
823 else if (arg.compare( 0, 10, "FILEDIALOG") == 0)
824 {
826 return;
827 }
828
829 nlohmann::json cj = nlohmann::json::parse(arg);
830 if (gDebug > 0)
831 ::Info("REveManager::WindowData", "MIR test %s\n", cj.dump().c_str());
832
833 std::string cmd = cj["mir"];
834 int id = cj["fElementId"];
835 std::string ctype = cj["class"];
836
837 ScheduleMIR(cmd, id, ctype);
838}
839
840//
841//____________________________________________________________________
842void REveManager::ScheduleMIR(const std::string &cmd, ElementId_t id, const std::string& ctype)
843{
844 std::unique_lock<std::mutex> lock(fServerState.fMutex);
845 fServerStatus.fTLastMir = std::time(nullptr);
846 fMIRqueue.push(std::shared_ptr<MIR>(new MIR(cmd, id, ctype)));
848 fServerState.fCV.notify_all();
849}
850
851//
852//____________________________________________________________________
853void REveManager::ExecuteMIR(std::shared_ptr<MIR> mir)
854{
855 static const REveException eh("REveManager::ExecuteMIR ");
856
857 class ChangeSentry {
858 public:
859 ChangeSentry()
860 {
862 gEve->GetScenes()->AcceptChanges(true);
863 }
864 ~ChangeSentry()
865 {
866 gEve->GetScenes()->AcceptChanges(false);
868 }
869 };
870 ChangeSentry cs;
871
872 //if (gDebug > 0)
873 ::Info("REveManager::ExecuteCommand", "MIR cmd %s", mir->fCmd.c_str());
874
875 try {
876 REveElement *el = FindElementById(mir->fId);
877 if ( ! el) throw eh + "Element with id " + mir->fId + " not found";
878
879 static const std::regex cmd_re("^(\\w[\\w\\d]*)\\(\\s*(.*)\\s*\\)\\s*;?\\s*$", std::regex::optimize);
880 std::smatch m;
881 std::regex_search(mir->fCmd, m, cmd_re);
882 if (m.size() != 3)
883 throw eh + "Command string parse error: '" + mir->fCmd + "'.";
884
885 static const TClass *elem_cls = TClass::GetClass<REX::REveElement>();
886
887 TClass *call_cls = TClass::GetClass(mir->fCtype.c_str());
888 if ( ! call_cls)
889 throw eh + "Class '" + mir->fCtype + "' not found.";
890
891 void *el_casted = call_cls->DynamicCast(elem_cls, el, false);
892 if ( ! el_casted)
893 throw eh + "Dynamic cast from REveElement to '" + mir->fCtype + "' failed.";
894
895 std::string tag(mir->fCtype + "::" + m.str(1));
896 std::shared_ptr<TMethodCall> mc;
897
898 auto mmi = fMethCallMap.find(tag);
899 if (mmi != fMethCallMap.end())
900 {
901 mc = mmi->second;
902 }
903 else
904 {
905 const TMethod *meth = call_cls->GetMethodAllAny(m.str(1).c_str());
906 if ( ! meth)
907 throw eh + "Can not find TMethod matching '" + m.str(1) + "'.";
908 mc = std::make_shared<TMethodCall>(meth);
909 fMethCallMap.insert(std::make_pair(tag, mc));
910 }
911
913 mc->Execute(el_casted, m.str(2).c_str());
914
915 // Alternative implementation through Cling. "Leaks" 200 kB per call.
916 // This might be needed for function calls that involve data-types TMethodCall
917 // can not handle.
918 // std::stringstream cmd;
919 // cmd << "((" << mir->fCtype << "*)" << std::hex << std::showbase << (size_t)el << ")->" << mir->fCmd << ";";
920 // std::cout << cmd.str() << std::endl;
921 // gROOT->ProcessLine(cmd.str().c_str());
922 } catch (std::exception &e) {
923 R__LOG_ERROR(REveLog()) << "REveManager::ExecuteCommand " << e.what() << std::endl;
924 } catch (...) {
925 R__LOG_ERROR(REveLog()) << "REveManager::ExecuteCommand unknow execption \n";
926 }
927}
928
929//
930//____________________________________________________________________
932{
933 nlohmann::json jobj = {};
934 jobj["content"] = "BeginChanges";
935 fWebWindow->Send(0, jobj.dump());
936
937 // Process changes in scenes.
940 jobj["content"] = "EndChanges";
941
942 if (!gEveLogEntries.empty()) {
943
944 constexpr static int numLevels = static_cast<int>(ELogLevel::kDebug) + 1;
945 constexpr static std::array<const char *, numLevels> sTag{
946 {"{unset-error-level please report}", "FATAL", "Error", "Warning", "Info", "Debug"}};
947
948 std::stringstream strm;
949 for (auto entry : gEveLogEntries) {
950
951 auto channel = entry.fChannel;
952 if (channel && !channel->GetName().empty())
953 strm << '[' << channel->GetName() << "] ";
954
955 int cappedLevel = std::min(static_cast<int>(entry.fLevel), numLevels - 1);
956 strm << sTag[cappedLevel];
957
958 if (!entry.fLocation.fFile.empty())
959 strm << " " << entry.fLocation.fFile << ':' << entry.fLocation.fLine;
960 if (!entry.fLocation.fFuncName.empty())
961 strm << " in " << entry.fLocation.fFuncName;
962 }
963 jobj["log"] = strm.str();
964 gEveLogEntries.clear();
965 }
966
967 fWebWindow->Send(0, jobj.dump());
968}
969
970//
971//____________________________________________________________________
973{
974#if defined(R__LINUX)
975 pthread_setname_np(pthread_self(), "mir_exec");
976#endif
977 while (true)
978 {
979 std::unique_lock<std::mutex> lock(fServerState.fMutex);
980 abcLabel:
981 if (fMIRqueue.empty())
982 {
983 fServerState.fCV.wait(lock);
984 goto abcLabel;
985 }
987 {
988 std::shared_ptr<MIR> mir = fMIRqueue.front();
989 fMIRqueue.pop();
990
992 lock.unlock();
993
994 ExecuteMIR(mir);
995
996 lock.lock();
999 }
1000 }
1001}
1002
1003
1004//____________________________________________________________________
1005void REveManager::Send(unsigned connid, const std::string &data)
1006{
1007 fWebWindow->Send(connid, data);
1008}
1009
1010void REveManager::SendBinary(unsigned connid, const void *data, std::size_t len)
1011{
1012 fWebWindow->SendBinary(connid, data, len);
1013}
1014
1016{
1017 for (auto &conn : fConnList) {
1018 if (conn.fState != Conn::Free)
1019 return false;
1020 }
1021
1022 return true;
1023}
1024
1026{
1027 for (auto &conn : fConnList) {
1028 if (conn.fId == cinnId)
1029 {
1030 conn.fState = Conn::WaitingResponse;
1031 break;
1032 }
1033 }
1034}
1035
1037{
1038 for (auto &conn : fConnList) {
1039 if (conn.fId == cinnId)
1040 {
1041 conn.fState = Conn::Processing;
1042 break;
1043 }
1044 }
1045}
1046
1047//////////////////////////////////////////////////////////////////
1048/// Show eve manager in specified browser.
1049
1050/// If rootrc variable WebEve.DisableShow is set, HTTP server will be
1051/// started and access URL printed on stdout.
1052
1054{
1055 if (gEnv->GetValue("WebEve.DisableShow", 0) != 0) {
1056 std::string url = fWebWindow->GetUrl(true);
1057 printf("EVE URL %s\n", url.c_str());
1058 } else {
1059 fWebWindow->Show(args);
1060 }
1061}
1062
1063//////////////////////////////////////////////////////////////////
1064/// Show current geometry in web browser
1065
1066std::shared_ptr<REveGeomViewer> REveManager::ShowGeometry(const RWebDisplayArgs &args)
1067{
1068 if (!gGeoManager) {
1069 Error("ShowGeometry", "No geometry is loaded");
1070 return nullptr;
1071 }
1072
1073 auto viewer = std::make_shared<REveGeomViewer>(gGeoManager);
1074
1075 viewer->Show(args);
1076
1077 return viewer;
1078}
1079
1080//____________________________________________________________________
1082{
1083 {
1084 std::unique_lock<std::mutex> lock(fServerState.fMutex);
1086 fServerState.fCV.wait(lock);
1087 }
1089 }
1091 GetScenes()->AcceptChanges(true);
1092}
1093
1094//____________________________________________________________________
1096{
1097 GetScenes()->AcceptChanges(false);
1099
1101
1102 std::unique_lock<std::mutex> lock(fServerState.fMutex);
1104 fServerState.fCV.notify_all();
1105}
1106
1107//____________________________________________________________________
1109{
1110 std::unique_lock<std::mutex> lock(fServerState.fMutex);
1112#if defined(_MSC_VER)
1113 std::timespec_get(&fServerStatus.fTReport, TIME_UTC);
1114#else
1115 fServerStatus.fTReport = std::time_t(nullptr);
1116#endif
1117 st = fServerStatus;
1118}
1119
1120/** \class REveManager::ChangeGuard
1121\ingroup REve
1122RAII guard for locking Eve manager (ctor) and processing changes (dtor).
1123*/
1124
1125//////////////////////////////////////////////////////////////////////
1126//
1127// Helper struct to guard update mechanism
1128//
1130{
1131 gEve->BeginChange();
1132}
1133
1135{
1136 gEve->EndChange();
1137}
1138
1139/** \class REveManager::RExceptionHandler
1140\ingroup REve
1141Exception handler for Eve exceptions.
1142*/
1143
1144////////////////////////////////////////////////////////////////////////////////
1145/// Handle exceptions deriving from REveException.
1146
1148{
1149 REveException *ex = dynamic_cast<REveException *>(&exc);
1150 if (ex) {
1151 Info("Handle", "Exception %s", ex->what());
1152 // REX::gEve->SetStatusLine(ex->Data());
1153 gSystem->Beep();
1154 return kSEHandled;
1155 }
1156 return kSEProceed;
1157}
1158
1159
1160////////////////////////////////////////////////////////////////////////////////
1161/// Utility to stream loggs to client.
1162
1164{
1165 gEveLogEntries.emplace_back(entry);
1166 return true;
1167}
typedef void(GLAPIENTRYP _GLUfuncptr)(void)
ROOT::R::TRInterface & r
Definition: Object.C:4
thread_local std::vector< RLogEntry > gEveLogEntries
Definition: REveManager.cxx:55
#define R__LOG_ERROR(...)
Definition: RLogger.hxx:362
#define b(i)
Definition: RSha256.hxx:100
#define f(i)
Definition: RSha256.hxx:104
#define c(i)
Definition: RSha256.hxx:101
#define g(i)
Definition: RSha256.hxx:105
#define e(i)
Definition: RSha256.hxx:103
static void update(gsl_integration_workspace *workspace, double a1, double b1, double area1, double error1, double a2, double b2, double area2, double error2)
int Int_t
Definition: RtypesCore.h:45
const Bool_t kFALSE
Definition: RtypesCore.h:101
bool Bool_t
Definition: RtypesCore.h:63
float Float_t
Definition: RtypesCore.h:57
const Bool_t kTRUE
Definition: RtypesCore.h:100
@ kRed
Definition: Rtypes.h:66
@ kGreen
Definition: Rtypes.h:66
@ kCyan
Definition: Rtypes.h:66
@ kViolet
Definition: Rtypes.h:67
R__EXTERN TApplication * gApplication
Definition: TApplication.h:165
R__EXTERN TEnv * gEnv
Definition: TEnv.h:170
void Info(const char *location, const char *msgfmt,...)
Use this function for informational messages.
Definition: TError.cxx:220
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition: TError.cxx:187
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition: TError.cxx:231
R__EXTERN TEveManager * gEve
Definition: TEveManager.h:243
XFontStruct * id
Definition: TGX11.cxx:109
char name[80]
Definition: TGX11.cxx:110
R__EXTERN TGeoManager * gGeoManager
Definition: TGeoManager.h:602
R__EXTERN TGeoIdentity * gGeoIdentity
Definition: TGeoMatrix.h:478
R__EXTERN TVirtualMutex * gInterpreterMutex
Definition: TInterpreter.h:44
#define R__LOCKGUARD_CLING(mutex)
Definition: TInterpreter.h:51
Int_t gDebug
Definition: TROOT.cxx:592
#define gROOT
Definition: TROOT.h:404
char * Form(const char *fmt,...)
R__EXTERN TVirtualMutex * gSystemMutex
Definition: TSystem.h:244
R__EXTERN TSystem * gSystem
Definition: TSystem.h:559
#define R__LOCKGUARD2(mutex)
void DecDenyDestroy()
Decreases the deny-destroy count of the element.
void SaveVizParams(std::ostream &out, const TString &tag, const TString &var)
Save visualization parameters for this element with given tag.
const char * GetCTitle() const
const char * GetCName() const
virtual void AddElement(REveElement *el)
Add el to the list of children.
virtual Bool_t SetRnrChildren(Bool_t rnr)
Set render state of this element's children, i.e.
virtual void DestroyElements()
Destroy all children of this element.
REveElement * FirstChild() const
Returns the first child element or 0 if the list is empty.
void IncDenyDestroy()
Increases the deny-destroy count of the element.
virtual void CopyVizParams(const REveElement *el)
Copy visualization parameters from element el.
void SetVizModel(REveElement *model)
Set visualization-parameter model element.
virtual void RemoveElement(REveElement *el)
Remove el from the list of children.
virtual void PropagateVizParamsToProjecteds()
Propagate visualization parameters to dependent elements.
REveException Exception-type thrown by Eve classes.
Definition: REveTypes.hxx:41
bool Emit(const RLogEntry &entry) override
Utility to stream loggs to client.
virtual EStatus Handle(std::exception &exc)
Handle exceptions deriving from REveException.
void ClearROOTClassSaved()
Work-around uber ugly hack used in SavePrimitive and co.
void ScheduleMIR(const std::string &cmd, ElementId_t i, const std::string &ctype)
void RegisterGeometryAlias(const TString &alias, const TString &filename)
Register 'name' as an alias for geometry file 'filename'.
void PreDeleteElement(REveElement *element)
Called from REveElement prior to its destruction so the framework components (like object editor) can...
void ExecuteMIR(std::shared_ptr< MIR > mir)
REveSceneList * GetScenes() const
void ClearAllSelections()
Clear all selection objects.
RExceptionHandler * fExcHandler
exception handler
void AssignElementId(REveElement *element)
Assign a unique ElementId to given element.
TGeoManager * GetDefaultGeometry()
Get the default geometry.
static void ExecuteInMainThread(std::function< void()> func)
void GetServerStatus(REveServerStatus &)
void SetDefaultHtmlPage(const std::string &path)
Set content of default window HTML page.
void AddLocation(const std::string &name, const std::string &path)
Register new directory to THttpServer.
void Send(unsigned connid, const std::string &data)
static void Terminate()
Properly terminate global REveManager.
REveElement * FindElementById(ElementId_t id) const
Lookup ElementId in element map and return corresponding REveElement*.
void SaveVizDB(const TString &filename)
Save visualization-parameter database to file filename.
TGeoManager * GetGeometryByAlias(const TString &alias)
Get geometry with given alias.
std::unordered_map< ElementId_t, REveElement * > fElementIdMap
static REveManager * Create()
If global REveManager* REX::gEve is not set initialize it.
std::unordered_map< std::string, std::shared_ptr< TMethodCall > > fMethCallMap
void WindowConnect(unsigned connid)
Process new connection from web window.
std::vector< Conn > fConnList
REveElement * FindVizDBEntry(const TString &tag)
Find a visualization-parameter database entry corresponding to tag.
TGeoManager * GetGeometry(const TString &filename)
Get geometry with given filename.
std::shared_ptr< ROOT::Experimental::RWebWindow > fWebWindow
std::shared_ptr< REveGeomViewer > ShowGeometry(const RWebDisplayArgs &args="")
Show current geometry in web browser.
std::queue< std::shared_ptr< MIR > > fMIRqueue
void LoadVizDB(const TString &filename, Bool_t replace, Bool_t update)
Load visualization-parameter database from file filename.
void DoRedraw3D()
Perform 3D redraw of scenes and viewers whose contents has changed.
void SendBinary(unsigned connid, const void *data, std::size_t len)
void AddElement(REveElement *element, REveElement *parent=nullptr)
Add an element.
void SetClientVersion(const std::string &version)
Set client version, used as prefix in scripts URL When changed, web browser will reload all related J...
void AddGlobalElement(REveElement *element, REveElement *parent=nullptr)
Add a global element, i.e.
void Redraw3D(Bool_t resetCameras=kFALSE, Bool_t dropLogicals=kFALSE)
void SceneSubscriberWaitingResponse(unsigned cinnId)
REveScene * SpawnNewScene(const char *name, const char *title="")
Create a new scene.
void SceneSubscriberProcessingChanges(unsigned cinnId)
void RemoveElement(REveElement *element, REveElement *parent)
Remove element from parent.
virtual ~REveManager()
Destructor.
void WindowDisconnect(unsigned connid)
Process disconnect of web window.
void FullRedraw3D(Bool_t resetCameras=kFALSE, Bool_t dropLogicals=kFALSE)
Perform 3D redraw of all scenes and viewers.
REveViewer * SpawnNewViewer(const char *name, const char *title="")
Create a new GL viewer.
Bool_t InsertVizDBEntry(const TString &tag, REveElement *model, Bool_t replace, Bool_t update)
Insert a new visualization-parameter database entry.
REveScene * GetWorld() const
void BrowseElement(ElementId_t id)
Activate EVE browser (summary view) for specified element id.
void WindowData(unsigned connid, const std::string &arg)
Process data from web window.
void Show(const RWebDisplayArgs &args="")
Show eve manager in specified browser.
void DestroyScenes()
Destroy all scenes and their contents.
Definition: REveScene.cxx:490
void AcceptChanges(bool)
Set accept changes flag on all scenes.
Definition: REveScene.cxx:504
void AddSubscriber(std::unique_ptr< REveClient > &&sub)
Definition: REveScene.cxx:60
std::vector< char > fOutputBinary
!
Definition: REveScene.hxx:78
void RemoveSubscriber(unsigned int)
Definition: REveScene.cxx:71
REveSelection Container for selected and highlighted elements.
int RemoveImpliedSelectedReferencesTo(REveElement *el)
Remove pointers to el from implied selected sets.
void SetHighlightMode()
Set to 'highlight' mode.
static void Macro(const char *mac)
Execute macro 'mac'. Do not reload the macro.
Definition: REveUtil.cxx:94
REveViewerList List of Viewers providing common operations on REveViewer collections.
Definition: REveViewer.hxx:62
void AddElement(REveElement *el) override
Call base-class implementation.
Definition: REveViewer.cxx:145
REveViewer Reve representation of TGLViewer.
Definition: REveViewer.hxx:28
static std::shared_ptr< RFileDialog > Embedded(const std::shared_ptr< RWebWindow > &window, const std::string &args)
Create dialog instance to use as embedded dialog inside other widget Embedded dialog started on the c...
A diagnostic that can be emitted by the RLogManager.
Definition: RLogger.hxx:178
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
static std::shared_ptr< RWebWindow > Create()
Create new RWebWindow Using default RWebWindowsManager.
virtual void Terminate(Int_t status=0)
virtual Color_t GetLineColor() const
Return the line color.
Definition: TAttLine.h:33
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition: TClass.h:80
void * DynamicCast(const TClass *base, void *obj, Bool_t up=kTRUE)
Cast obj of this class type up to baseclass cl if up is true.
Definition: TClass.cxx:4908
TMethod * GetMethodAllAny(const char *method)
Return pointer to method without looking at parameters.
Definition: TClass.cxx:4377
@ kClassSaved
Definition: TClass.h:94
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition: TClass.cxx:2955
The color creation and management class.
Definition: TColor.h:19
virtual void GetRGB(Float_t &r, Float_t &g, Float_t &b) const
Definition: TColor.h:51
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:1775
static void SetColorThreshold(Float_t t)
This method specifies the color threshold used by GetColor to retrieve a color.
Definition: TColor.cxx:1847
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition: TEnv.cxx:491
A ROOT file is a suite of consecutive data records (TKey instances) with a well defined format.
Definition: TFile.h:54
An identity transformation.
Definition: TGeoMatrix.h:384
The manager class for any TGeo geometry.
Definition: TGeoManager.h:45
static void UnlockGeometry()
Unlock current geometry.
TObjArray * GetListOfVolumes() const
Definition: TGeoManager.h:493
TObjArray * GetListOfMatrices() const
Definition: TGeoManager.h:490
static Bool_t IsLocked()
Check lock state.
static TGeoManager * Import(const char *filename, const char *name="", Option_t *option="")
static function Import a geometry from a gdml or ROOT file
static void LockGeometry()
Lock current geometry so that no other geometry can be imported.
TGeoVolume * GetTopVolume() const
Definition: TGeoManager.h:532
TGeoVolume, TGeoVolumeMulti, TGeoVolumeAssembly are the volume classes.
Definition: TGeoVolume.h:49
void VisibleDaughters(Bool_t vis=kTRUE)
set visibility for daughters
virtual void SetLineColor(Color_t lcolor)
Set the line color.
TMap implements an associative array of (key,value) pairs using a THashTable for efficient retrieval ...
Definition: TMap.h:40
void Add(TObject *obj)
This function may not be used (but we need to provide it since it is a pure virtual in TCollection).
Definition: TMap.cxx:54
virtual void SetOwnerKeyValue(Bool_t ownkeys=kTRUE, Bool_t ownvals=kTRUE)
Set ownership for keys and values.
Definition: TMap.cxx:352
TObject * GetValue(const char *keyname) const
Returns a pointer to the value associated with keyname as name of the key.
Definition: TMap.cxx:236
TObject * FindObject(const char *keyname) const
Check if a (key,value) pair exists with keyname as name of the key.
Definition: TMap.cxx:215
Each ROOT class (see TClass) has a linked list of methods.
Definition: TMethod.h:38
An array of TObjects.
Definition: TObjArray.h:37
TObject * At(Int_t idx) const
Definition: TObjArray.h:166
Collectable string class.
Definition: TObjString.h:28
TString & String()
Definition: TObjString.h:48
Mother of all ROOT objects.
Definition: TObject.h:37
void ResetBit(UInt_t f)
Definition: TObject.h:186
Wrapper for PCRE library (Perl Compatible Regular Expressions).
Definition: TPRegexp.h:97
Int_t Match(const TString &s, UInt_t start=0)
Runs a match on s against the regex 'this' was created with.
Definition: TPRegexp.cxx:706
Class used by TMap to store (key,value) pairs.
Definition: TMap.h:102
void SetValue(TObject *val)
Definition: TMap.h:122
TObject * Value() const
Definition: TMap.h:121
Basic string class.
Definition: TString.h:136
const char * Data() const
Definition: TString.h:369
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition: TString.cxx:2314
void Beep(Int_t freq=-1, Int_t duration=-1, Bool_t setDefault=kFALSE)
Beep for duration milliseconds with a tone of frequency freq.
Definition: TSystem.cxx:327
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition: TSystem.cxx:1274
virtual int GetPid()
Get process id.
Definition: TSystem.cxx:710
virtual void AddTimer(TTimer *t)
Add timer to list of system timers.
Definition: TSystem.cxx:474
virtual int GetProcInfo(ProcInfo_t *info) const
Returns cpu and memory used by this process into the ProcInfo_t structure.
Definition: TSystem.cxx:2495
virtual TTimer * RemoveTimer(TTimer *t)
Remove timer from list of system timers.
Definition: TSystem.cxx:484
Handles synchronous and a-synchronous timer events.
Definition: TTimer.h:51
Double_t ex[n]
Definition: legend1.C:17
R__EXTERN REveManager * gEve
unsigned int ElementId_t
Definition: REveTypes.hxx:25
@ kDebug
Debug information; only useful for developers; can have added verbosity up to 255-kDebug.
RLogChannel & REveLog()
Log channel for Eve diagnostics.
Definition: REveTypes.cxx:47
void function(const Char_t *name_, T fun, const Char_t *docstring=0)
Definition: RExports.h:150
static constexpr double s
auto * m
Definition: textangle.C:8