Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RGeomViewer.cxx
Go to the documentation of this file.
1// Author: Sergey Linev, 13.12.2018
2
3/*************************************************************************
4 * Copyright (C) 1995-203, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11#include <ROOT/RGeomViewer.hxx>
12
14#include <ROOT/RLogger.hxx>
15#include <ROOT/RWebWindow.hxx>
16
17#include "TSystem.h"
18#include "TBase64.h"
19#include "TROOT.h"
20#include "TFile.h"
21#include "TEnv.h"
22#include "THttpServer.h"
23#include "TBufferJSON.h"
24#include "TGeoManager.h"
25
26#include <fstream>
27
28using namespace std::string_literals;
29
30using namespace ROOT;
31
32//////////////////////////////////////////////////////////////////////////////////////////////
33/// constructor
34
35RGeomViewer::RGeomViewer(TGeoManager *mgr, const std::string &volname)
36{
37 if (!gROOT->IsWebDisplayBatch()) {
39 fWebWindow->SetDefaultPage("file:rootui5sys/geom/index.html");
40
41 // this is call-back, invoked when message received via websocket
42 fWebWindow->SetDataCallBack([this](unsigned connid, const std::string &arg) { WebWindowCallback(connid, arg); });
43 fWebWindow->SetDisconnectCallBack([this](unsigned connid) { WebWindowDisconnect(connid); });
44
45 fWebWindow->SetGeometry(900, 700); // configure predefined window geometry
46 fWebWindow->SetConnLimit(0); // allow any connections numbers at the same time
47 fWebWindow->SetMaxQueueLength(30); // number of allowed entries in the window queue
48 }
49
50 fDesc.SetPreferredOffline(gEnv->GetValue("WebGui.PreferredOffline", 0) != 0);
52 fDesc.SetBuildShapes(gEnv->GetValue("WebGui.GeomBuildShapes", 1));
53
54 fDesc.AddSignalHandler(this, [this](const std::string &kind) { ProcessSignal(kind); });
55
56 if (mgr)
57 SetGeometry(mgr, volname);
58}
59
60//////////////////////////////////////////////////////////////////////////////////////////////
61/// destructor
62
64{
66}
67
68//////////////////////////////////////////////////////////////////////////////////////////////
69/// assign new geometry to the viewer
70
71void RGeomViewer::SetGeometry(TGeoManager *mgr, const std::string &volname)
72{
73 fGeoManager = mgr;
74 fSelectedVolume = volname;
75
76 fDesc.Build(mgr, volname);
77
78 Update();
79}
80
81/////////////////////////////////////////////////////////////////////////////////
82/// Select visible top volume, all other volumes will be disabled
83
84void RGeomViewer::SelectVolume(const std::string &volname)
85{
86 if ((volname != fSelectedVolume) && fGeoManager)
87 SetGeometry(fGeoManager, volname);
88}
89
90/////////////////////////////////////////////////////////////////////////////////
91/// Draw only specified volume, special case when volume stored without valid geomanager
92
94{
95 fGeoManager = nullptr;
96 fSelectedVolume = "";
97
98 fDesc.Build(vol);
99
100 Update();
101}
102
103/////////////////////////////////////////////////////////////////////////////////
104/// Show or update geometry in web window
105/// If web browser already started - just refresh drawing like "reload" button does
106/// If no web window exists or \param always_start_new_browser configured, starts new window
107/// \param args arguments to display
108
109void RGeomViewer::Show(const RWebDisplayArgs &args, bool always_start_new_browser)
110{
111 if (!fWebWindow)
112 return;
113
114 std::string user_args = "";
115 if (!GetShowHierarchy())
116 user_args = "{ nobrowser: true }";
117 else if (GetShowColumns())
118 user_args = "{ show_columns: true }";
119 fWebWindow->SetUserArgs(user_args);
120
121 if (args.GetWidgetKind().empty())
122 const_cast<RWebDisplayArgs *>(&args)->SetWidgetKind("RGeomViewer");
123
124 if ((fWebWindow->NumConnections(true) == 0) || always_start_new_browser)
125 fWebWindow->Show(args);
126 else
127 Update();
128}
129
130//////////////////////////////////////////////////////////////////////////////////////////////
131/// Return web window address (name) used for geometry viewer
132
133std::string RGeomViewer::GetWindowAddr() const
134{
135 return fWebWindow ? fWebWindow->GetAddr() : ""s;
136}
137
138//////////////////////////////////////////////////////////////////////////////////////////////
139/// Return web window URL which can be used for connection
140/// See \ref ROOT::RWebWindow::GetUrl docu for more details
141
142std::string RGeomViewer::GetWindowUrl(bool remote)
143{
144 return fWebWindow ? fWebWindow->GetUrl(remote) : ""s;
145}
146
147//////////////////////////////////////////////////////////////////////////////////////////////
148/// Update geometry drawings in all web displays
149
151{
153
154 // update hierarchy
155 if (fWebHierarchy)
156 fWebHierarchy->Update();
157
158 SendGeometry(0);
159}
160
161//////////////////////////////////////////////////////////////////////////////////////////////
162/// convert JSON into stack array
163
164std::vector<int> RGeomViewer::GetStackFromJson(const std::string &json, bool node_ids)
165{
166 std::vector<int> *stack{nullptr}, res;
167
168 if (TBufferJSON::FromJSON(stack, json.c_str())) {
169 if (node_ids)
170 res = fDesc.MakeStackByIds(*stack);
171 else
172 res = *stack;
173 delete stack;
174 } else {
175 R__LOG_ERROR(RGeomLog()) << "Fail convert " << json << " into vector<int>";
176 }
177
178 return res;
179}
180
181//////////////////////////////////////////////////////////////////////////////////////////////
182/// Send data for principal geometry draw
183/// Should be used when essential settings were changed in geometry description
184
185void RGeomViewer::SendGeometry(unsigned connid, bool first_time)
186{
187 if (!fDesc.HasDrawData())
189
190 // updates search data when necessary
192
193 auto json0 = fDesc.GetDrawJson();
194 auto json1 = fDesc.GetSearchJson();
195
196 R__LOG_DEBUG(0, RGeomLog()) << "Produce geometry JSON len: " << json0.length();
197
198 if (!fWebWindow)
199 return;
200
201 // for the first time always send full drawing
202 if (first_time || json1.empty())
203 fWebWindow->Send(connid, json0);
204 else
205 fWebWindow->Send(connid, json1);
206}
207
208//////////////////////////////////////////////////////////////////////////////////////////////
209/// Configures draw option for geometry
210/// Normally has effect before first drawing of the geometry
211/// When geometry displayed, only "axis" and "rotate" options are updated
212
213void RGeomViewer::SetDrawOptions(const std::string &opt)
214{
216
217 unsigned connid = fWebWindow ? fWebWindow->GetConnectionId() : 0;
218 if (connid)
219 fWebWindow->Send(connid, "DROPT:"s + opt);
220}
221
222//////////////////////////////////////////////////////////////////////////////////////////////
223/// Produce PNG image of the geometry
224/// If web-browser is shown and drawing completed, image is requested from the browser.
225/// In this case method executed asynchronously - it returns immediately and image will stored shortly afterwards when
226/// received from the client Height and width parameters are ignored in that case and derived from actual drawing size
227/// in the browser. Another possibility is to invoke headless browser, providing positive width and height parameter
228/// explicitely
229///
230
231void RGeomViewer::SaveImage(const std::string &fname, int width, int height)
232{
233 unsigned connid = fWebWindow ? fWebWindow->GetConnectionId() : 0;
234
235 if (connid && (width <= 0) && (height <= 0)) {
236 fWebWindow->Send(connid, "IMAGE:"s + fname);
237 } else {
238 if (width <= 0)
239 width = 800;
240 if (height <= 0)
241 height = width;
242
243 if (!fDesc.HasDrawData())
245
246 std::string json = fDesc.GetDrawJson();
247 if (json.find("GDRAW:") != 0) {
248 printf("GDRAW missing!!!!\n");
249 return;
250 }
251 json.erase(0, 6);
252
253 RWebDisplayHandle::ProduceImage(fname, json, width, height, "/js/files/geom_batch.htm");
254 }
255}
256
257//////////////////////////////////////////////////////////////////////////////////////////////
258/// Process data from client
259
260void RGeomViewer::WebWindowCallback(unsigned connid, const std::string &arg)
261{
262 if (arg == "GETDRAW") {
263
264 SendGeometry(connid, true);
265
266 } else if (arg == "QUIT_ROOT") {
267
268 fWebWindow->TerminateROOT();
269
270 } else if (arg.compare(0, 9, "HCHANNEL:") == 0) {
271
272 int chid = std::stoi(arg.substr(9));
273
274 if (!fWebHierarchy)
275 fWebHierarchy = std::make_shared<RGeomHierarchy>(fDesc);
276 fWebHierarchy->Show({fWebWindow, connid, chid});
277
278 } else if (arg.compare(0, 4, "GET:") == 0) {
279 // provide exact shape
280
281 auto stack = GetStackFromJson(arg.substr(4));
282
283 auto nodeid = fDesc.FindNodeId(stack);
284
285 std::string json{"SHAPE:"};
286
288
289 fWebWindow->Send(connid, json);
290
291 } else if (arg.compare(0, 10, "HIGHLIGHT:") == 0) {
292 auto stack = TBufferJSON::FromJSON<std::vector<int>>(arg.substr(10));
293 if (stack && fDesc.SetHighlightedItem(*stack))
294 fDesc.IssueSignal(this, "HighlightItem");
295 } else if (arg.compare(0, 6, "IMAGE:") == 0) {
296 auto separ = arg.find("::", 6);
297 if (separ == std::string::npos)
298 return;
299
300 std::string fname = arg.substr(6, separ - 6);
301 if (fname.empty()) {
302 int cnt = 0;
303 do {
304 fname = "geometry"s;
305 if (cnt++ > 0)
306 fname += std::to_string(cnt);
307 fname += ".png"s;
308 } while (!gSystem->AccessPathName(fname.c_str()));
309 }
310
311 TString binary = TBase64::Decode(arg.c_str() + separ + 2);
312
313 std::ofstream ofs(fname);
314 ofs.write(binary.Data(), binary.Length());
315 ofs.close();
316
317 printf("Image file %s size %d has been created\n", fname.c_str(), (int)binary.Length());
318
319 } else if (arg.compare(0, 4, "CFG:") == 0) {
320
321 if (fDesc.ChangeConfiguration(arg.substr(4)))
322 SendGeometry(connid);
323
324 } else if (arg == "RELOAD") {
325
326 SendGeometry(connid);
327
328 } else if (arg.compare(0, 9, "ACTIVATE:") == 0) {
329 fDesc.SetActiveItem(arg.substr(9));
330 fDesc.IssueSignal(this, "ActiveItem");
331 } else if (arg.compare(0, 11, "INFOACTIVE:") == 0) {
332 fInfoActive = (arg.substr(11) == "true");
333 } else if (arg.compare(0, 11, "HIDE_ITEMS:") == 0) {
334 auto items = TBufferJSON::FromJSON<std::vector<std::string>>(arg.substr(11));
335 bool changed = false;
336 if (items)
337 for (auto &itemname : *items)
338 if (fDesc.SetPhysNodeVisibility(itemname, false))
339 changed = true;
340 if (changed) {
341 SendGeometry(connid);
342 fDesc.IssueSignal(this, "NodeVisibility");
343 }
344 } else if (arg == "SAVEMACRO") {
345 SaveAsMacro("viewer.cxx");
346 }
347}
348
349//////////////////////////////////////////////////////////////////////////////////////////////
350/// Process disconnect event
351/// Clear cache data and dependent connections
352
354{
355 fWebHierarchy.reset();
356
358
359 fInfoActive = false;
360}
361
362//////////////////////////////////////////////////////////////////////////////////////////////
363/// Process signal from geom description when it changed by any means
364
365void RGeomViewer::ProcessSignal(const std::string &kind)
366{
367 if ((kind == "SelectTop") || (kind == "NodeVisibility")) {
368 SendGeometry();
369 } else if (kind == "ChangeSearch") {
370 auto json = fDesc.GetSearchJson();
371 if (json.empty())
372 json = "CLRSCH";
373 if (fWebWindow)
374 fWebWindow->Send(0, json);
375 } else if (kind == "ClearSearch") {
376 if (fWebWindow)
377 fWebWindow->Send(0, "CLRSCH"); // 6 letters
378 } else if (kind == "HighlightItem") {
379 auto stack = fDesc.GetHighlightedItem();
380 if (fWebWindow)
381 fWebWindow->Send(0, "HIGHL:"s + TBufferJSON::ToJSON(&stack).Data());
382 } else if (kind == "ClickItem") {
383 if (fInfoActive) {
384 auto stack = fDesc.GetClickedItem();
385 auto info = fDesc.MakeNodeInfo(stack);
386 if (info && fWebWindow)
387 fWebWindow->Send(
388 0, "NINFO:"s +
390 }
391 }
392}
393
394//////////////////////////////////////////////////////////////////////////////////////////////
395/// Save viewer configuration as macro
396
397void RGeomViewer::SaveAsMacro(const std::string &fname)
398{
399 std::ofstream fs(fname);
400 if (!fs)
401 return;
402 std::string prefix = " ";
403
404 auto p = fname.find('.');
405 if (p > 0) {
406 fs << "void " << fname.substr(0, p) << "() { " << std::endl;
407 } else {
408 fs << "{" << std::endl;
409 }
410
411 if ((fDesc.GetNumNodes() < 2000) && fGeoManager) {
413 fs << prefix << "gGeoManager->SetVisLevel(" << fGeoManager->GetVisLevel() << ");" << std::endl;
414 } else {
415 fs << prefix << "// geometry is too large, please provide import like:" << std::endl << std::endl;
416 fs << prefix << "// TGeoManager::Import(\"filename.root\");" << std::endl;
417 }
418
419 fs << std::endl;
420
421 fs << prefix << "auto viewer = std::make_shared<ROOT::RGeomViewer>(gGeoManager";
422 if (!fSelectedVolume.empty())
423 fs << ", \"" << fSelectedVolume << "\"";
424 fs << ");" << std::endl;
425
426 fDesc.SavePrimitive(fs, "viewer->Description().");
427
428 fs << prefix << "viewer->SetShowHierarchy(" << (fShowHierarchy ? "true" : "false") << ");" << std::endl;
429 fs << prefix << "viewer->SetShowColumns(" << (fShowColumns ? "true" : "false") << ");" << std::endl;
430
431 fs << std::endl;
432
433 fs << prefix << "viewer->Show();" << std::endl << std::endl;
434
435 fs << prefix << "ROOT::Experimental::RDirectory::Heap().Add(\"geom_viewer\", viewer);" << std::endl;
436
437 fs << "}" << std::endl;
438}
439
440//////////////////////////////////////////////////////////////////////////////////////////////
441/// Set handle which will be cleared when connection is closed
442/// Must be called after window is shown
443
444void RGeomViewer::ClearOnClose(const std::shared_ptr<void> &handle)
445{
446 if (fWebWindow)
447 fWebWindow->SetClearOnClose(handle);
448}
nlohmann::json json
#define R__LOG_ERROR(...)
Definition RLogger.hxx:362
#define R__LOG_DEBUG(DEBUGLEVEL,...)
Definition RLogger.hxx:365
R__EXTERN TEnv * gEnv
Definition TEnv.h:170
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t width
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize fs
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t height
#define gROOT
Definition TROOT.h:406
R__EXTERN TSystem * gSystem
Definition TSystem.h:555
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::unique_ptr< RGeomNodeInfo > MakeNodeInfo(const std::vector< int > &stack)
Change visibility for specified element Returns true if changes was performed.
bool HasDrawData() const
Check if there is draw data available.
void IssueSignal(const void *handler, const std::string &kind)
Issue signal, which distributed on all handlers - excluding source handler.
bool SetHighlightedItem(const std::vector< int > &stack)
void SetPreferredOffline(bool on)
Set preference of offline operations.
void ClearCache()
Clear cached data, need to be clear when connection broken.
std::string GetDrawJson() const
std::vector< int > MakeStackByIds(const std::vector< int > &ids)
Creates stack for given array of ids, first element always should be 0.
void SavePrimitive(std::ostream &fs, const std::string &name)
Save geometry configuration as C++ macro.
bool SetActiveItem(const std::string &itemname)
int GetNumNodes() const
Number of unique nodes in the geometry.
void ProduceDrawData()
Collect all information required to draw geometry on the client This includes list of each visible no...
bool SetPhysNodeVisibility(const std::vector< std::string > &path, bool on=true)
Set visibility of physical node by path It overrules TGeo visibility flags - but only for specific ph...
void AddSignalHandler(const void *handler, RGeomSignalFunc_t func)
Add signal handler.
bool ChangeConfiguration(const std::string &json)
Change configuration by client Returns true if any parameter was really changed.
std::vector< int > GetClickedItem() const
void ProduceSearchData()
Produces search data if necessary.
void SetJsonComp(int comp=0)
Set JSON compression level for data transfer.
void SetBuildShapes(int lvl=1)
Instruct to build binary 3D model already on the server (true) or send TGeoShape as is to client,...
void SetDrawOptions(const std::string &opt="")
Set draw options as string for JSROOT TGeoPainter.
std::vector< int > GetHighlightedItem() const
void RemoveSignalHandler(const void *handler)
Remove signal handler.
int FindNodeId(const std::vector< int > &stack)
Returns nodeid for given stack array, returns -1 in case of failure.
std::string GetSearchJson() const
int GetJsonComp() const
Returns JSON compression level for data transfer.
void Build(TGeoManager *mgr, const std::string &volname="")
Collect information about geometry hierarchy into flat list like it done in JSROOT ClonedNodes....
RGeomViewer(TGeoManager *mgr=nullptr, const std::string &volname="")
constructor
void Show(const RWebDisplayArgs &args="", bool always_start_new_browser=false)
Show or update geometry in web window If web browser already started - just refresh drawing like "rel...
void SetGeometry(TGeoManager *mgr, const std::string &volname="")
assign new geometry to the viewer
std::vector< int > GetStackFromJson(const std::string &json, bool node_ids=false)
convert JSON into stack array
virtual ~RGeomViewer()
destructor
void SendGeometry(unsigned connid=0, bool first_time=false)
Send data for principal geometry draw Should be used when essential settings were changed in geometry...
std::string GetWindowAddr() const
Return web window address (name) used for geometry viewer.
std::shared_ptr< RGeomHierarchy > fWebHierarchy
! web handle for hierarchy part
void WebWindowDisconnect(unsigned connid)
Process disconnect event Clear cache data and dependent connections.
void ClearOnClose(const std::shared_ptr< void > &handle)
Set handle which will be cleared when connection is closed Must be called after window is shown.
bool fShowHierarchy
! if hierarchy visible by default
bool GetShowColumns() const
void SaveAsMacro(const std::string &fname)
Save viewer configuration as macro.
std::shared_ptr< RWebWindow > fWebWindow
! web window to show geometry
void Update()
Update geometry drawings in all web displays.
void ProcessSignal(const std::string &)
Process signal from geom description when it changed by any means.
bool GetShowHierarchy() const
Returns default hierarchy browser visibility.
void SetDrawOptions(const std::string &opt)
Configures draw option for geometry Normally has effect before first drawing of the geometry When geo...
TGeoManager * fGeoManager
! geometry to show
std::string GetWindowUrl(bool remote)
Return web window URL which can be used for connection See ROOT::RWebWindow::GetUrl docu for more det...
bool fShowColumns
! show columns in hierarchy
std::string fSelectedVolume
! name of selected volume
void WebWindowCallback(unsigned connid, const std::string &arg)
Process data from client.
bool fInfoActive
! true when info page active and node info need to be provided
void SetOnlyVolume(TGeoVolume *vol)
Draw only specified volume, special case when volume stored without valid geomanager.
void SelectVolume(const std::string &volname)
Select visible top volume, all other volumes will be disabled.
void SaveImage(const std::string &fname="geometry.png", int width=0, int height=0)
Produce PNG image of the geometry If web-browser is shown and drawing completed, image is requested f...
RGeomDescription fDesc
! geometry description, send to the client as first message
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
const std::string & GetWidgetKind() const
returns widget kind
static bool ProduceImage(const std::string &fname, const std::string &json, int width=800, int height=600, const char *batch_file=nullptr)
Produce image file using JSON data as source Invokes JSROOT drawing functionality in headless browser...
static std::shared_ptr< RWebWindow > Create()
Create new RWebWindow Using default RWebWindowsManager.
static TString Decode(const char *data)
Decode a base64 string date into a generic TString.
Definition TBase64.cxx:131
static TString ToJSON(const T *obj, Int_t compact=0, const char *member_name=nullptr)
Definition TBufferJSON.h:75
@ kSkipTypeInfo
do not store typenames in JSON
Definition TBufferJSON.h:48
@ kNoSpaces
no new lines plus remove all spaces around "," and ":" symbols
Definition TBufferJSON.h:39
@ kSameSuppression
zero suppression plus compress many similar values together
Definition TBufferJSON.h:45
static Bool_t FromJSON(T *&obj, const char *json)
Definition TBufferJSON.h:81
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:491
The manager class for any TGeo geometry.
Definition TGeoManager.h:44
Int_t GetVisLevel() const
Returns current depth to which geometry is drawn.
TGeoVolume * GetTopVolume() const
TGeoVolume, TGeoVolumeMulti, TGeoVolumeAssembly are the volume classes.
Definition TGeoVolume.h:43
void SavePrimitive(std::ostream &out, Option_t *option="") override
Save a primitive as a C++ statement(s) on output stream "out".
Basic string class.
Definition TString.h:139
Ssiz_t Length() const
Definition TString.h:417
const char * Data() const
Definition TString.h:376
virtual Bool_t AccessPathName(const char *path, EAccessMode mode=kFileExists)
Returns FALSE if one can access a file using the specified access mode.
Definition TSystem.cxx:1296
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
Experimental::RLogChannel & RGeomLog()
Log channel for Geomviewer diagnostics.
Definition RGeomData.cxx:49