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 URL address of web window used for geometry viewer
132
133std::string RGeomViewer::GetWindowAddr() const
134{
135 return fWebWindow ? fWebWindow->GetAddr() : ""s;
136}
137
138//////////////////////////////////////////////////////////////////////////////////////////////
139/// Update geometry drawings in all web displays
140
142{
144
145 // update hierarchy
146 if (fWebHierarchy)
147 fWebHierarchy->Update();
148
149 SendGeometry(0);
150}
151
152//////////////////////////////////////////////////////////////////////////////////////////////
153/// convert JSON into stack array
154
155std::vector<int> RGeomViewer::GetStackFromJson(const std::string &json, bool node_ids)
156{
157 std::vector<int> *stack{nullptr}, res;
158
159 if (TBufferJSON::FromJSON(stack, json.c_str())) {
160 if (node_ids)
161 res = fDesc.MakeStackByIds(*stack);
162 else
163 res = *stack;
164 delete stack;
165 } else {
166 R__LOG_ERROR(RGeomLog()) << "Fail convert " << json << " into vector<int>";
167 }
168
169 return res;
170}
171
172//////////////////////////////////////////////////////////////////////////////////////////////
173/// Send data for principal geometry draw
174/// Should be used when essential settings were changed in geometry description
175
176void RGeomViewer::SendGeometry(unsigned connid, bool first_time)
177{
178 if (!fDesc.HasDrawData())
180
181 // updates search data when necessary
183
184 auto json0 = fDesc.GetDrawJson();
185 auto json1 = fDesc.GetSearchJson();
186
187 R__LOG_DEBUG(0, RGeomLog()) << "Produce geometry JSON len: " << json0.length();
188
189 if (!fWebWindow)
190 return;
191
192 // for the first time always send full drawing
193 if (first_time || json1.empty())
194 fWebWindow->Send(connid, json0);
195 else
196 fWebWindow->Send(connid, json1);
197}
198
199//////////////////////////////////////////////////////////////////////////////////////////////
200/// Configures draw option for geometry
201/// Normally has effect before first drawing of the geometry
202/// When geometry displayed, only "axis" and "rotate" options are updated
203
204void RGeomViewer::SetDrawOptions(const std::string &opt)
205{
207
208 unsigned connid = fWebWindow ? fWebWindow->GetConnectionId() : 0;
209 if (connid)
210 fWebWindow->Send(connid, "DROPT:"s + opt);
211}
212
213//////////////////////////////////////////////////////////////////////////////////////////////
214/// Produce PNG image of the geometry
215/// If web-browser is shown and drawing completed, image is requested from the browser.
216/// In this case method executed asynchronously - it returns immediately and image will stored shortly afterwards when
217/// received from the client Height and width parameters are ignored in that case and derived from actual drawing size
218/// in the browser. Another possibility is to invoke headless browser, providing positive width and height parameter
219/// explicitely
220///
221
222void RGeomViewer::SaveImage(const std::string &fname, int width, int height)
223{
224 unsigned connid = fWebWindow ? fWebWindow->GetConnectionId() : 0;
225
226 if (connid && (width <= 0) && (height <= 0)) {
227 fWebWindow->Send(connid, "IMAGE:"s + fname);
228 } else {
229 if (width <= 0)
230 width = 800;
231 if (height <= 0)
232 height = width;
233
234 if (!fDesc.HasDrawData())
236
237 std::string json = fDesc.GetDrawJson();
238 if (json.find("GDRAW:") != 0) {
239 printf("GDRAW missing!!!!\n");
240 return;
241 }
242 json.erase(0, 6);
243
244 RWebDisplayHandle::ProduceImage(fname, json, width, height, "/js/files/geom_batch.htm");
245 }
246}
247
248//////////////////////////////////////////////////////////////////////////////////////////////
249/// Process data from client
250
251void RGeomViewer::WebWindowCallback(unsigned connid, const std::string &arg)
252{
253 if (arg == "GETDRAW") {
254
255 SendGeometry(connid, true);
256
257 } else if (arg == "QUIT_ROOT") {
258
259 fWebWindow->TerminateROOT();
260
261 } else if (arg.compare(0, 9, "HCHANNEL:") == 0) {
262
263 int chid = std::stoi(arg.substr(9));
264
265 if (!fWebHierarchy)
266 fWebHierarchy = std::make_shared<RGeomHierarchy>(fDesc);
267 fWebHierarchy->Show({fWebWindow, connid, chid});
268
269 } else if (arg.compare(0, 4, "GET:") == 0) {
270 // provide exact shape
271
272 auto stack = GetStackFromJson(arg.substr(4));
273
274 auto nodeid = fDesc.FindNodeId(stack);
275
276 std::string json{"SHAPE:"};
277
279
280 fWebWindow->Send(connid, json);
281
282 } else if (arg.compare(0, 10, "HIGHLIGHT:") == 0) {
283 auto stack = TBufferJSON::FromJSON<std::vector<int>>(arg.substr(10));
284 if (stack && fDesc.SetHighlightedItem(*stack))
285 fDesc.IssueSignal(this, "HighlightItem");
286 } else if (arg.compare(0, 6, "IMAGE:") == 0) {
287 auto separ = arg.find("::", 6);
288 if (separ == std::string::npos)
289 return;
290
291 std::string fname = arg.substr(6, separ - 6);
292 if (fname.empty()) {
293 int cnt = 0;
294 do {
295 fname = "geometry"s;
296 if (cnt++ > 0)
297 fname += std::to_string(cnt);
298 fname += ".png"s;
299 } while (!gSystem->AccessPathName(fname.c_str()));
300 }
301
302 TString binary = TBase64::Decode(arg.c_str() + separ + 2);
303
304 std::ofstream ofs(fname);
305 ofs.write(binary.Data(), binary.Length());
306 ofs.close();
307
308 printf("Image file %s size %d has been created\n", fname.c_str(), (int)binary.Length());
309
310 } else if (arg.compare(0, 4, "CFG:") == 0) {
311
312 if (fDesc.ChangeConfiguration(arg.substr(4)))
313 SendGeometry(connid);
314
315 } else if (arg == "RELOAD") {
316
317 SendGeometry(connid);
318
319 } else if (arg.compare(0, 9, "ACTIVATE:") == 0) {
320 fDesc.SetActiveItem(arg.substr(9));
321 fDesc.IssueSignal(this, "ActiveItem");
322 } else if (arg.compare(0, 11, "INFOACTIVE:") == 0) {
323 fInfoActive = (arg.substr(11) == "true");
324 } else if (arg.compare(0, 11, "HIDE_ITEMS:") == 0) {
325 auto items = TBufferJSON::FromJSON<std::vector<std::string>>(arg.substr(11));
326 bool changed = false;
327 if (items)
328 for (auto &itemname : *items)
329 if (fDesc.SetPhysNodeVisibility(itemname, false))
330 changed = true;
331 if (changed) {
332 SendGeometry(connid);
333 fDesc.IssueSignal(this, "NodeVisibility");
334 }
335 } else if (arg == "SAVEMACRO") {
336 SaveAsMacro("viewer.cxx");
337 }
338}
339
340//////////////////////////////////////////////////////////////////////////////////////////////
341/// Process disconnect event
342/// Clear cache data and dependent connections
343
345{
346 fWebHierarchy.reset();
347
349
350 fInfoActive = false;
351}
352
353//////////////////////////////////////////////////////////////////////////////////////////////
354/// Process signal from geom description when it changed by any means
355
356void RGeomViewer::ProcessSignal(const std::string &kind)
357{
358 if ((kind == "SelectTop") || (kind == "NodeVisibility")) {
359 SendGeometry();
360 } else if (kind == "ChangeSearch") {
361 auto json = fDesc.GetSearchJson();
362 if (json.empty())
363 json = "CLRSCH";
364 if (fWebWindow)
365 fWebWindow->Send(0, json);
366 } else if (kind == "ClearSearch") {
367 if (fWebWindow)
368 fWebWindow->Send(0, "CLRSCH"); // 6 letters
369 } else if (kind == "HighlightItem") {
370 auto stack = fDesc.GetHighlightedItem();
371 if (fWebWindow)
372 fWebWindow->Send(0, "HIGHL:"s + TBufferJSON::ToJSON(&stack).Data());
373 } else if (kind == "ClickItem") {
374 if (fInfoActive) {
375 auto stack = fDesc.GetClickedItem();
376 auto info = fDesc.MakeNodeInfo(stack);
377 if (info && fWebWindow)
378 fWebWindow->Send(
379 0, "NINFO:"s +
381 }
382 }
383}
384
385//////////////////////////////////////////////////////////////////////////////////////////////
386/// Save viewer configuration as macro
387
388void RGeomViewer::SaveAsMacro(const std::string &fname)
389{
390 std::ofstream fs(fname);
391 if (!fs)
392 return;
393 std::string prefix = " ";
394
395 auto p = fname.find(".");
396 if (p > 0) {
397 fs << "void " << fname.substr(0, p) << "() { " << std::endl;
398 } else {
399 fs << "{" << std::endl;
400 }
401
402 if ((fDesc.GetNumNodes() < 2000) && fGeoManager) {
404 fs << prefix << "gGeoManager->SetVisLevel(" << fGeoManager->GetVisLevel() << ");" << std::endl;
405 } else {
406 fs << prefix << "// geometry is too large, please provide import like:" << std::endl << std::endl;
407 fs << prefix << "// TGeoManager::Import(\"filename.root\");" << std::endl;
408 }
409
410 fs << std::endl;
411
412 fs << prefix << "auto viewer = std::make_shared<ROOT::RGeomViewer>(gGeoManager";
413 if (!fSelectedVolume.empty())
414 fs << ", \"" << fSelectedVolume << "\"";
415 fs << ");" << std::endl;
416
417 fDesc.SavePrimitive(fs, "viewer->Description().");
418
419 fs << prefix << "viewer->SetShowHierarchy(" << (fShowHierarchy ? "true" : "false") << ");" << std::endl;
420 fs << prefix << "viewer->SetShowColumns(" << (fShowColumns ? "true" : "false") << ");" << std::endl;
421
422 fs << std::endl;
423
424 fs << prefix << "viewer->Show();" << std::endl << std::endl;
425
426 fs << prefix << "ROOT::Experimental::RDirectory::Heap().Add(\"geom_viewer\", viewer);" << std::endl;
427
428 fs << "}" << std::endl;
429}
430
431//////////////////////////////////////////////////////////////////////////////////////////////
432/// Set handle which will be cleared when connection is closed
433/// Must be called after window is shown
434
435void RGeomViewer::ClearOnClose(const std::shared_ptr<void> &handle)
436{
437 if (fWebWindow)
438 fWebWindow->SetClearOnClose(handle);
439}
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:407
R__EXTERN TSystem * gSystem
Definition TSystem.h:560
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 URL address of web window 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
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:421
const char * Data() const
Definition TString.h:380
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:1283
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
Experimental::RLogChannel & RGeomLog()
Log channel for Geomviewer diagnostics.
Definition RGeomData.cxx:49