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