Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TParallelCoordVar.cxx
Go to the documentation of this file.
1 // @(#)root/treeviewer:$Id$
2 // Author: Bastien Dalla Piazza 02/08/2007
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2007, 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 "TParallelCoordVar.h"
13 #include "TParallelCoord.h"
14 #include "TParallelCoordRange.h"
15 
16 #include "Riostream.h"
17 #include "TROOT.h"
18 #include "TLatex.h"
19 #include "TLine.h"
20 #include "TVirtualPad.h"
21 #include "TMath.h"
22 #include "TH1.h"
23 #include "TStyle.h"
24 #include "TBox.h"
25 #include "TCollection.h"
26 #include "TList.h"
27 #include "TFrame.h"
28 #include "TCanvas.h"
29 #include "TMarker.h"
30 
31 ClassImp(TParallelCoordVar);
32 
33 /** \class TParallelCoordVar
34 
35 TParallelCoord axes. Class containing a variable for the TParallelCoord.
36 
37 Options can be defined each axis separately using the right mouse click. These
38 options can be applied to every axes using the editor.
39 
40  - Axis width: If set to 0, the axis is simply a line. If higher, a color
41  histogram is drawn on the axis.
42  - Axis histogram height: If not 0, a usual bar histogram is drawn on the plot.
43 
44 The order in which the variables are drawn is essential to see the clusters. The
45 axes can be dragged to change their position. A zoom is also available. The
46 logarithm scale is also available by right clicking on the axis.
47 */
48 
49 ////////////////////////////////////////////////////////////////////////////////
50 /// Default constructor.
51 
52 TParallelCoordVar::TParallelCoordVar()
53  :TNamed(), TAttLine(), TAttFill(kOrange+9,0)
54 {
55  Init();
56 }
57 
58 ////////////////////////////////////////////////////////////////////////////////
59 ///Destructor.
60 
61 TParallelCoordVar::~TParallelCoordVar()
62 {
63  if (fHistogram) delete fHistogram;
64  if (fRanges){
65  TIter next(fRanges);
66  TParallelCoordRange* range;
67  while((range = (TParallelCoordRange*)next())) fParallel->CleanUpSelections(range);
68  fRanges->Delete();
69  delete fRanges;
70  }
71  if (fVal) delete [] fVal;
72 }
73 
74 ////////////////////////////////////////////////////////////////////////////////
75 /// Normal constructor. By default, the title and the name are the expression
76 /// given to TTree::Draw. The name can be changed by the user (the label on the
77 /// plot) but not the title.
78 
79 TParallelCoordVar::TParallelCoordVar(Double_t *val, const char* title, Int_t id, TParallelCoord* parallel)
80  :TNamed(title,title), TAttLine(1,1,1), TAttFill(kOrange+9,3001)
81 {
82  Init();
83  fId = id;
84  fParallel = parallel;
85  fRanges = new TList();
86  fNentries = fParallel->GetNentries();
87 
88  fVal = new Double_t[fParallel->GetNentries()];
89 
90  for(Long64_t ui = 0;ui<fParallel->GetNentries();++ui) fVal[ui]=val[ui];
91 
92  GetMinMaxMean();
93  GetHistogram();
94  GetQuantiles();
95 }
96 
97 ////////////////////////////////////////////////////////////////////////////////
98 /// Add a range to the current selection on the axis.
99 
100 void TParallelCoordVar::AddRange(TParallelCoordRange* range)
101 {
102  if (!range) {
103  TParallelCoordSelect *select = fParallel->GetCurrentSelection();
104  if (select) {
105  range = new TParallelCoordRange(this,0,0,select);
106  fRanges->Add(range);
107  range->GetSelection()->Add(range);
108  } else {
109  Error("AddRange","You must create a selection before adding ranges.");
110  }
111  } else {
112  fRanges->Add(range);
113  range->GetSelection()->Add(range);
114  }
115 }
116 
117 ////////////////////////////////////////////////////////////////////////////////
118 /// Delete variables.
119 
120 void TParallelCoordVar::DeleteVariable()
121 {
122  fParallel->RemoveVariable(this);
123  delete this;
124 }
125 
126 ////////////////////////////////////////////////////////////////////////////////
127 /// Computes the distance from the axis.
128 
129 Int_t TParallelCoordVar::DistancetoPrimitive(Int_t px, Int_t py)
130 {
131  if(!gPad) return 9999;
132  Double_t xx = gPad->AbsPixeltoX(px);
133  Double_t yy = gPad->AbsPixeltoY(py);
134  Double_t dist = 9999;
135 
136  if (fX1==fX2) {
137  if (yy>fY1+0.01 && yy<fY2-0.01) dist = TMath::Abs(xx - fX1);
138  } else {
139  if (xx>fX1+0.01 && xx<fX2-0.01) dist = TMath::Abs(yy - fY1);
140  }
141  if (dist<=0.005) return 0;
142  else return 9999;
143 }
144 
145 ////////////////////////////////////////////////////////////////////////////////
146 /// Draw the axis.
147 
148 void TParallelCoordVar::Draw(Option_t *option)
149 {
150  AppendPad(option);
151 }
152 
153 ////////////////////////////////////////////////////////////////////////////////
154 /// Check if the entry is within the range(s) of "select". Two ranges on a
155 /// single axis are conjugated as a "or": to be selected, the entry must be in
156 /// one of the ranges.
157 
158 Bool_t TParallelCoordVar::Eval(Long64_t evtidx, TParallelCoordSelect *select)
159 {
160  if (fRanges->GetSize() > 0){
161  TIter next(fRanges);
162  Bool_t inarange = kFALSE;
163  Bool_t noOwnedRange = kTRUE;
164  TParallelCoordRange *range;
165  while ((range = (TParallelCoordRange*)next())){
166  if(select->Contains(range)) {
167  noOwnedRange = kFALSE;
168  if(range->IsIn(fVal[evtidx])) inarange = kTRUE;
169  }
170  }
171  if (noOwnedRange) return kTRUE;
172  else return inarange;
173  }
174  else return kTRUE;
175 }
176 
177 ////////////////////////////////////////////////////////////////////////////////
178 /// Execute the corresponding entry.
179 
180 void TParallelCoordVar::ExecuteEvent(Int_t entry, Int_t px, Int_t py)
181 {
182  if (!gPad) return;
183  if (!gPad->IsEditable() && entry!=kMouseEnter) return;
184 
185  static Int_t pxold, pyold;
186  static Int_t zoom; // -1:nothing zoomed or translated, 0:translating the axis, 1:zooming
187  static Int_t pzoomold;
188  static Bool_t first = kTRUE;
189 
190  Int_t px1,px2,py1,py2,n=-1;
191  px1 = gPad->XtoAbsPixel(fX1);
192  px2 = gPad->XtoAbsPixel(fX2);
193  py1 = gPad->YtoAbsPixel(fY1);
194  py2 = gPad->YtoAbsPixel(fY2);
195 
196  if(fX1 == fX2) {
197  if(gPad->AbsPixeltoX(px)-fX1 > 0) gPad->SetCursor(kArrowVer);
198  else gPad->SetCursor(kArrowHor);
199  } else {
200  if(gPad->AbsPixeltoY(py)-fY1 > 0) gPad->SetCursor(kArrowHor);
201  else gPad->SetCursor(kArrowVer);
202  }
203 
204  gVirtualX->SetLineColor(-1);
205  switch (entry) {
206  case kButton1Down:
207  if (fX1==fX2){
208  ((TCanvas*)gPad)->Selected(gPad,fParallel,1);
209  if(gPad->AbsPixeltoX(px)-fX1 > 0){
210  zoom = 1;
211  gVirtualX->DrawLine(gPad->XtoAbsPixel(fX1-0.05),py,gPad->XtoAbsPixel(fX1+0.05),py);
212  first = kTRUE;
213  pzoomold = py;
214  } else {
215  zoom = 0;
216  gVirtualX->DrawLine(px,py1,px,py2);
217  }
218  } else {
219  if(gPad->AbsPixeltoY(py)-fY1 > 0){
220  zoom = 1;
221  gVirtualX->DrawLine(px,gPad->YtoAbsPixel(fY1-0.05),px,gPad->YtoAbsPixel(fY1+0.05));
222  first=kTRUE;
223  pzoomold = px;
224  } else {
225  zoom = 0;
226  gVirtualX->DrawLine(px1,py,px2,py);
227  }
228  }
229  pxold = px;
230  pyold = py;
231  break;
232  case kButton1Up: {
233  Double_t xx = gPad->AbsPixeltoX(px);
234  Double_t yy = gPad->AbsPixeltoY(py);
235  TFrame *frame = gPad->GetFrame();
236  if (fX1==fX2) {
237  if(zoom == 0){
238  Double_t axisSpace = (frame->GetX2() - frame->GetX1())/(fParallel->GetNvar() - 1);
239  Double_t pos = (xx - frame->GetX1())/axisSpace;
240  if (pos < 0) n = -1;
241  else n = (Int_t)pos;
242  } else {
243  Double_t min = GetValuefromXY(xx,yy);
244  Double_t max = GetValuefromXY(xx,gPad->AbsPixeltoY(pzoomold));
245  if(TMath::Abs(min-max) < 0.00001) return; // Avoid zooming if the axis is just clicked.
246  if (fParallel->TestBit(TParallelCoord::kGlobalScale)) {
247  if (min>max) {
248  Double_t mem = min;
249  min = max; max = mem;
250  }
251  fParallel->SetGlobalMin(min);
252  fParallel->SetGlobalMax(max);
253  } else {
254  SetCurrentLimits(min,max);
255  }
256  }
257  } else {
258  if(zoom == 0) {
259  Double_t axisSpace = (frame->GetY2() - frame->GetY1())/(fParallel->GetNvar() - 1);
260  Double_t pos = (yy-frame->GetY1())/axisSpace;
261  if (pos < 0) n= -1;
262  else n = (Int_t)pos;
263  } else {
264  Double_t min = GetValuefromXY(xx,yy);
265  Double_t max = GetValuefromXY(gPad->AbsPixeltoX(pzoomold),yy);
266  SetCurrentLimits(min,max);
267  }
268  }
269  if(zoom == 0){
270  if (n>=0 && (UInt_t)n>=fParallel->GetNvar()) --n;
271  else if (n<fParallel->GetVarList()->IndexOf(this)) ++n;
272  fParallel->GetVarList()->Remove(this);
273  fParallel->GetVarList()->AddAt(this,n);
274  }
275  gPad->Modified();
276  break;
277  }
278  case kMouseMotion:
279  pxold=px;
280  pyold=py;
281  break;
282  case kButton1Motion:
283  if(fX1==fX2){
284  if(zoom==0){
285  gPad->SetCursor(kArrowHor);
286  gVirtualX->DrawLine(pxold,py1,pxold,py2);
287  gVirtualX->DrawLine(px,py1,px,py2);
288  } else if(zoom==1) {
289  gPad->SetCursor(kArrowVer);
290  if(!first) gVirtualX->DrawLine(gPad->XtoAbsPixel(fX1-0.05),pyold,gPad->XtoAbsPixel(fX1+0.05),pyold);
291  gVirtualX->DrawLine(gPad->XtoAbsPixel(fX1-0.05),py,gPad->XtoAbsPixel(fX1+0.05),py);
292  first = kFALSE;
293  }
294  } else {
295  if(zoom==0){
296  gPad->SetCursor(kArrowVer);
297  gVirtualX->DrawLine(px1,pyold,px2,pyold);
298  gVirtualX->DrawLine(px1,py,px2,py);
299  } else if(zoom==1){
300  gPad->SetCursor(kArrowHor);
301  if(!first) gVirtualX->DrawLine(pxold,gPad->YtoAbsPixel(fY1-0.05),pxold,gPad->YtoAbsPixel(fY1+0.05));
302  gVirtualX->DrawLine(px,gPad->YtoAbsPixel(fY1-0.05),px,gPad->YtoAbsPixel(fY1+0.05));
303  first = kFALSE;
304  }
305  }
306  pxold = px;
307  pyold = py;
308  break;
309  }
310 }
311 
312 ////////////////////////////////////////////////////////////////////////////////
313 /// Get the position of the variable on the graph for the n'th entry.
314 
315 void TParallelCoordVar::GetEntryXY(Long64_t n, Double_t & x, Double_t & y)
316 {
317  if(fX1==fX2){
318  x = fX1;
319  if (fMinCurrent != fMaxCurrent) {
320  if (TestBit(kLogScale)) y = fY1 + (fY2 - fY1) *
321  (TMath::Log10(fVal[n]/fMinCurrent)) / (TMath::Log10(fMaxCurrent/fMinCurrent));
322  else y = fY1 + (fY2 - fY1) *
323  (fVal[n] - fMinCurrent) / (fMaxCurrent - fMinCurrent);
324  } else {
325  y = fY1 + 0.5*(fY2-fY1);
326  }
327  } else {
328  y = fY1;
329  if (fMinCurrent != fMaxCurrent) {
330  if (TestBit(kLogScale)) x = fX1 + (fX2 - fX1) *
331  (TMath::Log10(fVal[n]/fMinCurrent)) / (TMath::Log10(fMaxCurrent/fMinCurrent));
332  else x = fX1 + (fX2 - fX1) *
333  (fVal[n] - fMinCurrent) / (fMaxCurrent - fMinCurrent);
334  } else {
335  x = fX1 + 0.5*(fX2-fX1);
336  }
337  }
338 }
339 
340 ////////////////////////////////////////////////////////////////////////////////
341 /// Get the entry weight: The weight of an entry for a given variable
342 /// is the bin content of the histogram bin the entry is going through.
343 
344 Int_t TParallelCoordVar::GetEntryWeight(Long64_t evtidx)
345 {
346  Int_t bin = 1 + (Int_t)((fVal[evtidx] - fMinCurrent)/((fMaxCurrent-fMinCurrent)/fNbins));
347  return (Int_t)fHistogram->GetBinContent(bin);
348 }
349 
350 ////////////////////////////////////////////////////////////////////////////////
351 /// Create or recreate the histogram.
352 
353 TH1F* TParallelCoordVar::GetHistogram()
354 {
355  if (fHistogram) delete fHistogram;
356  fHistogram = NULL;
357  fHistogram = new TH1F("hpa", "hpa", fNbins, fMinCurrent, fMaxCurrent+0.0001*(fMaxCurrent-fMinCurrent));
358  fHistogram->SetDirectory(0);
359  Long64_t first = fParallel->GetCurrentFirst();
360  Long64_t nentries = fParallel->GetCurrentN();
361  for(Long64_t li=first; li<first+nentries;++li) {
362  if(fVal[li] >= fMinCurrent && fVal[li] <= fMaxCurrent) fHistogram->Fill(fVal[li]);
363  }
364  return fHistogram;
365 }
366 
367 ////////////////////////////////////////////////////////////////////////////////
368 /// Get mean, min and max of those variable.
369 
370 void TParallelCoordVar::GetMinMaxMean()
371 {
372  Double_t min,max,ave = 0;
373  min = DBL_MAX;
374  max = -DBL_MAX;
375  Long64_t first,nentries;
376  first = fParallel->GetCurrentFirst();
377  nentries = fParallel->GetCurrentN();
378  for(Long64_t li=first; li<first+nentries;++li){
379  if(fVal[li]<min) min = fVal[li];
380  if(fVal[li]>max) max = fVal[li];
381  ave+=fVal[li];
382  }
383 
384  fMean = ave/((Double_t)nentries);
385  fMinCurrent = fMinInit = min;
386  fMaxCurrent = fMaxInit = max;
387 }
388 
389 ////////////////////////////////////////////////////////////////////////////////
390 /// Returns info about this axis.
391 
392 char* TParallelCoordVar::GetObjectInfo(Int_t px, Int_t py) const
393 {
394  static char info[128];
395  info[0] = 0;
396 
397  if (!gPad) return info;
398  Double_t xx = gPad->AbsPixeltoX(px);
399  Double_t yy = gPad->AbsPixeltoY(py);
400  if (fX1 == fX2) {
401  if (yy<fY1) {
402  snprintf(info,128,"%s = %f", GetTitle(), fMinCurrent);
403  } else if (yy>fY2) {
404  snprintf(info,128,"%s = %f", GetTitle(), fMaxCurrent);
405  } else {
406  Double_t axislength = fY2-fY1;
407  Double_t pos = (yy-fY1)/axislength;
408  snprintf(info,128,"%s = %f", GetTitle(), fMinCurrent + pos*(fMaxCurrent-fMinCurrent));
409  }
410  } else {
411  if (xx<fX1) {
412  snprintf(info,128,"%s = %f", GetTitle(), fMinCurrent);
413  } else if(xx>fX2) {
414  snprintf(info,128,"%s = %f", GetTitle(), fMaxCurrent);
415  } else {
416  Double_t axislength = fX2-fX1;
417  Double_t pos = (xx-fX1)/axislength;
418  snprintf(info,128,"%s = %f", GetTitle(), pos*(fMaxCurrent-fMinCurrent));
419  }
420  }
421  return info;
422 }
423 
424 ////////////////////////////////////////////////////////////////////////////////
425 /// Get the box plot values (quantiles).
426 
427 void TParallelCoordVar::GetQuantiles()
428 {
429  Double_t *quantiles = new Double_t[3];
430  quantiles[0]=0.; quantiles[1]=0.; quantiles[2] = 0.;
431  Double_t *prob = new Double_t[3];
432  prob[0]=0.25; prob[1]=0.5; prob[2] = 0.75;
433  Long64_t first = fParallel->GetCurrentFirst();
434  Long64_t nentries = fParallel->GetCurrentN();
435  if (!TestBit(kLogScale) && first==0 && nentries==fNentries) TMath::Quantiles(fNentries,3,fVal,quantiles,prob,kFALSE);
436  else {
437  Double_t* val = new Double_t[nentries];
438  Int_t selected = 0;
439  if(fMinInit<=0) {
440  for (Long64_t n=first;n<first+nentries;++n) {
441  if (fVal[n] >= fMinCurrent) {
442  if (TestBit(kLogScale)) val[selected] = TMath::Log10(fVal[n]);
443  else val[selected] = fVal[n];
444  ++selected;
445  }
446  }
447  } else {
448  for (Long64_t n=first;n<first+nentries;++n) {
449  if (TestBit(kLogScale)) val[selected] = TMath::Log10(fVal[n]);
450  else val[selected] = fVal[n];
451  ++selected;
452  }
453  }
454  TMath::Quantiles(selected,3,val,quantiles,prob,kFALSE);
455  delete [] val;
456  }
457  fQua1 = quantiles[0];
458  fMed = quantiles[1];
459  fQua3 = quantiles[2];
460  delete [] quantiles;
461  delete [] prob;
462 }
463 
464 ////////////////////////////////////////////////////////////////////////////////
465 /// Get the value corresponding to the position.
466 
467 Double_t TParallelCoordVar::GetValuefromXY(Double_t x,Double_t y)
468 {
469  Double_t pos;
470  if (fMinCurrent == fMaxCurrent) return fMinCurrent;
471  if (fX1 == fX2) {
472  if (y<=fY1) pos = fMinCurrent;
473  else if (y>=fY2) pos = fMaxCurrent;
474  else pos = fMinCurrent + ((y-fY1)/(fY2-fY1))*(fMaxCurrent-fMinCurrent);
475  } else {
476  if (x<=fX1) pos = fMinCurrent;
477  else if (x>=fX2) pos = fMaxCurrent;
478  else pos = fMinCurrent + ((x-fX1)/(fX2-fX1))*(fMaxCurrent-fMinCurrent);
479  }
480  return pos;
481 }
482 
483 ////////////////////////////////////////////////////////////////////////////////
484 /// Get a position corresponding to the value on the axis.
485 
486 void TParallelCoordVar::GetXYfromValue(Double_t value, Double_t & x, Double_t & y)
487 {
488  if(value < fMinCurrent || value > fMaxCurrent) return;
489 
490  if (fX1==fX2) {
491  x = fX1;
492  if (fMinCurrent != fMaxCurrent) {
493  if (TestBit(kLogScale)) y = fY1 + (fY2 - fY1) *
494  (TMath::Log10(value/fMinCurrent)) / (TMath::Log10(fMaxCurrent/fMinCurrent));
495  else y = fY1 + (fY2 - fY1) *
496  (value - fMinCurrent) / (fMaxCurrent - fMinCurrent);
497  } else {
498  y = fY1 + 0.5*(fY2-fY1);
499  }
500  } else {
501  y = fY1;
502  if (fMinCurrent != fMaxCurrent) {
503  if (TestBit(kLogScale)) x = fX1 + (fX2 - fX1) *
504  (TMath::Log10(value/fMinCurrent)) / (TMath::Log10(fMaxCurrent/fMinCurrent));
505  else x = fX1 + (fX2 - fX1) *
506  (value - fMinCurrent) / (fMaxCurrent - fMinCurrent);
507  } else {
508  x = fX1 + 0.5*(fX2-fX1);
509  }
510  }
511 }
512 
513 ////////////////////////////////////////////////////////////////////////////////
514 /// Initialise the TParallelVar variables.
515 
516 void TParallelCoordVar::Init()
517 {
518  fX1 = 0;
519  fX2 = 0;
520  fY1 = 0;
521  fY2 = 0;
522  fId = 0;
523  fVal = NULL;
524  fMean = 0;
525  fMinInit = 0;
526  fMinCurrent = 0;
527  fMaxInit = 0;
528  fMaxCurrent = 0;
529  fMed = 0;
530  fQua1 = 0;
531  fQua3 = 0;
532  fNentries = 0;
533  fParallel = NULL;
534  fHistogram = NULL;
535  fNbins = 100;
536  fHistoLW = 2;
537  fHistoHeight = 0.5;
538  fRanges = NULL;
539  SetBit(kLogScale,kFALSE);
540  SetBit(kShowBox,kFALSE);
541  SetBit(kShowBarHisto,kTRUE);
542 }
543 
544 ////////////////////////////////////////////////////////////////////////////////
545 /// Paint the axis.
546 
547 void TParallelCoordVar::Paint(Option_t* option)
548 {
549  TIter next(fRanges);
550  TParallelCoordRange* range;
551  while ((range = (TParallelCoordRange*)next())) range->Paint(option);
552 
553  PaintHistogram();
554  if (TestBit(kShowBox)) PaintBoxPlot();
555  PaintLabels();
556 }
557 
558 ////////////////////////////////////////////////////////////////////////////////
559 /// Paint the boxes in the case of a candle chart.
560 
561 void TParallelCoordVar::PaintBoxPlot()
562 {
563  TLine *line = new TLine();
564  line->SetLineColor(GetLineColor());
565  line->SetLineWidth(1);
566  TBox *box = new TBox();
567  box->SetLineWidth(1);
568  box->SetLineColor(GetLineColor());
569  box->SetLineStyle(1);
570  box->SetFillStyle(0);
571 
572  TFrame* frame = gPad->GetFrame();
573 
574  Double_t boxSize;
575  if (fParallel->GetNvar() > 1) {
576  if (fX1==fX2) boxSize = fHistoHeight*((frame->GetY2()-frame->GetY1())/(fParallel->GetNvar()-1));
577  else boxSize = fHistoHeight*((frame->GetX2()-frame->GetX1())/(fParallel->GetNvar()-1));
578  if (boxSize >= 0.03) boxSize = 0.03;
579  }
580  else boxSize = 0.03;
581 
582  Double_t qua1,med,qua3,max,min;
583  Double_t a,b,maxinit,mininit;
584  if (TestBit(kLogScale)) {
585  a = TMath::Log10(fMinCurrent);
586  b = TMath::Log10(fMaxCurrent/fMinCurrent);
587  if(fMinInit > 0) mininit = TMath::Log10(fMinInit);
588  else mininit = TMath::Log10(fMinCurrent);
589  maxinit = TMath::Log10(fMaxInit);
590  } else {
591  a = fMinCurrent;
592  b = fMaxCurrent-fMinCurrent;
593  mininit = fMinInit;
594  maxinit = fMaxInit;
595  }
596  if(fX1==fX2) {
597  qua1 = fY1 + ((fQua1-a)/b)*(fY2-fY1);
598  qua3 = fY1 + ((fQua3-a)/b)*(fY2-fY1);
599  med = fY1 + ((fMed-a)/b)*(fY2-fY1);
600  max = fY1 + ((maxinit-a)/b)*(fY2-fY1);
601  min = fY1 + ((mininit-a)/b)*(fY2-fY1);
602  } else {
603  qua1 = fX1 + ((fQua1-a)/b)*(fX2-fX1);
604  qua3 = fX1 + ((fQua3-a)/b)*(fX2-fX1);
605  med = fX1 + ((fMed-a)/b)*(fX2-fX1);
606  max = fX1 + ((maxinit-a)/b)*(fX2-fX1);
607  min = fX1 + ((mininit-a)/b)*(fX2-fX1);
608  }
609 
610  // min and max lines.
611  if (fX1==fX2) {
612  line->PaintLine(fX1-boxSize,min,fX1+boxSize,min);
613  line->PaintLine(fX2-boxSize,max,fX2+boxSize,max);
614  } else {
615  line->PaintLine(min,fY1-boxSize,min,fY1+boxSize);
616  line->PaintLine(max,fY2-boxSize,max,fY2+boxSize);
617  }
618 
619  // lines from min and max to the box.
620  line->SetLineStyle(7);
621  if (fX1==fX2) {
622  if (min<frame->GetY1()) min = frame->GetY1();
623  if (max>frame->GetY2()) max = frame->GetY2();
624  line->PaintLine(fX1,min,fX1,qua1);
625  line->PaintLine(fX1,qua3,fX1,max);
626  } else {
627  if (min<frame->GetX1()) min = frame->GetX1();
628  if (max>frame->GetX2()) max = frame->GetX2();
629  line->PaintLine(min,fY1,qua1,fY2);
630  line->PaintLine(qua3,fY1,max,fY2);
631  }
632 
633  // Box
634  if(fX1==fX2) box->PaintBox(fX1-boxSize,qua1,fX1+boxSize,qua3);
635  else box->PaintBox(qua1,fY1-boxSize,qua3,fY1+boxSize);
636 
637  // Median line
638  line->SetLineStyle(1);
639  if(fX1==fX2) line->PaintLine(fX1-boxSize,med,fX1+boxSize,med);
640  else line->PaintLine(med,fY1-boxSize,med,fY1+boxSize);
641 
642  // Paint average
643  if (!TestBit(kLogScale) || (TestBit(kLogScale) && fMean > 0)) {
644  Double_t mean;
645  if (TestBit(kLogScale)) mean = TMath::Log10(fMean);
646  else mean = fMean;
647  TMarker *mark = NULL;
648  if(fX1==fX2) mark = new TMarker(fX1,fY1 + ((mean-a)/b)*(fY2-fY1),24);
649  else mark = new TMarker(fX1 + ((mean-a)/b)*(fX2-fX1),fY1,24);
650  mark->Paint();
651  delete mark;
652  }
653 
654  delete line;
655  delete box;
656 }
657 
658 ////////////////////////////////////////////////////////////////////////////////
659 /// Paint the histogram on the axis.
660 
661 void TParallelCoordVar::PaintHistogram()
662 {
663  Int_t i;
664 
665  TFrame *frame = gPad->GetFrame();
666 
667  if (!fHistogram) GetHistogram();
668 
669  // Paint the axis body.
670  if (fHistoHeight!=0 && TestBit(kShowBarHisto)) {
671  // Paint the axis body using bar chart.
672  TBox *b = new TBox();
673  b->SetFillStyle(GetFillStyle());
674  b->SetFillColor(GetFillColor());
675  b->SetLineStyle(1);
676  b->SetLineColor(GetFillColor());
677  b->SetLineWidth(1);
678  Double_t hmin = fHistogram->GetMinimum();
679  Double_t hmax = fHistogram->GetMaximum();
680  if (fX1 == fX2) {
681  // Vertical case.
682  Double_t dy = (fY2-fY1)/fNbins;
683  Double_t dv = (fMaxCurrent - fMinCurrent)/fNbins;
684  Double_t v = fMinCurrent;
685  Double_t y1 = fY1,x2,y2;
686  for (i=1; i<=fNbins; i++) {
687  x2 = fX1+((fHistogram->GetBinContent(i)-hmin)/(hmax-hmin))*fHistoHeight*
688  ((frame->GetX2()-frame->GetX1())/(fParallel->GetNvar()-1));
689  if(TestBit(kLogScale)) y2 = fY1 + (fY2-fY1)*(TMath::Log10((v+dv)/fMinCurrent)) / (TMath::Log10(fMaxCurrent/fMinCurrent));
690  else y2=y1+dy;
691  b->PaintBox(fX1,y1,x2,y2,"l");
692  y1=y2;
693  v += dv;
694  }
695  } else {
696  // Horizontal case.
697  Double_t dx = (fX2-fX1)/fNbins;
698  Double_t dv = (fMaxCurrent - fMinCurrent)/fNbins;
699  Double_t v = fMinCurrent;
700  Double_t x1 = fX1,x2,y2;
701  for (i=1; i<=fNbins; i++) {
702  y2 = fY1+((fHistogram->GetBinContent(i)-hmin)/(hmax-hmin))*fHistoHeight*((frame->GetY2()-frame->GetY1())/(fParallel->GetNvar()-1));
703  if(TestBit(kLogScale)) x2 = fX1 + (fX2-fX1)*(TMath::Log10((v+dv)/fMinCurrent)) / (TMath::Log10(fMaxCurrent/fMinCurrent));
704  else x2=x1+dx;
705  b->PaintBox(x1,fY1,x2,y2,"l");
706  x1=x2;
707  v+=dv;
708  }
709  }
710  delete b;
711  }
712  if (fHistoLW==0 && !TestBit(kShowBox)) {
713  // Paint the axis body as a simple line.
714  TLine* l = new TLine(fX1,fY1,fX2,fY2);
715  l->SetLineWidth(GetLineWidth());
716  l->SetLineColor(GetLineColor());
717  l->SetLineStyle(GetLineColor());
718  l->Paint();
719  delete l;
720  } else if (fHistoLW!=0){
721  // Paint the axis body using the color palette.
722  TLine *lb = new TLine();
723  lb->SetLineWidth(fHistoLW);
724  Double_t hmin = fHistogram->GetMinimum();
725  Double_t hmax = fHistogram->GetMaximum();
726  Int_t theColor;
727  Int_t ncolors = gStyle->GetNumberOfColors();
728  if (fX1 == fX2) {
729  // Vertical case.
730  Double_t dy = (fY2-fY1)/fNbins;
731  Double_t y1 = fY1,y2;
732  Double_t dv = (fMaxCurrent - fMinCurrent)/fNbins;
733  Double_t v = fMinCurrent;
734  for (i=1; i<=fNbins; i++) {
735  theColor = (Int_t)( ((fHistogram->GetBinContent(i)-hmin)/(hmax-hmin))*(ncolors-1) );
736  if(TestBit(kLogScale)) y2 = fY1 + (fY2-fY1)*(TMath::Log10((v+dv)/fMinCurrent)) / (TMath::Log10(fMaxCurrent/fMinCurrent));
737  else y2=y1+dy;
738  lb->SetLineColor(gStyle->GetColorPalette(theColor));
739  lb->PaintLine(fX1,y1,fX1,y2);
740  y1=y2;
741  v+=dv;
742  }
743  } else {
744  // Horizontal case.
745  Double_t dx = (fX2-fX1)/fNbins;
746  Double_t dv = (fMaxCurrent - fMinCurrent)/fNbins;
747  Double_t v = fMinCurrent;
748  Double_t x1 = fX1,x2;
749  for (i=1; i<=fNbins; i++) {
750  theColor = (Int_t)( ((fHistogram->GetBinContent(i)-hmin)/(hmax-hmin))*(ncolors-1) );
751  lb->SetLineColor(gStyle->GetColorPalette(theColor));
752  if(TestBit(kLogScale)) x2 = fX1 + (fX2-fX1)*(TMath::Log10((v+dv)/fMinCurrent)) / (TMath::Log10(fMaxCurrent/fMinCurrent));
753  else x2=x1+dx;
754  lb->PaintLine(x1,fY1,x2,fY1);
755  x1=x2;
756  v+=dv;
757  }
758  }
759  delete lb;
760  }
761 }
762 
763 ////////////////////////////////////////////////////////////////////////////////
764 /// Paint the axis labels and titles.
765 
766 void TParallelCoordVar::PaintLabels()
767 {
768  TLatex* t = new TLatex();
769  TFrame *frame = gPad->GetFrame();
770  t->SetTextSize(0.03);
771  if (fX1==fX2) {
772  t->SetText(fX1,frame->GetY1() - 0.04 - t->GetTextSize(),GetName());
773  Double_t tlength = t->GetXsize();
774  if (fX1-0.5*tlength<0.01) {
775  t->SetTextAlign(11);
776  t->SetText(0.01, frame->GetY1() - 0.04 - t->GetTextSize(), GetName());
777  t->Paint();
778  } else if (fX1+0.5*tlength > 0.99) {
779  t->SetTextAlign(31);
780  t->SetText(0.99,frame->GetY1() - 0.04 - t->GetTextSize(),GetName());
781  t->Paint();
782  } else {
783  t->SetTextAlign(21);
784  t->PaintLatex(fX1,frame->GetY1() - 0.04 - t->GetTextSize(),0,0.03,GetName());
785  }
786  if (!fParallel->TestBit(TParallelCoord::kCandleChart)) {
787  t->SetTextAlign(21);
788  t->PaintLatex(fX1,frame->GetY2() + 0.005,0,0.025,Form("%g",fMaxCurrent));
789  t->SetTextAlign(23);
790  t->PaintLatex(fX1,frame->GetY1() - 0.005,0,0.025,Form("%g",fMinCurrent));
791  }
792  } else {
793  t->SetText(fX1-0.04,fY1+0.02,GetName());
794  t->SetTextSize(0.03);
795  Double_t tlength = t->GetXsize();
796  if (fX1-0.04-tlength<0.01) {
797  t->SetTextAlign(12);
798  t->SetText(0.01,fY1+0.02,GetName());
799  t->Paint();
800  } else {
801  t->SetTextAlign(32);
802  t->PaintLatex(fX1-0.04,fY1+0.02,0,0.03,GetName());
803  }
804  if (!fParallel->TestBit(TParallelCoord::kCandleChart)) {
805  t->SetTextAlign(12);
806  t->PaintLatex(0.01,fY1-0.02,0,0.025,Form("%g",fMinCurrent));
807  t->SetTextAlign(32);
808  t->PaintLatex(0.99,fY1-0.02,0,0.025,Form("%g",fMaxCurrent));
809  }
810  }
811  delete t;
812 }
813 
814 ////////////////////////////////////////////////////////////////////////////////
815 /// Print the axis main data.
816 
817 void TParallelCoordVar::Print(Option_t* /*option*/) const
818 {
819  printf("**************variable #%d**************\n",fParallel->GetVarList()->IndexOf(this));
820  printf("at x1=%f, y1=%f, x2=%f, y2=%f.\n",fX1,fY1,fX2,fY2);
821  printf("min = %f, Q1 = %f, Med = %f, Q3 = %f, Max = %f\n", fMinInit, fQua1, fMed, fQua3, fMaxInit);
822 }
823 
824 ////////////////////////////////////////////////////////////////////////////////
825 /// Save the TParallelCoordVar as a macro. Can be used only in the context
826 /// of TParallelCoord::SavePrimitive (pointer "TParallelCoord* para" is
827 /// defined in TParallelCoord::SavePrimitive) with the option "pcalled".
828 
829 void TParallelCoordVar::SavePrimitive(std::ostream & out, Option_t* options)
830 {
831  TString opt = options;
832  if (opt.Contains("pcalled")) {
833  out<<" var->SetBit(TParallelCoordVar::kLogScale,"<<TestBit(kLogScale)<<");"<<std::endl;
834  out<<" var->SetBit(TParallelCoordVar::kShowBox,"<<TestBit(kShowBox)<<");"<<std::endl;
835  out<<" var->SetBit(TParallelCoordVar::kShowBarHisto,"<<TestBit(kShowBarHisto)<<");"<<std::endl;
836  out<<" var->SetHistogramBinning("<<fNbins<<");"<<std::endl;
837  out<<" var->SetHistogramLineWidth("<<fHistoLW<<");"<<std::endl;
838  out<<" var->SetInitMin("<<fMinInit<<");"<<std::endl;
839  out<<" var->SetInitMax("<<fMaxInit<<");"<<std::endl;
840  out<<" var->SetHistogramHeight("<<fHistoHeight<<");"<<std::endl;
841  out<<" var->GetMinMaxMean();"<<std::endl;
842  out<<" var->GetHistogram();"<<std::endl;
843  out<<" var->SetFillStyle("<<GetFillStyle()<<");"<<std::endl;
844  out<<" var->SetFillColor("<<GetFillColor()<<");"<<std::endl;
845  out<<" var->SetLineColor("<<GetLineColor()<<");"<<std::endl;
846  out<<" var->SetLineWidth("<<GetLineWidth()<<");"<<std::endl;
847  out<<" var->SetLineStyle("<<GetLineStyle()<<");"<<std::endl;
848  if (TestBit(kShowBox)) out<<" var->GetQuantiles();"<<std::endl;
849  TIter next(fRanges);
850  TParallelCoordRange* range;
851  Int_t i = 1;
852  while ((range = (TParallelCoordRange*)next())) {
853  out<<" //***************************************"<<std::endl;
854  out<<" // Create the "<<i<<"th range owned by the axis \""<<GetTitle()<<"\"."<<std::endl;
855  out<<" TParallelCoordSelect* sel = para->GetSelection(\""<<range->GetSelection()->GetTitle()<<"\");"<<std::endl;
856  out<<" TParallelCoordRange* newrange = new TParallelCoordRange(var,"<<range->GetMin()<<","<<range->GetMax()<<",sel);"<<std::endl;
857  out<<" var->AddRange(newrange);"<<std::endl;
858  out<<" sel->Add(newrange);"<<std::endl;
859  ++i;
860  }
861  }
862 }
863 
864 ////////////////////////////////////////////////////////////////////////////////
865 /// Set the axis to display a candle.
866 
867 void TParallelCoordVar::SetBoxPlot(Bool_t box)
868 {
869  SetBit(kShowBox,box);
870  if (box) SetHistogramHeight(0.5);
871  else {
872  SetHistogramHeight(0);
873  SetHistogramLineWidth(2);
874  }
875 }
876 
877 ////////////////////////////////////////////////////////////////////////////////
878 /// Set the histogram binning.
879 
880 void TParallelCoordVar::SetHistogramBinning(Int_t n)
881 {
882  if (n < 0 || n == fNbins) return;
883  fNbins = n;
884  GetHistogram();
885 }
886 
887 ////////////////////////////////////////////////////////////////////////////////
888 /// Set the height of the bar histogram.
889 
890 void TParallelCoordVar::SetHistogramHeight(Double_t h)
891 {
892  fHistoHeight = h;
893  if (!fParallel->TestBit(TParallelCoord::kCandleChart)){
894  if(h!=0) SetBit(kShowBarHisto,kTRUE);
895  else SetBit(kShowBarHisto,kFALSE);
896  }
897 }
898 
899 ////////////////////////////////////////////////////////////////////////////////
900 /// Set the current minimum of the axis.
901 
902 void TParallelCoordVar::SetCurrentMin(Double_t min)
903 {
904  fMinCurrent = min;
905 }
906 
907 ////////////////////////////////////////////////////////////////////////////////
908 /// Set the current maximum of the axis.
909 
910 void TParallelCoordVar::SetCurrentMax(Double_t max)
911 {
912  fMaxCurrent = max;
913 }
914 
915 ////////////////////////////////////////////////////////////////////////////////
916 /// Set the limits within which one the entries must be painted.
917 
918 void TParallelCoordVar::SetCurrentLimits(Double_t min, Double_t max)
919 {
920  if (min>max) {
921  Double_t mem = min;
922  min = max;
923  max = mem;
924  }
925  if(TestBit(kLogScale) && max<=0) return;
926  if(TestBit(kLogScale) && min<=0) min = 0.00001*max;
927  fMinCurrent = min;
928  fMaxCurrent = max;
929 
930  delete fHistogram;
931  fHistogram = NULL;
932  GetHistogram();
933 
934  if (fParallel->TestBit(TParallelCoord::kGlobalScale)) {
935  fParallel->SetGlobalMin(min);
936  fParallel->SetGlobalMax(max);
937  }
938 }
939 
940 ////////////////////////////////////////////////////////////////////////////////
941 /// If true, the pad is updated while the motion of a dragged range.
942 
943 void TParallelCoordVar::SetLiveRangesUpdate(Bool_t on)
944 {
945  TIter next(fRanges);
946  TParallelCoordRange* range;
947  while ((range = (TParallelCoordRange*)next())) range->SetBit(TParallelCoordRange::kLiveUpdate,on);
948 }
949 
950 ////////////////////////////////////////////////////////////////////////////////
951 /// Set the axis in log scale.
952 
953 void TParallelCoordVar::SetLogScale(Bool_t log)
954 {
955  if (log == TestBit (kLogScale)) return;
956  if (fMaxInit < 0) SetBit(kLogScale,kFALSE);
957  else if (log) {
958  if (fMaxCurrent < 0 ) fMaxCurrent = fMaxInit;
959  if (fMinCurrent < 0 ) fMinCurrent = 0.00001*fMaxCurrent;
960  SetBit(kLogScale,kTRUE);
961  SetCurrentMin(fMinCurrent);
962  SetCurrentMax(fMaxCurrent);
963  } else {
964  SetBit(kLogScale,kFALSE);
965  SetCurrentMin(fMinInit);
966  SetCurrentMax(fMaxInit);
967  }
968  GetQuantiles();
969  GetHistogram();
970 }
971 
972 ////////////////////////////////////////////////////////////////////////////////
973 /// Set the variable values.
974 
975 void TParallelCoordVar::SetValues(Long64_t length, Double_t* val)
976 {
977  if (fVal) delete [] fVal;
978  fVal = new Double_t[length];
979  fNentries = length;
980  for (Long64_t li = 0; li < length; ++li) fVal[li] = val[li];
981  GetMinMaxMean();
982  GetHistogram();
983  if (TestBit(kShowBox)) GetQuantiles();
984 }
985 
986 ////////////////////////////////////////////////////////////////////////////////
987 /// Set the X position of the axis in the case of a vertical axis.
988 /// and rotate the axis if it was horizontal.
989 
990 void TParallelCoordVar::SetX(Double_t x, Bool_t gl)
991 {
992  TFrame *frame = gPad->GetFrame();
993  if (!gl) {
994  fY1 = frame->GetY1();
995  fY2 = frame->GetY2();
996  } else {
997  Double_t gmin = fParallel->GetGlobalMin();
998  Double_t gmax = fParallel->GetGlobalMax();
999  fY1 = frame->GetY1() + ((fMinCurrent-gmin)/(gmax-gmin))*(frame->GetY2()-frame->GetY1());
1000  fY2 = frame->GetY1() + ((fMaxCurrent-gmin)/(gmax-gmin))*(frame->GetY2()-frame->GetY1());
1001  }
1002  fX1 = fX2 = x;
1003 }
1004 
1005 ////////////////////////////////////////////////////////////////////////////////
1006 /// Set the Y position of the axis in the case of a horizontal axis.
1007 /// and rotate the axis if it was vertical.
1008 
1009 void TParallelCoordVar::SetY(Double_t y, Bool_t gl)
1010 {
1011  TFrame *frame = gPad->GetFrame();
1012  if (!gl) {
1013  fX1 = frame->GetX1();
1014  fX2 = frame->GetX2();
1015  } else {
1016  Double_t gmin = fParallel->GetGlobalMin();
1017  Double_t gmax = fParallel->GetGlobalMax();
1018  fX1 = frame->GetX1() + ((fMinCurrent-gmin)/(gmax-gmin))*(frame->GetX2()-frame->GetX1());
1019  fX2 = frame->GetX1() + ((fMaxCurrent-gmin)/(gmax-gmin))*(frame->GetX2()-frame->GetX1());
1020  }
1021  fY1 = fY2 = y;
1022 }