Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TGL5DPainter.cxx
Go to the documentation of this file.
1 // @(#)root/gl:$Id$
2 // Author: Timur Pocheptsov 28/07/2009
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2009, 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 "TVirtualPad.h"
13 #include "KeySymbols.h"
14 #include "TVirtualX.h"
15 #include "Buttons.h"
16 #include "TString.h"
17 #include "TError.h"
18 #include "TROOT.h"
19 #include "TMath.h"
20 
21 #include "TGLPlotCamera.h"
22 #include "TGL5DPainter.h"
23 #include "TGLPadUtils.h"
24 #include "TGLIncludes.h"
25 #include "TGL5D.h"
26 
27 /** \class TGL5DPainter
28 \ingroup opengl
29 TGL5DPainter implements "gl5d" option for TTree::Draw.
30 Data (4D) is visualized as a set of iso-surfaces. 5D.
31 */
32 
33 ////////////////////////////////////////////////////////////////////////////////
34 ///Constructor.
35 
36 TGL5DPainter::TGL5DPainter(TGL5DDataSet *data, TGLPlotCamera *camera, TGLPlotCoordinates *coord)
37  : TGLPlotPainter(data, camera, coord),
38  fMeshBuilder(kTRUE),//kTRUE == average normals.
39  fInit(kFALSE),
40  fData(data),
41  fShowSlider(kFALSE),
42  fAlpha(0.4),
43  fNContours(kNContours)
44 {
45  if (fData->fV4IsString)
46  fNContours = Int_t(fData->fV4MinMax.second) - Int_t(fData->fV4MinMax.first) + 1;
47 }
48 
49 ////////////////////////////////////////////////////////////////////////////////
50 ///Try to add new iso-surface.
51 ///If something goes wrong, return
52 ///iterator to the end of fIsos.
53 
54 TGL5DPainter::SurfIter_t TGL5DPainter::AddSurface(Double_t v4, Color_t ci,
55  Double_t iso, Double_t sigma,
56  Double_t range, Int_t lownps)
57 {
58  fData->SelectPoints(v4, range);
59 
60  if (fData->SelectedSize() < size_type(lownps)) {
61  Warning("TGL5DPainter::AddSurface", "Too little points: %d", Int_t(fData->SelectedSize()));
62  return fIsos.end();//This is a valid iterator, but an invalid surface.
63  } else {
64  Info("TGL5DPainter::AddSurface", "Selected %d points", Int_t(fData->SelectedSize()));
65  }
66 
67  fKDE.BuildModel(fData, sigma);//Prepare density estimator.
68 
69  Info("TGL5DPainter::AddSurface", "Building the mesh ...");
70  //Prepare grid parameters.
71  Rgl::Mc::TGridGeometry<Float_t> geom(fXAxis, fYAxis, fZAxis,
72  fCoord->GetXScale(),
73  fCoord->GetYScale(),
74  fCoord->GetZScale());
75  Mesh_t mesh;
76  fMeshBuilder.SetGeometry(fData);
77  //Build a new mesh.
78  fMeshBuilder.BuildMesh(&fKDE, geom, &mesh, iso);
79 
80  Info("TGL5DPainter::AddSurface", "Mesh has %d vertices", Int_t(mesh.fVerts.size() / 3));
81 
82  if (!mesh.fVerts.size())//I do not need an empty mesh.
83  return fIsos.end();
84  //Add surface with empty mesh and swap meshes.
85  fIsos.push_front(fDummy);
86 
87  fIsos.front().fMesh.Swap(mesh);
88  fIsos.front().f4D = v4;
89  fIsos.front().fRange = range;
90  fIsos.front().fShowCloud = kFALSE;
91  fIsos.front().fHide = kFALSE;
92  fIsos.front().fColor = ci;
93 
94  //Predictions for the 5-th variable.
95  //Not-implemented yet.
96  return fIsos.begin();
97 }
98 
99 ////////////////////////////////////////////////////////////////////////////////
100 ///Add new surface. Simplified version for ged.
101 
102 void TGL5DPainter::AddSurface(Double_t v4)
103 {
104  const Rgl::Range_t &v4R = fData->fV4MinMax;
105  const Bool_t isString = fData->fV4IsString;
106  const Double_t rms = TMath::RMS(fData->fNP, fData->fV4); //RMS of the N points.
107  const Double_t d = isString ? (v4R.second - v4R.first) / (fNContours - 1)
108  : 6 * rms / fNContours;
109  //alpha is in [0.1, 0.5], 1e-3 -s good for strings.
110  const Double_t range = isString ? 1e-3 : fAlpha * d;
111 
112  AddSurface(v4, 1, 0.125, 0.05, range);
113 }
114 
115 ////////////////////////////////////////////////////////////////////////////////
116 ///Remove iso-surface.
117 
118 void TGL5DPainter::RemoveSurface(SurfIter_t surf)
119 {
120  if (surf == fIsos.end()) {
121  Error("TGL5DPainter::RemoveSurface", "Invalid iterator, surface does not exist.");
122  return;
123  }
124 
125  fIsos.erase(surf);
126 }
127 
128 ////////////////////////////////////////////////////////////////////////////////
129 ///Return info for plot part under cursor.
130 
131 char *TGL5DPainter::GetPlotInfo(Int_t /*px*/, Int_t /*py*/)
132 {
133  static char mess[] = {"gl5d"};
134  return mess;
135 }
136 
137 ////////////////////////////////////////////////////////////////////////////////
138 ///Create mesh.
139 ///InitGeometry creates surfaces for auto-iso levels.
140 ///Called the first time and each time number of auto-levels is
141 ///reset via the editor.
142 
143 Bool_t TGL5DPainter::InitGeometry()
144 {
145  if (fInit)
146  return kTRUE;
147  //Only in cartesian.
148  fCoord->SetCoordType(kGLCartesian);
149 
150  if (!fCoord->SetRanges(fXAxis, fYAxis, fZAxis))
151  return kFALSE;
152 
153  fIsos.clear();
154 
155  fBackBox.SetPlotBox(fCoord->GetXRangeScaled(),
156  fCoord->GetYRangeScaled(),
157  fCoord->GetZRangeScaled());
158  if (fCamera)
159  fCamera->SetViewVolume(fBackBox.Get3DBox());
160 
161  const Rgl::Range_t &v4R = fData->fV4MinMax;
162  const Bool_t isString = fData->fV4IsString;
163 
164  //Rene's code to automatically find iso-levels.
165  const Double_t mean = TMath::Mean(fData->fNP, fData->fV4); //mean value of the NP points.
166  const Double_t rms = TMath::RMS(fData->fNP, fData->fV4); //RMS of the N points.
167  const Double_t min = isString ? v4R.first : mean - 3 * rms; //take a range +- 3*xrms
168  const Double_t d = isString ? (v4R.second - v4R.first) / (fNContours - 1)
169  : 6 * rms / fNContours;
170  //alpha is in [0.1, 0.5], 1e-3 -s good for strings.
171  const Double_t range = isString ? 1e-3 : fAlpha * d;
172 
173  Info("InitGeometry", "min = %g, mean = %g, rms = %g, dx = %g", min, mean, rms, d);
174 
175  for (Int_t j = 0; j < fNContours; ++j) {
176  const Double_t isoLevel = min + j * d;
177  Info("TGL5DPainter::InitGeometry", "Iso-level %g, range is %g ...", isoLevel, range);
178  const Color_t color = j * 6 + 1;
179  AddSurface(isoLevel, color, 0.125, 0.05, range);
180  }
181 
182  if (fIsos.size())
183  fBoxCut.TurnOnOff();
184 
185  return fInit = kTRUE;
186 }
187 
188 ////////////////////////////////////////////////////////////////////////////////
189 ///User clicks right mouse button (in a pad).
190 
191 void TGL5DPainter::StartPan(Int_t px, Int_t py)
192 {
193  fMousePosition.fX = px;
194  fMousePosition.fY = fCamera->GetHeight() - py;
195  fCamera->StartPan(px, py);
196  fBoxCut.StartMovement(px, fCamera->GetHeight() - py);
197 }
198 
199 ////////////////////////////////////////////////////////////////////////////////
200 ///Mouse events handler.
201 
202 void TGL5DPainter::Pan(Int_t px, Int_t py)
203 {
204  if (fSelectedPart >= fSelectionBase) {//Pan camera.
205  SaveModelviewMatrix();
206  SaveProjectionMatrix();
207 
208  fCamera->SetCamera();
209  fCamera->Apply(fPadPhi, fPadTheta);
210  fCamera->Pan(px, py);
211 
212  RestoreProjectionMatrix();
213  RestoreModelviewMatrix();
214  } else if (fSelectedPart > 0) {
215  //Convert py into bottom-top orientation.
216  py = fCamera->GetHeight() - py;
217 
218  SaveModelviewMatrix();
219  SaveProjectionMatrix();
220 
221  fCamera->SetCamera();
222  fCamera->Apply(fPadPhi, fPadTheta);
223 
224  if (!fHighColor) {
225  if (fBoxCut.IsActive() && (fSelectedPart >= kXAxis && fSelectedPart <= kZAxis)) {
226  fBoxCut.MoveBox(px, py, fSelectedPart);
227  }
228  }
229 
230  RestoreProjectionMatrix();
231  RestoreModelviewMatrix();
232  }
233 
234  fMousePosition.fX = px, fMousePosition.fY = py;
235  fUpdateSelection = kTRUE;
236 }
237 
238 ////////////////////////////////////////////////////////////////////////////////
239 ///No additional options for TGL5DPainter.
240 
241 void TGL5DPainter::AddOption(const TString &/*option*/)
242 {
243 }
244 
245 ////////////////////////////////////////////////////////////////////////////////
246 
247 void TGL5DPainter::ProcessEvent(Int_t event, Int_t /*px*/, Int_t py)
248 {
249  //Change color scheme.
250  if (event == kKeyPress) {
251  if (py == kKey_c || py == kKey_C) {
252  if (fHighColor)
253  Info("ProcessEvent", "Cut box does not work in high color, please, switch to true color");
254  else {
255  fBoxCut.TurnOnOff();
256  fUpdateSelection = kTRUE;
257  }
258  }
259  } else if (event == kButton1Double && fBoxCut.IsActive()) {
260  if (fBoxCut.IsActive())
261  fBoxCut.TurnOnOff();
262  if (!gVirtualX->IsCmdThread())
263  gROOT->ProcessLineFast(Form("((TGLPlotPainter *)0x%lx)->Paint()", (ULong_t)this));
264  else
265  Paint();
266  }
267 }
268 
269 ////////////////////////////////////////////////////////////////////////////////
270 ///Set selection range parameter.
271 
272 void TGL5DPainter::SetAlpha(Double_t newVal)
273 {
274  if (fAlpha != newVal && !fData->fV4IsString) {
275  fAlpha = newVal;
276  fInit = kFALSE;
277  InitGeometry();
278  }
279 
280  if (fData->fV4IsString)
281  Warning("SetAlpha", "Alpha is not required for string data (your 4-th dimension is string).");
282 }
283 
284 ////////////////////////////////////////////////////////////////////////////////
285 ///Set the number of predefined contours.
286 
287 void TGL5DPainter::SetNContours(Int_t n)
288 {
289  if (n <= 0) {
290  Warning("SetNContours", "Bad number of contours: %d", n);
291  return;
292  }
293 
294  fNContours = n;
295  fInit = kFALSE;
296  InitGeometry();
297 }
298 
299 ////////////////////////////////////////////////////////////////////////////////
300 ///No need to create or delete meshes,
301 ///number of meshes (iso-levels) are
302 ///the same, but meshes must be rebuilt
303 ///in new ranges.
304 ///Only in cartesian.
305 
306 void TGL5DPainter::ResetGeometryRanges()
307 {
308  fCoord->SetRanges(fXAxis, fYAxis, fZAxis);
309  fBackBox.SetPlotBox(fCoord->GetXRangeScaled(),
310  fCoord->GetYRangeScaled(),
311  fCoord->GetZRangeScaled());
312  if (fCamera)
313  fCamera->SetViewVolume(fBackBox.Get3DBox());
314  //Iterate through all surfaces and re-calculate them.
315  for (SurfIter_t surf = fIsos.begin(); surf != fIsos.end(); ++surf) {
316  fData->SelectPoints(surf->f4D, surf->fRange);
317  fKDE.BuildModel(fData, 0.05);//0.05 is sigma, will be controlled via GUI.
318  Info("TGL5DPainter::ResetGeometryRanges", "Building the mesh ...");
319  //Prepare grid parameters.
320  Rgl::Mc::TGridGeometry<Float_t> geom(fXAxis, fYAxis, fZAxis,
321  fCoord->GetXScale(),
322  fCoord->GetYScale(),
323  fCoord->GetZScale());
324  fMeshBuilder.SetGeometry(fData);
325  Mesh_t &mesh = surf->fMesh;
326  //Clear old data.
327  mesh.fVerts.clear();
328  mesh.fNorms.clear();
329  mesh.fTris.clear();
330  //Build new mesh.
331  fMeshBuilder.BuildMesh(&fKDE, geom, &mesh, 0.125);//0.125 will be set via GUI.
332  Info("TGL5DPainter::AddSurface", "Mesh has %d vertices", Int_t(mesh.fVerts.size() / 3));
333  }
334 
335  fBoxCut.ResetBoxGeometry();
336 }
337 
338 ////////////////////////////////////////////////////////////////////////////////
339 ///std::list::begin.
340 
341 TGL5DPainter::SurfIter_t TGL5DPainter::SurfacesBegin()
342 {
343  return fIsos.begin();
344 }
345 
346 ////////////////////////////////////////////////////////////////////////////////
347 ///std::list::end.
348 
349 TGL5DPainter::SurfIter_t TGL5DPainter::SurfacesEnd()
350 {
351  return fIsos.end();
352 }
353 
354 ////////////////////////////////////////////////////////////////////////////////
355 ///Initialize OpenGL state variables.
356 
357 void TGL5DPainter::InitGL() const
358 {
359  glEnable(GL_LIGHTING);
360  glEnable(GL_LIGHT0);
361  glEnable(GL_DEPTH_TEST);
362  glDisable(GL_CULL_FACE);
363  glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
364 }
365 
366 ////////////////////////////////////////////////////////////////////////////////
367 ///Return some gl states to original values.
368 
369 void TGL5DPainter::DeInitGL()const
370 {
371  glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
372  glDisable(GL_CULL_FACE);
373  glDisable(GL_DEPTH_TEST);
374  glDisable(GL_LIGHT0);
375  glDisable(GL_LIGHTING);
376 }
377 
378 ////////////////////////////////////////////////////////////////////////////////
379 ///Draw a set of meshes.
380 
381 void TGL5DPainter::DrawPlot() const
382 {
383  //Shift plot to point of origin.
384  const Rgl::PlotTranslation trGuard(this);
385 
386  fBackBox.DrawBox(fSelectedPart, fSelectionPass, fZLevels, fHighColor);
387  //
388  if (!fIsos.size())
389  DrawCloud();
390  else {
391  //Two passes. First, non-transparent surfaces.
392  Bool_t needSecondPass = kFALSE;
393  for (ConstSurfIter_t it = fIsos.begin(); it != fIsos.end(); ++it) {
394  //
395  if (it->fHide)
396  continue;
397  if (it->fAlpha != 100) {
398  needSecondPass = kTRUE;
399  continue;
400  }
401  if (!fSelectionPass)
402  SetSurfaceColor(it);
403  glEnable(GL_POLYGON_OFFSET_FILL);
404  glPolygonOffset(1.f, 1.f);
405  DrawMesh(it);
406  glDisable(GL_POLYGON_OFFSET_FILL);
407 
408  if (!fSelectionPass && it->fHighlight) {
409  const TGLDisableGuard lightGuard(GL_LIGHTING);
410  const TGLEnableGuard blendGuard(GL_BLEND);
411  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
412  glColor4d(1., 0.4, 0., 0.5);
413  DrawMesh(it);
414  }
415  }
416  //Second pass - semi-transparent surfaces.
417  if (needSecondPass) {
418  const TGLEnableGuard blendGuard(GL_BLEND);
419  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
420  glDepthMask(GL_FALSE);
421  for (ConstSurfIter_t it = fIsos.begin(); it != fIsos.end(); ++it) {
422  //
423  if (it->fAlpha == 100)
424  continue;
425  if (!fSelectionPass)
426  SetSurfaceColor(it);
427 
428  glEnable(GL_POLYGON_OFFSET_FILL);
429  glPolygonOffset(1.f, 1.f);
430  DrawMesh(it);
431  glDisable(GL_POLYGON_OFFSET_FILL);
432 
433  if (!fSelectionPass && it->fHighlight) {
434  const TGLDisableGuard lightGuard(GL_LIGHTING);
435  glColor4d(1., 0.4, 0., it->fAlpha / 150.);
436  DrawMesh(it);
437  }
438  }
439  glDepthMask(GL_TRUE);
440  }
441  }
442 
443  if (fBoxCut.IsActive())
444  fBoxCut.DrawBox(fSelectionPass, fSelectedPart);
445 }
446 
447 ////////////////////////////////////////////////////////////////////////////////
448 ///Set the color for iso-surface.
449 
450 void TGL5DPainter::SetSurfaceColor(ConstSurfIter_t it)const
451 {
452  Color_t ind = it->fColor;
453  Float_t rgba[] = {0.f, 0.f, 0.f, static_cast<Float_t>(it->fAlpha / 100.)};
454  Rgl::Pad::ExtractRGBA(ind, rgba);
455  //Set color for surface.
456  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, rgba);
457  const Float_t specColor[] = {1.f, 1.f, 1.f, 1.f};
458  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specColor);
459  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.f);
460 }
461 
462 ////////////////////////////////////////////////////////////////////////////////
463 ///Draw full cloud of points.
464 
465 void TGL5DPainter::DrawCloud()const
466 {
467  const TGLDisableGuard light(GL_LIGHTING);
468  const TGLDisableGuard depth(GL_DEPTH_TEST);
469 
470  glColor3d(0.4, 0., 1.);
471  glPointSize(3.f);
472 
473  glBegin(GL_POINTS);
474 
475  const Double_t xs = fCoord->GetXScale();
476  const Double_t ys = fCoord->GetYScale();
477  const Double_t zs = fCoord->GetZScale();
478 
479  for (Int_t i = 0; i < fData->fNP; ++i)
480  glVertex3d(fData->fV1[i] * xs, fData->fV2[i] * ys, fData->fV3[i] * zs);
481 
482  glEnd();
483 
484  glPointSize(1.f);
485 }
486 
487 ////////////////////////////////////////////////////////////////////////////////
488 ///Draw cloud for selected iso-surface.
489 
490 void TGL5DPainter::DrawSubCloud(Double_t v4, Double_t range, Color_t ci)const
491 {
492  const TGLDisableGuard light(GL_LIGHTING);
493 
494  Float_t rgba[4] = {};
495  Rgl::Pad::ExtractRGBA(ci, rgba);
496 
497  glColor3fv(rgba);
498  glPointSize(3.f);
499 
500  glBegin(GL_POINTS);
501 
502  const Double_t xs = fCoord->GetXScale();
503  const Double_t ys = fCoord->GetYScale();
504  const Double_t zs = fCoord->GetZScale();
505 
506  for (Int_t i = 0; i < fData->fNP; ++i)
507  if (TMath::Abs(fData->fV4[i] - v4) < range)
508  glVertex3d(fData->fV1[i] * xs, fData->fV2[i] * ys, fData->fV3[i] * zs);
509 
510  glEnd();
511 
512  glPointSize(1.f);
513 }
514 
515 ////////////////////////////////////////////////////////////////////////////////
516 ///Draw one iso-surface.
517 
518 void TGL5DPainter::DrawMesh(ConstSurfIter_t surf)const
519 {
520  const Mesh_t &m = surf->fMesh;
521 
522  if (!fBoxCut.IsActive()) {
523  if (!fSelectionPass)
524  Rgl::DrawMesh(m.fVerts, m.fNorms, m.fTris);
525  else {
526  Rgl::ObjectIDToColor(fSelectionBase, fHighColor);
527  Rgl::DrawMesh(m.fVerts, m.fTris);
528  }
529  } else {
530  if (!fSelectionPass) {
531  Rgl::DrawMesh(m.fVerts, m.fNorms, m.fTris, fBoxCut);
532  } else {
533  Rgl::ObjectIDToColor(fSelectionBase, fHighColor);
534  Rgl::DrawMesh(m.fVerts, m.fTris, fBoxCut);
535  }
536  }
537 }