Belle II Software development
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
29using namespace Belle2;
30
31namespace {
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
174void 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
207void 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:
243 break;
244
245 case kSaveGeometryExtract:
247 break;
248
249 default:
250 break;
251 }
252}
253
254void 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
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
312}
313
314void 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
354void 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
413void 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.