Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TPaletteAxis.cxx
Go to the documentation of this file.
1 // @(#)root/histpainter:$Id$
2 // Author: Rene Brun 15/11/2002
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, 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 "Riostream.h"
13 #include "TROOT.h"
14 #include "TPaletteAxis.h"
15 #include "TVirtualPad.h"
16 #include "TStyle.h"
17 #include "TMath.h"
18 #include "TView.h"
19 #include "TH1.h"
20 #include "TGaxis.h"
21 #include "TLatex.h"
22 
23 ClassImp(TPaletteAxis);
24 
25 
26 ////////////////////////////////////////////////////////////////////////////////
27 
28 /*! \class TPaletteAxis
29 \ingroup Histpainter
30 \brief The palette painting class.
31 
32 A `TPaletteAxis` object is used to display the color palette when
33 drawing 2-d histograms.
34 
35 The `TPaletteAxis` is automatically created drawn when drawing a 2-D
36 histogram when the option "Z" is specified.
37 
38 A `TPaletteAxis` object is added to the histogram list of functions and
39 can be retrieved doing:
40 
41  TPaletteAxis *palette = (TPaletteAxis*)h->GetListOfFunctions()->FindObject("palette");
42 
43 then the pointer `palette` can be used to change the palette attributes.
44 
45 Because the palette is created at painting time only, one must issue a:
46 
47  gPad->Update();
48 
49 before retrieving the palette pointer in order to create the palette. The following
50 macro gives an example.
51 
52 Begin_Macro(source)
53 {
54  TCanvas *c1 = new TCanvas("c1","c1",600,400);
55  TH2F *h2 = new TH2F("h2","Example of a resized palette ",40,-4,4,40,-20,20);
56  Float_t px, py;
57  for (Int_t i = 0; i < 25000; i++) {
58  gRandom->Rannor(px,py);
59  h2->Fill(px,5*py);
60  }
61  gStyle->SetPalette(1);
62  h2->Draw("COLZ");
63  gPad->Update();
64  TPaletteAxis *palette = (TPaletteAxis*)h2->GetListOfFunctions()->FindObject("palette");
65  palette->SetY2NDC(0.7);
66  return c1;
67 }
68 End_Macro
69 
70 `TPaletteAxis` inherits from `TBox` and `TPave`. The methods
71 allowing to specify the palette position are inherited from these two classes.
72 
73 The palette can be interactively moved and resized. The context menu
74 can be used to set the axis attributes.
75 
76 It is possible to select a range on the axis to set the min/max in z
77 
78 As default labels and ticks are drawn by `TGAxis` at equidistant (lin or log)
79 points as controlled by SetNdivisions.
80 If option "CJUST" is given labels and ticks are justified at the
81 color boundaries defined by the contour levels.
82 In this case no optimization can be done. It is responsiblity of the
83 user to adjust minimum, maximum of the histogram and/or the contour levels
84 to get a reasonable look of the plot.
85 Only overlap of the labels is avoided if too many contour levels are used.
86 
87 This option is especially useful with user defined contours.
88 An example is shown here:
89 Begin_Macro(source)
90 {
91  gStyle->SetOptStat(0);
92  TCanvas *c1 = new TCanvas("c1","exa_CJUST",300,10,400,400);
93  TH2F *hpxpy = new TH2F("hpxpy","py vs px",40,-4,4,40,-4,4);
94  // Fill histograms randomly
95  TRandom3 randomNum;
96  Float_t px, py;
97  for (Int_t i = 0; i < 25000; i++) {
98  randomNum.Rannor(px,py);
99  hpxpy->Fill(px,py);
100  }
101  hpxpy->SetMaximum(200);
102  Double_t zcontours[5] = {0, 20, 40, 80, 120};
103  hpxpy->SetContour(5, zcontours);
104  hpxpy->GetZaxis()->SetTickSize(0.01);
105  hpxpy->GetZaxis()->SetLabelOffset(0.01);
106  gPad->SetRightMargin(0.13);
107  hpxpy->SetTitle("User contours, CJUST");
108  hpxpy->Draw("COL Z CJUST");
109 }
110 End_Macro
111 */
112 
113 
114 ////////////////////////////////////////////////////////////////////////////////
115 /// Palette default constructor.
116 
117 TPaletteAxis::TPaletteAxis(): TPave()
118 {
119  fH = 0;
120  SetName("");
121 }
122 
123 
124 ////////////////////////////////////////////////////////////////////////////////
125 /// Palette normal constructor.
126 
127 TPaletteAxis::TPaletteAxis(Double_t x1, Double_t y1, Double_t x2, Double_t y2, TH1 *h)
128  : TPave(x1, y1, x2, y2)
129 {
130  fH = h;
131  SetName("palette");
132  TAxis *zaxis = fH->GetZaxis();
133  fAxis.ImportAxisAttributes(zaxis);
134  if (gPad->GetView()) SetBit(kHasView);
135 }
136 
137 
138 ////////////////////////////////////////////////////////////////////////////////
139 /// Palette destructor.
140 
141 TPaletteAxis::~TPaletteAxis()
142 {
143 }
144 
145 
146 ////////////////////////////////////////////////////////////////////////////////
147 /// Palette copy constructor.
148 
149 TPaletteAxis::TPaletteAxis(const TPaletteAxis &palette) : TPave(palette)
150 {
151  ((TPaletteAxis&)palette).Copy(*this);
152 }
153 
154 
155 ////////////////////////////////////////////////////////////////////////////////
156 /// Assignment operator.
157 
158 TPaletteAxis& TPaletteAxis::operator=(const TPaletteAxis &orig)
159 {
160  orig.Copy( *this );
161  return *this;
162 }
163 
164 
165 ////////////////////////////////////////////////////////////////////////////////
166 /// Copy a palette to a palette.
167 
168 void TPaletteAxis::Copy(TObject &obj) const
169 {
170  TPave::Copy(obj);
171  ((TPaletteAxis&)obj).fH = fH;
172  ((TPaletteAxis&)obj).fName = fName;
173 }
174 
175 
176 ////////////////////////////////////////////////////////////////////////////////
177 /// Check if mouse on the axis region.
178 
179 Int_t TPaletteAxis::DistancetoPrimitive(Int_t px, Int_t py)
180 {
181  Int_t plxmax = gPad->XtoAbsPixel(fX2);
182  Int_t plymin = gPad->YtoAbsPixel(fY1);
183  Int_t plymax = gPad->YtoAbsPixel(fY2);
184  if (px > plxmax && px < plxmax + 30 && py >= plymax && py <= plymin) return px - plxmax;
185 
186  //otherwise check if inside the box
187  return TPave::DistancetoPrimitive(px, py);
188 }
189 
190 
191 ////////////////////////////////////////////////////////////////////////////////
192 /// Check if mouse on the axis region.
193 
194 void TPaletteAxis::ExecuteEvent(Int_t event, Int_t px, Int_t py)
195 {
196  if (!gPad) return;
197 
198  static Int_t kmode = 0;
199  Int_t plxmin = gPad->XtoAbsPixel(fX1);
200  Int_t plxmax = gPad->XtoAbsPixel(fX2);
201  if (kmode != 0 || px <= plxmax) {
202  if (event == kButton1Down) kmode = 1;
203  TBox::ExecuteEvent(event, px, py);
204  if (event == kButton1Up) kmode = 0;
205  // In case palette coordinates have been modified, recompute NDC coordinates
206  Double_t dpx = gPad->GetX2() - gPad->GetX1();
207  Double_t dpy = gPad->GetY2() - gPad->GetY1();
208  Double_t xp1 = gPad->GetX1();
209  Double_t yp1 = gPad->GetY1();
210  fX1NDC = (fX1 - xp1) / dpx;
211  fY1NDC = (fY1 - yp1) / dpy;
212  fX2NDC = (fX2 - xp1) / dpx;
213  fY2NDC = (fY2 - yp1) / dpy;
214  return;
215  }
216  gPad->SetCursor(kHand);
217  static Double_t ratio1, ratio2;
218  static Int_t px1old, py1old, px2old, py2old;
219  Double_t temp, xmin, xmax;
220 
221  switch (event) {
222 
223  case kButton1Down:
224  ratio1 = (gPad->AbsPixeltoY(py) - fY1) / (fY2 - fY1);
225  py1old = gPad->YtoAbsPixel(fY1 + ratio1 * (fY2 - fY1));
226  px1old = plxmin;
227  px2old = plxmax;
228  py2old = py1old;
229  gVirtualX->DrawBox(px1old, py1old, px2old, py2old, TVirtualX::kHollow);
230  gVirtualX->SetLineColor(-1);
231  // No break !!!
232 
233  case kButton1Motion:
234  gVirtualX->DrawBox(px1old, py1old, px2old, py2old, TVirtualX::kHollow);
235  ratio2 = (gPad->AbsPixeltoY(py) - fY1) / (fY2 - fY1);
236  py2old = gPad->YtoAbsPixel(fY1 + ratio2 * (fY2 - fY1));
237  gVirtualX->DrawBox(px1old, py1old, px2old, py2old, TVirtualX::kHollow);
238  break;
239 
240  case kButton1Up:
241  if (gROOT->IsEscaped()) {
242  gROOT->SetEscape(kFALSE);
243  break;
244  }
245 
246  ratio2 = (gPad->AbsPixeltoY(py) - fY1) / (fY2 - fY1);
247  xmin = ratio1;
248  xmax = ratio2;
249  if (xmin > xmax) {
250  temp = xmin;
251  xmin = xmax;
252  xmax = temp;
253  temp = ratio1;
254  ratio1 = ratio2;
255  ratio2 = temp;
256  }
257  if (ratio2 - ratio1 > 0.05) {
258  if (fH->GetDimension() == 2) {
259  Double_t zmin = fH->GetMinimum();
260  Double_t zmax = fH->GetMaximum();
261  if (gPad->GetLogz()) {
262  if (zmin <= 0 && zmax > 0) zmin = TMath::Min((Double_t)1,
263  (Double_t)0.001 * zmax);
264  zmin = TMath::Log10(zmin);
265  zmax = TMath::Log10(zmax);
266  }
267  Double_t newmin = zmin + (zmax - zmin) * ratio1;
268  Double_t newmax = zmin + (zmax - zmin) * ratio2;
269  if (newmin < zmin)newmin = fH->GetBinContent(fH->GetMinimumBin());
270  if (newmax > zmax)newmax = fH->GetBinContent(fH->GetMaximumBin());
271  if (gPad->GetLogz()) {
272  newmin = TMath::Exp(2.302585092994 * newmin);
273  newmax = TMath::Exp(2.302585092994 * newmax);
274  }
275  fH->SetMinimum(newmin);
276  fH->SetMaximum(newmax);
277  fH->SetBit(TH1::kIsZoomed);
278  }
279  gPad->Modified(kTRUE);
280  }
281  gVirtualX->SetLineColor(-1);
282  kmode = 0;
283  break;
284  }
285 }
286 
287 
288 ////////////////////////////////////////////////////////////////////////////////
289 /// Returns the color index of the bin (i,j).
290 ///
291 /// This function should be used after an histogram has been plotted with the
292 /// option COL or COLZ like in the following example:
293 ///
294 /// h2->Draw("COLZ");
295 /// gPad->Update();
296 /// TPaletteAxis *palette = (TPaletteAxis*)h2->GetListOfFunctions()->FindObject("palette");
297 /// Int_t ci = palette->GetBinColor(20,15);
298 ///
299 /// Then it is possible to retrieve the RGB components in the following way:
300 ///
301 /// TColor *c = gROOT->GetColor(ci);
302 /// float x,y,z;
303 /// c->GetRGB(x,y,z);
304 
305 Int_t TPaletteAxis::GetBinColor(Int_t i, Int_t j)
306 {
307  Double_t zc = fH->GetBinContent(i, j);
308  return GetValueColor(zc);
309 }
310 
311 
312 ////////////////////////////////////////////////////////////////////////////////
313 /// Displays the z value corresponding to cursor position py.
314 
315 char *TPaletteAxis::GetObjectInfo(Int_t /* px */, Int_t py) const
316 {
317  Double_t z;
318  static char info[64];
319 
320  Double_t zmin = fH->GetMinimum();
321  Double_t zmax = fH->GetMaximum();
322  Int_t y1 = gPad->GetWh() - gPad->VtoPixel(fY1NDC);
323  Int_t y2 = gPad->GetWh() - gPad->VtoPixel(fY2NDC);
324  Int_t y = gPad->GetWh() - py;
325 
326  if (gPad->GetLogz()) {
327  if (zmin <= 0 && zmax > 0) zmin = TMath::Min((Double_t)1,
328  (Double_t)0.001 * zmax);
329  Double_t zminl = TMath::Log10(zmin);
330  Double_t zmaxl = TMath::Log10(zmax);
331  Double_t zl = (zmaxl - zminl) * ((Double_t)(y - y1) / (Double_t)(y2 - y1)) + zminl;
332  z = TMath::Power(10., zl);
333  } else {
334  z = (zmax - zmin) * ((Double_t)(y - y1) / (Double_t)(y2 - y1)) + zmin;
335  }
336 
337  snprintf(info, 64, "(z=%g)", z);
338  return info;
339 }
340 
341 
342 ////////////////////////////////////////////////////////////////////////////////
343 /// Returns the color index of the given z value
344 ///
345 /// This function should be used after an histogram has been plotted with the
346 /// option COL or COLZ like in the following example:
347 ///
348 /// h2->Draw("COLZ");
349 /// gPad->Update();
350 /// TPaletteAxis *palette = (TPaletteAxis*)h2->GetListOfFunctions()->FindObject("palette");
351 /// Int_t ci = palette->GetValueColor(30.);
352 ///
353 /// Then it is possible to retrieve the RGB components in the following way:
354 ///
355 /// TColor *c = gROOT->GetColor(ci);
356 /// float x,y,z;
357 /// c->GetRGB(x,y,z);
358 
359 Int_t TPaletteAxis::GetValueColor(Double_t zc)
360 {
361  Double_t wmin = fH->GetMinimum();
362  Double_t wmax = fH->GetMaximum();
363  Double_t wlmin = wmin;
364  Double_t wlmax = wmax;
365 
366  if (gPad->GetLogz()) {
367  if (wmin <= 0 && wmax > 0) wmin = TMath::Min((Double_t)1,
368  (Double_t)0.001 * wmax);
369  wlmin = TMath::Log10(wmin);
370  wlmax = TMath::Log10(wmax);
371  }
372 
373  Int_t ncolors = gStyle->GetNumberOfColors();
374  Int_t ndivz = fH->GetContour();
375  if (ndivz == 0) return 0;
376  ndivz = TMath::Abs(ndivz);
377  Int_t theColor, color;
378  Double_t scale = ndivz / (wlmax - wlmin);
379 
380  if (fH->TestBit(TH1::kUserContour) && gPad->GetLogz()) zc = TMath::Log10(zc);
381  if (zc < wlmin) zc = wlmin;
382 
383  color = Int_t(0.01 + (zc - wlmin) * scale);
384 
385  theColor = Int_t((color + 0.99) * Double_t(ncolors) / Double_t(ndivz));
386  return gStyle->GetColorPalette(theColor);
387 }
388 
389 
390 ////////////////////////////////////////////////////////////////////////////////
391 /// Paint the palette.
392 
393 void TPaletteAxis::Paint(Option_t *)
394 {
395  ConvertNDCtoPad();
396 
397  SetFillStyle(1001);
398  Double_t ymin = fY1;
399  Double_t ymax = fY2;
400  Double_t xmin = fX1;
401  Double_t xmax = fX2;
402  Double_t wmin = fH->GetMinimum();
403  Double_t wmax = fH->GetMaximum();
404  Double_t wlmin = wmin;
405  Double_t wlmax = wmax;
406  Double_t y1, y2, w1, w2, zc;
407 
408  if ((wlmax - wlmin) <= 0) {
409  Double_t mz = wlmin * 0.1;
410  if (mz == 0) mz = 0.1;
411  wlmin = wlmin - mz;
412  wlmax = wlmax + mz;
413  wmin = wlmin;
414  wmax = wlmax;
415  }
416 
417  if (gPad->GetLogz()) {
418  if (wmin <= 0 && wmax > 0) wmin = TMath::Min((Double_t)1,
419  (Double_t)0.001 * wmax);
420  wlmin = TMath::Log10(wmin);
421  wlmax = TMath::Log10(wmax);
422  }
423  Double_t ws = wlmax - wlmin;
424  Int_t ncolors = gStyle->GetNumberOfColors();
425  Int_t ndivz = fH->GetContour();
426  if (ndivz == 0) return;
427  ndivz = TMath::Abs(ndivz);
428  Int_t theColor, color;
429  // import Attributes already here since we might need them for CJUST
430  fAxis.ImportAxisAttributes(fH->GetZaxis());
431  // case option "CJUST": put labels directly at color boundaries
432  TLatex *label = NULL;
433  TLine *line = NULL;
434  Double_t prevlab = 0;
435  TString opt(fH->GetDrawOption());
436  if (opt.Contains("CJUST", TString::kIgnoreCase)) {
437  label = new TLatex();
438  label->SetTextFont(fAxis.GetLabelFont());
439  label->SetTextColor(fAxis.GetLabelColor());
440  label->SetTextAlign(kHAlignLeft+kVAlignCenter);
441  line = new TLine();
442  line->SetLineColor(fAxis.GetLineColor());
443  line->PaintLine(xmax, ymin, xmax, ymax);
444  }
445  Double_t scale = ndivz / (wlmax - wlmin);
446  for (Int_t i = 0; i < ndivz; i++) {
447 
448  zc = fH->GetContourLevel(i);
449  if (fH->TestBit(TH1::kUserContour) && gPad->GetLogz())
450  zc = TMath::Log10(zc);
451  w1 = zc;
452  if (w1 < wlmin) w1 = wlmin;
453 
454  w2 = wlmax;
455  if (i < ndivz - 1) {
456  zc = fH->GetContourLevel(i + 1);
457  if (fH->TestBit(TH1::kUserContour) && gPad->GetLogz())
458  zc = TMath::Log10(zc);
459  w2 = zc;
460  }
461 
462  if (w2 <= wlmin) continue;
463  y1 = ymin + (w1 - wlmin) * (ymax - ymin) / ws;
464  y2 = ymin + (w2 - wlmin) * (ymax - ymin) / ws;
465 
466  if (fH->TestBit(TH1::kUserContour)) {
467  color = i;
468  } else {
469  color = Int_t(0.01 + (w1 - wlmin) * scale);
470  }
471 
472  theColor = Int_t((color + 0.99) * Double_t(ncolors) / Double_t(ndivz));
473  SetFillColor(gStyle->GetColorPalette(theColor));
474  TAttFill::Modify();
475  gPad->PaintBox(xmin, y1, xmax, y2);
476  // case option "CJUST": put labels directly
477  if (label) {
478  Double_t lof = fAxis.GetLabelOffset()*(gPad->GetUxmax()-gPad->GetUxmin());
479  // the following assumes option "S"
480  Double_t tlength = fAxis.GetTickSize() * (gPad->GetUxmax()-gPad->GetUxmin());
481  Double_t lsize = fAxis.GetLabelSize();
482  Double_t lsize_user = lsize*(gPad->GetUymax()-gPad->GetUymin());
483  Double_t zlab = fH->GetContourLevel(i);
484  if (gPad->GetLogz()&& !fH->TestBit(TH1::kUserContour)) {
485  zlab = TMath::Power(10, zlab);
486  }
487  // make sure labels dont overlap
488  if (i == 0 || (y1 - prevlab) > 1.5*lsize_user) {
489  label->PaintLatex(xmax + lof, y1, 0, lsize, Form("%g", zlab));
490  prevlab = y1;
491  }
492  line->PaintLine(xmax-tlength, y1, xmax, y1);
493  if (i == ndivz-1) {
494  // label + tick at top of axis
495  if ((y2 - prevlab > 1.5*lsize_user))
496  label->PaintLatex(xmax + lof, y2, 0, lsize, Form("%g",fH->GetMaximum()));
497  line->PaintLine(xmax-tlength, y2, xmax, y2);
498  }
499  }
500  }
501 
502  // Take primary divisions only
503  Int_t ndiv = fH->GetZaxis()->GetNdivisions();
504  Bool_t isOptimized = ndiv>0;
505  Int_t absDiv = abs(ndiv);
506  Int_t maxD = absDiv/1000000;
507  ndiv = absDiv%100 + maxD*1000000;
508  if (!isOptimized) ndiv = -ndiv;
509 
510  char chopt[6] = "S ";
511  chopt[1] = 0;
512  strncat(chopt, "+L", 3);
513  if (ndiv < 0) {
514  ndiv = TMath::Abs(ndiv);
515  strncat(chopt, "N", 2);
516  }
517  if (gPad->GetLogz()) {
518  wmin = TMath::Power(10., wlmin);
519  wmax = TMath::Power(10., wlmax);
520  strncat(chopt, "G", 2);
521  }
522  if (label) {
523  // case option "CJUST", cleanup
524  delete label;
525  delete line;
526  } else {
527  // default
528  fAxis.PaintAxis(xmax, ymin, xmax, ymax, wmin, wmax, ndiv, chopt);
529  }
530 }
531 
532 
533 ////////////////////////////////////////////////////////////////////////////////
534 /// Save primitive as a C++ statement(s) on output stream out.
535 
536 void TPaletteAxis::SavePrimitive(std::ostream &out, Option_t * /*= ""*/)
537 {
538  //char quote = '"';
539  out << " " << std::endl;
540  if (gROOT->ClassSaved(TPaletteAxis::Class())) {
541  out << " ";
542  } else {
543  out << " " << ClassName() << " *";
544  }
545  if (fOption.Contains("NDC")) {
546  out << "palette = new " << ClassName() << "(" << fX1NDC << "," << fY1NDC << "," << fX2NDC << "," << fY2NDC
547  << "," << fH->GetName() << ");" << std::endl;
548  } else {
549  out << "palette = new " << ClassName() << "(" << fX1 << "," << fY1 << "," << fX2 << "," << fY2
550  << "," << fH->GetName() << ");" << std::endl;
551  }
552  out << " palette->SetLabelColor(" << fAxis.GetLabelColor() << ");" << std::endl;
553  out << " palette->SetLabelFont(" << fAxis.GetLabelFont() << ");" << std::endl;
554  out << " palette->SetLabelOffset(" << fAxis.GetLabelOffset() << ");" << std::endl;
555  out << " palette->SetLabelSize(" << fAxis.GetLabelSize() << ");" << std::endl;
556  out << " palette->SetTitleOffset(" << fAxis.GetTitleOffset() << ");" << std::endl;
557  out << " palette->SetTitleSize(" << fAxis.GetTitleSize() << ");" << std::endl;
558  SaveFillAttributes(out, "palette", -1, -1);
559  SaveLineAttributes(out, "palette", 1, 1, 1);
560 }
561 
562 
563 ////////////////////////////////////////////////////////////////////////////////
564 /// Unzoom the palette
565 
566 void TPaletteAxis::UnZoom()
567 {
568  TView *view = gPad->GetView();
569  if (view) {
570  delete view;
571  gPad->SetView(0);
572  }
573  fH->GetZaxis()->SetRange(0, 0);
574  if (fH->GetDimension() == 2) {
575  fH->SetMinimum();
576  fH->SetMaximum();
577  fH->ResetBit(TH1::kIsZoomed);
578  }
579 }