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/** \class ROOT::RGeomViewer
34\ingroup webwidgets
35
36\brief Web-based %ROOT geometry viewer
37*/
38
39
40//////////////////////////////////////////////////////////////////////////////////////////////
41/// constructor
42
43RGeomViewer::RGeomViewer(TGeoManager *mgr, const std::string &volname)
44{
45 if (!gROOT->IsWebDisplayBatch()) {
47 fWebWindow->SetDefaultPage("file:rootui5sys/geom/index.html");
48
49 // this is call-back, invoked when message received via websocket
50 fWebWindow->SetDataCallBack([this](unsigned connid, const std::string &arg) { WebWindowCallback(connid, arg); });
51 fWebWindow->SetDisconnectCallBack([this](unsigned connid) { WebWindowDisconnect(connid); });
52
53 fWebWindow->SetGeometry(900, 700); // configure predefined window geometry
54 fWebWindow->SetConnLimit(0); // allow any connections numbers at the same time
55 fWebWindow->SetMaxQueueLength(30); // number of allowed entries in the window queue
56 }
57
58 fDesc.SetPreferredOffline(gEnv->GetValue("WebGui.PreferredOffline", 0) != 0);
59 fDesc.SetJsonComp(gEnv->GetValue("WebGui.JsonComp", TBufferJSON::kSkipTypeInfo + TBufferJSON::kNoSpaces));
60 fDesc.SetBuildShapes(gEnv->GetValue("WebGui.GeomBuildShapes", 1));
61
62 fDesc.AddSignalHandler(this, [this](const std::string &kind) { ProcessSignal(kind); });
63
64 if (mgr)
65 SetGeometry(mgr, volname);
66}
67
68//////////////////////////////////////////////////////////////////////////////////////////////
69/// destructor
70
72{
73 fDesc.RemoveSignalHandler(this);
74
75 if (fWebWindow)
76 fWebWindow->Reset();
77}
78
79//////////////////////////////////////////////////////////////////////////////////////////////
80/// assign new geometry to the viewer
81
82void RGeomViewer::SetGeometry(TGeoManager *mgr, const std::string &volname)
83{
84 fGeoManager = mgr;
85 fSelectedVolume = volname;
86
87 fDesc.Build(mgr, volname);
88
89 Update();
90}
91
92/////////////////////////////////////////////////////////////////////////////////
93/// Select visible top volume, all other volumes will be disabled
94
95void RGeomViewer::SelectVolume(const std::string &volname)
96{
97 if ((volname != fSelectedVolume) && fGeoManager)
98 SetGeometry(fGeoManager, volname);
99}
100
101/////////////////////////////////////////////////////////////////////////////////
102/// Draw only specified volume, special case when volume stored without valid geomanager
103
105{
106 fGeoManager = nullptr;
107 fSelectedVolume = "";
108
109 fDesc.Build(vol);
110
111 Update();
112}
113
114/////////////////////////////////////////////////////////////////////////////////
115/// Show or update geometry in web window
116/// If web browser already started - just refresh drawing like "reload" button does
117/// If no web window exists or \param always_start_new_browser configured, starts new window
118/// \param args arguments to display
119
120void RGeomViewer::Show(const RWebDisplayArgs &args, bool always_start_new_browser)
121{
122 if (!fWebWindow)
123 return;
124
125 std::string user_args = "";
126 if (!GetShowHierarchy())
127 user_args = "{ nobrowser: true }";
128 else if (GetShowColumns())
129 user_args = "{ show_columns: true }";
130 fWebWindow->SetUserArgs(user_args);
131
132 if (args.GetWidgetKind().empty())
133 const_cast<RWebDisplayArgs *>(&args)->SetWidgetKind("RGeomViewer");
134
135 if ((fWebWindow->NumConnections(true) == 0) || always_start_new_browser)
136 fWebWindow->Show(args);
137 else
138 Update();
139}
140
141//////////////////////////////////////////////////////////////////////////////////////////////
142/// Return web window address (name) used for geometry viewer
143
144std::string RGeomViewer::GetWindowAddr() const
145{
146 return fWebWindow ? fWebWindow->GetAddr() : ""s;
147}
148
149//////////////////////////////////////////////////////////////////////////////////////////////
150/// Return web window URL which can be used for connection
151/// See \ref ROOT::RWebWindow::GetUrl docu for more details
152
153std::string RGeomViewer::GetWindowUrl(bool remote)
154{
155 return fWebWindow ? fWebWindow->GetUrl(remote) : ""s;
156}
157
158//////////////////////////////////////////////////////////////////////////////////////////////
159/// Update geometry drawings in all web displays
160
162{
163 fDesc.ClearCache();
164
165 // update hierarchy
166 if (fWebHierarchy)
167 fWebHierarchy->Update();
168
169 if (fWebWindow && (fWebWindow->NumConnections() > 0))
170 SendGeometry();
171}
172
173//////////////////////////////////////////////////////////////////////////////////////////////
174/// convert JSON into stack array
175
176std::vector<int> RGeomViewer::GetStackFromJson(const std::string &json, bool node_ids)
177{
178 std::vector<int> *stack{nullptr}, res;
179
180 if (TBufferJSON::FromJSON(stack, json.c_str())) {
181 if (node_ids)
182 res = fDesc.MakeStackByIds(*stack);
183 else
184 res = *stack;
185 delete stack;
186 } else {
187 R__LOG_ERROR(RGeomLog()) << "Fail convert " << json << " into vector<int>";
188 }
189
190 return res;
191}
192
193//////////////////////////////////////////////////////////////////////////////////////////////
194/// Send data for principal geometry draw
195/// Should be used when essential settings were changed in geometry description
196
197void RGeomViewer::SendGeometry(unsigned connid, bool first_time)
198{
199 if (!fDesc.HasDrawData())
200 fDesc.ProduceDrawData();
201
202 // updates search data when necessary
203 fDesc.ProduceSearchData();
204
205 auto json0 = fDesc.GetDrawJson();
206 auto json1 = fDesc.GetSearchJson();
207
208 R__LOG_DEBUG(0, RGeomLog()) << "Produce geometry JSON len: " << json0.length();
209
210 if (!fWebWindow)
211 return;
212
213 // for the first time always send full drawing
214 if (first_time || json1.empty())
215 fWebWindow->Send(connid, json0);
216 else
217 fWebWindow->Send(connid, json1);
218}
219
220//////////////////////////////////////////////////////////////////////////////////////////////
221/// Configures draw option for geometry
222/// Normally has effect before first drawing of the geometry
223/// When geometry displayed, only "axis" and "rotate" options are updated
224
225void RGeomViewer::SetDrawOptions(const std::string &opt)
226{
227 fDesc.SetDrawOptions(opt);
228
229 unsigned connid = fWebWindow ? fWebWindow->GetConnectionId() : 0;
230 if (connid)
231 fWebWindow->Send(connid, "DROPT:"s + opt);
232}
233
234//////////////////////////////////////////////////////////////////////////////////////////////
235/// Produce PNG image of the geometry
236/// If web-browser is shown and drawing completed, image is requested from the browser.
237/// In this case method executed asynchronously - it returns immediately and image will stored shortly afterwards when
238/// received from the client Height and width parameters are ignored in that case and derived from actual drawing size
239/// in the browser. Another possibility is to invoke headless browser, providing positive width and height parameter
240/// explicitly
241
242void RGeomViewer::SaveImage(const std::string &fname, int width, int height)
243{
244 unsigned connid = fWebWindow ? fWebWindow->GetConnectionId() : 0;
245
246 if (connid && (width <= 0) && (height <= 0)) {
247 fWebWindow->Send(connid, "IMAGE:"s + fname);
248 } else {
249 if (width <= 0)
250 width = 800;
251 if (height <= 0)
252 height = width;
253
254 if (!fDesc.HasDrawData())
255 fDesc.ProduceDrawData();
256
257 std::string json = fDesc.GetDrawJson();
258 if (json.find("GDRAW:") != 0) {
259 printf("GDRAW missing!!!!\n");
260 return;
261 }
262 json.erase(0, 6);
263
264 RWebDisplayHandle::ProduceImage(fname, json, width, height, "/js/files/geom_batch.htm");
265 }
266}
267
268//////////////////////////////////////////////////////////////////////////////////////////////
269/// Process data from client
270
271void RGeomViewer::WebWindowCallback(unsigned connid, const std::string &arg)
272{
273 if (arg == "GETDRAW") {
274
275 SendGeometry(connid, true);
276
277 } else if (arg == "QUIT_ROOT") {
278
279 fWebWindow->TerminateROOT();
280
281 } else if (arg.compare(0, 9, "HCHANNEL:") == 0) {
282
283 int chid = std::stoi(arg.substr(9));
284
285 if (!fWebHierarchy)
286 fWebHierarchy = std::make_shared<RGeomHierarchy>(fDesc);
287 fWebHierarchy->Show({fWebWindow, connid, chid});
288
289 } else if (arg.compare(0, 4, "GET:") == 0) {
290 // provide exact shape
291
292 auto stack = GetStackFromJson(arg.substr(4));
293
294 auto nodeid = fDesc.FindNodeId(stack);
295
296 std::string json{"SHAPE:"};
297
298 fDesc.ProduceDrawingFor(nodeid, json);
299
300 fWebWindow->Send(connid, json);
301
302 } else if (arg.compare(0, 10, "HIGHLIGHT:") == 0) {
303 auto stack = TBufferJSON::FromJSON<std::vector<int>>(arg.substr(10));
304 if (stack && fDesc.SetHighlightedItem(*stack))
305 fDesc.IssueSignal(this, "HighlightItem");
306 } else if (arg.compare(0, 6, "IMAGE:") == 0) {
307 auto separ = arg.find("::", 6);
308 if (separ == std::string::npos)
309 return;
310
311 std::string fname = arg.substr(6, separ - 6);
312 if (fname.empty()) {
313 int cnt = 0;
314 do {
315 fname = "geometry"s;
316 if (cnt++ > 0)
317 fname += std::to_string(cnt);
318 fname += ".png"s;
319 } while (!gSystem->AccessPathName(fname.c_str()));
320 }
321
322 TString binary = TBase64::Decode(arg.c_str() + separ + 2);
323
324 std::ofstream ofs(fname);
325 ofs.write(binary.Data(), binary.Length());
326 ofs.close();
327
328 printf("Image file %s size %d has been created\n", fname.c_str(), (int)binary.Length());
329
330 } else if (arg.compare(0, 4, "CFG:") == 0) {
331
332 if (fDesc.ChangeConfiguration(arg.substr(4)))
333 SendGeometry(connid);
334
335 } else if (arg == "RELOAD") {
336
337 SendGeometry(connid);
338
339 } else if (arg.compare(0, 9, "ACTIVATE:") == 0) {
340 fDesc.SetActiveItem(arg.substr(9));
341 fDesc.IssueSignal(this, "ActiveItem");
342 } else if (arg.compare(0, 11, "INFOACTIVE:") == 0) {
343 fInfoActive = (arg.substr(11) == "true");
344 } else if (arg.compare(0, 11, "HIDE_ITEMS:") == 0) {
345 auto items = TBufferJSON::FromJSON<std::vector<std::string>>(arg.substr(11));
346 bool changed = false;
347 if (items)
348 for (auto &itemname : *items)
349 if (fDesc.SetPhysNodeVisibility(itemname, false))
350 changed = true;
351 if (changed) {
352 SendGeometry(connid);
353 fDesc.IssueSignal(this, "NodeVisibility");
354 }
355 } else if (arg == "SAVEMACRO") {
356 SaveAsMacro("viewer.cxx");
357 }
358}
359
360//////////////////////////////////////////////////////////////////////////////////////////////
361/// Process disconnect event
362/// Clear cache data and dependent connections
363
365{
366 fWebHierarchy.reset();
367
368 fDesc.ClearCache();
369
370 fInfoActive = false;
371}
372
373//////////////////////////////////////////////////////////////////////////////////////////////
374/// Process signal from geom description when it changed by any means
375
376void RGeomViewer::ProcessSignal(const std::string &kind)
377{
378 if ((kind == "SelectTop") || (kind == "NodeVisibility")) {
379 SendGeometry();
380 } else if (kind == "ChangeSearch") {
381 auto json = fDesc.GetSearchJson();
382 if (json.empty())
383 json = "CLRSCH";
384 if (fWebWindow)
385 fWebWindow->Send(0, json);
386 } else if (kind == "ClearSearch") {
387 if (fWebWindow)
388 fWebWindow->Send(0, "CLRSCH"); // 6 letters
389 } else if (kind == "HighlightItem") {
390 auto stack = fDesc.GetHighlightedItem();
391 if (fWebWindow)
392 fWebWindow->Send(0, "HIGHL:"s + TBufferJSON::ToJSON(&stack).Data());
393 } else if (kind == "ClickItem") {
394 if (fInfoActive) {
395 auto stack = fDesc.GetClickedItem();
396 auto info = fDesc.MakeNodeInfo(stack);
397 if (info && fWebWindow)
398 fWebWindow->Send(
399 0, "NINFO:"s +
400 TBufferJSON::ToJSON(info.get(), (fDesc.GetJsonComp() % 5) + TBufferJSON::kSameSuppression).Data());
401 }
402 }
403}
404
405//////////////////////////////////////////////////////////////////////////////////////////////
406/// Save viewer configuration as macro
407
408void RGeomViewer::SaveAsMacro(const std::string &fname)
409{
410 std::ofstream fs(fname);
411 if (!fs)
412 return;
413 std::string prefix = " ";
414
415 fs << "std::shared_ptr<ROOT::RGeomViewer> rgeom_viewer;\n\n";
416
417 auto p = fname.find('.');
418 if (p > 0)
419 fs << "void " << fname.substr(0, p) << "()\n";
420 fs << "{\n";
421
422 if ((fDesc.GetNumNodes() < 2000) && fGeoManager) {
423 fGeoManager->GetTopVolume()->SavePrimitive(fs);
424 fs << prefix << "gGeoManager->SetVisLevel(" << fGeoManager->GetVisLevel() << ");\n";
425 } else {
426 fs << prefix << "// geometry is too large, please provide import like:\n";
427 fs << prefix << "// TGeoManager::Import(\"filename.root\");\n";
428 }
429
430 fs << prefix << "\n";
431
432 fs << prefix << "rgeom_viewer = std::make_shared<ROOT::RGeomViewer>(gGeoManager";
433 if (!fSelectedVolume.empty())
434 fs << ", \"" << fSelectedVolume << "\"";
435 fs << ");\n";
436
437 fDesc.SavePrimitive(fs, "rgeom_viewer->Description().");
438
439 fs << prefix << "rgeom_viewer->SetShowHierarchy(" << (fShowHierarchy ? "true" : "false") << ");\n";
440 fs << prefix << "rgeom_viewer->SetShowColumns(" << (fShowColumns ? "true" : "false") << ");\n";
441
442 fs << prefix << "\n";
443
444 fs << prefix << "rgeom_viewer->Show();\n";
445
446 fs << "}\n";
447
448 printf("Macro %s has been created\n", fname.c_str());
449}
450
451//////////////////////////////////////////////////////////////////////////////////////////////
452/// Set handle which will be cleared when connection is closed
453/// Must be called after window is shown
454
455void RGeomViewer::ClearOnClose(const std::shared_ptr<void> &handle)
456{
457 if (fWebWindow)
458 fWebWindow->SetClearOnClose(handle);
459}
#define R__LOG_ERROR(...)
Definition RLogger.hxx:356
#define R__LOG_DEBUG(DEBUGLEVEL,...)
Definition RLogger.hxx:359
externTEnv * gEnv
Definition TEnv.h:170
#define gROOT
Definition TROOT.h:417
externTSystem * gSystem
Definition TSystem.h:582
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:130
static TString ToJSON(const T *obj, Int_t compact=0, const char *member_name=nullptr)
Definition TBufferJSON.h:77
@ 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:85
The manager class for any TGeo geometry.
Definition TGeoManager.h:46
TGeoVolume, TGeoVolumeMulti, TGeoVolumeAssembly are the volume classes.
Definition TGeoVolume.h:43
Basic string class.
Definition TString.h:138
Ssiz_t Length() const
Definition TString.h:425
const char * Data() const
Definition TString.h:384
RLogChannel & RGeomLog()
Log channel for Geomviewer diagnostics.
Definition RGeomData.cxx:49