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);
51 fDesc.SetJsonComp(gEnv->GetValue("WebGui.JsonComp", TBufferJSON::kSkipTypeInfo + TBufferJSON::kNoSpaces));
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{
65 fDesc.RemoveSignalHandler(this);
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{
152 fDesc.ClearCache();
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())
188 fDesc.ProduceDrawData();
189
190 // updates search data when necessary
191 fDesc.ProduceSearchData();
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{
215 fDesc.SetDrawOptions(opt);
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())
244 fDesc.ProduceDrawData();
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
287 fDesc.ProduceDrawingFor(nodeid, json);
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
357 fDesc.ClearCache();
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 +
389 TBufferJSON::ToJSON(info.get(), (fDesc.GetJsonComp() % 5) + TBufferJSON::kSameSuppression).Data());
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) {
412 fGeoManager->GetTopVolume()->SavePrimitive(fs);
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}
#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
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:414
R__EXTERN TSystem * gSystem
Definition TSystem.h:566
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
The manager class for any TGeo geometry.
Definition TGeoManager.h:44
TGeoVolume, TGeoVolumeMulti, TGeoVolumeAssembly are the volume classes.
Definition TGeoVolume.h:43
Basic string class.
Definition TString.h:139
Ssiz_t Length() const
Definition TString.h:417
const char * Data() const
Definition TString.h:376
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