Logo ROOT  
Reference Guide
 
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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
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);
60 fDesc.SetBuildShapes(gEnv->GetValue("WebGui.GeomBuildShapes", 1));
61
62 fDesc.AddSignalHandler(this, [this](const std::string &kind) { ProcessSignal(kind); });
63
64 if (mgr)
66}
67
68//////////////////////////////////////////////////////////////////////////////////////////////
69/// destructor
70
72{
74
75 if (fWebWindow)
76 fWebWindow->Reset();
77}
78
79//////////////////////////////////////////////////////////////////////////////////////////////
80/// assign new geometry to the viewer
81
83{
86
88
89 Update();
90}
91
92/////////////////////////////////////////////////////////////////////////////////
93/// Select visible top volume, all other volumes will be disabled
94
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
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
154{
155 return fWebWindow ? fWebWindow->GetUrl(remote) : ""s;
156}
157
158//////////////////////////////////////////////////////////////////////////////////////////////
159/// Update geometry drawings in all web displays
160
162{
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())
201
202 // updates search data when necessary
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{
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())
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
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)
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
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 +
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 auto p = fname.find('.');
416 if (p > 0) {
417 fs << "void " << fname.substr(0, p) << "() { " << std::endl;
418 } else {
419 fs << "{" << std::endl;
420 }
421
422 if ((fDesc.GetNumNodes() < 2000) && fGeoManager) {
424 fs << prefix << "gGeoManager->SetVisLevel(" << fGeoManager->GetVisLevel() << ");" << std::endl;
425 } else {
426 fs << prefix << "// geometry is too large, please provide import like:" << std::endl << std::endl;
427 fs << prefix << "// TGeoManager::Import(\"filename.root\");" << std::endl;
428 }
429
430 fs << std::endl;
431
432 fs << prefix << "auto viewer = std::make_shared<ROOT::RGeomViewer>(gGeoManager";
433 if (!fSelectedVolume.empty())
434 fs << ", \"" << fSelectedVolume << "\"";
435 fs << ");" << std::endl;
436
437 fDesc.SavePrimitive(fs, "viewer->Description().");
438
439 fs << prefix << "viewer->SetShowHierarchy(" << (fShowHierarchy ? "true" : "false") << ");" << std::endl;
440 fs << prefix << "viewer->SetShowColumns(" << (fShowColumns ? "true" : "false") << ");" << std::endl;
441
442 fs << std::endl;
443
444 fs << prefix << "viewer->Show();" << std::endl << std::endl;
445
446 fs << prefix << "ROOT::Experimental::RDirectory::Heap().Add(\"geom_viewer\", viewer);" << std::endl;
447
448 fs << "}" << std::endl;
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}
nlohmann::json json
#define R__LOG_ERROR(...)
Definition RLogger.hxx:357
#define R__LOG_DEBUG(DEBUGLEVEL,...)
Definition RLogger.hxx:360
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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:572
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
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...
RLogChannel & RGeomLog()
Log channel for Geomviewer diagnostics.
Definition RGeomData.cxx:49