Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RGeomViewer.cxx
Go to the documentation of this file.
1// @(#)root/eve7:$Id$
2// Author: Sergey Linev, 13.12.2018
3
4/*************************************************************************
5 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#include <ROOT/RGeomViewer.hxx>
13
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 "TEnv.h"
21#include "THttpServer.h"
22#include "TBufferJSON.h"
23#include "TGeoManager.h"
24
25#include <fstream>
26
27using namespace std::string_literals;
28
29using namespace ROOT::Experimental;
30
31//////////////////////////////////////////////////////////////////////////////////////////////
32/// constructor
33
34RGeomViewer::RGeomViewer(TGeoManager *mgr, const std::string &volname)
35{
36 if (!gROOT->IsWebDisplayBatch()) {
38 fWebWindow->SetDefaultPage("file:rootui5sys/geom/index.html");
39
40 // this is call-back, invoked when message received via websocket
41 fWebWindow->SetDataCallBack([this](unsigned connid, const std::string &arg) { WebWindowCallback(connid, arg); });
42 fWebWindow->SetGeometry(900, 700); // configure predefined window geometry
43 fWebWindow->SetConnLimit(0); // allow any connections numbers at the same time
44 fWebWindow->SetMaxQueueLength(30); // number of allowed entries in the window queue
45 }
46
47 fDesc.SetPreferredOffline(gEnv->GetValue("WebGui.PreferredOffline",0) != 0);
49 fDesc.SetBuildShapes(gEnv->GetValue("WebGui.GeomBuildShapes", 1));
50
51 if (mgr) SetGeometry(mgr, volname);
52}
53
54//////////////////////////////////////////////////////////////////////////////////////////////
55/// destructor
56
58{
59}
60
61//////////////////////////////////////////////////////////////////////////////////////////////
62/// assign new geometry to the viewer
63
64void RGeomViewer::SetGeometry(TGeoManager *mgr, const std::string &volname)
65{
66 fGeoManager = mgr;
67 fSelectedVolume = volname;
68
69 fDesc.Build(mgr, volname);
70
71 Update();
72}
73
74/////////////////////////////////////////////////////////////////////////////////
75/// Select visible top volume, all other volumes will be disabled
76
77void RGeomViewer::SelectVolume(const std::string &volname)
78{
79 if ((volname != fSelectedVolume) && fGeoManager)
80 SetGeometry(fGeoManager, volname);
81}
82
83/////////////////////////////////////////////////////////////////////////////////
84/// Draw only specified volume, special case when volume stored without valid geomanager
85
87{
88 fGeoManager = nullptr;
89 fSelectedVolume = "";
90
91 fDesc.Build(vol);
92
93 Update();
94}
95
96/////////////////////////////////////////////////////////////////////////////////
97/// Show or update geometry in web window
98/// If web browser already started - just refresh drawing like "reload" button does
99/// If no web window exists or \param always_start_new_browser configured, starts new window
100/// \param args arguments to display
101
102void RGeomViewer::Show(const RWebDisplayArgs &args, bool always_start_new_browser)
103{
104 if (!fWebWindow)
105 return;
106
107 std::string user_args = "";
108 if (!GetShowHierarchy()) user_args = "{ nobrowser: true }";
109 fWebWindow->SetUserArgs(user_args);
110
111 if (args.GetWidgetKind().empty())
112 const_cast<RWebDisplayArgs *>(&args)->SetWidgetKind("RGeomViewer");
113
114 if ((fWebWindow->NumConnections(true) == 0) || always_start_new_browser)
115 fWebWindow->Show(args);
116 else
117 Update();
118}
119
120//////////////////////////////////////////////////////////////////////////////////////////////
121/// Return URL address of web window used for geometry viewer
122
123std::string RGeomViewer::GetWindowAddr() const
124{
125 return fWebWindow ? fWebWindow->GetAddr() : ""s;
126}
127
128//////////////////////////////////////////////////////////////////////////////////////////////
129/// Update geometry drawings in all web displays
130
132{
133 if (fWebWindow)
134 fWebWindow->Send(0, "RELOAD");
135}
136
137//////////////////////////////////////////////////////////////////////////////////////////////
138/// convert JSON into stack array
139
140std::vector<int> RGeomViewer::GetStackFromJson(const std::string &json, bool node_ids)
141{
142 std::vector<int> *stack{nullptr}, res;
143
144 if (TBufferJSON::FromJSON(stack, json.c_str())) {
145 if (node_ids) res = fDesc.MakeStackByIds(*stack);
146 else res = *stack;
147 delete stack;
148 } else {
149 R__LOG_ERROR(RGeomLog()) << "Fail convert " << json << " into vector<int>";
150 }
151
152 return res;
153}
154
155//////////////////////////////////////////////////////////////////////////////////////////////
156/// Send data for principal geometry draw
157
158void RGeomViewer::SendGeometry(unsigned connid)
159{
160 if (!fDesc.HasDrawData())
162
163 auto &json = fDesc.GetDrawJson();
164
165 R__LOG_DEBUG(0, RGeomLog()) << "Produce geometry JSON len: " << json.length();
166
167 if (fWebWindow)
168 fWebWindow->Send(connid, json);
169}
170
171//////////////////////////////////////////////////////////////////////////////////////////////
172/// Configures draw option for geometry
173/// Normally has effect before first drawing of the geometry
174/// When geometry displayed, only "axis" and "rotate" options are updated
175
176void RGeomViewer::SetDrawOptions(const std::string &opt)
177{
179
180 unsigned connid = fWebWindow ? fWebWindow->GetConnectionId() : 0;
181 if (connid)
182 fWebWindow->Send(connid, "DROPT:"s + opt);
183}
184
185//////////////////////////////////////////////////////////////////////////////////////////////
186/// Produce PNG image of the geometry
187/// If web-browser is shown and drawing completed, image is requested from the browser.
188/// In this case method executed asynchronously - it returns immediately and image will stored shortly afterwards when received from the client
189/// Height and width parameters are ignored in that case and derived from actual drawing size in the browser.
190/// Another possibility is to invoke headless browser, providing positive width and height parameter explicitely
191///
192
193void RGeomViewer::SaveImage(const std::string &fname, int width, int height)
194{
195 unsigned connid = fWebWindow ? fWebWindow->GetConnectionId() : 0;
196
197 if (connid && (width <= 0) && (height <= 0)) {
198 fWebWindow->Send(connid, "IMAGE:"s + fname);
199 } else {
200 if (width <= 0) width = 800;
201 if (height <= 0) height = width;
202
203 if (!fDesc.HasDrawData())
205
206 std::string json = fDesc.GetDrawJson();
207 if (json.find("GDRAW:") != 0) {
208 printf("GDRAW missing!!!!\n");
209 return;
210 }
211 json.erase(0, 6);
212
213 RWebDisplayHandle::ProduceImage(fname, json, width, height, "/js/files/geom_batch.htm");
214 }
215}
216
217//////////////////////////////////////////////////////////////////////////////////////////////
218/// receive data from client
219
220void RGeomViewer::WebWindowCallback(unsigned connid, const std::string &arg)
221{
222 if (arg == "GETDRAW") {
223
224 SendGeometry(connid);
225
226 } else if (arg == "QUIT_ROOT") {
227
228 fWebWindow->TerminateROOT();
229
230 } else if (arg.compare(0, 7, "SEARCH:") == 0) {
231
232 std::string query = arg.substr(7);
233
234 std::string hjson, json;
235
236 /* auto nmatches = */ fDesc.SearchVisibles(query, hjson, json);
237
238 // send reply with appropriate header - NOFOUND, FOUND0:, FOUND1:
239 fWebWindow->Send(connid, hjson);
240
241 if (!json.empty())
242 fWebWindow->Send(connid, json);
243
244 } else if (arg.compare(0,4,"GET:") == 0) {
245 // provide exact shape
246
247 auto stack = GetStackFromJson(arg.substr(4));
248
249 auto nodeid = fDesc.FindNodeId(stack);
250
251 std::string json{"SHAPE:"};
252
254
255 fWebWindow->Send(connid, json);
256
257 } else if (arg.compare(0, 6, "GVREQ:") == 0) {
258
259 auto req = TBufferJSON::FromJSON<RGeomRequest>(arg.substr(6));
260
261 if (req && (req->oper == "HOVER")) {
262 if ((req->path.size() > 0 ) && (req->path[0] != "OFF"))
263 req->stack = fDesc.MakeStackByPath(req->path);
264 req->path.clear();
265 } else if (req && (req->oper == "HIGHL")) {
266 if (req->stack.size() > 0)
267 req->path = fDesc.MakePathByStack(req->stack);
268 req->stack.clear();
269 } else if (req && (req->oper == "INFO")) {
270
271 auto info = fDesc.MakeNodeInfo(req->path);
272 if (info)
273 fWebWindow->Send(connid, "NINFO:"s + TBufferJSON::ToJSON(info.get(), (fDesc.GetJsonComp() % 5) + TBufferJSON::kSameSuppression).Data());
274
275 // not request but different object type is send
276 req.reset(nullptr);
277
278 } else {
279 req.reset(nullptr);
280 }
281
282 if (req)
284
285 } else if ((arg.compare(0, 7, "SETVI0:") == 0) || (arg.compare(0, 7, "SETVI1:") == 0)) {
286 // change visibility for specified nodeid
287
288 auto nodeid = std::stoi(arg.substr(7));
289
290 bool selected = (arg[5] == '1');
291
292 if (fDesc.ChangeNodeVisibility(nodeid, selected)) {
293
294 // send only modified entries, includes all nodes with same volume
295 std::string json0 = fDesc.ProduceModifyReply(nodeid);
296
297 // when visibility disabled, client will automatically remove node from drawing
298 fWebWindow->Send(connid, json0);
299
300 if (selected && fDesc.IsPrincipalEndNode(nodeid)) {
301 // we need to send changes in drawing elements
302 // there can be many elements, which reference same volume
303
304 std::string json{"APPND:"};
305
306 if (fDesc.ProduceDrawingFor(nodeid, json, true))
307 fWebWindow->Send(connid, json);
308 } else if (selected) {
309
310 // just resend full geometry
311 // TODO: one can improve here and send only nodes which are not exists on client
312 // TODO: for that one should remember all information send to client
313
315 if (json.length() > 0) fWebWindow->Send(connid, json);
316
317 SendGeometry(connid);
318 }
319 }
320 } else if (arg.compare(0,6, "BRREQ:") == 0) {
321
322 // central place for processing browser requests
323
325
326 auto json = fDesc.ProcessBrowserRequest(arg.substr(6));
327 if (json.length() > 0) fWebWindow->Send(connid, json);
328 } else if (arg.compare(0,6, "IMAGE:") == 0) {
329 auto separ = arg.find("::",6);
330 if (separ == std::string::npos) return;
331
332 std::string fname = arg.substr(6, separ-6);
333 if (fname.empty()) {
334 int cnt = 0;
335 do {
336 fname = "geometry"s;
337 if (cnt++>0) fname += std::to_string(cnt);
338 fname += ".png"s;
339 } while (!gSystem->AccessPathName(fname.c_str()));
340 }
341
342 TString binary = TBase64::Decode(arg.c_str() + separ + 2);
343
344 std::ofstream ofs(fname);
345 ofs.write(binary.Data(), binary.Length());
346 ofs.close();
347
348 printf("Image file %s size %d has been created\n", fname.c_str(), (int) binary.Length());
349
350 } else if (arg.compare(0,4, "CFG:") == 0) {
351
352 if (fDesc.ChangeConfiguration(arg.substr(4)))
353 SendGeometry(connid);
354
355 } else if (arg == "RELOAD") {
356
357 SendGeometry(connid);
358 }
359}
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
Option_t Option_t width
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:405
R__EXTERN TSystem * gSystem
Definition TSystem.h:560
std::string ProcessBrowserRequest(const std::string &req="")
Find description object for requested shape If not exists - will be created.
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...
int GetJsonComp() const
Returns JSON compression level for data transfer.
bool IsPrincipalEndNode(int nodeid)
return true when node used in main geometry drawing and does not have childs for such nodes one could...
std::vector< int > MakeStackByIds(const std::vector< int > &ids)
Creates stack for given array of ids, first element always should be 0.
const std::string & GetDrawJson() const
std::unique_ptr< RGeomNodeInfo > MakeNodeInfo(const std::vector< std::string > &path)
Change visibility for specified element Returns true if changes was performed.
void ProduceDrawData()
Collect all information required to draw geometry on the client This includes list of each visible no...
int SearchVisibles(const std::string &find, std::string &hjson, std::string &json)
Search visible nodes for provided name If number of found elements less than 100, create description ...
std::vector< std::string > MakePathByStack(const std::vector< int > &stack)
Returns path string for provided stack.
bool ChangeConfiguration(const std::string &json)
Change configuration by client Returns true if any parameter was really changed.
std::vector< int > MakeStackByPath(const std::vector< std::string > &path)
Produce stack based on string path Used to highlight geo volumes by browser hover event.
void SetBuildShapes(int lvl=1)
Instruct to build binary 3D model already on the server (true) or send TGeoShape as is to client,...
void SetPreferredOffline(bool on)
Set preference of offline operations.
int FindNodeId(const std::vector< int > &stack)
Returns nodeid for given stack array, returns -1 in case of failure.
bool ChangeNodeVisibility(int nodeid, bool selected)
Change visibility for specified element Returns true if changes was performed.
std::string ProduceModifyReply(int nodeid)
Return string with only part of nodes description which were modified Checks also volume.
void SetDrawOptions(const std::string &opt="")
Set draw options as string for JSROOT TGeoPainter.
void SetJsonComp(int comp=0)
Set 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
std::shared_ptr< RWebWindow > fWebWindow
! web window to show geometry
std::string GetWindowAddr() const
Return URL address of web window used for geometry viewer.
std::string fSelectedVolume
! name of selected volume
void Update()
Update geometry drawings in all web displays.
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
RGeomDescription fDesc
! geometry description, send to the client as first message
void SendGeometry(unsigned connid)
Send data for principal geometry draw.
void WebWindowCallback(unsigned connid, const std::string &arg)
receive data from client
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...
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:45
TGeoVolume, TGeoVolumeMulti, TGeoVolumeAssembly are the volume classes.
Definition TGeoVolume.h:49
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:1299
RLogChannel & RGeomLog()
Log channel for Eve diagnostics.
Definition RGeomData.cxx:40