Belle II Software  release-08-01-10
SplitGLView.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 #include <display/SplitGLView.h>
9 #include <display/EveGeometry.h>
10 #include <display/InfoWidget.h>
11 #include <framework/logging/Logger.h>
12 
13 #include "TEveScene.h"
14 #include "TEveManager.h"
15 #include "TEveBrowser.h"
16 #include "TEveProjectionAxes.h"
17 #include "TEveViewer.h"
18 #include "TEveWindowManager.h"
19 #include "TGMenu.h"
20 #include "TGStatusBar.h"
21 #include "TGTab.h"
22 #include "TGLPhysicalShape.h"
23 #include "TGLLogicalShape.h"
24 #include "TGLWidget.h"
25 #include "TVirtualX.h"
26 
27 #include "TString.h"
28 
29 using namespace Belle2;
30 
31 namespace {
33  bool toggleMenuEntry(TGPopupMenu* menu, int entryid)
34  {
35  if (menu->IsEntryChecked(entryid))
36  menu->UnCheckEntry(entryid);
37  else
38  menu->CheckEntry(entryid);
39  return menu->IsEntryChecked(entryid);
40  }
41 }
42 
44  m_activeViewer(-1),
45  m_infoWidget(nullptr)
46 {
47  const char* projectionName[3] = { "3D", "Rho/Z", "R/Phi" };
48  TGLViewer::ECameraType cameraType[3] = { TGLViewer::kCameraPerspXOZ, TGLViewer::kCameraOrthoXOY, TGLViewer::kCameraOrthoXOY };
49 
50  TEveWindowSlot* slot = TEveWindow::CreateWindowMainFrame();
51  TEveWindowPack* pack = slot->MakePack();
52  pack->SetVertical();
53  pack->SetShowTitleBar(kFALSE);
54 
55  TEveWindowSlot* viewerslots[3];
56  auto pack1 = pack->NewSlot()->MakePack();
57  pack1->SetHorizontal();
58  pack1->SetShowTitleBar(kFALSE);
59  viewerslots[0] = pack1->NewSlotWithWeight(0.7);
60  TEveWindowSlot* infoslot = pack1->NewSlotWithWeight(0.3);
61 
62  auto pack2 = pack->NewSlot()->MakePack();
63  pack2->SetHorizontal();
64  pack2->SetShowTitleBar(kFALSE);
65  viewerslots[1] = pack2->NewSlot();
66  viewerslots[2] = pack2->NewSlot();
67 
68 
69  //0: 3d, 1: rhoz, 2: rphi
70  for (int iFrame = 0; iFrame < 3; iFrame++) {
71  TEveViewer* viewer = new TEveViewer(TString::Format("%s viewer", projectionName[iFrame]));
72  m_glViewer[iFrame] = viewer->SpawnGLEmbeddedViewer();
73  viewerslots[iFrame]->ReplaceWindow(viewer);
74  viewerslots[iFrame] = nullptr; // invalid after ReplaceWindow()
75 
76  viewer->SetShowTitleBar(kFALSE); //might want to show these?
77  m_window[iFrame] = viewer;
78 
79  m_glViewer[iFrame]->SetCurrentCamera(cameraType[iFrame]);
80  //create projection managers
81  TEveProjectionManager* projectionMgr = 0;
82  if (iFrame == 1) {
83  m_rhozManager = new TEveProjectionManager(TEveProjection::kPT_RhoZ);
84  projectionMgr = m_rhozManager;
85  m_glViewer[iFrame]->CurrentCamera().Zoom(+300, false, false);
86  } else if (iFrame == 2) {
87  m_rphiManager = new TEveProjectionManager(TEveProjection::kPT_RPhi);
88  projectionMgr = m_rphiManager;
89  m_glViewer[iFrame]->CurrentCamera().Zoom(-50, false, false);
90  }
91 
92  // connect signals we are interested in
93  m_glViewer[iFrame]->Connect("MouseOver(TGLPhysicalShape*)", "Belle2::SplitGLView", this, "onMouseOver(TGLPhysicalShape*)");
94  m_glViewer[iFrame]->Connect("Clicked(TObject*)", "Belle2::SplitGLView", this, "onClicked(TObject*)");
95  if (iFrame == 0) {
96  viewer->AddScene(gEve->GetGlobalScene());
97  viewer->AddScene(gEve->GetEventScene());
98  }
99  TEveScene* s = 0;
100  if (projectionMgr) {
101  s = gEve->SpawnNewScene(TString::Format("%s projection", projectionName[iFrame]));
102  viewer->AddScene(s);
103  }
104 
105  gEve->GetViewers()->AddElement(viewer);
106  if (projectionMgr) {
107  s->AddElement(projectionMgr);
108  gEve->AddToListTree(projectionMgr, kTRUE);
109  TEveProjectionAxes* axes = new TEveProjectionAxes(projectionMgr);
110  projectionMgr->AddElement(axes);
111  }
112  }
113 
114  m_infoWidget = new InfoWidget(gClient->GetRoot());
115 
116  TEveWindowFrame* eveFrame = new TEveWindowFrame(m_infoWidget, "DataStore Info");
117  infoslot->ReplaceWindow(eveFrame);
118  eveFrame->SetShowTitleBar(kFALSE);
119  infoslot = nullptr; // invalid after ReplaceWindow()
120 
121 
122  // create the "camera" popup menu
123  m_cameraMenu = new TGPopupMenu(gClient->GetDefaultRoot());
124  m_cameraMenu->AddEntry("Perspective (Floor X/Z)", kGLPerspXOZ);
125  m_cameraMenu->AddEntry("Perspective (Floor Y/Z)", kGLPerspYOZ);
126  m_cameraMenu->AddEntry("Perspective (Floor X/Y)", kGLPerspXOY);
127  m_cameraMenu->AddEntry("Orthographic (X/Y)", kGLXOY);
128  m_cameraMenu->AddEntry("Orthographic (X/Z)", kGLXOZ);
129  m_cameraMenu->AddEntry("Orthographic (Z/Y)", kGLZOY);
130  m_cameraMenu->AddSeparator();
131  m_cameraMenu->AddEntry("Orthographic: Allow Rotating", kGLOrthoRotate);
132  m_cameraMenu->AddEntry("Orthographic: Right Click to Dolly", kGLOrthoDolly);
133  m_cameraMenu->AddSeparator();
134  m_cameraMenu->AddEntry("Perspective: Enable Stereographic 3D (Requires Hardware or stereowrap)", kGLStereo);
135 
136  m_sceneMenu = new TGPopupMenu(gClient->GetDefaultRoot());
137  m_sceneMenu->AddEntry("&Update Current", kSceneUpdate);
138  m_sceneMenu->AddEntry("Update &All", kSceneUpdateAll);
139  m_sceneMenu->AddSeparator();
140  m_sceneMenu->AddEntry("&Show Scale for Projections", kShowScale);
141  m_sceneMenu->CheckEntry(kShowScale);
142  m_sceneMenu->AddSeparator();
143  m_sceneMenu->AddEntry("Save &Geometry Extract", kSaveGeometryExtract);
144 
145  TGMenuBar* menuBar = gEve->GetBrowser()->GetMenuBar();
146  menuBar->AddPopup("&Camera", m_cameraMenu, new TGLayoutHints(kLHintsTop | kLHintsLeft, 0, 4, 0, 0));
147  menuBar->AddPopup("&Scene", m_sceneMenu, new TGLayoutHints(kLHintsTop | kLHintsLeft, 0, 4, 0, 0));
148 
149  // connect menu signals to our menu handler slot
150  m_cameraMenu->Connect("Activated(Int_t)", "Belle2::SplitGLView", this, "handleMenu(Int_t)");
151  m_sceneMenu->Connect("Activated(Int_t)", "Belle2::SplitGLView", this, "handleMenu(Int_t)");
152 
153  // use status bar from the browser
154  m_statusBar = gEve->GetBrowser()->GetStatusBar();
155 
156  //needs to come after menu
158 
159  gEve->GetListTree()->Connect("Clicked(TGListTreeItem*, Int_t, Int_t, Int_t)",
160  "Belle2::SplitGLView", this, "itemClicked(TGListTreeItem*, Int_t, Int_t, Int_t)");
161 }
162 
164 {
165  // Clean up main frame...
166  for (int i = 0; i < 3; i++) {
167  delete m_glViewer[i];
168  }
169  delete m_cameraMenu;
170  delete m_sceneMenu;
171  delete m_infoWidget;
172 }
173 
174 void SplitGLView::updateCamera(int cameraAction)
175 {
176  TGLEmbeddedViewer* viewer = getActiveGLViewer();
177  if (!viewer)
178  return;
179 
180  TGLViewer::ECameraType cameraType;
181  switch (cameraAction) {
182  case kGLPerspYOZ:
183  cameraType = TGLViewer::kCameraPerspYOZ;
184  break;
185  case kGLPerspXOZ:
186  cameraType = TGLViewer::kCameraPerspXOZ;
187  break;
188  case kGLPerspXOY:
189  cameraType = TGLViewer::kCameraPerspXOY;
190  break;
191  case kGLXOY:
192  cameraType = TGLViewer::kCameraOrthoXOY;
193  break;
194  case kGLXOZ:
195  cameraType = TGLViewer::kCameraOrthoXOZ;
196  break;
197  case kGLZOY:
198  cameraType = TGLViewer::kCameraOrthoZOY;
199  break;
200  default:
201  B2ERROR("unknown camera action " << cameraAction);
202  return;
203  }
204  viewer->SetCurrentCamera(cameraType);
205 }
206 
207 void SplitGLView::handleMenu(Int_t menuCommand)
208 {
209  // Handle menu items.
210 
211  switch (menuCommand) {
212  case kGLPerspYOZ:
213  case kGLPerspXOZ:
214  case kGLPerspXOY:
215  case kGLXOY:
216  case kGLXOZ:
217  case kGLZOY:
218  updateCamera(menuCommand);
219  break;
220  case kGLOrthoRotate:
222  break;
223  case kGLOrthoDolly:
225  break;
226 
227  case kGLStereo:
228  toggleStereo();
229  break;
230 
231  case kSceneUpdate:
232  if (getActiveGLViewer())
233  getActiveGLViewer()->UpdateScene();
234  break;
235 
236  case kSceneUpdateAll:
237  for (int i = 0; i < 3; i++)
238  m_glViewer[i]->UpdateScene();
239  break;
240 
241  case kShowScale:
242  toggleShowScale();
243  break;
244 
245  case kSaveGeometryExtract:
247  break;
248 
249  default:
250  break;
251  }
252 }
253 
254 void SplitGLView::onClicked(TObject* obj)
255 {
256  // Handle click events in GL viewer
257 
258  if (obj) {
259  m_statusBar->SetText(Form("User clicked on: \"%s\"", obj->GetName()), 1);
260 
261  TEveElement* elem = dynamic_cast<TEveElement*>(obj);
262  if (TEveProjected* projected = dynamic_cast<TEveProjected*>(obj)) {
263  elem = dynamic_cast<TEveElement*>(projected->GetProjectable());
264  }
265  if (!elem)
266  return;
267 
268  TGListTreeItem* eventItem = gEve->GetListTree()->FindItemByPathname("Event");
269  TGListTreeItem* item = gEve->GetListTree()->FindItemByObj(eventItem, elem);
270  if (item) {
271  //focus Eve tab
272  gEve->GetBrowser()->GetTabLeft()->SetTab("Eve");
273 
274  //open all parent nodes
275  TGListTreeItem* parent = item;
276  while ((parent = parent->GetParent()) != nullptr)
277  gEve->GetListTree()->OpenItem(parent);
278 
279  //scroll to clicked item
280  gEve->GetListTree()->AdjustPosition(item);
281  }
282  } else {
283  m_statusBar->SetText("", 1);
284  }
285 
286 
287  // change the active GL viewer to the one who emitted the signal
288  TGLEmbeddedViewer* sender = dynamic_cast<TGLEmbeddedViewer*>(static_cast<TQObject*>(gTQSender));
289 
290  if (!sender) {
291  B2WARNING("onClicked() signal not from a TGLEmbeddedViewer?");
292  return;
293  }
294  setActiveViewer(sender);
295 }
296 
297 TGLEmbeddedViewer* SplitGLView::getActiveGLViewer()
298 {
299  const TEveWindow* currentWindow = gEve->GetWindowManager()->GetCurrentWindow();
300  if (m_activeViewer < 0 or m_window[m_activeViewer] != currentWindow) {
301  //check if some other viewer was selected
302  for (int i = 0; i < 3; i++) {
303  if (m_window[i] == currentWindow) {
304  m_activeViewer = i;
305  }
306  }
307  }
308  if (m_activeViewer < 0)
309  return NULL;
310 
311  return m_glViewer[m_activeViewer];
312 }
313 
314 void SplitGLView::setActiveViewer(TGLEmbeddedViewer* v)
315 {
316  bool found = false;
317  for (int i = 0; i < 3; i++) {
318  if (m_glViewer[i] == v) {
319  m_activeViewer = i;
320  found = true;
321  }
322  }
323  if (!found) {
324  B2WARNING("setActiveViewer(): viewer not found!");
325  m_activeViewer = -1;
326  }
327  if (m_activeViewer >= 0) {
328  //activate corresponding window
329  m_window[m_activeViewer]->MakeCurrent();
330 
331  // update menu entries to match active viewer's options
332  if (getActiveGLViewer()->GetOrthoXOYCamera()->GetDollyToZoom() &&
333  getActiveGLViewer()->GetOrthoXOZCamera()->GetDollyToZoom() &&
334  getActiveGLViewer()->GetOrthoZOYCamera()->GetDollyToZoom())
335  m_cameraMenu->UnCheckEntry(kGLOrthoDolly);
336  else
337  m_cameraMenu->CheckEntry(kGLOrthoDolly);
338 
339  if (getActiveGLViewer()->GetOrthoXOYCamera()->GetEnableRotate() &&
340  getActiveGLViewer()->GetOrthoXOZCamera()->GetEnableRotate() &&
341  getActiveGLViewer()->GetOrthoZOYCamera()->GetEnableRotate())
342  m_cameraMenu->CheckEntry(kGLOrthoRotate);
343  else
344  m_cameraMenu->UnCheckEntry(kGLOrthoRotate);
345 
346  if (getActiveGLViewer()->GetStereo())
347  m_cameraMenu->CheckEntry(kGLStereo);
348  else
349  m_cameraMenu->UnCheckEntry(kGLStereo);
350  }
351 }
352 
353 
354 void SplitGLView::onMouseOver(TGLPhysicalShape* shape)
355 {
356  // Slot used to handle "onMouseOver" signal coming from any GL viewer.
357  // We receive a pointer on the physical shape in which the mouse cursor is.
358 
359  // display informations on the physical shape in the status bar
360  if (shape && shape->GetLogical() && shape->GetLogical()->GetExternal())
361  m_statusBar->SetText(Form("Mouse Over: \"%s\"",
362  shape->GetLogical()->GetExternal()->GetName()), 0);
363  else
364  m_statusBar->SetText("", 0);
365 }
366 
367 
369 {
370  Bool_t state = toggleMenuEntry(m_cameraMenu, kGLOrthoRotate);
371  if (getActiveGLViewer()) {
372  getActiveGLViewer()->GetOrthoXOYCamera()->SetEnableRotate(state);
373  getActiveGLViewer()->GetOrthoXOZCamera()->SetEnableRotate(state);
374  getActiveGLViewer()->GetOrthoZOYCamera()->SetEnableRotate(state);
375  }
376 }
377 
379 {
380  Bool_t state = !toggleMenuEntry(m_cameraMenu, kGLOrthoDolly);
381  if (getActiveGLViewer()) {
382  getActiveGLViewer()->GetOrthoXOYCamera()->SetDollyToZoom(state);
383  getActiveGLViewer()->GetOrthoXOZCamera()->SetDollyToZoom(state);
384  getActiveGLViewer()->GetOrthoZOYCamera()->SetDollyToZoom(state);
385  }
386 }
387 
389 {
390  if (!getActiveGLViewer())
391  return;
392 
393  getActiveGLViewer()->SetStereo(toggleMenuEntry(m_cameraMenu, kGLStereo));
394 }
395 
397 {
398  bool state = toggleMenuEntry(m_sceneMenu, kShowScale);
399 
400  std::vector<TEveProjectionManager*> projections = {m_rhozManager, m_rphiManager};
401  for (auto mgr : projections) {
402  TEveElement::List_ci end_it = mgr->EndChildren();
403  for (TEveElement::List_i it = mgr->BeginChildren(); it != end_it; ++it) {
404  TEveProjectionAxes* a = dynamic_cast<TEveProjectionAxes*>(*it);
405  if (a) {
406  a->SetRnrSelf(state);
407  }
408  }
409  }
410  gEve->Redraw3D(false); //do not reset camera when redrawing
411 }
412 
413 void SplitGLView::itemClicked(TGListTreeItem* item, Int_t, Int_t, Int_t)
414 {
415  // Item has been clicked, based on mouse button do:
416 
417  static const TEveException eh("SplitGLView::itemClicked ");
418  TEveElement* re = static_cast<TEveElement*>(item->GetUserData());
419  if (re == 0) return;
420  TObject* obj = re->GetObject(eh);
421  if (obj->InheritsFrom("TEveViewer")) {
422  TGLViewer* v = static_cast<TEveViewer*>(obj)->GetGLViewer();
423  //v->Activated();
424  if (v->InheritsFrom("TGLEmbeddedViewer")) {
425  TGLEmbeddedViewer* ev = (TGLEmbeddedViewer*)v;
426  gVirtualX->SetInputFocus(ev->GetGLWidget()->GetId());
427  }
428  }
429 }
text-based info viewer showing DataStore contents.
Definition: InfoWidget.h:27
void onMouseOver(TGLPhysicalShape *shape)
show name of shape in status bar.
Definition: SplitGLView.cc:354
InfoWidget * m_infoWidget
text-based info viewer.
Definition: SplitGLView.h:88
void itemClicked(TGListTreeItem *item, Int_t btn, Int_t x, Int_t y=43)
handler for clicks inside GL viewer.
Definition: SplitGLView.cc:413
void toggleOrthoRotate()
toggle wether the active viewer may be rotated (not that useful for projections).
Definition: SplitGLView.cc:368
virtual ~SplitGLView()
destructor.
Definition: SplitGLView.cc:163
TEveWindow * m_window[3]
corresponding windows
Definition: SplitGLView.h:78
void toggleShowScale()
Toggle visibility of axes for projections.
Definition: SplitGLView.cc:396
TGPopupMenu * m_sceneMenu
'Scene' popup menu
Definition: SplitGLView.h:84
TEveProjectionManager * m_rhozManager
Rho-Z projection.
Definition: SplitGLView.h:81
TGPopupMenu * m_cameraMenu
'Camera' popup menu
Definition: SplitGLView.h:83
void updateCamera(int cameraAction)
change camera type, given one of EMenuCommands.
Definition: SplitGLView.cc:174
TGLEmbeddedViewer * getActiveGLViewer()
return TGLEmbeddedViewer that is active right now.
Definition: SplitGLView.cc:297
void onClicked(TObject *obj)
make current viewer active & show name of obj in status bar.
Definition: SplitGLView.cc:254
void toggleOrthoDolly()
Toggle state of the 'Ortho allow dolly' menu entry.
Definition: SplitGLView.cc:378
void toggleStereo()
Toggle stereo viewing for perspective viewer.
Definition: SplitGLView.cc:388
int m_activeViewer
selected viewer/window, or -1.
Definition: SplitGLView.h:79
SplitGLView()
constructor.
Definition: SplitGLView.cc:43
void setActiveViewer(TGLEmbeddedViewer *v)
set m_activeViewer and update UI accordingly.
Definition: SplitGLView.cc:314
TEveProjectionManager * m_rphiManager
R-Phi projection.
Definition: SplitGLView.h:80
void handleMenu(Int_t menuCommand)
menu item handler
Definition: SplitGLView.cc:207
TGStatusBar * m_statusBar
status bar
Definition: SplitGLView.h:85
TGLEmbeddedViewer * m_glViewer[3]
GL viewers.
Definition: SplitGLView.h:77
void saveExtract()
Save a geometry extract from the current state of the TGeo geometry.
Definition: EveGeometry.cc:202
Abstract base class for different kinds of events.