Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
REveGeomViewer.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/REveGeomViewer.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 
27 using namespace std::string_literals;
28 
29 //////////////////////////////////////////////////////////////////////////////////////////////
30 /// constructor
31 
32 ROOT::Experimental::REveGeomViewer::REveGeomViewer(TGeoManager *mgr, const std::string &volname)
33 {
34  fWebWindow = RWebWindow::Create();
35  fWebWindow->SetDefaultPage("file:rootui5sys/eve7/geom.html");
36 
37  // this is call-back, invoked when message received via websocket
38  fWebWindow->SetDataCallBack([this](unsigned connid, const std::string &arg) { this->WebWindowCallback(connid, arg); });
39  fWebWindow->SetGeometry(900, 700); // configure predefined window geometry
40  fWebWindow->SetConnLimit(1); // the only connection is allowed
41  fWebWindow->SetMaxQueueLength(30); // number of allowed entries in the window queue
42 
43  fDesc.SetPreferredOffline(gEnv->GetValue("WebGui.PreferredOffline",0) != 0);
44  fDesc.SetJsonComp(gEnv->GetValue("WebGui.JsonComp", TBufferJSON::kSkipTypeInfo + TBufferJSON::kNoSpaces));
45  fDesc.SetBuildShapes(gEnv->GetValue("WebGui.GeomBuildShapes", 1));
46 
47  if (mgr) SetGeometry(mgr, volname);
48 }
49 
50 //////////////////////////////////////////////////////////////////////////////////////////////
51 /// destructor
52 
53 ROOT::Experimental::REveGeomViewer::~REveGeomViewer()
54 {
55 
56 }
57 
58 //////////////////////////////////////////////////////////////////////////////////////////////
59 /// assign new geometry to the viewer
60 
61 void ROOT::Experimental::REveGeomViewer::SetGeometry(TGeoManager *mgr, const std::string &volname)
62 {
63  fGeoManager = mgr;
64  fSelectedVolume = volname;
65 
66  fDesc.Build(mgr, volname);
67 
68  Update();
69 }
70 
71 
72 /////////////////////////////////////////////////////////////////////////////////
73 /// Select visible top volume, all other volumes will be disabled
74 
75 void ROOT::Experimental::REveGeomViewer::SelectVolume(const std::string &volname)
76 {
77  if (volname != fSelectedVolume)
78  SetGeometry(fGeoManager, volname);
79 }
80 
81 /////////////////////////////////////////////////////////////////////////////////
82 /// Show or update geometry in web window
83 /// If web browser already started - just refresh drawing like "reload" button does
84 /// If no web window exists or \param always_start_new_browser configured, starts new window
85 
86 void ROOT::Experimental::REveGeomViewer::Show(const RWebDisplayArgs &args, bool always_start_new_browser)
87 {
88  std::string user_args = "";
89  if (!GetShowHierarchy()) user_args = "{ nobrowser: true }";
90  fWebWindow->SetUserArgs(user_args);
91 
92  if ((fWebWindow->NumConnections(true) == 0) || always_start_new_browser)
93  fWebWindow->Show(args);
94 }
95 
96 //////////////////////////////////////////////////////////////////////////////////////////////
97 /// Update geometry drawings in all web displays
98 
99 void ROOT::Experimental::REveGeomViewer::Update()
100 {
101  fWebWindow->Send(0, "RELOAD");
102 }
103 
104 //////////////////////////////////////////////////////////////////////////////////////////////
105 /// convert JSON into stack array
106 
107 std::vector<int> ROOT::Experimental::REveGeomViewer::GetStackFromJson(const std::string &json, bool node_ids)
108 {
109  std::vector<int> *stack{nullptr}, res;
110 
111  if (TBufferJSON::FromJSON(stack, json.c_str())) {
112  if (node_ids) res = fDesc.MakeStackByIds(*stack);
113  else res = *stack;
114  delete stack;
115  } else {
116  R__ERROR_HERE("webeve") << "Fail convert " << json << " into vector<int>";
117  }
118 
119  return res;
120 }
121 
122 //////////////////////////////////////////////////////////////////////////////////////////////
123 /// Send data for principal geometry draw
124 
125 void ROOT::Experimental::REveGeomViewer::SendGeometry(unsigned connid)
126 {
127  if (!fDesc.HasDrawData())
128  fDesc.CollectVisibles();
129 
130  auto &json = fDesc.GetDrawJson();
131 
132  printf("Produce geometry JSON %d\n", (int) json.length());
133 
134  fWebWindow->Send(connid, json);
135 }
136 
137 //////////////////////////////////////////////////////////////////////////////////////////////
138 /// Configures draw option for geometry
139 /// Normally has effect before first drawing of the geometry
140 /// When geometry displayed, only "axis" and "rotate" options are updated
141 
142 void ROOT::Experimental::REveGeomViewer::SetDrawOptions(const std::string &opt)
143 {
144  fDesc.SetDrawOptions(opt);
145 
146  fWebWindow->Send(0, "DROPT:"s + opt);
147 }
148 
149 //////////////////////////////////////////////////////////////////////////////////////////////
150 /// Produce PNG image of drawn geometry
151 /// Drawing should be completed at the moment
152 /// Executed asynchronous - method returns immediately, image stored when received from the client
153 
154 void ROOT::Experimental::REveGeomViewer::SaveImage(const std::string &fname)
155 {
156  unsigned connid = fWebWindow->GetConnectionId();
157  if (connid)
158  fWebWindow->Send(connid, "IMAGE:"s + fname);
159 }
160 
161 //////////////////////////////////////////////////////////////////////////////////////////////
162 /// receive data from client
163 
164 void ROOT::Experimental::REveGeomViewer::WebWindowCallback(unsigned connid, const std::string &arg)
165 {
166  printf("Recv %s\n", arg.substr(0,100).c_str());
167 
168  if (arg == "GETDRAW") {
169 
170  SendGeometry(connid);
171 
172  } else if (arg == "QUIT_ROOT") {
173 
174  fWebWindow->TerminateROOT();
175 
176  } else if (arg.compare(0, 7, "SEARCH:") == 0) {
177 
178  std::string query = arg.substr(7);
179 
180  std::string hjson, json;
181 
182  auto nmatches = fDesc.SearchVisibles(query, hjson, json);
183 
184  printf("Searches %s found %d hjson %d json %d\n", query.c_str(), nmatches, (int) hjson.length(), (int) json.length());
185 
186  // send reply with appropriate header - NOFOUND, FOUND0:, FOUND1:
187  fWebWindow->Send(connid, hjson);
188 
189  if (!json.empty())
190  fWebWindow->Send(connid, json);
191 
192  } else if (arg.compare(0,4,"GET:") == 0) {
193  // provide exact shape
194 
195  auto stack = GetStackFromJson(arg.substr(4));
196 
197  auto nodeid = fDesc.FindNodeId(stack);
198 
199  std::string json{"SHAPE:"};
200 
201  fDesc.ProduceDrawingFor(nodeid, json);
202 
203  printf("Produce shape for stack json %d\n", (int) json.length());
204 
205  fWebWindow->Send(connid, json);
206 
207  } else if (arg.compare(0, 6, "GVREQ:") == 0) {
208 
209  auto req = TBufferJSON::FromJSON<REveGeomRequest>(arg.substr(6));
210 
211  if (req && (req->oper == "HOVER")) {
212  if (req->path != "OFF")
213  req->stack = fDesc.MakeStackByPath(req->path);
214  req->path.clear();
215  } else if (req && (req->oper == "HIGHL")) {
216  if (req->stack.size() > 0)
217  req->path = fDesc.MakePathByStack(req->stack);
218  req->stack.clear();
219  } else if (req && (req->oper == "INFO")) {
220 
221  auto info = fDesc.MakeNodeInfo(req->path);
222  if (info)
223  fWebWindow->Send(connid, "NINFO:"s + TBufferJSON::ToJSON(info.get(), (fDesc.GetJsonComp() % 5) + TBufferJSON::kSameSuppression).Data());
224 
225  // not request but different object type is send
226  req.reset(nullptr);
227 
228  } else {
229  req.reset(nullptr);
230  }
231 
232  if (req)
233  fWebWindow->Send(connid, "GVRPL:"s + TBufferJSON::ToJSON(req.get(), TBufferJSON::kSkipTypeInfo + TBufferJSON::kNoSpaces).Data());
234 
235  } else if ((arg.compare(0, 7, "SETVI0:") == 0) || (arg.compare(0, 7, "SETVI1:") == 0)) {
236  // change visibility for specified nodeid
237 
238  auto nodeid = std::stoi(arg.substr(7));
239 
240  bool selected = (arg[5] == '1');
241 
242  if (fDesc.ChangeNodeVisibility(nodeid, selected)) {
243 
244  // send only modified entries, includes all nodes with same volume
245  std::string json0 = fDesc.ProduceModifyReply(nodeid);
246 
247  // when visibility disabled, client will automatically remove node from drawing
248  fWebWindow->Send(connid, json0);
249 
250  if (selected && fDesc.IsPrincipalEndNode(nodeid)) {
251  // we need to send changes in drawing elements
252  // there can be many elements, which reference same volume
253 
254  std::string json{"APPND:"};
255 
256  if (fDesc.ProduceDrawingFor(nodeid, json, true)) {
257 
258  printf("Send appending JSON %d\n", (int) json.length());
259 
260  fWebWindow->Send(connid, json);
261  }
262  } else if (selected) {
263 
264  // just resend full geometry
265  // TODO: one can improve here and send only nodes which are not exists on client
266  // TODO: for that one should remember all information send to client
267 
268  auto json = fDesc.ProcessBrowserRequest();
269  if (json.length() > 0) fWebWindow->Send(connid, json);
270 
271  SendGeometry(connid);
272  }
273  }
274  } else if (arg.compare(0,6, "BRREQ:") == 0) {
275 
276  // central place for processing browser requests
277 
278  if (!fDesc.IsBuild()) fDesc.Build(fGeoManager);
279 
280  auto json = fDesc.ProcessBrowserRequest(arg.substr(6));
281  if (json.length() > 0) fWebWindow->Send(connid, json);
282  } else if (arg.compare(0,6, "IMAGE:") == 0) {
283  auto separ = arg.find("::",6);
284  if (separ == std::string::npos) return;
285 
286  std::string fname = arg.substr(6, separ-6);
287  if (fname.empty()) {
288  int cnt = 0;
289  do {
290  fname = "geometry"s;
291  if (cnt++>0) fname += std::to_string(cnt);
292  fname += ".png"s;
293  } while (!gSystem->AccessPathName(fname.c_str()));
294  }
295 
296  TString binary = TBase64::Decode(arg.c_str() + separ + 2);
297 
298  std::ofstream ofs(fname);
299  ofs.write(binary.Data(), binary.Length());
300  ofs.close();
301 
302  printf("Image file %s size %d has been created\n", fname.c_str(), (int) binary.Length());
303 
304  } else if (arg.compare(0,4, "CFG:") == 0) {
305 
306  if (fDesc.ChangeConfiguration(arg.substr(4)))
307  SendGeometry(connid);
308 
309  } else if (arg == "RELOAD") {
310 
311  SendGeometry(connid);
312  }
313 }