Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TPad.cxx
Go to the documentation of this file.
1// @(#)root/gpad:$Id$
2// Author: Rene Brun 12/12/94
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 <cstring>
13#include <cstdlib>
14#include <iostream>
15
16#include "TROOT.h"
17#include "TBuffer.h"
18#include "TError.h"
19#include "TMath.h"
20#include "TSystem.h"
21#include "TStyle.h"
22#include "TH1.h"
23#include "TH2.h"
24#include "TH3.h"
25#include "TClass.h"
26#include "TBaseClass.h"
27#include "TClassTable.h"
28#include "TVirtualPS.h"
29#include "TVirtualX.h"
30#include "TVirtualViewer3D.h"
31#include "TView.h"
32#include "TPoint.h"
33#include "TGraph.h"
34#include "TMultiGraph.h"
35#include "THStack.h"
36#include "TPaveText.h"
37#include "TPaveStats.h"
38#include "TGroupButton.h"
39#include "TBrowser.h"
40#include "TVirtualGL.h"
41#include "TString.h"
42#include "TDataMember.h"
43#include "TMethod.h"
44#include "TDataType.h"
45#include "TFrame.h"
46#include "TExec.h"
47#include "TDatime.h"
48#include "TColor.h"
49#include "TCanvas.h"
50#include "TPluginManager.h"
51#include "TEnv.h"
52#include "TImage.h"
53#include "TViewer3DPad.h"
54#include "TCreatePrimitives.h"
55#include "TLegend.h"
56#include "TAtt3D.h"
57#include "TVirtualPadPainter.h"
58#include "strlcpy.h"
59#include "snprintf.h"
60
61#include "TVirtualMutex.h"
62
63static Int_t gReadLevel = 0;
64
66
68
69/** \class TPad
70\ingroup gpad
71
72The most important graphics class in the ROOT system.
73
74A Pad is contained in a Canvas.
75
76A Pad may contain other pads (unlimited pad hierarchy).
77
78A pad is a linked list of primitives of any type (graphics objects,
79histograms, detectors, tracks, etc.).
80
81Adding a new element into a pad is in general performed by the Draw
82member function of the object classes.
83
84It is important to realize that the pad is a linked list of references
85to the original object.
86For example, in case of a histogram, the histogram.Draw() operation
87only stores a reference to the histogram object and not a graphical
88representation of this histogram.
89When the mouse is used to change (say the bin content), the bin content
90of the original histogram is changed.
91
92The convention used in ROOT is that a Draw operation only adds
93a reference to the object. The effective drawing is performed
94when the canvas receives a signal to be painted.
95
96\image html gpad_pad1.png
97
98This signal is generally sent when typing carriage return in the
99command input or when a graphical operation has been performed on one
100of the pads of this canvas.
101When a Canvas/Pad is repainted, the member function Paint for all
102objects in the Pad linked list is invoked.
103
104\image html gpad_pad2.png
105
106When the mouse is moved on the Pad, The member function DistancetoPrimitive
107is called for all the elements in the pad. DistancetoPrimitive returns
108the distance in pixels to this object.
109
110When the object is within the distance window, the member function
111ExecuteEvent is called for this object.
112
113In ExecuteEvent, move, changes can be performed on the object.
114
115For examples of DistancetoPrimitive and ExecuteEvent functions,
116see classes
117~~~ {.cpp}
118 TLine::DistancetoPrimitive, TLine::ExecuteEvent
119 TBox::DistancetoPrimitive, TBox::ExecuteEvent
120 TH1::DistancetoPrimitive, TH1::ExecuteEvent
121~~~
122A Pad supports linear and log scales coordinate systems.
123The transformation coefficients are explained in TPad::ResizePad.
124*/
125
126////////////////////////////////////////////////////////////////////////////////
127/// Pad default constructor.
128
130{
131 fModified = kTRUE;
132 fTip = nullptr;
133 fPadPointer = nullptr;
134 fPrimitives = nullptr;
135 fExecs = nullptr;
136 fCanvas = nullptr;
137 fPadPaint = 0;
138 fPixmapID = -1;
139 fGLDevice = -1;
140 fCopyGLDevice = kFALSE;
141 fEmbeddedGL = kFALSE;
142 fTheta = 30;
143 fPhi = 30;
144 fNumber = 0;
145 fAbsCoord = kFALSE;
146 fEditable = kTRUE;
147 fCrosshair = 0;
148 fCrosshairPos = 0;
149 fPadView3D = nullptr;
150 fMother = (TPad*)gPad;
151
152 fAbsHNDC = 0.;
153 fAbsPixeltoXk = 0.;
154 fAbsPixeltoYk = 0.;
155 fAbsWNDC = 0.;
156 fAbsXlowNDC = 0.;
157 fAbsYlowNDC = 0.;
158 fBorderMode = 0;
159 fBorderSize = 0;
160 fPixeltoX = 0;
161 fPixeltoXk = 0.;
162 fPixeltoY = 0.;
163 fPixeltoYk = 0.;
164 fUtoAbsPixelk = 0.;
165 fUtoPixel = 0.;
166 fUtoPixelk = 0.;
167 fVtoAbsPixelk = 0.;
168 fVtoPixel = 0.;
169 fVtoPixelk = 0.;
170 fXtoAbsPixelk = 0.;
171 fXtoPixel = 0.;
172 fXtoPixelk = 0.;
173 fYtoAbsPixelk = 0.;
174 fYtoPixel = 0.;
175 fYtoPixelk = 0.;
176 fXUpNDC = 0.;
177 fYUpNDC = 0.;
178
179 fFixedAspectRatio = kFALSE;
180 fAspectRatio = 0.;
181
182 fNumPaletteColor = 0;
183 fNextPaletteColor = 0;
184 fCollideGrid = nullptr;
185 fCGnx = 0;
186 fCGny = 0;
187
188 fLogx = 0;
189 fLogy = 0;
190 fLogz = 0;
191 fGridx = 0;
192 fGridy = 0;
193 fTickx = 0;
194 fTicky = 0;
195 fFrame = nullptr;
196 fView = nullptr;
197
198 fUxmin = fUymin = fUxmax = fUymax = 0;
199
200 // Set default world coordinates to NDC [0,1]
201 fX1 = 0;
202 fX2 = 1;
203 fY1 = 0;
204 fY2 = 1;
205
206 // Set default pad range
207 fXlowNDC = 0;
208 fYlowNDC = 0;
209 fWNDC = 1;
210 fHNDC = 1;
211
212 fViewer3D = nullptr;
213 SetBit(kMustCleanup);
214
215 // the following line is temporarily disabled. It has side effects
216 // when the pad is a TDrawPanelHist or a TFitPanel.
217 // the line was supposed to fix a problem with DrawClonePad
218 // gROOT->SetSelectedPad(this);
219}
220
221////////////////////////////////////////////////////////////////////////////////
222/// Pad constructor.
223///
224/// A pad is a linked list of primitives.
225/// A pad is contained in a canvas. It may contain other pads.
226/// A pad has attributes. When a pad is created, the attributes
227/// defined in the current style are copied to the pad attributes.
228///
229/// \param[in] name pad name
230/// \param[in] title pad title
231/// \param[in] xlow [0,1] is the position of the bottom left point of the pad
232/// expressed in the mother pad reference system
233/// \param[in] ylow [0,1] is the Y position of this point.
234/// \param[in] xup [0,1] is the x position of the top right point of the pad
235/// expressed in the mother pad reference system
236/// \param[in] yup [0,1] is the Y position of this point.
237/// \param[in] color pad color
238/// \param[in] bordersize border size in pixels
239/// \param[in] bordermode border mode
240/// - bordermode = -1 box looks as it is behind the screen
241/// - bordermode = 0 no special effects
242/// - bordermode = 1 box looks as it is in front of the screen
243
244TPad::TPad(const char *name, const char *title, Double_t xlow,
245 Double_t ylow, Double_t xup, Double_t yup,
246 Color_t color, Short_t bordersize, Short_t bordermode)
247 : TVirtualPad(name,title,xlow,ylow,xup,yup,color,bordersize,bordermode)
248{
250 fTip = nullptr;
251 fBorderSize = bordersize;
252 fBorderMode = bordermode;
253 if (gPad) fCanvas = gPad->GetCanvas();
254 else fCanvas = (TCanvas*)this;
255 fMother = (TPad*)gPad;
256 fPrimitives = new TList;
257 fExecs = new TList;
258 fPadPointer = nullptr;
259 fTheta = 30;
260 fPhi = 30;
265 fFrame = nullptr;
266 fView = nullptr;
267 fPadPaint = 0;
268 fPadView3D = nullptr;
269 fPixmapID = -1; // -1 means pixmap will be created by ResizePad()
272 fNumber = 0;
275 fCrosshair = 0;
276 fCrosshairPos = 0;
277
278 fVtoAbsPixelk = 0.;
279 fVtoPixelk = 0.;
280 fVtoPixel = 0.;
281 fAbsPixeltoXk = 0.;
282 fPixeltoXk = 0.;
283 fPixeltoX = 0;
284 fAbsPixeltoYk = 0.;
285 fPixeltoYk = 0.;
286 fPixeltoY = 0.;
287 fXlowNDC = 0;
288 fYlowNDC = 0;
289 fWNDC = 1;
290 fHNDC = 1;
291 fXUpNDC = 0.;
292 fYUpNDC = 0.;
293 fAbsXlowNDC = 0.;
294 fAbsYlowNDC = 0.;
295 fAbsWNDC = 0.;
296 fAbsHNDC = 0.;
297 fXtoAbsPixelk = 0.;
298 fXtoPixelk = 0.;
299 fXtoPixel = 0.;
300 fYtoAbsPixelk = 0.;
301 fYtoPixelk = 0.;
302 fYtoPixel = 0.;
303 fUtoAbsPixelk = 0.;
304 fUtoPixelk = 0.;
305 fUtoPixel = 0.;
306
307 fUxmin = fUymin = fUxmax = fUymax = 0;
311
313 fAspectRatio = 0.;
314
317 fCollideGrid = nullptr;
318 fCGnx = 0;
319 fCGny = 0;
320
321 fViewer3D = nullptr;
322
324 // Set default world coordinates to NDC [0,1]
325 fX1 = 0;
326 fX2 = 1;
327 fY1 = 0;
328 fY2 = 1;
329
330 if (!gPad) {
331 Error("TPad", "You must create a TCanvas before creating a TPad");
332 MakeZombie();
333 return;
334 }
335
336 TPad *padsav = (TPad*)gPad;
337
338 if ((xlow < 0) || (xlow > 1) || (ylow < 0) || (ylow > 1)) {
339 Error("TPad", "illegal bottom left position: x=%f, y=%f", xlow, ylow);
340 goto zombie;
341 }
342 if ((xup < 0) || (xup > 1) || (yup < 0) || (yup > 1)) {
343 Error("TPad", "illegal top right position: x=%f, y=%f", xup, yup);
344 goto zombie;
345 }
346 if (xup-xlow <= 0) {
347 Error("TPad", "illegal width: %f", xup-xlow);
348 goto zombie;
349 }
350 if (yup-ylow <= 0) {
351 Error("TPad", "illegal height: %f", yup-ylow);
352 goto zombie;
353 }
354
358
359 fUxmin = fUymin = fUxmax = fUymax = 0;
360
361 // Set pad parameters and Compute conversion coefficients
362 SetPad(name, title, xlow, ylow, xup, yup, color, bordersize, bordermode);
363 Range(0, 0, 1, 1);
366
367 padsav->cd();
368 return;
369
370zombie:
371 // error in creating pad occurred, make this pad a zombie
372 MakeZombie();
373 padsav->cd();
374}
375
376
377////////////////////////////////////////////////////////////////////////////////
378/// Pad destructor.
379
381{
382 if (!TestBit(kNotDeleted)) return;
383 Close();
386 auto primitives = fPrimitives;
387 // In some cases, fPrimitives has the kMustCleanup bit set which will lead
388 // its destructor to call RecursiveRemove and since this pad is still
389 // likely to be (indirectly) in the list of cleanups, we must set
390 // fPrimitives to nullptr to avoid TPad::RecursiveRemove from calling
391 // a member function of a partially destructed object.
392 fPrimitives = nullptr;
393 delete primitives;
395 delete fViewer3D;
396 if (fCollideGrid) delete [] fCollideGrid;
397
398 // Required since we overload TObject::Hash.
400 if (this == gPad) gPad=nullptr;
401}
402
403////////////////////////////////////////////////////////////////////////////////
404/// Add a new TExec object to the list of Execs.
405///
406/// When an event occurs in the pad (mouse click, etc) the list of C++ commands
407/// in the list of Execs are executed via TPad::AutoExec.
408///
409/// When a pad event occurs (mouse move, click, etc) all the commands
410/// contained in the fExecs list are executed in the order found in the list.
411///
412/// This facility is activated by default. It can be deactivated by using
413/// the canvas "Option" menu.
414///
415/// The following examples of TExec commands are provided in the tutorials:
416/// macros exec1.C and exec2.C.
417///
418/// ### Example1 of use of exec1.C
419///
420/// ~~~ {.cpp}
421/// Root > TFile f("hsimple.root")
422/// Root > hpx.Draw()
423/// Root > c1.AddExec("ex1",".x exec1.C")
424/// ~~~
425///
426/// At this point you can use the mouse to click on the contour of
427/// the histogram hpx. When the mouse is clicked, the bin number and its
428/// contents are printed.
429///
430/// ### Example2 of use of exec1.C
431///
432/// ~~~ {.cpp}
433/// Root > TFile f("hsimple.root")
434/// Root > hpxpy.Draw()
435/// Root > c1.AddExec("ex2",".x exec2.C")
436/// ~~~
437///
438/// When moving the mouse in the canvas, a second canvas shows the
439/// projection along X of the bin corresponding to the Y position
440/// of the mouse. The resulting histogram is fitted with a gaussian.
441/// A "dynamic" line shows the current bin position in Y.
442/// This more elaborated example can be used as a starting point
443/// to develop more powerful interactive applications exploiting the C++
444/// interpreter as a development engine.
445
446void TPad::AddExec(const char *name, const char*command)
447{
448 if (!fExecs) fExecs = new TList;
449 TExec *ex = new TExec(name,command);
450 fExecs->Add(ex);
451}
452
453////////////////////////////////////////////////////////////////////////////////
454/// Execute the list of Execs when a pad event occurs.
455
457{
459
460 if (!fExecs) fExecs = new TList;
461 TIter next(fExecs);
462 TExec *exec;
463 while ((exec = (TExec*)next())) {
464 exec->Exec();
465 }
466}
467
468////////////////////////////////////////////////////////////////////////////////
469/// Browse pad.
470
472{
473 cd();
475}
476
477////////////////////////////////////////////////////////////////////////////////
478/// Build a legend from the graphical objects in the pad.
479///
480/// A simple method to build automatically a TLegend from the primitives in a TPad.
481///
482/// Only those deriving from TAttLine, TAttMarker and TAttFill are added, excluding
483/// TPave and TFrame derived classes.
484///
485/// \return The built TLegend
486///
487/// \param[in] x1, y1, x2, y2 The TLegend coordinates
488/// \param[in] title The legend title. By default it is " "
489/// \param[in] option The TLegend option
490///
491/// The caller program owns the returned TLegend.
492///
493/// If the pad contains some TMultiGraph or THStack the individual
494/// graphs or histograms in them are added to the TLegend.
495///
496/// ### Automatic placement of the legend
497/// If `x1` is equal to `x2` and `y1` is equal to `y2` the legend will be automatically
498/// placed to avoid overlapping with the existing primitives already displayed.
499/// `x1` is considered as the width of the legend and `y1` the height. By default
500/// the legend is automatically placed with width = `x1`= `x2` = 0.3 and
501/// height = `y1`= `y2` = 0.21.
502
504 const char* title, Option_t *option)
505{
507 if (!lop) return 0;
508 TLegend *leg=0;
509 TIter next(lop);
510 TString mes;
511 TObject *o=0;
512 TString opt("");
513 while( (o=next()) ) {
514 if ((o->InheritsFrom(TAttLine::Class()) || o->InheritsFrom(TAttMarker::Class()) ||
515 o->InheritsFrom(TAttFill::Class())) &&
516 ( !(o->InheritsFrom(TFrame::Class())) && !(o->InheritsFrom(TPave::Class())) )) {
517 if (!leg) leg = new TLegend(x1, y1, x2, y2, title);
518 if (o->InheritsFrom(TNamed::Class()) && strlen(((TNamed *)o)->GetTitle()))
519 mes = ((TNamed *)o)->GetTitle();
520 else if (strlen(o->GetName()))
521 mes = o->GetName();
522 else
523 mes = o->ClassName();
524 if (strlen(option)) {
525 opt = option;
526 } else {
527 if (o->InheritsFrom(TAttLine::Class())) opt += "l";
528 if (o->InheritsFrom(TAttMarker::Class())) opt += "p";
529 if (o->InheritsFrom(TAttFill::Class())) opt += "f";
530 }
531 leg->AddEntry(o,mes.Data(),opt.Data());
532 } else if ( o->InheritsFrom(TMultiGraph::Class() ) ) {
533 if (!leg) leg = new TLegend(x1, y1, x2, y2, title);
534 TList * grlist = ((TMultiGraph *)o)->GetListOfGraphs();
535 TIter nextgraph(grlist);
536 TGraph * gr;
537 TObject * obj;
538 while ((obj = nextgraph())) {
539 gr = (TGraph*) obj;
540 if (strlen(gr->GetTitle())) mes = gr->GetTitle();
541 else if (strlen(gr->GetName())) mes = gr->GetName();
542 else mes = gr->ClassName();
543 if (strlen(option)) opt = option;
544 else opt = "lpf";
545 leg->AddEntry( obj, mes.Data(), opt );
546 }
547 } else if ( o->InheritsFrom(THStack::Class() ) ) {
548 if (!leg) leg = new TLegend(x1, y1, x2, y2, title);
549 TList * hlist = ((THStack *)o)->GetHists();
550 TIter nexthist(hlist);
551 TH1 * hist;
552 TObject * obj;
553 while ((obj = nexthist())) {
554 hist = (TH1*) obj;
555 if (strlen(hist->GetTitle())) mes = hist->GetTitle();
556 else if (strlen(hist->GetName())) mes = hist->GetName();
557 else mes = hist->ClassName();
558 if (strlen(option)) opt = option;
559 else opt = "lpf";
560 leg->AddEntry( obj, mes.Data(), opt );
561 }
562 }
563 }
564 if (leg) {
565 TVirtualPad *gpadsave;
566 gpadsave = gPad;
567 this->cd();
568 leg->Draw();
569 gpadsave->cd();
570 } else {
571 Info("BuildLegend(void)","No object to build a TLegend.");
572 }
573 return leg;
574}
575
576////////////////////////////////////////////////////////////////////////////////
577/// Set Current pad.
578///
579/// When a canvas/pad is divided via TPad::Divide, one can directly
580/// set the current path to one of the subdivisions.
581/// See TPad::Divide for the convention to number sub-pads.
582///
583/// Returns the new current pad, or 0 in case of failure.
584///
585/// For example:
586/// ~~~ {.cpp}
587/// c1.Divide(2,3); // create 6 pads (2 divisions along x, 3 along y).
588/// ~~~
589/// To set the current pad to the bottom right pad, do
590/// ~~~ {.cpp}
591/// c1.cd(6);
592/// ~~~
593/// Note1: c1.cd() is equivalent to c1.cd(0) and sets the current pad
594/// to c1 itself.
595///
596/// Note2: after a statement like c1.cd(6), the global variable gPad
597/// points to the current pad. One can use gPad to set attributes
598/// of the current pad.
599///
600/// Note3: One can get a pointer to one of the sub-pads of pad with:
601/// TPad *subpad = (TPad*)pad->GetPad(subpadnumber);
602
604{
605 if (!subpadnumber) {
606 gPad = this;
607 if (!gPad->IsBatch() && GetPainter()) GetPainter()->SelectDrawable(fPixmapID);
608 if (!fPrimitives) fPrimitives = new TList;
609 return gPad;
610 }
611
612 TObject *obj;
613 if (!fPrimitives) fPrimitives = new TList;
614 TIter next(fPrimitives);
615 while ((obj = next())) {
616 if (obj->InheritsFrom(TPad::Class())) {
617 Int_t n = ((TPad*)obj)->GetNumber();
618 if (n == subpadnumber) {
619 return ((TPad*)obj)->cd();
620 }
621 }
622 }
623 return 0;
624}
625
626////////////////////////////////////////////////////////////////////////////////
627/// Delete all pad primitives.
628///
629/// If the bit kClearAfterCR has been set for this pad, the Clear function
630/// will execute only after having pressed a CarriageReturn
631/// Set the bit with `mypad->SetBit(TPad::kClearAfterCR)`
632
634{
635 if (!IsEditable()) return;
636
638
639 if (!fPadPaint) {
641 if (fPrimitives) fPrimitives->Clear(option);
642 if (fFrame) {
643 if (fFrame->TestBit(kNotDeleted)) delete fFrame;
644 fFrame = nullptr;
645 }
646 }
647 if (fCanvas) fCanvas->Cleared(this);
648
649 cd();
650
651 if (TestBit(kClearAfterCR)) {
652 // Intentional do not use the return value of getchar,
653 // we just want to get it and forget it
654 getchar();
655 }
656
657 if (!gPad->IsBatch() && GetPainter()) GetPainter()->ClearDrawable();
658 if (gVirtualPS && gPad == gPad->GetCanvas()) gVirtualPS->NewPage();
659
661 fCrosshairPos = 0;
663 if (fCollideGrid) {
664 delete [] fCollideGrid;
665 fCollideGrid = nullptr;
666 fCGnx = 0;
667 fCGny = 0;
668 }
670}
671
672////////////////////////////////////////////////////////////////////////////////
673/// Clipping routine: Cohen Sutherland algorithm.
674///
675/// - If Clip ==2 the segment is outside the boundary.
676/// - If Clip ==1 the segment has one point outside the boundary.
677/// - If Clip ==0 the segment is inside the boundary.
678///
679/// \param[in] x[],y[] Segment coordinates (2 points)
680/// \param[in] xclipl,yclipb,xclipr,yclipt Clipping boundary
681/// \param[out] x[],y[] New segment coordinates( 2 points)
682
683Int_t TPad::Clip(Float_t *x, Float_t *y, Float_t xclipl, Float_t yclipb, Float_t xclipr, Float_t yclipt)
684{
685 const Float_t kP=10000;
686 Int_t clip = 0;
687
688 for (Int_t i=0;i<2;i++) {
689 if (TMath::Abs(xclipl-x[i]) <= TMath::Abs(xclipr-xclipl)/kP) x[i] = xclipl;
690 if (TMath::Abs(xclipr-x[i]) <= TMath::Abs(xclipr-xclipl)/kP) x[i] = xclipr;
691 if (TMath::Abs(yclipb-y[i]) <= TMath::Abs(yclipt-yclipb)/kP) y[i] = yclipb;
692 if (TMath::Abs(yclipt-y[i]) <= TMath::Abs(yclipt-yclipb)/kP) y[i] = yclipt;
693 }
694
695 // Compute the first endpoint codes.
696 Int_t code1 = ClippingCode(x[0],y[0],xclipl,yclipb,xclipr,yclipt);
697 Int_t code2 = ClippingCode(x[1],y[1],xclipl,yclipb,xclipr,yclipt);
698
699 Double_t xt=0, yt=0;
700 Int_t clipped = 0; //this variable could be used in a future version
701 while(code1 + code2) {
702 clipped = 1;
703
704 // The line lies entirely outside the clipping boundary
705 if (code1&code2) {
706 clip = 2;
707 return clip;
708 }
709
710 // The line is subdivided into several parts
711 Int_t ic = code1;
712 if (ic == 0) ic = code2;
713 if (ic & 0x1) {
714 yt = y[0] + (y[1]-y[0])*(xclipl-x[0])/(x[1]-x[0]);
715 xt = xclipl;
716 }
717 if (ic & 0x2) {
718 yt = y[0] + (y[1]-y[0])*(xclipr-x[0])/(x[1]-x[0]);
719 xt = xclipr;
720 }
721 if (ic & 0x4) {
722 xt = x[0] + (x[1]-x[0])*(yclipb-y[0])/(y[1]-y[0]);
723 yt = yclipb;
724 }
725 if (ic & 0x8) {
726 xt = x[0] + (x[1]-x[0])*(yclipt-y[0])/(y[1]-y[0]);
727 yt = yclipt;
728 }
729 if (ic == code1) {
730 x[0] = xt;
731 y[0] = yt;
732 code1 = ClippingCode(xt,yt,xclipl,yclipb,xclipr,yclipt);
733 } else {
734 x[1] = xt;
735 y[1] = yt;
736 code2 = ClippingCode(xt,yt,xclipl,yclipb,xclipr,yclipt);
737 }
738 }
739 clip = clipped;
740 return clip;
741}
742
743////////////////////////////////////////////////////////////////////////////////
744/// Clipping routine: Cohen Sutherland algorithm.
745///
746/// - If Clip ==2 the segment is outside the boundary.
747/// - If Clip ==1 the segment has one point outside the boundary.
748/// - If Clip ==0 the segment is inside the boundary.
749///
750/// \param[in] x[],y[] Segment coordinates (2 points)
751/// \param[in] xclipl,yclipb,xclipr,yclipt Clipping boundary
752/// \param[out] x[],y[] New segment coordinates(2 points)
753
755{
756 const Double_t kP=10000;
757 Int_t clip = 0;
758
759 for (Int_t i=0;i<2;i++) {
760 if (TMath::Abs(xclipl-x[i]) <= TMath::Abs(xclipr-xclipl)/kP) x[i] = xclipl;
761 if (TMath::Abs(xclipr-x[i]) <= TMath::Abs(xclipr-xclipl)/kP) x[i] = xclipr;
762 if (TMath::Abs(yclipb-y[i]) <= TMath::Abs(yclipt-yclipb)/kP) y[i] = yclipb;
763 if (TMath::Abs(yclipt-y[i]) <= TMath::Abs(yclipt-yclipb)/kP) y[i] = yclipt;
764 }
765
766 // Compute the first endpoint codes.
767 Int_t code1 = 0;
768 if (x[0] < xclipl) code1 = code1 | 0x1;
769 if (x[0] > xclipr) code1 = code1 | 0x2;
770 if (y[0] < yclipb) code1 = code1 | 0x4;
771 if (y[0] > yclipt) code1 = code1 | 0x8;
772 Int_t code2 = 0;
773 if (x[1] < xclipl) code2 = code2 | 0x1;
774 if (x[1] > xclipr) code2 = code2 | 0x2;
775 if (y[1] < yclipb) code2 = code2 | 0x4;
776 if (y[1] > yclipt) code2 = code2 | 0x8;
777
778 Double_t xt=0, yt=0;
779 Int_t clipped = 0; //this variable could be used in a future version
780 while(code1 + code2) {
781 clipped = 1;
782
783 // The line lies entirely outside the clipping boundary
784 if (code1&code2) {
785 clip = 2;
786 return clip;
787 }
788
789 // The line is subdivided into several parts
790 Int_t ic = code1;
791 if (ic == 0) ic = code2;
792 if (ic & 0x1) {
793 yt = y[0] + (y[1]-y[0])*(xclipl-x[0])/(x[1]-x[0]);
794 xt = xclipl;
795 }
796 if (ic & 0x2) {
797 yt = y[0] + (y[1]-y[0])*(xclipr-x[0])/(x[1]-x[0]);
798 xt = xclipr;
799 }
800 if (ic & 0x4) {
801 xt = x[0] + (x[1]-x[0])*(yclipb-y[0])/(y[1]-y[0]);
802 yt = yclipb;
803 }
804 if (ic & 0x8) {
805 xt = x[0] + (x[1]-x[0])*(yclipt-y[0])/(y[1]-y[0]);
806 yt = yclipt;
807 }
808 if (ic == code1) {
809 x[0] = xt;
810 y[0] = yt;
811 code1 = ClippingCode(xt,yt,xclipl,yclipb,xclipr,yclipt);
812 } else {
813 x[1] = xt;
814 y[1] = yt;
815 code2 = ClippingCode(xt,yt,xclipl,yclipb,xclipr,yclipt);
816 }
817 }
818 clip = clipped;
819 return clip;
820}
821
822////////////////////////////////////////////////////////////////////////////////
823/// Compute the endpoint codes for TPad::Clip.
824
826{
827 Int_t code = 0;
828 if (x < xcl1) code = code | 0x1;
829 if (x > xcl2) code = code | 0x2;
830 if (y < ycl1) code = code | 0x4;
831 if (y > ycl2) code = code | 0x8;
832 return code;
833}
834
835////////////////////////////////////////////////////////////////////////////////
836/// Clip polygon using the Sutherland-Hodgman algorithm.
837///
838/// \param[in] n Number of points in the polygon to
839/// be clipped
840/// \param[in] x[n],y[n] Polygon do be clipped vertices
841/// \param[in] xclipl,yclipb,xclipr,yclipt Clipping boundary
842/// \param[out] nn Number of points in xc and yc
843/// \param[out] xc,yc Clipped polygon vertices. The Int_t
844/// returned by this function is
845/// the number of points in the clipped
846/// polygon. These vectors must
847/// be allocated by the calling function.
848/// A size of 2*n for each is
849/// enough.
850///
851/// Sutherland and Hodgman's polygon-clipping algorithm uses a divide-and-conquer
852/// strategy: It solves a series of simple and identical problems that, when
853/// combined, solve the overall problem. The simple problem is to clip a polygon
854/// against a single infinite clip edge. Four clip edges, each defining one boundary
855/// of the clip rectangle, successively clip a polygon against a clip rectangle.
856///
857/// Steps of Sutherland-Hodgman's polygon-clipping algorithm:
858///
859/// * Polygons can be clipped against each edge of the window one at a time.
860/// Windows/edge intersections, if any, are easy to find since the X or Y coordinates
861/// are already known.
862/// * Vertices which are kept after clipping against one window edge are saved for
863/// clipping against the remaining edges.
864/// * Note that the number of vertices usually changes and will often increases.
865///
866/// The clip boundary determines a visible and invisible region. The edges from
867/// vertex i to vertex i+1 can be one of four types:
868///
869/// * Case 1 : Wholly inside visible region - save endpoint
870/// * Case 2 : Exit visible region - save the intersection
871/// * Case 3 : Wholly outside visible region - save nothing
872/// * Case 4 : Enter visible region - save intersection and endpoint
873
875{
876 Int_t nc, nc2;
877 Double_t x1, y1, x2, y2, slope; // Segment to be clipped
878
879 Double_t *xc2 = new Double_t[nn];
880 Double_t *yc2 = new Double_t[nn];
881
882 // Clip against the left boundary
883 x1 = x[n-1]; y1 = y[n-1];
884 nc2 = 0;
885 Int_t i;
886 for (i=0; i<n; i++) {
887 x2 = x[i]; y2 = y[i];
888 if (x1 == x2) {
889 slope = 0;
890 } else {
891 slope = (y2-y1)/(x2-x1);
892 }
893 if (x1 >= xclipl) {
894 if (x2 < xclipl) {
895 xc2[nc2] = xclipl; yc2[nc2++] = slope*(xclipl-x1)+y1;
896 } else {
897 xc2[nc2] = x2; yc2[nc2++] = y2;
898 }
899 } else {
900 if (x2 >= xclipl) {
901 xc2[nc2] = xclipl; yc2[nc2++] = slope*(xclipl-x1)+y1;
902 xc2[nc2] = x2; yc2[nc2++] = y2;
903 }
904 }
905 x1 = x2; y1 = y2;
906 }
907
908 // Clip against the top boundary
909 x1 = xc2[nc2-1]; y1 = yc2[nc2-1];
910 nc = 0;
911 for (i=0; i<nc2; i++) {
912 x2 = xc2[i]; y2 = yc2[i];
913 if (y1 == y2) {
914 slope = 0;
915 } else {
916 slope = (x2-x1)/(y2-y1);
917 }
918 if (y1 <= yclipt) {
919 if (y2 > yclipt) {
920 xc[nc] = x1+(yclipt-y1)*slope; yc[nc++] = yclipt;
921 } else {
922 xc[nc] = x2; yc[nc++] = y2;
923 }
924 } else {
925 if (y2 <= yclipt) {
926 xc[nc] = x1+(yclipt-y1)*slope; yc[nc++] = yclipt;
927 xc[nc] = x2; yc[nc++] = y2;
928 }
929 }
930 x1 = x2; y1 = y2;
931 }
932
933 if (nc>0) {
934
935 // Clip against the right boundary
936 x1 = xc[nc-1]; y1 = yc[nc-1];
937 nc2 = 0;
938 for (i=0; i<nc; i++) {
939 x2 = xc[i]; y2 = yc[i];
940 if (x1 == x2) {
941 slope = 0;
942 } else {
943 slope = (y2-y1)/(x2-x1);
944 }
945 if (x1 <= xclipr) {
946 if (x2 > xclipr) {
947 xc2[nc2] = xclipr; yc2[nc2++] = slope*(xclipr-x1)+y1;
948 } else {
949 xc2[nc2] = x2; yc2[nc2++] = y2;
950 }
951 } else {
952 if (x2 <= xclipr) {
953 xc2[nc2] = xclipr; yc2[nc2++] = slope*(xclipr-x1)+y1;
954 xc2[nc2] = x2; yc2[nc2++] = y2;
955 }
956 }
957 x1 = x2; y1 = y2;
958 }
959
960 // Clip against the bottom boundary
961 x1 = xc2[nc2-1]; y1 = yc2[nc2-1];
962 nc = 0;
963 for (i=0; i<nc2; i++) {
964 x2 = xc2[i]; y2 = yc2[i];
965 if (y1 == y2) {
966 slope = 0;
967 } else {
968 slope = (x2-x1)/(y2-y1);
969 }
970 if (y1 >= yclipb) {
971 if (y2 < yclipb) {
972 xc[nc] = x1+(yclipb-y1)*slope; yc[nc++] = yclipb;
973 } else {
974 xc[nc] = x2; yc[nc++] = y2;
975 }
976 } else {
977 if (y2 >= yclipb) {
978 xc[nc] = x1+(yclipb-y1)*slope; yc[nc++] = yclipb;
979 xc[nc] = x2; yc[nc++] = y2;
980 }
981 }
982 x1 = x2; y1 = y2;
983 }
984 }
985
986 delete [] xc2;
987 delete [] yc2;
988
989 if (nc < 3) nc =0;
990 return nc;
991}
992
993////////////////////////////////////////////////////////////////////////////////
994/// Delete all primitives in pad and pad itself.
995/// Pad cannot be used anymore after this call.
996/// Emits signal "Closed()".
997
999{
1000 if (!TestBit(kNotDeleted)) return;
1001 if (!fMother) return;
1002 if (!fMother->TestBit(kNotDeleted)) return;
1003
1004 if (fPrimitives)
1005 fPrimitives->Clear();
1006 if (fView) {
1007 if (fView->TestBit(kNotDeleted)) delete fView;
1008 fView = nullptr;
1009 }
1010 if (fFrame) {
1011 if (fFrame->TestBit(kNotDeleted)) delete fFrame;
1012 fFrame = nullptr;
1013 }
1014
1015 // emit signal
1016 if (IsA() != TCanvas::Class())
1017 Closed();
1018
1019 if (fPixmapID != -1) {
1020 if (gPad) {
1021 if (!gPad->IsBatch() && GetPainter())
1023 }
1024 fPixmapID = -1;
1025
1026 if (!gROOT->GetListOfCanvases()) return;
1027 if (fMother == this) {
1028 gROOT->GetListOfCanvases()->Remove(this);
1029 return; // in case of TCanvas
1030 }
1031
1032 // remove from the mother's list of primitives
1033 if (fMother) {
1036
1037 if (gPad == this) fMother->cd();
1038 }
1039 if (fCanvas) {
1040 if (fCanvas->GetPadSave() == this)
1042 if (fCanvas->GetSelectedPad() == this)
1044 if (fCanvas->GetClickSelectedPad() == this)
1046 }
1047 }
1048
1049 fMother = nullptr;
1050 if (gROOT->GetSelectedPad() == this) gROOT->SetSelectedPad(nullptr);
1051}
1052
1053////////////////////////////////////////////////////////////////////////////////
1054/// Copy the pixmap of the pad to the canvas.
1055
1057{
1058 int px, py;
1059 XYtoAbsPixel(fX1, fY2, px, py);
1060
1061 if (fPixmapID != -1 && GetPainter())
1062 GetPainter()->CopyDrawable(fPixmapID, px, py);
1063
1064 if (this == gPad) HighLight(gPad->GetHighLightColor());
1065}
1066
1067////////////////////////////////////////////////////////////////////////////////
1068/// Copy the sub-pixmaps of the pad to the canvas.
1069
1071{
1072 TObject *obj;
1073 if (!fPrimitives) fPrimitives = new TList;
1074 TIter next(GetListOfPrimitives());
1075 while ((obj = next())) {
1076 if (obj->InheritsFrom(TPad::Class())) {
1077 ((TPad*)obj)->CopyPixmap();
1078 ((TPad*)obj)->CopyPixmaps();
1079 }
1080 }
1081}
1082
1083////////////////////////////////////////////////////////////////////////////////
1084/// Remove TExec name from the list of Execs.
1085
1086void TPad::DeleteExec(const char *name)
1087{
1088 if (!fExecs) fExecs = new TList;
1090 if (!ex) return;
1091 fExecs->Remove(ex);
1092 delete ex;
1093}
1094
1095////////////////////////////////////////////////////////////////////////////////
1096/// Compute distance from point px,py to a box.
1097///
1098/// Compute the closest distance of approach from point px,py to the
1099/// edges of this pad.
1100/// The distance is computed in pixels units.
1101
1103{
1104 Int_t pxl, pyl, pxt, pyt;
1105 Int_t px1 = gPad->XtoAbsPixel(fX1);
1106 Int_t py1 = gPad->YtoAbsPixel(fY1);
1107 Int_t px2 = gPad->XtoAbsPixel(fX2);
1108 Int_t py2 = gPad->YtoAbsPixel(fY2);
1109 if (px1 < px2) {pxl = px1; pxt = px2;}
1110 else {pxl = px2; pxt = px1;}
1111 if (py1 < py2) {pyl = py1; pyt = py2;}
1112 else {pyl = py2; pyt = py1;}
1113
1114 // Are we inside the box?
1115 // ======================
1116 if ( (px > pxl && px < pxt) && (py > pyl && py < pyt) ) {
1117 if (GetFillStyle()) return 0; //*-* if pad is filled
1118 }
1119
1120 // Are we on the edges?
1121 // ====================
1122 Int_t dxl = TMath::Abs(px - pxl);
1123 if (py < pyl) dxl += pyl - py;
1124 if (py > pyt) dxl += py - pyt;
1125 Int_t dxt = TMath::Abs(px - pxt);
1126 if (py < pyl) dxt += pyl - py;
1127 if (py > pyt) dxt += py - pyt;
1128 Int_t dyl = TMath::Abs(py - pyl);
1129 if (px < pxl) dyl += pxl - px;
1130 if (px > pxt) dyl += px - pxt;
1131 Int_t dyt = TMath::Abs(py - pyt);
1132 if (px < pxl) dyt += pxl - px;
1133 if (px > pxt) dyt += px - pxt;
1134
1135 Int_t distance = dxl;
1136 if (dxt < distance) distance = dxt;
1137 if (dyl < distance) distance = dyl;
1138 if (dyt < distance) distance = dyt;
1139
1140 return distance - Int_t(0.5*fLineWidth);
1141}
1142
1143////////////////////////////////////////////////////////////////////////////////
1144/// Automatic pad generation by division.
1145///
1146/// - The current canvas is divided in nx by ny equal divisions (pads).
1147/// - xmargin is the space along x between pads in percent of canvas.
1148/// - ymargin is the space along y between pads in percent of canvas.
1149/// - color is the color of the new pads. If 0, color is the canvas color.
1150///
1151/// Pads are automatically named `canvasname_n` where `n` is the division number
1152/// starting from top left pad.
1153///
1154/// Example if canvasname=c1 , nx=2, ny=3:
1155///
1156/// \image html gpad_pad3.png
1157///
1158/// Once a pad is divided into sub-pads, one can set the current pad
1159/// to a subpad with a given division number as illustrated above
1160/// with TPad::cd(subpad_number).
1161///
1162/// For example, to set the current pad to c1_4, one can do:
1163/// ~~~ {.cpp}
1164/// c1->cd(4)
1165/// ~~~
1166/// __Note1:__ c1.cd() is equivalent to c1.cd(0) and sets the current pad
1167/// to c1 itself.
1168///
1169/// __Note2:__ after a statement like c1.cd(6), the global variable gPad
1170/// points to the current pad. One can use gPad to set attributes
1171/// of the current pad.
1172///
1173/// __Note3:__ in case xmargin <=0 and ymargin <= 0, there is no space
1174/// between pads. The current pad margins are recomputed to
1175/// optimize the layout.
1176
1177void TPad::Divide(Int_t nx, Int_t ny, Float_t xmargin, Float_t ymargin, Int_t color)
1178{
1179 if (!IsEditable()) return;
1180
1181
1182 if (gThreadXAR) {
1183 void *arr[7];
1184 arr[1] = this; arr[2] = (void*)&nx;arr[3] = (void*)& ny;
1185 arr[4] = (void*)&xmargin; arr[5] = (void *)& ymargin; arr[6] = (void *)&color;
1186 if ((*gThreadXAR)("PDCD", 7, arr, 0)) return;
1187 }
1188
1189 TPad *padsav = (TPad*)gPad;
1190 cd();
1191 if (nx <= 0) nx = 1;
1192 if (ny <= 0) ny = 1;
1193 Int_t ix,iy;
1194 Double_t x1,y1,x2,y2;
1195 Double_t dx,dy;
1196 TPad *pad;
1197 Int_t nchname = strlen(GetName())+6;
1198 Int_t nchtitle = strlen(GetTitle())+6;
1199 char *name = new char [nchname];
1200 char *title = new char [nchtitle];
1201 Int_t n = 0;
1202 if (color == 0) color = GetFillColor();
1203 if (xmargin > 0 && ymargin > 0) {
1204 //general case
1205 dy = 1/Double_t(ny);
1206 dx = 1/Double_t(nx);
1207 for (iy=0;iy<ny;iy++) {
1208 y2 = 1 - iy*dy - ymargin;
1209 y1 = y2 - dy + 2*ymargin;
1210 if (y1 < 0) y1 = 0;
1211 if (y1 > y2) continue;
1212 for (ix=0;ix<nx;ix++) {
1213 x1 = ix*dx + xmargin;
1214 x2 = x1 +dx -2*xmargin;
1215 if (x1 > x2) continue;
1216 n++;
1217 snprintf(name,nchname,"%s_%d",GetName(),n);
1218 pad = new TPad(name,name,x1,y1,x2,y2,color);
1219 pad->SetNumber(n);
1220 pad->Draw();
1221 }
1222 }
1223 } else {
1224 // special case when xmargin <= 0 && ymargin <= 0
1225 Double_t xl = GetLeftMargin();
1226 Double_t xr = GetRightMargin();
1228 Double_t yt = GetTopMargin();
1229 xl /= (1-xl+xr)*nx;
1230 xr /= (1-xl+xr)*nx;
1231 yb /= (1-yb+yt)*ny;
1232 yt /= (1-yb+yt)*ny;
1233 SetLeftMargin(xl);
1234 SetRightMargin(xr);
1235 SetBottomMargin(yb);
1236 SetTopMargin(yt);
1237 dx = (1-xl-xr)/nx;
1238 dy = (1-yb-yt)/ny;
1239 Int_t number = 0;
1240 for (Int_t i=0;i<nx;i++) {
1241 x1 = i*dx+xl;
1242 x2 = x1 + dx;
1243 if (i == 0) x1 = 0;
1244 if (i == nx-1) x2 = 1-xr;
1245 for (Int_t j=0;j<ny;j++) {
1246 number = j*nx + i +1;
1247 y2 = 1 -j*dy -yt;
1248 y1 = y2 - dy;
1249 if (j == 0) y2 = 1-yt;
1250 if (j == ny-1) y1 = 0;
1251 snprintf(name,nchname,"%s_%d",GetName(),number);
1252 snprintf(title,nchtitle,"%s_%d",GetTitle(),number);
1253 pad = new TPad(name,title,x1,y1,x2,y2);
1254 pad->SetNumber(number);
1255 pad->SetBorderMode(0);
1256 if (i == 0) pad->SetLeftMargin(xl*nx);
1257 else pad->SetLeftMargin(0);
1258 pad->SetRightMargin(0);
1259 pad->SetTopMargin(0);
1260 if (j == ny-1) pad->SetBottomMargin(yb*ny);
1261 else pad->SetBottomMargin(0);
1262 pad->Draw();
1263 }
1264 }
1265 }
1266 delete [] name;
1267 delete [] title;
1268 Modified();
1269 if (padsav) padsav->cd();
1270}
1271
1272////////////////////////////////////////////////////////////////////////////////
1273/// "n" is the total number of sub-pads. The number of sub-pads along the X
1274/// and Y axis are computed according to the square root of n.
1275
1276void TPad::DivideSquare(Int_t n, Float_t xmargin, Float_t ymargin, Int_t color)
1277{
1278 Int_t w = 1, h = 1;
1279 if (!fCanvas) {
1280 Error("DivideSquare", "No canvas associated with this pad.");
1281 return;
1282 }
1286 if (w*h < n) w++;
1287 } else {
1290 if (w*h < n) h++;
1291 }
1292
1293 Divide( w, h, xmargin, ymargin, color);
1294}
1295
1296////////////////////////////////////////////////////////////////////////////////
1297/// Draw Pad in Current pad (re-parent pad if necessary).
1298
1300{
1301 // if no canvas opened yet create a default canvas
1302 if (!gPad) {
1303 gROOT->MakeDefCanvas();
1304 }
1305
1306 // pad cannot be in itself and it can only be in one other pad at a time
1307 if (!fPrimitives) fPrimitives = new TList;
1308 if (gPad != this) {
1311 TPad *oldMother = fMother;
1312 fCanvas = gPad->GetCanvas();
1313 //
1314 fMother = (TPad*)gPad;
1315 if (oldMother != fMother || fPixmapID == -1) ResizePad();
1316 }
1317
1318 Paint();
1319
1320 if (gPad->IsRetained() && gPad != this && fMother)
1321 if (fMother->GetListOfPrimitives()) fMother->GetListOfPrimitives()->Add(this, option);
1322}
1323
1324////////////////////////////////////////////////////////////////////////////////
1325/// Draw class inheritance tree of the class to which obj belongs.
1326///
1327/// If a class B inherits from a class A, description of B is drawn
1328/// on the right side of description of A.
1329///
1330/// Member functions overridden by B are shown in class A with a blue line
1331/// crossing-out the corresponding member function.
1332
1333void TPad::DrawClassObject(const TObject *classobj, Option_t *option)
1334{
1335 if (!classobj) return;
1336 char dname[256];
1337 const Int_t kMAXLEVELS = 10;
1338 TClass *clevel[kMAXLEVELS], *cl, *cll;
1339 TBaseClass *base, *cinherit;
1340 TText *ptext = 0;
1341 TString opt=option;
1342 Double_t x,y,dy,y1,v1,v2,dv;
1343 Int_t nd,nf,nc,nkd,nkf,i,j;
1344 TPaveText *pt;
1345 Int_t maxlev = 4;
1346 if (opt.Contains("2")) maxlev = 2;
1347 if (opt.Contains("3")) maxlev = 3;
1348 if (opt.Contains("5")) maxlev = 5;
1349 if (opt.Contains("6")) maxlev = 6;
1350 if (opt.Contains("7")) maxlev = 7;
1351
1352 // Clear and Set Pad range
1353 Double_t xpad = 20.5;
1354 Double_t ypad = 27.5;
1355 Clear();
1356 Range(0,0,xpad,ypad);
1357
1358 // Find number of levels
1359 Int_t nlevel = 0;
1360 TClass *obj = (TClass*)classobj;
1361 clevel[nlevel] = obj;
1362 TList *lbase = obj->GetListOfBases();
1363 while(lbase) {
1364 base = (TBaseClass*)lbase->First();
1365 if (!base) break;
1366 if ( base->GetClassPointer() == 0) break;
1367 nlevel++;
1368 clevel[nlevel] = base->GetClassPointer();
1369 lbase = clevel[nlevel]->GetListOfBases();
1370 if (nlevel >= maxlev-1) break;
1371 }
1372 Int_t maxelem = 0;
1373 Int_t ncdraw = 0;
1374 Int_t ilevel, nelem;
1375 for (ilevel=nlevel;ilevel>=0;ilevel--) {
1376 cl = clevel[ilevel];
1377 nelem = cl->GetNdata() + cl->GetNmethods();
1378 if (nelem > maxelem) maxelem = nelem;
1379 nc = (nelem/50) + 1;
1380 ncdraw += nc;
1381 }
1382
1383 Double_t tsizcm = 0.40;
1384 Double_t x1 = 0.25;
1385 Double_t x2 = 0;
1386 Double_t dx = 3.5;
1387 if (ncdraw > 4) {
1388 dx = dx - 0.42*Double_t(ncdraw-5);
1389 if (dx < 1.3) dx = 1.3;
1390 tsizcm = tsizcm - 0.03*Double_t(ncdraw-5);
1391 if (tsizcm < 0.27) tsizcm = 0.27;
1392 }
1393 Double_t tsiz = 1.2*tsizcm/ypad;
1394
1395 // Now loop on levels
1396 for (ilevel=nlevel;ilevel>=0;ilevel--) {
1397 cl = clevel[ilevel];
1398 nelem = cl->GetNdata() + cl->GetNmethods();
1399 if (nelem > maxelem) maxelem = nelem;
1400 nc = (nelem/50) + 1;
1401 dy = 0.45;
1402 if (ilevel < nlevel) x1 = x2 + 0.5;
1403 x2 = x1 + nc*dx;
1404 v2 = ypad - 0.5;
1405 lbase = cl->GetListOfBases();
1406 cinherit = 0;
1407 if (lbase) cinherit = (TBaseClass*)lbase->First();
1408
1409 do {
1410 nd = cl->GetNdata();
1411 nf = cl->GetNmethods() - 2; //do not show default constructor and destructor
1412 if (cl->GetListOfMethods()->FindObject("Dictionary")) {
1413 nf -= 6; // do not count the Dictionary/ClassDef functions
1414 }
1415 nkf= nf/nc +1;
1416 nkd= nd/nc +1;
1417 if (nd == 0) nkd=0;
1418 if (nf == 0) nkf=0;
1419 y1 = v2 - 0.7;
1420 v1 = y1 - Double_t(nkf+nkd+nc-1)*dy;
1421 dv = v2 - v1;
1422
1423 // Create a new PaveText
1424 pt = new TPaveText(x1,v1,x2,v2);
1426 pt->SetFillColor(19);
1427 pt->Draw();
1428 pt->SetTextColor(4);
1429 pt->SetTextFont(61);
1430 pt->SetTextAlign(12);
1431 pt->SetTextSize(tsiz);
1432 TBox *box = pt->AddBox(0,(y1+0.01-v1)/dv,0,(v2-0.01-v1)/dv);
1433 if (box) box->SetFillColor(17);
1434 pt->AddLine(0,(y1-v1)/dv,0,(y1-v1)/dv);
1435 TText *title = pt->AddText(0.5,(0.5*(y1+v2)-v1)/dv,(char*)cl->GetName());
1436 title->SetTextAlign(22);
1437 title->SetTextSize(0.6*(v2-y1)/ypad);
1438
1439 // Draw data Members
1440 i = 0;
1441 x = 0.03;
1442 y = y1 + 0.5*dy;
1443 TDataMember *d;
1444 TIter nextd(cl->GetListOfDataMembers());
1445 while ((d = (TDataMember *) nextd())) {
1446 if (i >= nkd) { i = 1; y = y1 - 0.5*dy; x += 1/Double_t(nc); }
1447 else { i++; y -= dy; }
1448
1449 // Take in account the room the array index will occupy
1450
1451 Int_t dim = d->GetArrayDim();
1452 Int_t indx = 0;
1453 snprintf(dname,256,"%s",d->GetName());
1454 Int_t ldname = 0;
1455 while (indx < dim ){
1456 ldname = strlen(dname);
1457 snprintf(&dname[ldname],256-ldname,"[%d]",d->GetMaxIndex(indx));
1458 indx++;
1459 }
1460 pt->AddText(x,(y-v1)/dv,dname);
1461 }
1462
1463 // Draw a separator line
1464 Double_t ysep;
1465 if (nd) {
1466 ysep = y1 - Double_t(nkd)*dy;
1467 pt->AddLine(0,(ysep-v1)/dv,0,(ysep-v1)/dv);
1468 ysep -= 0.5*dy;
1469 } else ysep = y1;
1470
1471 // Draw Member Functions
1472 Int_t fcount = 0;
1473 i = 0;
1474 x = 0.03;
1475 y = ysep + 0.5*dy;
1476 TMethod *m;
1477 TIter nextm(cl->GetListOfMethods());
1478 while ((m = (TMethod *) nextm())) {
1479 if (
1480 !strcmp( m->GetName(), "Dictionary" ) ||
1481 !strcmp( m->GetName(), "Class_Version" ) ||
1482 !strcmp( m->GetName(), "DeclFileName" ) ||
1483 !strcmp( m->GetName(), "DeclFileLine" ) ||
1484 !strcmp( m->GetName(), "ImplFileName" ) ||
1485 !strcmp( m->GetName(), "ImplFileLine" )
1486 ) continue;
1487 fcount++;
1488 if (fcount > nf) break;
1489 if (i >= nkf) { i = 1; y = ysep - 0.5*dy; x += 1/Double_t(nc); }
1490 else { i++; y -= dy; }
1491
1492 ptext = pt->AddText(x,(y-v1)/dv,m->GetName());
1493 // Check if method is overloaded in a derived class
1494 // If yes, Change the color of the text to blue
1495 for (j=ilevel-1;j>=0;j--) {
1496 if (cl == clevel[ilevel]) {
1497 if (clevel[j]->GetMethodAny((char*)m->GetName())) {
1498 ptext->SetTextColor(15);
1499 break;
1500 }
1501 }
1502 }
1503 }
1504
1505 // Draw second inheritance classes for this class
1506 cll = 0;
1507 if (cinherit) {
1508 cinherit = (TBaseClass*)lbase->After(cinherit);
1509 if (cinherit) {
1510 cl = cinherit->GetClassPointer();
1511 cll = cl;
1512 v2 = v1 -0.4;
1513 dy = 0.35;
1514 }
1515 }
1516 } while (cll);
1517 }
1518 Update();
1519}
1520
1521////////////////////////////////////////////////////////////////////////////////
1522/// Function called to draw a crosshair in the canvas
1523///
1524/// Example:
1525/// ~~~ {.cpp}
1526/// Root > TFile f("hsimple.root");
1527/// Root > hpxpy.Draw();
1528/// Root > c1.SetCrosshair();
1529/// ~~~
1530/// When moving the mouse in the canvas, a crosshair is drawn
1531///
1532/// - if the canvas fCrosshair = 1 , the crosshair spans the full canvas
1533/// - if the canvas fCrosshair > 1 , the crosshair spans only the pad
1534
1536{
1537 if (gPad->GetEvent() == kMouseEnter) return;
1538
1539 TPad *cpad = (TPad*)gPad;
1540 TCanvas *canvas = cpad->GetCanvas();
1541 canvas->FeedbackMode(kTRUE);
1542
1543 //erase old position and draw a line at current position
1544 Int_t pxmin,pxmax,pymin,pymax,pxold,pyold,px,py;
1545 pxold = fCrosshairPos%10000;
1546 pyold = fCrosshairPos/10000;
1547 px = cpad->GetEventX();
1548 py = cpad->GetEventY()+1;
1549 if (canvas->GetCrosshair() > 1) { //crosshair only in the current pad
1550 pxmin = cpad->XtoAbsPixel(fX1);
1551 pxmax = cpad->XtoAbsPixel(fX2);
1552 pymin = cpad->YtoAbsPixel(fY1);
1553 pymax = cpad->YtoAbsPixel(fY2);
1554 } else { //default; crosshair spans the full canvas
1555 pxmin = 0;
1556 pxmax = canvas->GetWw();
1557 pymin = 0;
1558 pymax = cpad->GetWh();
1559 }
1560#ifndef R__HAS_COCOA
1561 // Not needed, no XOR with Cocoa.
1562 if(pxold) gVirtualX->DrawLine(pxold,pymin,pxold,pymax);
1563 if(pyold) gVirtualX->DrawLine(pxmin,pyold,pxmax,pyold);
1564#endif // R__HAS_COCOA
1565 if (cpad->GetEvent() == kButton1Down ||
1566 cpad->GetEvent() == kButton1Up ||
1567 cpad->GetEvent() == kMouseLeave) {
1568 fCrosshairPos = 0;
1569 return;
1570 }
1571 gVirtualX->DrawLine(px,pymin,px,pymax);
1572 gVirtualX->DrawLine(pxmin,py,pxmax,py);
1573 fCrosshairPos = px + 10000*py;
1574}
1575
1576////////////////////////////////////////////////////////////////////////////////
1577/// Draw an empty pad frame with X and Y axis.
1578///
1579/// \return The pointer to the histogram used to draw the frame.
1580///
1581/// \param[in] xmin X axis lower limit
1582/// \param[in] xmax X axis upper limit
1583/// \param[in] ymin Y axis lower limit
1584/// \param[in] ymax Y axis upper limit
1585/// \param[in] title Pad title.If title is of the form "stringt;stringx;stringy"
1586/// the pad title is set to stringt, the x axis title to
1587/// stringx, the y axis title to stringy.
1588///
1589/// #### Example:
1590///
1591/// Begin_Macro(source)
1592/// {
1593/// auto c = new TCanvas("c","c",200,10,500,300);
1594///
1595/// const Int_t n = 50;
1596/// auto g = new TGraph();
1597/// for (Int_t i=0;i<n;i++) g->SetPoint(i,i*0.1,100*sin(i*0.1+0.2));
1598///
1599/// auto frame = c->DrawFrame(0, -110, 2, 110);
1600/// frame->GetXaxis()->SetTitle("X axis");
1601///
1602/// g->Draw("L*");
1603/// }
1604/// End_Macro
1605
1607{
1608 if (!IsEditable()) return 0;
1609 TPad *padsav = (TPad*)gPad;
1610 if (this != padsav) {
1611 Warning("DrawFrame","Must be called for the current pad only");
1612 return padsav->DrawFrame(xmin,ymin,xmax,ymax,title);
1613 }
1614
1615 cd();
1616
1617 TH1F *hframe = (TH1F*)FindObject("hframe");
1618 if (hframe) delete hframe;
1619 Int_t nbins = 1000;
1620 //if log scale in X, use variable bin size linear with log(x)
1621 //this gives a better precision when zooming on the axis
1622 if (fLogx && xmin > 0 && xmax > xmin) {
1623 Double_t xminl = TMath::Log(xmin);
1624 Double_t xmaxl = TMath::Log(xmax);
1625 Double_t dx = (xmaxl-xminl)/nbins;
1626 Double_t *xbins = new Double_t[nbins+1];
1627 xbins[0] = xmin;
1628 for (Int_t i=1;i<=nbins;i++) {
1629 xbins[i] = TMath::Exp(xminl+i*dx);
1630 }
1631 hframe = new TH1F("hframe",title,nbins,xbins);
1632 delete [] xbins;
1633 } else {
1634 hframe = new TH1F("hframe",title,nbins,xmin,xmax);
1635 }
1636 hframe->SetBit(TH1::kNoStats);
1637 hframe->SetBit(kCanDelete);
1638 hframe->SetMinimum(ymin);
1639 hframe->SetMaximum(ymax);
1640 hframe->GetYaxis()->SetLimits(ymin,ymax);
1641 hframe->SetDirectory(0);
1642 hframe->Draw(" ");
1643 Update();
1644 if (padsav) padsav->cd();
1645 return hframe;
1646}
1647
1648////////////////////////////////////////////////////////////////////////////////
1649/// Static function to Display Color Table in a pad.
1650
1652{
1653 Int_t i, j;
1654 Int_t color;
1655 Double_t xlow, ylow, xup, yup, hs, ws;
1656 Double_t x1, y1, x2, y2;
1657 x1 = y1 = 0;
1658 x2 = y2 = 20;
1659
1660 gPad->SetFillColor(0);
1661 gPad->Clear();
1662 gPad->Range(x1,y1,x2,y2);
1663
1664 TText *text = new TText(0,0,"");
1665 text->SetTextFont(61);
1666 text->SetTextSize(0.07);
1667 text->SetTextAlign(22);
1668
1669 TBox *box = new TBox();
1670
1671 // Draw color table boxes.
1672 hs = (y2-y1)/Double_t(5);
1673 ws = (x2-x1)/Double_t(10);
1674 for (i=0;i<10;i++) {
1675 xlow = x1 + ws*(Double_t(i)+0.1);
1676 xup = x1 + ws*(Double_t(i)+0.9);
1677 for (j=0;j<5;j++) {
1678 ylow = y1 + hs*(Double_t(j)+0.1);
1679 yup = y1 + hs*(Double_t(j)+0.9);
1680 color = 10*j + i;
1681 box->SetFillStyle(1001);
1682 box->SetFillColor(color);
1683 box->DrawBox(xlow, ylow, xup, yup);
1684 box->SetFillStyle(0);
1685 box->SetLineColor(1);
1686 box->DrawBox(xlow, ylow, xup, yup);
1687 if (color == 1) text->SetTextColor(0);
1688 else text->SetTextColor(1);
1689 text->DrawText(0.5*(xlow+xup), 0.5*(ylow+yup), Form("%d",color));
1690 }
1691 }
1692}
1693
1694////////////////////////////////////////////////////////////////////////////////
1695/// Execute action corresponding to one event.
1696///
1697/// This member function is called when a TPad object is clicked.
1698///
1699/// If the mouse is clicked in one of the 4 corners of the pad (pA,pB,pC,pD)
1700/// the pad is resized with the rubber rectangle.
1701///
1702/// If the mouse is clicked inside the pad, the pad is moved.
1703///
1704/// If the mouse is clicked on the 4 edges (pL,pR,pTop,pBot), the pad is scaled
1705/// parallel to this edge.
1706///
1707/// \image html gpad_pad4.png
1708///
1709/// Note that this function duplicates on purpose the functionality
1710/// already implemented in TBox::ExecuteEvent.
1711/// If somebody modifies this function, may be similar changes should also
1712/// be applied to TBox::ExecuteEvent.
1713
1715{
1716 const Int_t kMaxDiff = 5;
1717 const Int_t kMinSize = 20;
1718 static Int_t pxorg, pyorg;
1719 static Int_t px1, px2, py1, py2, pxl, pyl, pxt, pyt, pxold, pyold;
1720 static Int_t px1p, px2p, py1p, py2p, pxlp, pylp, pxtp, pytp;
1721 static Bool_t pA, pB, pC, pD, pTop, pL, pR, pBot, pINSIDE;
1722 Int_t wx, wy;
1723 Bool_t opaque = OpaqueMoving();
1724 Bool_t ropaque = OpaqueResizing();
1725 Bool_t fixedr = HasFixedAspectRatio();
1726
1727 if (!IsEditable() && event != kMouseEnter) return;
1728 TVirtualPad *parent = GetMother();
1729 if (!parent->IsEditable()) return;
1730
1731 HideToolTip(event);
1732
1733 if (fXlowNDC < 0 && event != kButton1Down) return;
1734 if (fYlowNDC < 0 && event != kButton1Down) return;
1735
1736 // keep old mouse position
1737 if (event == kButton1Down) {
1738 pxorg = px;
1739 pyorg = py;
1740 }
1741
1742 Int_t newcode = gROOT->GetEditorMode();
1743 if (newcode)
1744 pA = pB = pC = pD = pTop = pL = pR = pBot = pINSIDE = kFALSE;
1745 switch (newcode) {
1746 case kPad:
1747 TCreatePrimitives::Pad(event,px,py,0);
1748 break;
1749 case kMarker:
1750 case kText:
1751 TCreatePrimitives::Text(event,px,py,newcode);
1752 break;
1753 case kLine:
1754 TCreatePrimitives::Line(event,px,py,kLine);
1755 break;
1756 case kArrow:
1757 TCreatePrimitives::Line(event,px,py,kArrow);
1758 break;
1759 case kCurlyLine:
1761 break;
1762 case kCurlyArc:
1764 break;
1765 case kPolyLine:
1767 break;
1768 case kCutG:
1770 break;
1771 case kArc:
1772 TCreatePrimitives::Ellipse(event,px,py,kArc);
1773 break;
1774 case kEllipse:
1776 break;
1777 case kButton:
1778 case kPave:
1779 case kPaveLabel:
1780 case kPaveText:
1781 case kPavesText:
1782 case kDiamond:
1783 TCreatePrimitives::Pave(event,px,py,newcode);
1784 return;
1785 default:
1786 break;
1787 }
1788 if (newcode) return;
1789
1790 switch (event) {
1791
1792 case kMouseEnter:
1793 if (fTip)
1795 break;
1796
1797 case kArrowKeyPress:
1798 case kButton1Down:
1799
1802
1803 GetPainter()->SetLineColor(-1);
1804 TAttLine::Modify(); //Change line attributes only if necessary
1805 if (GetFillColor())
1807 else
1810
1811 // No break !!!
1812
1813 case kMouseMotion:
1814
1815 px1 = XtoAbsPixel(fX1);
1816 py1 = YtoAbsPixel(fY1);
1817 px2 = XtoAbsPixel(fX2);
1818 py2 = YtoAbsPixel(fY2);
1819
1820 if (px1 < px2) {
1821 pxl = px1;
1822 pxt = px2;
1823 } else {
1824 pxl = px2;
1825 pxt = px1;
1826 }
1827 if (py1 < py2) {
1828 pyl = py1;
1829 pyt = py2;
1830 } else {
1831 pyl = py2;
1832 pyt = py1;
1833 }
1834
1835 px1p = parent->XtoAbsPixel(parent->GetX1()) + parent->GetBorderSize();
1836 py1p = parent->YtoAbsPixel(parent->GetY1()) - parent->GetBorderSize();
1837 px2p = parent->XtoAbsPixel(parent->GetX2()) - parent->GetBorderSize();
1838 py2p = parent->YtoAbsPixel(parent->GetY2()) + parent->GetBorderSize();
1839
1840 if (px1p < px2p) {
1841 pxlp = px1p;
1842 pxtp = px2p;
1843 } else {
1844 pxlp = px2p;
1845 pxtp = px1p;
1846 }
1847 if (py1p < py2p) {
1848 pylp = py1p;
1849 pytp = py2p;
1850 } else {
1851 pylp = py2p;
1852 pytp = py1p;
1853 }
1854
1855 pA = pB = pC = pD = pTop = pL = pR = pBot = pINSIDE = kFALSE;
1856
1857 // case pA
1858 if (TMath::Abs(px - pxl) <= kMaxDiff && TMath::Abs(py - pyl) <= kMaxDiff) {
1859 pxold = pxl; pyold = pyl; pA = kTRUE;
1861 }
1862 // case pB
1863 if (TMath::Abs(px - pxt) <= kMaxDiff && TMath::Abs(py - pyl) <= kMaxDiff) {
1864 pxold = pxt; pyold = pyl; pB = kTRUE;
1866 }
1867 // case pC
1868 if (TMath::Abs(px - pxt) <= kMaxDiff && TMath::Abs(py - pyt) <= kMaxDiff) {
1869 pxold = pxt; pyold = pyt; pC = kTRUE;
1871 }
1872 // case pD
1873 if (TMath::Abs(px - pxl) <= kMaxDiff && TMath::Abs(py - pyt) <= kMaxDiff) {
1874 pxold = pxl; pyold = pyt; pD = kTRUE;
1876 }
1877
1878 if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) &&
1879 TMath::Abs(py - pyl) < kMaxDiff) { // top edge
1880 pxold = pxl; pyold = pyl; pTop = kTRUE;
1882 }
1883
1884 if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) &&
1885 TMath::Abs(py - pyt) < kMaxDiff) { // bottom edge
1886 pxold = pxt; pyold = pyt; pBot = kTRUE;
1888 }
1889
1890 if ((py > pyl+kMaxDiff && py < pyt-kMaxDiff) &&
1891 TMath::Abs(px - pxl) < kMaxDiff) { // left edge
1892 pxold = pxl; pyold = pyl; pL = kTRUE;
1894 }
1895
1896 if ((py > pyl+kMaxDiff && py < pyt-kMaxDiff) &&
1897 TMath::Abs(px - pxt) < kMaxDiff) { // right edge
1898 pxold = pxt; pyold = pyt; pR = kTRUE;
1900 }
1901
1902 if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) &&
1903 (py > pyl+kMaxDiff && py < pyt-kMaxDiff)) { // inside box
1904 pxold = px; pyold = py; pINSIDE = kTRUE;
1905 if (event == kButton1Down)
1907 else
1909 }
1910
1911 fResizing = kFALSE;
1912 if (pA || pB || pC || pD || pTop || pL || pR || pBot)
1913 fResizing = kTRUE;
1914
1915 if (!pA && !pB && !pC && !pD && !pTop && !pL && !pR && !pBot && !pINSIDE)
1917
1918 break;
1919
1920 case kArrowKeyRelease:
1921 case kButton1Motion:
1922
1923 if (TestBit(kCannotMove)) break;
1924 wx = wy = 0;
1925
1926 if (pA) {
1927 if (!ropaque) gVirtualX->DrawBox(pxold, pyt, pxt, pyold, TVirtualX::kHollow);
1928 if (px > pxt-kMinSize) { px = pxt-kMinSize; wx = px; }
1929 if (py > pyt-kMinSize) { py = pyt-kMinSize; wy = py; }
1930 if (px < pxlp) { px = pxlp; wx = px; }
1931 if (py < pylp) { py = pylp; wy = py; }
1932 if (fixedr) {
1933 Double_t dy = Double_t(TMath::Abs(pxt-px))/parent->UtoPixel(1.) /
1935 Int_t npy2 = pyt - TMath::Abs(parent->VtoAbsPixel(dy) -
1936 parent->VtoAbsPixel(0));
1937 if (npy2 < pylp) {
1938 px = pxold;
1939 py = pyold;
1940 } else
1941 py = npy2;
1942
1943 wx = wy = 0;
1944 }
1945 if (!ropaque) gVirtualX->DrawBox(px, pyt, pxt, py, TVirtualX::kHollow);
1946 }
1947 if (pB) {
1948 if (!ropaque) gVirtualX->DrawBox(pxl , pyt, pxold, pyold, TVirtualX::kHollow);
1949 if (px < pxl+kMinSize) { px = pxl+kMinSize; wx = px; }
1950 if (py > pyt-kMinSize) { py = pyt-kMinSize; wy = py; }
1951 if (px > pxtp) { px = pxtp; wx = px; }
1952 if (py < pylp) { py = pylp; wy = py; }
1953 if (fixedr) {
1954 Double_t dy = Double_t(TMath::Abs(pxl-px))/parent->UtoPixel(1.) /
1956 Int_t npy2 = pyt - TMath::Abs(parent->VtoAbsPixel(dy) -
1957 parent->VtoAbsPixel(0));
1958 if (npy2 < pylp) {
1959 px = pxold;
1960 py = pyold;
1961 } else
1962 py = npy2;
1963
1964 wx = wy = 0;
1965 }
1966 if (!ropaque) gVirtualX->DrawBox(pxl , pyt, px , py, TVirtualX::kHollow);
1967 }
1968 if (pC) {
1969 if (!ropaque) gVirtualX->DrawBox(pxl , pyl, pxold, pyold, TVirtualX::kHollow);
1970 if (px < pxl+kMinSize) { px = pxl+kMinSize; wx = px; }
1971 if (py < pyl+kMinSize) { py = pyl+kMinSize; wy = py; }
1972 if (px > pxtp) { px = pxtp; wx = px; }
1973 if (py > pytp) { py = pytp; wy = py; }
1974 if (fixedr) {
1975 Double_t dy = Double_t(TMath::Abs(pxl-px))/parent->UtoPixel(1.) /
1977 Int_t npy2 = pyl + TMath::Abs(parent->VtoAbsPixel(dy) -
1978 parent->VtoAbsPixel(0));
1979 if (npy2 > pytp) {
1980 px = pxold;
1981 py = pyold;
1982 } else
1983 py = npy2;
1984
1985 wx = wy = 0;
1986 }
1987 if (!ropaque) gVirtualX->DrawBox(pxl, pyl, px, py, TVirtualX::kHollow);
1988 }
1989 if (pD) {
1990 if (!ropaque) gVirtualX->DrawBox(pxold, pyold, pxt, pyl, TVirtualX::kHollow);
1991 if (px > pxt-kMinSize) { px = pxt-kMinSize; wx = px; }
1992 if (py < pyl+kMinSize) { py = pyl+kMinSize; wy = py; }
1993 if (px < pxlp) { px = pxlp; wx = px; }
1994 if (py > pytp) { py = pytp; wy = py; }
1995 if (fixedr) {
1996 Double_t dy = Double_t(TMath::Abs(pxt-px))/parent->UtoPixel(1.) /
1998 Int_t npy2 = pyl + TMath::Abs(parent->VtoAbsPixel(dy) -
1999 parent->VtoAbsPixel(0));
2000 if (npy2 > pytp) {
2001 px = pxold;
2002 py = pyold;
2003 } else
2004 py = npy2;
2005
2006 wx = wy = 0;
2007 }
2008 if (!ropaque) gVirtualX->DrawBox(px, py, pxt, pyl, TVirtualX::kHollow);
2009 }
2010 if (pTop) {
2011 if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2012 py2 += py - pyold;
2013 if (py2 > py1-kMinSize) { py2 = py1-kMinSize; wy = py2; }
2014 if (py2 < py2p) { py2 = py2p; wy = py2; }
2015 if (fixedr) {
2016 Double_t dx = Double_t(TMath::Abs(py2-py1))/parent->VtoPixel(0) *
2018 Int_t npx2 = px1 + parent->UtoPixel(dx);
2019 if (npx2 > px2p)
2020 py2 -= py - pyold;
2021 else
2022 px2 = npx2;
2023 }
2024 if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2025 }
2026 if (pBot) {
2027 if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2028 py1 += py - pyold;
2029 if (py1 < py2+kMinSize) { py1 = py2+kMinSize; wy = py1; }
2030 if (py1 > py1p) { py1 = py1p; wy = py1; }
2031 if (fixedr) {
2032 Double_t dx = Double_t(TMath::Abs(py2-py1))/parent->VtoPixel(0) *
2034 Int_t npx2 = px1 + parent->UtoPixel(dx);
2035 if (npx2 > px2p)
2036 py1 -= py - pyold;
2037 else
2038 px2 = npx2;
2039 }
2040 if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2041 }
2042 if (pL) {
2043 if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2044 px1 += px - pxold;
2045 if (px1 > px2-kMinSize) { px1 = px2-kMinSize; wx = px1; }
2046 if (px1 < px1p) { px1 = px1p; wx = px1; }
2047 if (fixedr) {
2048 Double_t dy = Double_t(TMath::Abs(px2-px1))/parent->UtoPixel(1.) /
2050 Int_t npy2 = py1 - TMath::Abs(parent->VtoAbsPixel(dy) -
2051 parent->VtoAbsPixel(0));
2052 if (npy2 < py2p)
2053 px1 -= px - pxold;
2054 else
2055 py2 = npy2;
2056 }
2057 if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2058 }
2059 if (pR) {
2060 if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2061 px2 += px - pxold;
2062 if (px2 < px1+kMinSize) { px2 = px1+kMinSize; wx = px2; }
2063 if (px2 > px2p) { px2 = px2p; wx = px2; }
2064 if (fixedr) {
2065 Double_t dy = Double_t(TMath::Abs(px2-px1))/parent->UtoPixel(1.) /
2067 Int_t npy2 = py1 - TMath::Abs(parent->VtoAbsPixel(dy) -
2068 parent->VtoAbsPixel(0));
2069 if (npy2 < py2p)
2070 px2 -= px - pxold;
2071 else
2072 py2 = npy2;
2073 }
2074 if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2075 }
2076 if (pINSIDE) {
2077 if (!opaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); // draw the old box
2078 Int_t dx = px - pxold;
2079 Int_t dy = py - pyold;
2080 px1 += dx; py1 += dy; px2 += dx; py2 += dy;
2081 if (px1 < px1p) { dx = px1p - px1; px1 += dx; px2 += dx; wx = px+dx; }
2082 if (px2 > px2p) { dx = px2 - px2p; px1 -= dx; px2 -= dx; wx = px-dx; }
2083 if (py1 > py1p) { dy = py1 - py1p; py1 -= dy; py2 -= dy; wy = py-dy; }
2084 if (py2 < py2p) { dy = py2p - py2; py1 += dy; py2 += dy; wy = py+dy; }
2085 if (!opaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); // draw the new box
2086 }
2087
2088 if (wx || wy) {
2089 if (wx) px = wx;
2090 if (wy) py = wy;
2091 gVirtualX->Warp(px, py);
2092 }
2093
2094 pxold = px;
2095 pyold = py;
2096
2097 Double_t x1, y1, x2, y2;
2098 x1 = x2 = y1 = y2 = 0;
2099
2100 if ((!fResizing && opaque) || (fResizing && ropaque)) {
2101 if (pA) {
2102 x1 = AbsPixeltoX(pxold);
2103 y1 = AbsPixeltoY(pyt);
2104 x2 = AbsPixeltoX(pxt);
2105 y2 = AbsPixeltoY(pyold);
2106 }
2107 if (pB) {
2108 x1 = AbsPixeltoX(pxl);
2109 y1 = AbsPixeltoY(pyt);
2110 x2 = AbsPixeltoX(pxold);
2111 y2 = AbsPixeltoY(pyold);
2112 }
2113 if (pC) {
2114 x1 = AbsPixeltoX(pxl);
2115 y1 = AbsPixeltoY(pyold);
2116 x2 = AbsPixeltoX(pxold);
2117 y2 = AbsPixeltoY(pyl);
2118 }
2119 if (pD) {
2120 x1 = AbsPixeltoX(pxold);
2121 y1 = AbsPixeltoY(pyold);
2122 x2 = AbsPixeltoX(pxt);
2123 y2 = AbsPixeltoY(pyl);
2124 }
2125 if (pTop || pBot || pL || pR || pINSIDE) {
2126 x1 = AbsPixeltoX(px1);
2127 y1 = AbsPixeltoY(py1);
2128 x2 = AbsPixeltoX(px2);
2129 y2 = AbsPixeltoY(py2);
2130 }
2131
2132 if (px != pxorg || py != pyorg) {
2133
2134 // Get parent corners pixels coordinates
2135 Int_t parentpx1 = fMother->XtoAbsPixel(parent->GetX1());
2136 Int_t parentpx2 = fMother->XtoAbsPixel(parent->GetX2());
2137 Int_t parentpy1 = fMother->YtoAbsPixel(parent->GetY1());
2138 Int_t parentpy2 = fMother->YtoAbsPixel(parent->GetY2());
2139
2140 // Get pad new corners pixels coordinates
2141 Int_t apx1 = XtoAbsPixel(x1); if (apx1 < parentpx1) {apx1 = parentpx1; }
2142 Int_t apx2 = XtoAbsPixel(x2); if (apx2 > parentpx2) {apx2 = parentpx2; }
2143 Int_t apy1 = YtoAbsPixel(y1); if (apy1 > parentpy1) {apy1 = parentpy1; }
2144 Int_t apy2 = YtoAbsPixel(y2); if (apy2 < parentpy2) {apy2 = parentpy2; }
2145
2146 // Compute new pad positions in the NDC space of parent
2147 fXlowNDC = Double_t(apx1 - parentpx1)/Double_t(parentpx2 - parentpx1);
2148 fYlowNDC = Double_t(apy1 - parentpy1)/Double_t(parentpy2 - parentpy1);
2149 fWNDC = Double_t(apx2 - apx1)/Double_t(parentpx2 - parentpx1);
2150 fHNDC = Double_t(apy2 - apy1)/Double_t(parentpy2 - parentpy1);
2151 }
2152
2153 // Reset pad parameters and recompute conversion coefficients
2154 ResizePad();
2155
2156 if (pINSIDE) gPad->ShowGuidelines(this, event);
2157 if (pTop) gPad->ShowGuidelines(this, event, 't', true);
2158 if (pBot) gPad->ShowGuidelines(this, event, 'b', true);
2159 if (pL) gPad->ShowGuidelines(this, event, 'l', true);
2160 if (pR) gPad->ShowGuidelines(this, event, 'r', true);
2161 if (pA) gPad->ShowGuidelines(this, event, '1', true);
2162 if (pB) gPad->ShowGuidelines(this, event, '2', true);
2163 if (pC) gPad->ShowGuidelines(this, event, '3', true);
2164 if (pD) gPad->ShowGuidelines(this, event, '4', true);
2165
2166 Modified(kTRUE);
2167 }
2168
2169 break;
2170
2171 case kButton1Up:
2172
2173 if (gROOT->IsEscaped()) {
2174 gROOT->SetEscape(kFALSE);
2175 break;
2176 }
2177
2178 if (opaque||ropaque) {
2179 ShowGuidelines(this, event);
2180 } else {
2181 x1 = x2 = y1 = y2 = 0;
2182
2183 if (pA) {
2184 x1 = AbsPixeltoX(pxold);
2185 y1 = AbsPixeltoY(pyt);
2186 x2 = AbsPixeltoX(pxt);
2187 y2 = AbsPixeltoY(pyold);
2188 }
2189 if (pB) {
2190 x1 = AbsPixeltoX(pxl);
2191 y1 = AbsPixeltoY(pyt);
2192 x2 = AbsPixeltoX(pxold);
2193 y2 = AbsPixeltoY(pyold);
2194 }
2195 if (pC) {
2196 x1 = AbsPixeltoX(pxl);
2197 y1 = AbsPixeltoY(pyold);
2198 x2 = AbsPixeltoX(pxold);
2199 y2 = AbsPixeltoY(pyl);
2200 }
2201 if (pD) {
2202 x1 = AbsPixeltoX(pxold);
2203 y1 = AbsPixeltoY(pyold);
2204 x2 = AbsPixeltoX(pxt);
2205 y2 = AbsPixeltoY(pyl);
2206 }
2207 if (pTop || pBot || pL || pR || pINSIDE) {
2208 x1 = AbsPixeltoX(px1);
2209 y1 = AbsPixeltoY(py1);
2210 x2 = AbsPixeltoX(px2);
2211 y2 = AbsPixeltoY(py2);
2212 }
2213
2214 if (pA || pB || pC || pD || pTop || pL || pR || pBot)
2215 Modified(kTRUE);
2216
2217 gVirtualX->SetLineColor(-1);
2218 gVirtualX->SetLineWidth(-1);
2219
2220 if (px != pxorg || py != pyorg) {
2221
2222 // Get parent corners pixels coordinates
2223 Int_t parentpx1 = fMother->XtoAbsPixel(parent->GetX1());
2224 Int_t parentpx2 = fMother->XtoAbsPixel(parent->GetX2());
2225 Int_t parentpy1 = fMother->YtoAbsPixel(parent->GetY1());
2226 Int_t parentpy2 = fMother->YtoAbsPixel(parent->GetY2());
2227
2228 // Get pad new corners pixels coordinates
2229 Int_t apx1 = XtoAbsPixel(x1); if (apx1 < parentpx1) {apx1 = parentpx1; }
2230 Int_t apx2 = XtoAbsPixel(x2); if (apx2 > parentpx2) {apx2 = parentpx2; }
2231 Int_t apy1 = YtoAbsPixel(y1); if (apy1 > parentpy1) {apy1 = parentpy1; }
2232 Int_t apy2 = YtoAbsPixel(y2); if (apy2 < parentpy2) {apy2 = parentpy2; }
2233
2234 // Compute new pad positions in the NDC space of parent
2235 fXlowNDC = Double_t(apx1 - parentpx1)/Double_t(parentpx2 - parentpx1);
2236 fYlowNDC = Double_t(apy1 - parentpy1)/Double_t(parentpy2 - parentpy1);
2237 fWNDC = Double_t(apx2 - apx1)/Double_t(parentpx2 - parentpx1);
2238 fHNDC = Double_t(apy2 - apy1)/Double_t(parentpy2 - parentpy1);
2239 }
2240
2241 // Reset pad parameters and recompute conversion coefficients
2242 ResizePad();
2243
2244
2245 // emit signal
2246 RangeChanged();
2247 }
2248
2249 break;
2250
2251 case kButton1Locate:
2252
2253 ExecuteEvent(kButton1Down, px, py);
2254
2255 while (1) {
2256 px = py = 0;
2257 event = gVirtualX->RequestLocator(1, 1, px, py);
2258
2260
2261 if (event != -1) { // button is released
2262 ExecuteEvent(kButton1Up, px, py);
2263 return;
2264 }
2265 }
2266
2267 case kButton2Down:
2268
2269 Pop();
2270 break;
2271
2272 }
2273}
2274
2275////////////////////////////////////////////////////////////////////////////////
2276/// Execute action corresponding to one event for a TAxis object
2277/// (called by TAxis::ExecuteEvent.)
2278/// This member function is called when an axis is clicked with the locator
2279///
2280/// The axis range is set between the position where the mouse is pressed
2281/// and the position where it is released.
2282///
2283/// If the mouse position is outside the current axis range when it is released
2284/// the axis is unzoomed with the corresponding proportions.
2285///
2286/// Note that the mouse does not need to be in the pad or even canvas
2287/// when it is released.
2288
2290{
2291 if (!IsEditable()) return;
2292 if (!axis) return;
2294
2295 TView *view = GetView();
2296 static Int_t axisNumber;
2297 static Double_t ratio1, ratio2;
2298 static Int_t px1old, py1old, px2old, py2old;
2299 Int_t bin1, bin2, first, last;
2300 Double_t temp, xmin,xmax;
2301 Bool_t opaque = gPad->OpaqueMoving();
2302 static TBox *zoombox;
2303 Double_t zbx1=0,zbx2=0,zby1=0,zby2=0;
2304
2305 // The CONT4 option, used to paint TH2, is a special case; it uses a 3D
2306 // drawing technique to paint a 2D plot.
2307 TString opt = axis->GetParent()->GetDrawOption();
2308 opt.ToLower();
2309 Bool_t kCont4 = kFALSE;
2310 if (strstr(opt,"cont4")) {
2311 view = 0;
2312 kCont4 = kTRUE;
2313 }
2314
2315 switch (event) {
2316
2317 case kButton1Down:
2318 axisNumber = 1;
2319 if (!strcmp(axis->GetName(),"xaxis")) {
2320 axisNumber = 1;
2321 if (!IsVertical()) axisNumber = 2;
2322 }
2323 if (!strcmp(axis->GetName(),"yaxis")) {
2324 axisNumber = 2;
2325 if (!IsVertical()) axisNumber = 1;
2326 }
2327 if (!strcmp(axis->GetName(),"zaxis")) {
2328 axisNumber = 3;
2329 }
2330 if (view) {
2331 view->GetDistancetoAxis(axisNumber, px, py, ratio1);
2332 } else {
2333 if (axisNumber == 1) {
2334 ratio1 = (AbsPixeltoX(px) - GetUxmin())/(GetUxmax() - GetUxmin());
2335 px1old = XtoAbsPixel(GetUxmin()+ratio1*(GetUxmax() - GetUxmin()));
2336 py1old = YtoAbsPixel(GetUymin());
2337 px2old = px1old;
2338 py2old = YtoAbsPixel(GetUymax());
2339 } else if (axisNumber == 2) {
2340 ratio1 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin());
2341 py1old = YtoAbsPixel(GetUymin()+ratio1*(GetUymax() - GetUymin()));
2342 px1old = XtoAbsPixel(GetUxmin());
2343 px2old = XtoAbsPixel(GetUxmax());
2344 py2old = py1old;
2345 } else {
2346 ratio1 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin());
2347 py1old = YtoAbsPixel(GetUymin()+ratio1*(GetUymax() - GetUymin()));
2348 px1old = XtoAbsPixel(GetUxmax());
2349 px2old = XtoAbsPixel(GetX2());
2350 py2old = py1old;
2351 }
2352 if (!opaque) {
2353 gVirtualX->DrawBox(px1old, py1old, px2old, py2old, TVirtualX::kHollow);
2354 } else {
2355 if (axisNumber == 1) {
2356 zbx1 = AbsPixeltoX(px1old);
2357 zbx2 = AbsPixeltoX(px2old);
2358 zby1 = GetUymin();
2359 zby2 = GetUymax();
2360 } else if (axisNumber == 2) {
2361 zbx1 = GetUxmin();
2362 zbx2 = GetUxmax();
2363 zby1 = AbsPixeltoY(py1old);
2364 zby2 = AbsPixeltoY(py2old);
2365 }
2366 if (GetLogx()) {
2367 zbx1 = TMath::Power(10,zbx1);
2368 zbx2 = TMath::Power(10,zbx2);
2369 }
2370 if (GetLogy()) {
2371 zby1 = TMath::Power(10,zby1);
2372 zby2 = TMath::Power(10,zby2);
2373 }
2374 zoombox = new TBox(zbx1, zby1, zbx2, zby2);
2375 Int_t ci = TColor::GetColor("#7d7dff");
2376 TColor *zoomcolor = gROOT->GetColor(ci);
2377 if (!TCanvas::SupportAlpha() || !zoomcolor) zoombox->SetFillStyle(3002);
2378 else zoomcolor->SetAlpha(0.5);
2379 zoombox->SetFillColor(ci);
2380 zoombox->Draw();
2381 gPad->Modified();
2382 gPad->Update();
2383 }
2384 }
2385 if (!opaque) gVirtualX->SetLineColor(-1);
2386 // No break !!!
2387
2388 case kButton1Motion:
2389 if (view) {
2390 view->GetDistancetoAxis(axisNumber, px, py, ratio2);
2391 } else {
2392 if (!opaque) gVirtualX->DrawBox(px1old, py1old, px2old, py2old, TVirtualX::kHollow);
2393 if (axisNumber == 1) {
2394 ratio2 = (AbsPixeltoX(px) - GetUxmin())/(GetUxmax() - GetUxmin());
2395 px2old = XtoAbsPixel(GetUxmin()+ratio2*(GetUxmax() - GetUxmin()));
2396 } else {
2397 ratio2 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin());
2398 py2old = YtoAbsPixel(GetUymin()+ratio2*(GetUymax() - GetUymin()));
2399 }
2400 if (!opaque) {
2401 gVirtualX->DrawBox(px1old, py1old, px2old, py2old, TVirtualX::kHollow);
2402 } else {
2403 if (axisNumber == 1) {
2404 zbx1 = AbsPixeltoX(px1old);
2405 zbx2 = AbsPixeltoX(px2old);
2406 zby1 = GetUymin();
2407 zby2 = GetUymax();
2408 } else if (axisNumber == 2) {
2409 zbx1 = GetUxmin();
2410 zbx2 = GetUxmax();
2411 zby1 = AbsPixeltoY(py1old);
2412 zby2 = AbsPixeltoY(py2old);
2413 }
2414 if (GetLogx()) {
2415 zbx1 = TMath::Power(10,zbx1);
2416 zbx2 = TMath::Power(10,zbx2);
2417 }
2418 if (GetLogy()) {
2419 zby1 = TMath::Power(10,zby1);
2420 zby2 = TMath::Power(10,zby2);
2421 }
2422 if (zoombox) {
2423 zoombox->SetX1(zbx1);
2424 zoombox->SetY1(zby1);
2425 zoombox->SetX2(zbx2);
2426 zoombox->SetY2(zby2);
2427 }
2428 gPad->Modified();
2429 gPad->Update();
2430 }
2431 }
2432 break;
2433
2434 case kWheelUp:
2435 bin1 = axis->GetFirst()+1;
2436 bin2 = axis->GetLast()-1;
2437 bin1 = TMath::Max(bin1, 1);
2438 bin2 = TMath::Min(bin2, axis->GetNbins());
2439 if (bin2>bin1) {
2440 axis->SetRange(bin1,bin2);
2441 gPad->Modified();
2442 gPad->Update();
2443 }
2444 break;
2445
2446 case kWheelDown:
2447 bin1 = axis->GetFirst()-1;
2448 bin2 = axis->GetLast()+1;
2449 bin1 = TMath::Max(bin1, 1);
2450 bin2 = TMath::Min(bin2, axis->GetNbins());
2451 if (bin2>bin1) {
2452 axis->SetRange(bin1,bin2);
2453 gPad->Modified();
2454 gPad->Update();
2455 }
2456 break;
2457
2458 case kButton1Up:
2459 if (gROOT->IsEscaped()) {
2460 gROOT->SetEscape(kFALSE);
2461 if (opaque && zoombox) {
2462 zoombox->Delete();
2463 zoombox = 0;
2464 }
2465 break;
2466 }
2467
2468 if (view) {
2469 view->GetDistancetoAxis(axisNumber, px, py, ratio2);
2470 if (ratio1 > ratio2) {
2471 temp = ratio1;
2472 ratio1 = ratio2;
2473 ratio2 = temp;
2474 }
2475 if (ratio2 - ratio1 > 0.05) {
2476 TH1 *hobj = (TH1*)axis->GetParent();
2477 if (axisNumber == 3 && hobj && hobj->GetDimension() != 3) {
2478 Float_t zmin = hobj->GetMinimum();
2479 Float_t zmax = hobj->GetMaximum();
2480 if(GetLogz()){
2481 if (zmin <= 0 && zmax > 0) zmin = TMath::Min((Double_t)1,
2482 (Double_t)0.001*zmax);
2483 zmin = TMath::Log10(zmin);
2484 zmax = TMath::Log10(zmax);
2485 }
2486 Float_t newmin = zmin + (zmax-zmin)*ratio1;
2487 Float_t newmax = zmin + (zmax-zmin)*ratio2;
2488 if (newmin < zmin) newmin = hobj->GetBinContent(hobj->GetMinimumBin());
2489 if (newmax > zmax) newmax = hobj->GetBinContent(hobj->GetMaximumBin());
2490 if (GetLogz()){
2491 newmin = TMath::Exp(2.302585092994*newmin);
2492 newmax = TMath::Exp(2.302585092994*newmax);
2493 }
2494 hobj->SetMinimum(newmin);
2495 hobj->SetMaximum(newmax);
2496 hobj->SetBit(TH1::kIsZoomed);
2497 } else {
2498 first = axis->GetFirst();
2499 last = axis->GetLast();
2500 bin1 = first + Int_t((last-first+1)*ratio1);
2501 bin2 = first + Int_t((last-first+1)*ratio2);
2502 bin1 = TMath::Max(bin1, 1);
2503 bin2 = TMath::Min(bin2, axis->GetNbins());
2504 axis->SetRange(bin1, bin2);
2505 }
2506 delete view;
2507 SetView(0);
2508 Modified(kTRUE);
2509 }
2510 } else {
2511 if (axisNumber == 1) {
2512 ratio2 = (AbsPixeltoX(px) - GetUxmin())/(GetUxmax() - GetUxmin());
2513 xmin = GetUxmin() +ratio1*(GetUxmax() - GetUxmin());
2514 xmax = GetUxmin() +ratio2*(GetUxmax() - GetUxmin());
2515 if (GetLogx() && !kCont4) {
2516 xmin = PadtoX(xmin);
2517 xmax = PadtoX(xmax);
2518 }
2519 } else if (axisNumber == 2) {
2520 ratio2 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin());
2521 xmin = GetUymin() +ratio1*(GetUymax() - GetUymin());
2522 xmax = GetUymin() +ratio2*(GetUymax() - GetUymin());
2523 if (GetLogy() && !kCont4) {
2524 xmin = PadtoY(xmin);
2525 xmax = PadtoY(xmax);
2526 }
2527 } else {
2528 ratio2 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin());
2529 xmin = ratio1;
2530 xmax = ratio2;
2531 }
2532 if (xmin > xmax) {
2533 temp = xmin;
2534 xmin = xmax;
2535 xmax = temp;
2536 temp = ratio1;
2537 ratio1 = ratio2;
2538 ratio2 = temp;
2539 }
2540
2541 // xmin and xmax need to be adjusted in case of CONT4.
2542 if (kCont4) {
2543 Double_t low = axis->GetBinLowEdge(axis->GetFirst());
2544 Double_t up = axis->GetBinUpEdge(axis->GetLast());
2545 Double_t xmi = GetUxmin();
2546 Double_t xma = GetUxmax();
2547 xmin = ((xmin-xmi)/(xma-xmi))*(up-low)+low;
2548 xmax = ((xmax-xmi)/(xma-xmi))*(up-low)+low;
2549 }
2550
2551 if (!strcmp(axis->GetName(),"xaxis")) axisNumber = 1;
2552 if (!strcmp(axis->GetName(),"yaxis")) axisNumber = 2;
2553 if (ratio2 - ratio1 > 0.05) {
2554 //update object owning this axis
2555 TH1 *hobj1 = (TH1*)axis->GetParent();
2556 bin1 = axis->FindFixBin(xmin);
2557 bin2 = axis->FindFixBin(xmax);
2558 bin1 = TMath::Max(bin1, 1);
2559 bin2 = TMath::Min(bin2, axis->GetNbins());
2560 if (axisNumber == 1) axis->SetRange(bin1,bin2);
2561 if (axisNumber == 2 && hobj1) {
2562 if (hobj1->GetDimension() == 1) {
2563 if (hobj1->GetNormFactor() != 0) {
2564 Double_t norm = hobj1->GetSumOfWeights()/hobj1->GetNormFactor();
2565 xmin *= norm;
2566 xmax *= norm;
2567 }
2568 hobj1->SetMinimum(xmin);
2569 hobj1->SetMaximum(xmax);
2570 hobj1->SetBit(TH1::kIsZoomed);
2571 } else {
2572 axis->SetRange(bin1,bin2);
2573 }
2574 }
2575 //update all histograms in the pad
2576 TIter next(GetListOfPrimitives());
2577 TObject *obj;
2578 while ((obj= next())) {
2579 if (!obj->InheritsFrom(TH1::Class())) continue;
2580 TH1 *hobj = (TH1*)obj;
2581 if (hobj == hobj1) continue;
2582 bin1 = hobj->GetXaxis()->FindFixBin(xmin);
2583 bin2 = hobj->GetXaxis()->FindFixBin(xmax);
2584 if (axisNumber == 1) {
2585 hobj->GetXaxis()->SetRange(bin1,bin2);
2586 } else if (axisNumber == 2) {
2587 if (hobj->GetDimension() == 1) {
2588 Double_t xxmin = xmin;
2589 Double_t xxmax = xmax;
2590 if (hobj->GetNormFactor() != 0) {
2591 Double_t norm = hobj->GetSumOfWeights()/hobj->GetNormFactor();
2592 xxmin *= norm;
2593 xxmax *= norm;
2594 }
2595 hobj->SetMinimum(xxmin);
2596 hobj->SetMaximum(xxmax);
2597 hobj->SetBit(TH1::kIsZoomed);
2598 } else {
2599 bin1 = hobj->GetYaxis()->FindFixBin(xmin);
2600 bin2 = hobj->GetYaxis()->FindFixBin(xmax);
2601 hobj->GetYaxis()->SetRange(bin1,bin2);
2602 }
2603 }
2604 }
2605 Modified(kTRUE);
2606 }
2607 }
2608 if (!opaque) {
2609 gVirtualX->SetLineColor(-1);
2610 } else {
2611 if (zoombox) {
2612 zoombox->Delete();
2613 zoombox = 0;
2614 }
2615 }
2616 break;
2617 }
2618}
2619
2620////////////////////////////////////////////////////////////////////////////////
2621/// Search if object named name is inside this pad or in pads inside this pad.
2622///
2623/// In case name is in several sub-pads the first one is returned.
2624
2625TObject *TPad::FindObject(const char *name) const
2626{
2627 if (!fPrimitives) return nullptr;
2629 if (found) return found;
2630 TObject *cur;
2631 TIter next(GetListOfPrimitives());
2632 while ((cur = next())) {
2633 if (cur->InheritsFrom(TPad::Class())) {
2634 found = ((TPad*)cur)->FindObject(name);
2635 if (found) return found;
2636 }
2637 }
2638 return nullptr;
2639}
2640
2641////////////////////////////////////////////////////////////////////////////////
2642/// Search if obj is in pad or in pads inside this pad.
2643///
2644/// In case obj is in several sub-pads the first one is returned.
2645
2647{
2648 if (!fPrimitives) return nullptr;
2649 TObject *found = fPrimitives->FindObject(obj);
2650 if (found) return found;
2651 TObject *cur;
2652 TIter next(GetListOfPrimitives());
2653 while ((cur = next())) {
2654 if (cur->InheritsFrom(TPad::Class())) {
2655 found = ((TPad*)cur)->FindObject(obj);
2656 if (found) return found;
2657 }
2658 }
2659 return nullptr;
2660}
2661
2662////////////////////////////////////////////////////////////////////////////////
2663/// Get canvas identifier.
2664
2666{
2667 return fCanvas ? fCanvas->GetCanvasID() : -1;
2668}
2669
2670////////////////////////////////////////////////////////////////////////////////
2671/// Get canvas implementation pointer if any
2672
2674{
2675 return fCanvas ? fCanvas->GetCanvasImp() : nullptr;
2676}
2677
2678////////////////////////////////////////////////////////////////////////////////
2679/// Get Event.
2680
2682{
2683 return fCanvas ? fCanvas->GetEvent() : 0;
2684}
2685
2686////////////////////////////////////////////////////////////////////////////////
2687/// Get X event.
2688
2690{
2691 return fCanvas ? fCanvas->GetEventX() : 0;
2692}
2693
2694////////////////////////////////////////////////////////////////////////////////
2695/// Get Y event.
2696
2698{
2699 return fCanvas ? fCanvas->GetEventY() : 0;
2700}
2701
2702////////////////////////////////////////////////////////////////////////////////
2703/// Get virtual canvas.
2704
2706{
2707 return fCanvas ? (TVirtualPad*) fCanvas : nullptr;
2708}
2709
2710////////////////////////////////////////////////////////////////////////////////
2711/// Get highlight color.
2712
2714{
2715 return fCanvas ? fCanvas->GetHighLightColor() : 0;
2716}
2717
2718////////////////////////////////////////////////////////////////////////////////
2719/// Static function (see also TPad::SetMaxPickDistance)
2720
2722{
2723 return fgMaxPickDistance;
2724}
2725
2726////////////////////////////////////////////////////////////////////////////////
2727/// Get selected.
2728
2730{
2731 if (fCanvas == this) return nullptr;
2732 return fCanvas ? fCanvas->GetSelected() : nullptr;
2733}
2734
2735////////////////////////////////////////////////////////////////////////////////
2736/// Get selected pad.
2737
2739{
2740 if (fCanvas == this) return nullptr;
2741 return fCanvas ? fCanvas->GetSelectedPad() : nullptr;
2742}
2743
2744////////////////////////////////////////////////////////////////////////////////
2745/// Get save pad.
2746
2748{
2749 if (fCanvas == this) return nullptr;
2750 return fCanvas ? fCanvas->GetPadSave() : nullptr;
2751}
2752
2753////////////////////////////////////////////////////////////////////////////////
2754/// Get Wh.
2755
2757{
2758 return fCanvas ? fCanvas->GetWh() : 0;
2759}
2760
2761////////////////////////////////////////////////////////////////////////////////
2762/// Get Ww.
2763
2765{
2766 return fCanvas ? fCanvas->GetWw() : 0;
2767}
2768
2769////////////////////////////////////////////////////////////////////////////////
2770/// Hide tool tip depending on the event type. Typically tool tips
2771/// are hidden when event is not a kMouseEnter and not a kMouseMotion
2772/// event.
2773
2775{
2776 if (event != kMouseEnter && event != kMouseMotion && fTip)
2777 gPad->CloseToolTip(fTip);
2778}
2779
2780////////////////////////////////////////////////////////////////////////////////
2781/// Is pad in batch mode ?
2782
2784{
2785 return fCanvas ? fCanvas->IsBatch() : kFALSE;
2786}
2787
2788////////////////////////////////////////////////////////////////////////////////
2789/// Is pad retained ?
2790
2792{
2793 return fCanvas ? fCanvas->IsRetained() : kFALSE;
2794}
2795
2796////////////////////////////////////////////////////////////////////////////////
2797/// Is pad moving in opaque mode ?
2798
2800{
2801 return fCanvas ? fCanvas->OpaqueMoving() : kFALSE;
2802}
2803
2804////////////////////////////////////////////////////////////////////////////////
2805/// Is pad resizing in opaque mode ?
2806
2808{
2809 return fCanvas ? fCanvas->OpaqueResizing() : kFALSE;
2810}
2811
2812////////////////////////////////////////////////////////////////////////////////
2813/// Set pad in batch mode.
2814
2816{
2817 if (fCanvas) fCanvas->SetBatch(batch);
2818}
2819
2820////////////////////////////////////////////////////////////////////////////////
2821/// Set canvas size.
2822
2824{
2825 if (fCanvas) fCanvas->SetCanvasSize(ww,wh);
2826}
2827
2828////////////////////////////////////////////////////////////////////////////////
2829/// Set cursor type.
2830
2832{
2833 if (fCanvas) fCanvas->SetCursor(cursor);
2834}
2835
2836////////////////////////////////////////////////////////////////////////////////
2837/// Set double buffer mode ON or OFF.
2838
2840{
2841 if (fCanvas) fCanvas->SetDoubleBuffer(mode);
2842}
2843
2844////////////////////////////////////////////////////////////////////////////////
2845/// Set selected.
2846
2848{
2849 if (fCanvas) fCanvas->SetSelected(obj);
2850}
2851
2852////////////////////////////////////////////////////////////////////////////////
2853/// Update pad.
2854
2856{
2857 if (fCanvas) fCanvas->Update();
2858}
2859
2860////////////////////////////////////////////////////////////////////////////////
2861/// Get frame.
2862
2864{
2865 if (!fPrimitives) fPrimitives = new TList;
2867 if (!frame) frame = (TFrame*)GetListOfPrimitives()->FindObject("TFrame");
2868 fFrame = frame;
2869 if (!fFrame) {
2870 if (!frame) fFrame = new TFrame(0,0,1,1);
2871 Int_t framecolor = GetFrameFillColor();
2872 if (!framecolor) framecolor = GetFillColor();
2873 fFrame->SetFillColor(framecolor);
2880 } else {
2881 // Preexisting and now assigned to fFrame, let's make sure it is not
2882 // deleted twice (the bit might have been set in TPad::Streamer)
2884 }
2885 return fFrame;
2886}
2887
2888////////////////////////////////////////////////////////////////////////////////
2889/// Get primitive.
2890
2892{
2893 if (!fPrimitives) return nullptr;
2894 TIter next(fPrimitives);
2895 TObject *found, *obj;
2896 while ((obj=next())) {
2897 if (!strcmp(name, obj->GetName())) return obj;
2898 if (obj->InheritsFrom(TPad::Class())) continue;
2899 found = obj->FindObject(name);
2900 if (found) return found;
2901 }
2902 return nullptr;
2903}
2904
2905////////////////////////////////////////////////////////////////////////////////
2906/// Get a pointer to subpadnumber of this pad.
2907
2908TVirtualPad *TPad::GetPad(Int_t subpadnumber) const
2909{
2910 if (!subpadnumber) {
2911 return (TVirtualPad*)this;
2912 }
2913
2914 TObject *obj;
2915 if (!fPrimitives) return nullptr;
2916 TIter next(GetListOfPrimitives());
2917 while ((obj = next())) {
2918 if (obj->InheritsFrom(TVirtualPad::Class())) {
2919 TVirtualPad *pad = (TVirtualPad*)obj;
2920 if (pad->GetNumber() == subpadnumber) return pad;
2921 }
2922 }
2923 return nullptr;
2924}
2925
2926////////////////////////////////////////////////////////////////////////////////
2927/// Return lower and upper bounds of the pad in NDC coordinates.
2928
2929void TPad::GetPadPar(Double_t &xlow, Double_t &ylow, Double_t &xup, Double_t &yup)
2930{
2931 xlow = fXlowNDC;
2932 ylow = fYlowNDC;
2933 xup = fXlowNDC+fWNDC;
2934 yup = fYlowNDC+fHNDC;
2935}
2936
2937////////////////////////////////////////////////////////////////////////////////
2938/// Return pad world coordinates range.
2939
2941{
2942 x1 = fX1;
2943 y1 = fY1;
2944 x2 = fX2;
2945 y2 = fY2;
2946}
2947
2948////////////////////////////////////////////////////////////////////////////////
2949/// Return pad axis coordinates range.
2950
2952{
2953 xmin = fUxmin;
2954 ymin = fUymin;
2955 xmax = fUxmax;
2956 ymax = fUymax;
2957}
2958
2959////////////////////////////////////////////////////////////////////////////////
2960/// Highlight pad.
2961/// do not highlight when printing on Postscript
2962
2964{
2965 if (gVirtualPS && gVirtualPS->TestBit(kPrintingPS)) return;
2966
2967 if (color <= 0) return;
2968
2970
2971 // We do not want to have active(executable) buttons, etc highlighted
2972 // in this manner, unless we want to edit'em
2973 if (GetMother() && GetMother()->IsEditable() && !InheritsFrom(TButton::Class())) {
2974 //When doing a DrawClone from the GUI you would do
2975 // - select an empty pad -
2976 // - right click on object -
2977 // - select DrawClone on menu -
2978 //
2979 // Without the SetSelectedPad(); in the HighLight function, the
2980 // above instruction lead to the clone to be drawn in the
2981 // same canvas as the original object. This is because the
2982 // 'right clicking' (via TCanvas::HandleInput) changes gPad
2983 // momentarily such that when DrawClone is called, it is
2984 // not the right value (for DrawClone). Should be FIXED.
2985 gROOT->SetSelectedPad(this);
2986 if (GetBorderMode()>0) {
2987 if (set) PaintBorder(-color, kFALSE);
2989 }
2990 }
2991
2993}
2994
2995////////////////////////////////////////////////////////////////////////////////
2996/// List all primitives in pad.
2997
2998void TPad::ls(Option_t *option) const
2999{
3001 std::cout <<IsA()->GetName()<<" fXlowNDC=" <<fXlowNDC<<" fYlowNDC="<<fYlowNDC<<" fWNDC="<<GetWNDC()<<" fHNDC="<<GetHNDC()
3002 <<" Name= "<<GetName()<<" Title= "<<GetTitle()<<" Option="<<option<<std::endl;
3004 if (!fPrimitives) return;
3005 fPrimitives->ls(option);
3007}
3008
3009////////////////////////////////////////////////////////////////////////////////
3010/// Increment (i==1) or set (i>1) the number of autocolor in the pad.
3011
3013{
3014 if (opt.Index("pfc")>=0 || opt.Index("plc")>=0 || opt.Index("pmc")>=0) {
3015 if (i==1) fNumPaletteColor++;
3016 else fNumPaletteColor = i;
3017 return fNumPaletteColor;
3018 } else {
3019 return 0;
3020 }
3021}
3022
3023////////////////////////////////////////////////////////////////////////////////
3024/// Get the next autocolor in the pad.
3025
3027{
3028 Int_t i = 0;
3029 Int_t ncolors = gStyle->GetNumberOfColors();
3030 if (fNumPaletteColor>1) {
3031 i = fNextPaletteColor*(ncolors/(fNumPaletteColor-1));
3032 if (i>=ncolors) i = ncolors-1;
3033 }
3036 return gStyle->GetColorPalette(i);
3037}
3038
3039////////////////////////////////////////////////////////////////////////////////
3040/// Initialise the grid used to find empty space when adding a box (Legend) in a pad
3041
3043{
3044 Int_t const cellSize = 10; // Sive of an individual grid cell in pixels.
3045
3046 if (fCGnx == 0 && fCGny == 0) {
3047 fCGnx = (Int_t)(gPad->GetWw())/cellSize;
3048 fCGny = (Int_t)(gPad->GetWh())/cellSize;
3049 } else {
3050 Int_t CGnx = (Int_t)(gPad->GetWw())/cellSize;
3051 Int_t CGny = (Int_t)(gPad->GetWh())/cellSize;
3052 if (fCGnx != CGnx || fCGny != CGny) {
3053 fCGnx = CGnx;
3054 fCGny = CGny;
3055 delete [] fCollideGrid;
3056 fCollideGrid = nullptr;
3057 }
3058 }
3059
3060 // Initialise the collide grid
3061 if (!fCollideGrid) {
3063 for (int i = 0; i<fCGnx; i++) {
3064 for (int j = 0; j<fCGny; j++) {
3065 fCollideGrid[i + j*fCGnx] = kTRUE;
3066 }
3067 }
3068 }
3069
3070 // Fill the collide grid
3072 if (!l) return;
3073 Int_t np = l->GetSize();
3074 TObject *o;
3075
3076 for (int i=0; i<np; i++) {
3077 o = (TObject *) l->At(i);
3078 if (o!=oi) {
3079 if (o->InheritsFrom(TFrame::Class())) { FillCollideGridTFrame(o); continue;}
3080 if (o->InheritsFrom(TBox::Class())) { FillCollideGridTBox(o); continue;}
3081 if (o->InheritsFrom(TH1::Class())) { FillCollideGridTH1(o); continue;}
3082 if (o->InheritsFrom(TGraph::Class())) { FillCollideGridTGraph(o); continue;}
3083 if (o->InheritsFrom(TMultiGraph::Class())) {
3084 TList * grlist = ((TMultiGraph *)o)->GetListOfGraphs();
3085 TIter nextgraph(grlist);
3086 TObject * og;
3087 while ((og = nextgraph())) FillCollideGridTGraph(og);
3088 }
3089 if (o->InheritsFrom(THStack::Class())) {
3090 TList * hlist = ((THStack *)o)->GetHists();
3091 TIter nexthist(hlist);
3092 TObject * oh;
3093 while ((oh = nexthist())) {
3094 if (oh->InheritsFrom(TH1::Class())) FillCollideGridTH1(oh);
3095 }
3096 }
3097 }
3098 }
3099}
3100
3101////////////////////////////////////////////////////////////////////////////////
3102/// Check if a box of size w and h collide some primitives in the pad at
3103/// position i,j
3104
3106{
3107 for (int r=i; r<w+i; r++) {
3108 for (int c=j; c<h+j; c++) {
3109 if (!fCollideGrid[r + c*fCGnx]) return kTRUE;
3110 }
3111 }
3112 return kFALSE;
3113}
3114
3115////////////////////////////////////////////////////////////////////////////////
3116/// Place a box in NDC space
3117///
3118/// \return `true` if the box could be placed, `false` if not.
3119///
3120/// \param[in] o pointer to the box to be placed
3121/// \param[in] w box width to be placed
3122/// \param[in] h box height to be placed
3123/// \param[out] xl x position of the bottom left corner of the placed box
3124/// \param[out] yb y position of the bottom left corner of the placed box
3125
3127{
3128 FillCollideGrid(o);
3129
3130 Int_t iw = (int)(fCGnx*w);
3131 Int_t ih = (int)(fCGny*h);
3132
3133 Int_t nxmax = fCGnx-iw-1;
3134 Int_t nymax = fCGny-ih-1;
3135
3136 for (Int_t i = 0; i<nxmax; i++) {
3137 for (Int_t j = 0; j<=nymax; j++) {
3138 if (Collide(i,j,iw,ih)) {
3139 continue;
3140 } else {
3141 xl = (Double_t)(i)/(Double_t)(fCGnx);
3142 yb = (Double_t)(j)/(Double_t)(fCGny);
3143 return kTRUE;
3144 }
3145 }
3146 }
3147 return kFALSE;
3148}
3149
3150#define NotFree(i, j) fCollideGrid[TMath::Max(TMath::Min(i+j*fCGnx,fCGnx*fCGny),0)] = kFALSE;
3151
3152////////////////////////////////////////////////////////////////////////////////
3153/// Mark as "not free" the cells along a line.
3154
3156{
3157 NotFree(x1, y1);
3158 NotFree(x2, y2);
3159 Int_t i, j, xt, yt;
3160
3161 // horizontal lines
3162 if (y1==y2) {
3163 for (i=x1+1; i<x2; i++) NotFree(i,y1);
3164 return;
3165 }
3166
3167 // vertical lines
3168 if (x1==x2) {
3169 for (i=y1+1; i<y2; i++) NotFree(x1,i);
3170 return;
3171 }
3172
3173 // other lines
3174 if (TMath::Abs(x2-x1)>TMath::Abs(y2-y1)) {
3175 if (x1>x2) {
3176 xt = x1; x1 = x2; x2 = xt;
3177 yt = y1; y1 = y2; y2 = yt;
3178 }
3179 for (i=x1+1; i<x2; i++) {
3180 j = (Int_t)((Double_t)(y2-y1)*(Double_t)((i-x1)/(Double_t)(x2-x1))+y1);
3181 NotFree(i,j);
3182 NotFree(i,(j+1));
3183 }
3184 } else {
3185 if (y1>y2) {
3186 yt = y1; y1 = y2; y2 = yt;
3187 xt = x1; x1 = x2; x2 = xt;
3188 }
3189 for (j=y1+1; j<y2; j++) {
3190 i = (Int_t)((Double_t)(x2-x1)*(Double_t)((j-y1)/(Double_t)(y2-y1))+x1);
3191 NotFree(i,j);
3192 NotFree((i+1),j);
3193 }
3194 }
3195}
3196
3197////////////////////////////////////////////////////////////////////////////////
3199{
3200 TBox *b = (TBox *)o;
3201 if (fCGnx==0||fCGny==0) return;
3202 Double_t xs = (fX2-fX1)/fCGnx;
3203 Double_t ys = (fY2-fY1)/fCGny;
3204
3205 Int_t x1 = (Int_t)((b->GetX1()-fX1)/xs);
3206 Int_t x2 = (Int_t)((b->GetX2()-fX1)/xs);
3207 Int_t y1 = (Int_t)((b->GetY1()-fY1)/ys);
3208 Int_t y2 = (Int_t)((b->GetY2()-fY1)/ys);
3209 for (int i = x1; i<=x2; i++) {
3210 for (int j = y1; j<=y2; j++) NotFree(i, j);
3211 }
3212}
3213
3214////////////////////////////////////////////////////////////////////////////////
3216{
3217 TFrame *f = (TFrame *)o;
3218 if (fCGnx==0||fCGny==0) return;
3219 Double_t xs = (fX2-fX1)/fCGnx;
3220 Double_t ys = (fY2-fY1)/fCGny;
3221
3222 Int_t x1 = (Int_t)((f->GetX1()-fX1)/xs);
3223 Int_t x2 = (Int_t)((f->GetX2()-fX1)/xs);
3224 Int_t y1 = (Int_t)((f->GetY1()-fY1)/ys);
3225 Int_t y2 = (Int_t)((f->GetY2()-fY1)/ys);
3226 Int_t i;
3227
3228 for (i = x1; i<=x2; i++) {
3229 NotFree(i, y1);
3230 NotFree(i, (y1-1));
3231 NotFree(i, (y1-2));
3232 }
3233 for (i = y1; i<=y2; i++) {
3234 NotFree(x1, i);
3235 NotFree((x1-1), i);
3236 NotFree((x1-2), i);
3237 }
3238}
3239
3240////////////////////////////////////////////////////////////////////////////////
3242{
3243 TGraph *g = (TGraph *)o;
3244 if (fCGnx==0||fCGny==0) return;
3245 Double_t xs = (fX2-fX1)/fCGnx;
3246 Double_t ys = (fY2-fY1)/fCGny;
3247
3248 Int_t n = g->GetN();
3249 Double_t x1, x2, y1, y2;
3250
3251 for (Int_t i=1; i<n; i++) {
3252 g->GetPoint(i-1,x1,y1);
3253 g->GetPoint(i ,x2,y2);
3254 if (fLogx) {
3255 if (x1 > 0) x1 = TMath::Log10(x1);
3256 else x1 = fUxmin;
3257 if (x2 > 0) x2 = TMath::Log10(x2);
3258 else x2 = fUxmin;
3259 }
3260 if (fLogy) {
3261 if (y1 > 0) y1 = TMath::Log10(y1);
3262 else y1 = fUymin;
3263 if (y2 > 0) y2 = TMath::Log10(y2);
3264 else y2 = fUymin;
3265 }
3266 LineNotFree((int)((x1-fX1)/xs), (int)((x2-fX1)/xs),
3267 (int)((y1-fY1)/ys), (int)((y2-fY1)/ys));
3268 }
3269}
3270
3271////////////////////////////////////////////////////////////////////////////////
3273{
3274 TH1 *h = (TH1 *)o;
3275 if (fCGnx==0||fCGny==0) return;
3276 if (o->InheritsFrom(TH2::Class())) return;
3277 if (o->InheritsFrom(TH3::Class())) return;
3278
3279 TString name = h->GetName();
3280 if (name.Index("hframe") >= 0) return;
3281
3282 Double_t xs = (fX2-fX1)/fCGnx;
3283 Double_t ys = (fY2-fY1)/fCGny;
3284
3285 bool haserrors = false;
3286 TString drawOption = h->GetDrawOption();
3287 drawOption.ToLower();
3288 drawOption.ReplaceAll("same","");
3289
3290 if (drawOption.Index("hist") < 0) {
3291 if (drawOption.Index("e") >= 0) haserrors = true;
3292 }
3293
3294 Int_t nx = h->GetNbinsX();
3295 Int_t x1, y1, y2;
3296 Int_t i, j;
3297 Double_t x1l, y1l, y2l;
3298
3299 for (i = 1; i<nx; i++) {
3300 if (haserrors) {
3301 x1l = h->GetBinCenter(i);
3302 if (fLogx) {
3303 if (x1l > 0) x1l = TMath::Log10(x1l);
3304 else x1l = fUxmin;
3305 }
3306 x1 = (Int_t)((x1l-fX1)/xs);
3307 y1l = h->GetBinContent(i)-h->GetBinErrorLow(i);
3308 if (fLogy) {
3309 if (y1l > 0) y1l = TMath::Log10(y1l);
3310 else y1l = fUymin;
3311 }
3312 y1 = (Int_t)((y1l-fY1)/ys);
3313 y2l = h->GetBinContent(i)+h->GetBinErrorUp(i);
3314 if (fLogy) {
3315 if (y2l > 0) y2l = TMath::Log10(y2l);
3316 else y2l = fUymin;
3317 }
3318 y2 = (Int_t)((y2l-fY1)/ys);
3319 for (j=y1; j<=y2; j++) {
3320 NotFree(x1, j);
3321 }
3322 }
3323 x1l = h->GetBinLowEdge(i);
3324 if (fLogx) {
3325 if (x1l > 0) x1l = TMath::Log10(x1l);
3326 else x1l = fUxmin;
3327 }
3328 x1 = (Int_t)((x1l-fX1)/xs);
3329 y1l = h->GetBinContent(i);
3330 if (fLogy) {
3331 if (y1l > 0) y1l = TMath::Log10(y1l);
3332 else y1l = fUymin;
3333 }
3334 y1 = (Int_t)((y1l-fY1)/ys);
3335 NotFree(x1, y1);
3336 x1l = h->GetBinLowEdge(i)+h->GetBinWidth(i);
3337 if (fLogx) {
3338 if (x1l > 0) x1l = TMath::Log10(x1l);
3339 else x1l = fUxmin;
3340 }
3341 x1 = (int)((x1l-fX1)/xs);
3342 NotFree(x1, y1);
3343 }
3344
3345 // Extra objects in the list of function
3346 TPaveStats *ps = (TPaveStats*)h->GetListOfFunctions()->FindObject("stats");
3347 if (ps) FillCollideGridTBox(ps);
3348}
3349
3350////////////////////////////////////////////////////////////////////////////////
3351/// This method draws the collide grid on top of the canvas. This is used for
3352/// debugging only. At some point it will be removed.
3353
3355{
3356 if (fCGnx==0||fCGny==0) return;
3357 auto box = new TBox();
3358 box->SetFillColorAlpha(kRed,0.5);
3359
3360 Double_t xs = (fX2-fX1)/fCGnx;
3361 Double_t ys = (fY2-fY1)/fCGny;
3362
3363 Double_t X1L, X2L, Y1L, Y2L;
3364 Double_t t = 0.15;
3365 Double_t Y1, Y2;
3366 Double_t X1 = fX1;
3367 Double_t X2 = X1+xs;
3368
3369 for (int i = 0; i<fCGnx; i++) {
3370 Y1 = fY1;
3371 Y2 = Y1+ys;
3372 for (int j = 0; j<fCGny; j++) {
3373 if (gPad->GetLogx()) {
3374 X1L = TMath::Power(10,X1);
3375 X2L = TMath::Power(10,X2);
3376 } else {
3377 X1L = X1;
3378 X2L = X2;
3379 }
3380 if (gPad->GetLogy()) {
3381 Y1L = TMath::Power(10,Y1);
3382 Y2L = TMath::Power(10,Y2);
3383 } else {
3384 Y1L = Y1;
3385 Y2L = Y2;
3386 }
3387 if (!fCollideGrid[i + j*fCGnx]) {
3388 box->SetFillColorAlpha(kBlack,t);
3389 box->DrawBox(X1L, Y1L, X2L, Y2L);
3390 } else {
3391 box->SetFillColorAlpha(kRed,t);
3392 box->DrawBox(X1L, Y1L, X2L, Y2L);
3393 }
3394 Y1 = Y2;
3395 Y2 = Y1+ys;
3396 if (t==0.15) t = 0.1;
3397 else t = 0.15;
3398 }
3399 X1 = X2;
3400 X2 = X1+xs;
3401 }
3402}
3403
3404
3405////////////////////////////////////////////////////////////////////////////////
3406/// Convert x from pad to X.
3407
3409{
3410 if (fLogx && x < 50) return Double_t(TMath::Exp(2.302585092994*x));
3411 return x;
3412}
3413
3414////////////////////////////////////////////////////////////////////////////////
3415/// Convert y from pad to Y.
3416
3418{
3419 if (fLogy && y < 50) return Double_t(TMath::Exp(2.302585092994*y));
3420 return y;
3421}
3422
3423////////////////////////////////////////////////////////////////////////////////
3424/// Convert x from X to pad.
3425
3427{
3428 if (fLogx) {
3429 if (x > 0) x = TMath::Log10(x);
3430 else x = fUxmin;
3431 }
3432 return x;
3433}
3434
3435////////////////////////////////////////////////////////////////////////////////
3436/// Convert y from Y to pad.
3437
3439{
3440 if (fLogy) {
3441 if (y > 0) y = TMath::Log10(y);
3442 else y = fUymin;
3443 }
3444 return y;
3445}
3446
3447////////////////////////////////////////////////////////////////////////////////
3448/// Paint all primitives in pad.
3449
3450void TPad::Paint(Option_t * /*option*/)
3451{
3452 if (!fPrimitives) fPrimitives = new TList;
3454 fViewer3D->PadPaint(this);
3456 if (GetGLDevice()!=-1 && gVirtualPS) {
3457 TPad *padsav = (TPad*)gPad;
3458 gPad = this;
3459 if (gGLManager) gGLManager->PrintViewer(GetViewer3D());
3460 gPad = padsav;
3461 }
3462 return;
3463 }
3464
3466
3467 TPad *padsav = (TPad*)gPad;
3468
3469 fPadPaint = 1;
3470 cd();
3471
3473 PaintDate();
3474
3476 TObject *obj;
3477
3478 Bool_t began3DScene = kFALSE;
3479 while (lnk) {
3480 obj = lnk->GetObject();
3481
3482 // Create a pad 3D viewer if none exists and we encounter a 3D shape
3483 if (!fViewer3D && obj->InheritsFrom(TAtt3D::Class())) {
3484 GetViewer3D("pad");
3485 }
3486
3487 // Open a 3D scene if required
3488 if (fViewer3D && !fViewer3D->BuildingScene()) {
3490 began3DScene = kTRUE;
3491 }
3492
3493 obj->Paint(lnk->GetOption());
3494 lnk = (TObjOptLink*)lnk->Next();
3495 }
3496
3497 if (padsav) padsav->cd();
3498 fPadPaint = 0;
3500
3501 // Close the 3D scene if we opened it. This must be done after modified
3502 // flag is cleared, as some viewers will invoke another paint by marking pad modified again
3503 if (began3DScene) {
3505 }
3506}
3507
3508////////////////////////////////////////////////////////////////////////////////
3509/// Paint the pad border.
3510/// Draw first a box as a normal filled box
3511
3513{
3514 if (color >= 0) {
3515 TAttLine::Modify(); //Change line attributes only if necessary
3516 TAttFill::Modify(); //Change fill area attributes only if necessary
3517
3518 //With Cocoa we have a transparency. But we also have
3519 //pixmaps, and if you just paint a new content over the old one
3520 //with alpha < 1., you'll be able to see the old content.
3521 if (!gROOT->IsBatch() && gVirtualX->InheritsFrom("TGCocoa") && GetPainter())
3523
3525 }
3526 if (color < 0) color = -color;
3527 // then paint 3d frame (depending on bordermode)
3528 if (IsTransparent()) return;
3529 // Paint a 3D frame around the pad.
3530
3531 if (fBorderMode == 0) return;
3532 Int_t bordersize = fBorderSize;
3533 if (bordersize <= 0) bordersize = 2;
3534
3535 const Double_t realBsX = bordersize / (GetAbsWNDC() * GetWw()) * (fX2 - fX1);
3536 const Double_t realBsY = bordersize / (GetAbsHNDC() * GetWh()) * (fY2 - fY1);
3537
3538 Short_t px1,py1,px2,py2;
3539 Double_t xl, xt, yl, yt;
3540
3541 // GetDarkColor() and GetLightColor() use GetFillColor()
3542 Color_t oldcolor = GetFillColor();
3543 SetFillColor(color);
3545 Color_t light = 0, dark = 0;
3546 if (color != 0) {
3547 light = TColor::GetColorBright(color);
3548 dark = TColor::GetColorDark(color);
3549 }
3550
3551 // Compute real left bottom & top right of the box in pixels
3552 px1 = XtoPixel(fX1); py1 = YtoPixel(fY1);
3553 px2 = XtoPixel(fX2); py2 = YtoPixel(fY2);
3554 if (px1 < px2) {xl = fX1; xt = fX2; }
3555 else {xl = fX2; xt = fX1;}
3556 if (py1 > py2) {yl = fY1; yt = fY2;}
3557 else {yl = fY2; yt = fY1;}
3558
3559 Double_t frameXs[7] = {}, frameYs[7] = {};
3560
3561 if (!IsBatch() && GetPainter()) {
3562 // Draw top&left part of the box
3563 frameXs[0] = xl; frameYs[0] = yl;
3564 frameXs[1] = xl + realBsX; frameYs[1] = yl + realBsY;
3565 frameXs[2] = frameXs[1]; frameYs[2] = yt - realBsY;
3566 frameXs[3] = xt - realBsX; frameYs[3] = frameYs[2];
3567 frameXs[4] = xt; frameYs[4] = yt;
3568 frameXs[5] = xl; frameYs[5] = yt;
3569 frameXs[6] = xl; frameYs[6] = yl;
3570
3571 if (fBorderMode == -1) GetPainter()->SetFillColor(dark);
3572 else GetPainter()->SetFillColor(light);
3573 GetPainter()->DrawFillArea(7, frameXs, frameYs);
3574
3575 // Draw bottom&right part of the box
3576 frameXs[0] = xl; frameYs[0] = yl;
3577 frameXs[1] = xl + realBsX; frameYs[1] = yl + realBsY;
3578 frameXs[2] = xt - realBsX; frameYs[2] = frameYs[1];
3579 frameXs[3] = frameXs[2]; frameYs[3] = yt - realBsY;
3580 frameXs[4] = xt; frameYs[4] = yt;
3581 frameXs[5] = xt; frameYs[5] = yl;
3582 frameXs[6] = xl; frameYs[6] = yl;
3583
3584 if (fBorderMode == -1) GetPainter()->SetFillColor(light);
3585 else GetPainter()->SetFillColor(dark);
3586 GetPainter()->DrawFillArea(7, frameXs, frameYs);
3587
3588 // If this pad is a button, highlight it
3589 if (InheritsFrom(TButton::Class()) && fBorderMode == -1) {
3590 if (TestBit(kFraming)) { // bit set in TButton::SetFraming
3591 if (GetFillColor() != 2) GetPainter()->SetLineColor(2);
3592 else GetPainter()->SetLineColor(4);
3593 GetPainter()->DrawBox(xl + realBsX, yl + realBsY, xt - realBsX, yt - realBsY, TVirtualPadPainter::kHollow);
3594 }
3595 }
3596 GetPainter()->SetFillColor(-1);
3597 SetFillColor(oldcolor);
3598 }
3599
3600 if (!tops) return;
3601
3602 PaintBorderPS(xl, yl, xt, yt, fBorderMode, bordersize, dark, light);
3603}
3604
3605////////////////////////////////////////////////////////////////////////////////
3606/// Paint a frame border with Postscript.
3607
3609{
3610 if (!gVirtualPS) return;
3611 gVirtualPS->DrawFrame(xl, yl, xt, yt, bmode,bsize,dark,light);
3612}
3613
3614////////////////////////////////////////////////////////////////////////////////
3615/// Paint the current date and time if the option date is on.
3616
3618{
3619 if (fCanvas == this && gStyle->GetOptDate()) {
3620 TDatime dt;
3621 const char *dates;
3622 char iso[16];
3623 if (gStyle->GetOptDate() < 10) {
3624 //by default use format like "Wed Sep 25 17:10:35 2002"
3625 dates = dt.AsString();
3626 } else if (gStyle->GetOptDate() < 20) {
3627 //use ISO format like 2002-09-25
3628 strlcpy(iso,dt.AsSQLString(),16);
3629 dates = iso;
3630 } else {
3631 //use ISO format like 2002-09-25 17:10:35
3632 dates = dt.AsSQLString();
3633 }
3634 TText tdate(gStyle->GetDateX(),gStyle->GetDateY(),dates);
3640 tdate.SetNDC();
3641 tdate.Paint();
3642 }
3643}
3644
3645////////////////////////////////////////////////////////////////////////////////
3646/// Paint histogram/graph frame.
3647
3649{
3650 if (!fPrimitives) fPrimitives = new TList;
3651 TList *glist = GetListOfPrimitives();
3652 TFrame *frame = GetFrame();
3653 frame->SetX1(xmin);
3654 frame->SetX2(xmax);
3655 frame->SetY1(ymin);
3656 frame->SetY2(ymax);
3657 if (!glist->FindObject(fFrame)) {
3658 glist->AddFirst(frame);
3660 }
3661 frame->Paint();
3662}
3663
3664////////////////////////////////////////////////////////////////////////////////
3665/// Traverse pad hierarchy and (re)paint only modified pads.
3666
3668{
3670 if (IsModified()) {
3671 fViewer3D->PadPaint(this);
3673 }
3674 TList *pList = GetListOfPrimitives();
3675 TObjOptLink *lnk = 0;
3676 if (pList) lnk = (TObjOptLink*)pList->FirstLink();
3677 TObject *obj;
3678 while (lnk) {
3679 obj = lnk->GetObject();
3680 if (obj->InheritsFrom(TPad::Class()))
3681 ((TPad*)obj)->PaintModified();
3682 lnk = (TObjOptLink*)lnk->Next();
3683 }
3684 return;
3685 }
3686
3688
3689 TPad *padsav = (TPad*)gPad;
3690 TVirtualPS *saveps = gVirtualPS;
3691 if (gVirtualPS) {
3693 }
3694 fPadPaint = 1;
3695 cd();
3696 if (IsModified() || IsTransparent()) {
3697 if ((fFillStyle < 3026) && (fFillStyle > 3000)) {
3698 if (!gPad->IsBatch() && GetPainter()) GetPainter()->ClearDrawable();
3699 }
3701 }
3702
3703 PaintDate();
3704
3705 TList *pList = GetListOfPrimitives();
3706 TObjOptLink *lnk = 0;
3707 if (pList) lnk = (TObjOptLink*)pList->FirstLink();
3708 TObject *obj;
3709
3710 Bool_t began3DScene = kFALSE;
3711
3712 while (lnk) {
3713 obj = lnk->GetObject();
3714 if (obj->InheritsFrom(TPad::Class())) {
3715 ((TPad*)obj)->PaintModified();
3716 } else if (IsModified() || IsTransparent()) {
3717
3718 // Create a pad 3D viewer if none exists and we encounter a
3719 // 3D shape
3720 if (!fViewer3D && obj->InheritsFrom(TAtt3D::Class())) {
3721 GetViewer3D("pad");
3722 }
3723
3724 // Open a 3D scene if required
3725 if (fViewer3D && !fViewer3D->BuildingScene()) {
3727 began3DScene = kTRUE;
3728 }
3729
3730 obj->Paint(lnk->GetOption());
3731 }
3732 lnk = (TObjOptLink*)lnk->Next();
3733 }
3734
3735 if (padsav) padsav->cd();
3736 fPadPaint = 0;
3738
3739 // This must be done after modified flag is cleared, as some
3740 // viewers will invoke another paint by marking pad modified again
3741 if (began3DScene) {
3743 }
3744
3745 gVirtualPS = saveps;
3746}
3747
3748////////////////////////////////////////////////////////////////////////////////
3749/// Paint box in CurrentPad World coordinates.
3750///
3751/// - if option[0] = 's' the box is forced to be paint with style=0
3752/// - if option[0] = 'l' the box contour is drawn
3753
3755{
3756 if (!gPad->IsBatch() && GetPainter()) {
3757 Int_t style0 = GetPainter()->GetFillStyle();
3758 Int_t style = style0;
3759 if (option[0] == 's') {
3761 style = 0;
3762 }
3763 if (style) {
3764 if (style > 3000 && style < 4000) {
3765 if (style < 3026) {
3766 // draw stipples with fFillColor foreground
3768 }
3769
3770 if (style >= 3100 && style < 4000) {
3771 Double_t xb[4], yb[4];
3772 xb[0] = x1; xb[1] = x1; xb[2] = x2; xb[3] = x2;
3773 yb[0] = y1; yb[1] = y2; yb[2] = y2; yb[3] = y1;
3774 PaintFillAreaHatches(4, xb, yb, style);
3775 return;
3776 }
3777 //special case for TAttFillCanvas
3778 if (GetPainter()->GetFillColor() == 10) {
3781 GetPainter()->SetFillColor(10);
3782 }
3783 } else if (style >= 4000 && style <= 4100) {
3784 // For style >=4000 we make the window transparent.
3785 // From 4000 to 4100 the window is 100% transparent to 100% opaque
3786
3787 //ignore this style option when this is the canvas itself
3788 if (this == fMother) {
3789 //It's clear, that virtual X checks a style (4000) and will render a hollow rect!
3790 const Style_t oldFillStyle = GetPainter()->GetFillStyle();
3791 if (gVirtualX->InheritsFrom("TGCocoa"))
3792 GetPainter()->SetFillStyle(1000);
3794 if (gVirtualX->InheritsFrom("TGCocoa"))
3795 GetPainter()->SetFillStyle(oldFillStyle);
3796 } else {
3797 //draw background by blitting all bottom pads
3798 int px, py;
3799 XYtoAbsPixel(fX1, fY2, px, py);
3800
3801 if (fMother) {
3803 CopyBackgroundPixmaps(fMother, this, px, py);
3804 }
3805
3806 GetPainter()->SetOpacity(style - 4000);
3807 }
3808 } else if (style >= 1000 && style <= 1999) {
3810 } else {
3812 }
3813 if (option[0] == 'l') GetPainter()->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kHollow);
3814 } else {
3816 if (option[0] == 's') GetPainter()->SetFillStyle(style0);
3817 }
3818 }
3819
3820 if (gVirtualPS) {
3821 Int_t style0 = gVirtualPS->GetFillStyle();
3822 if (option[0] == 's') {
3824 } else {
3825 if (style0 >= 3100 && style0 < 4000) {
3826 Double_t xb[4], yb[4];
3827 xb[0] = x1; xb[1] = x1; xb[2] = x2; xb[3] = x2;
3828 yb[0] = y1; yb[1] = y2; yb[2] = y2; yb[3] = y1;
3829 PaintFillAreaHatches(4, xb, yb, style0);
3830 return;
3831 }
3832 }
3833 gVirtualPS->DrawBox(x1, y1, x2, y2);
3834 if (option[0] == 'l') {
3836 gVirtualPS->DrawBox(x1, y1, x2, y2);
3837 }
3838 if (option[0] == 's' || option[0] == 'l') gVirtualPS->SetFillStyle(style0);
3839 }
3840
3841 Modified();
3842}
3843
3844////////////////////////////////////////////////////////////////////////////////
3845/// Copy pixmaps of pads laying below pad "stop" into pad "stop". This
3846/// gives the effect of pad "stop" being transparent.
3847
3849{
3850 if (!start) return;
3851 TObject *obj;
3852 if (!fPrimitives) fPrimitives = new TList;
3853 TIter next(start->GetListOfPrimitives());
3854 while ((obj = next())) {
3855 if (obj->InheritsFrom(TPad::Class())) {
3856 if (obj == stop) break;
3857 ((TPad*)obj)->CopyBackgroundPixmap(x, y);
3858 ((TPad*)obj)->CopyBackgroundPixmaps((TPad*)obj, stop, x, y);
3859 }
3860 }
3861}
3862
3863////////////////////////////////////////////////////////////////////////////////
3864/// Copy pixmap of this pad as background of the current pad.
3865
3867{
3868 int px, py;
3869 XYtoAbsPixel(fX1, fY2, px, py);
3870 if (GetPainter()) GetPainter()->CopyDrawable(GetPixmapID(), px-x, py-y);
3871}
3872
3873////////////////////////////////////////////////////////////////////////////////
3874
3876{
3877 Warning("TPad::PaintFillArea", "Float_t signature is obsolete. Use Double_t signature.");
3878}
3879
3880////////////////////////////////////////////////////////////////////////////////
3881/// Paint fill area in CurrentPad World coordinates.
3882
3884{
3885 if (nn <3) return;
3886 Int_t n=0;
3890 } else {
3891 xmin = fX1; ymin = fY1; xmax = fX2; ymax = fY2;
3892 }
3893
3894 Int_t nc = 2*nn+1;
3895 std::vector<Double_t> x(nc, 0.);
3896 std::vector<Double_t> y(nc, 0.);
3897
3898 n = ClipPolygon(nn, xx, yy, nc, &x.front(), &y.front(),xmin,ymin,xmax,ymax);
3899 if (!n)
3900 return;
3901
3902 // Paint the fill area with hatches
3903 Int_t fillstyle = GetPainter()?GetPainter()->GetFillStyle():1;
3904 if (gPad->IsBatch() && GetPainter() && gVirtualPS) fillstyle = gVirtualPS->GetFillStyle();
3905 if (fillstyle >= 3100 && fillstyle < 4000) {
3906 PaintFillAreaHatches(nn, &x.front(), &y.front(), fillstyle);
3907 return;
3908 }
3909
3910 if (!gPad->IsBatch() && GetPainter())
3911 // invoke the graphics subsystem
3912 GetPainter()->DrawFillArea(n, &x.front(), &y.front());
3913
3914 if (gVirtualPS)
3915 gVirtualPS->DrawPS(-n, &x.front(), &y.front());
3916
3917 Modified();
3918}
3919
3920////////////////////////////////////////////////////////////////////////////////
3921/// Paint fill area in CurrentPad NDC coordinates.
3922
3924{
3925 auto xw = new Double_t[n];
3926 auto yw = new Double_t[n];
3927 for (int i=0; i<n; i++) {
3928 xw[i] = fX1 + x[i]*(fX2 - fX1);
3929 yw[i] = fY1 + y[i]*(fY2 - fY1);
3930 }
3931 PaintFillArea(n, xw, yw, option);
3932 delete [] xw;
3933 delete [] yw;
3934}
3935
3936////////////////////////////////////////////////////////////////////////////////
3937/// This function paints hatched fill area according to the FillStyle value
3938/// The convention for the Hatch is the following:
3939///
3940/// `FillStyle = 3ijk`
3941///
3942/// - i (1-9) : specify the space between each hatch
3943/// 1 = minimum 9 = maximum
3944/// the final spacing is i*GetHatchesSpacing(). The hatches spacing
3945/// is set by SetHatchesSpacing()
3946/// - j (0-9) : specify angle between 0 and 90 degrees
3947/// * 0 = 0
3948/// * 1 = 10
3949/// * 2 = 20
3950/// * 3 = 30
3951/// * 4 = 45
3952/// * 5 = Not drawn
3953/// * 6 = 60
3954/// * 7 = 70
3955/// * 8 = 80
3956/// * 9 = 90
3957/// - k (0-9) : specify angle between 90 and 180 degrees
3958/// * 0 = 180
3959/// * 1 = 170
3960/// * 2 = 160
3961/// * 3 = 150
3962/// * 4 = 135
3963/// * 5 = Not drawn
3964/// * 6 = 120
3965/// * 7 = 110
3966/// * 8 = 100
3967/// * 9 = 90
3968
3970{
3971 static Double_t ang1[10] = { 0., 10., 20., 30., 45.,5., 60., 70., 80., 89.99};
3972 static Double_t ang2[10] = {180.,170.,160.,150.,135.,5.,120.,110.,100., 89.99};
3973
3974 Int_t fasi = FillStyle%1000;
3975 Int_t idSPA = (Int_t)(fasi/100);
3976 Int_t iAng2 = (Int_t)((fasi-100*idSPA)/10);
3977 Int_t iAng1 = fasi%10;
3978 Double_t dy = 0.003*(Double_t)(idSPA)*gStyle->GetHatchesSpacing();
3980 Short_t lws = 0;
3981 Int_t lss = 0;
3982 Int_t lcs = 0;
3983
3984 // Save the current line attributes
3985 if (!gPad->IsBatch() && GetPainter()) {
3986 lws = GetPainter()->GetLineWidth();
3987 lss = GetPainter()->GetLineStyle();
3988 lcs = GetPainter()->GetLineColor();
3989 } else {
3990 if (gVirtualPS) {
3991 lws = gVirtualPS->GetLineWidth();
3992 lss = gVirtualPS->GetLineStyle();
3993 lcs = gVirtualPS->GetLineColor();
3994 }
3995 }
3996
3997 // Change the current line attributes to draw the hatches
3998 if (!gPad->IsBatch() && GetPainter()) {
4002 }
4003 if (gVirtualPS) {
4007 }
4008
4009 // Draw the hatches
4010 if (ang1[iAng1] != 5.) PaintHatches(dy, ang1[iAng1], nn, xx, yy);
4011 if (ang2[iAng2] != 5.) PaintHatches(dy, ang2[iAng2], nn, xx, yy);
4012
4013 // Restore the line attributes
4014 if (!gPad->IsBatch() && GetPainter()) {
4015 GetPainter()->SetLineStyle(lss);
4016 GetPainter()->SetLineWidth(lws);
4017 GetPainter()->SetLineColor(lcs);
4018 }
4019 if (gVirtualPS) {
4023 }
4024}
4025
4026////////////////////////////////////////////////////////////////////////////////
4027/// This routine draw hatches inclined with the
4028/// angle "angle" and spaced of "dy" in normalized device
4029/// coordinates in the surface defined by n,xx,yy.
4030
4032 Int_t nn, Double_t *xx, Double_t *yy)
4033{
4034 Int_t i, i1, i2, nbi, m, inv;
4035 Double_t ratiox, ratioy, ymin, ymax, yrot, ycur;
4036 const Double_t angr = TMath::Pi()*(180.-angle)/180.;
4037 const Double_t epsil = 0.0001;
4038 const Int_t maxnbi = 100;
4039 Double_t xli[maxnbi], xlh[2], ylh[2], xt1, xt2, yt1, yt2;
4040 Double_t ll, x, y, x1, x2, y1, y2, a, b, xi, xip, xin, yi, yip;
4041
4042 Double_t rwxmin = gPad->GetX1();
4043 Double_t rwxmax = gPad->GetX2();
4044 Double_t rwymin = gPad->GetY1();
4045 Double_t rwymax = gPad->GetY2();
4046 ratiox = 1./(rwxmax-rwxmin);
4047 ratioy = 1./(rwymax-rwymin);
4048
4049 Double_t sina = TMath::Sin(angr), sinb;
4050 Double_t cosa = TMath::Cos(angr), cosb;
4051 if (TMath::Abs(cosa) <= epsil) cosa=0.;
4052 if (TMath::Abs(sina) <= epsil) sina=0.;
4053 sinb = -sina;
4054 cosb = cosa;
4055
4056 // Values needed to compute the hatches in TRUE normalized space (NDC)
4057 Int_t iw = (Int_t)gPad->GetWw();
4058 Int_t ih = (Int_t)gPad->GetWh();
4059 Double_t x1p,y1p,x2p,y2p;
4060 gPad->GetPadPar(x1p,y1p,x2p,y2p);
4061 iw = (Int_t)(iw*x2p)-(Int_t)(iw*x1p);
4062 ih = (Int_t)(ih*y2p)-(Int_t)(ih*y1p);
4063 Double_t wndc = TMath::Min(1.,(Double_t)iw/(Double_t)ih);
4064 Double_t hndc = TMath::Min(1.,(Double_t)ih/(Double_t)iw);
4065
4066 // Search ymin and ymax
4067 ymin = 1.;
4068 ymax = 0.;
4069 for (i=1; i<=nn; i++) {
4070 x = wndc*ratiox*(xx[i-1]-rwxmin);
4071 y = hndc*ratioy*(yy[i-1]-rwymin);
4072 yrot = sina*x+cosa*y;
4073 if (yrot > ymax) ymax = yrot;
4074 if (yrot < ymin) ymin = yrot;
4075 }
4076 ymax = (Double_t)((Int_t)(ymax/dy))*dy;
4077
4078 for (ycur=ymax; ycur>=ymin; ycur=ycur-dy) {
4079 nbi = 0;
4080 for (i=2; i<=nn+1; i++) {
4081 i2 = i;
4082 i1 = i-1;
4083 if (i == nn+1) i2=1;
4084 x1 = wndc*ratiox*(xx[i1-1]-rwxmin);
4085 y1 = hndc*ratioy*(yy[i1-1]-rwymin);
4086 x2 = wndc*ratiox*(xx[i2-1]-rwxmin);
4087 y2 = hndc*ratioy*(yy[i2-1]-rwymin);
4088 xt1 = cosa*x1-sina*y1;
4089 yt1 = sina*x1+cosa*y1;
4090 xt2 = cosa*x2-sina*y2;
4091 yt2 = sina*x2+cosa*y2;
4092
4093 // Line segment parallel to oy
4094 if (xt1 == xt2) {
4095 if (yt1 < yt2) {
4096 yi = yt1;
4097 yip = yt2;
4098 } else {
4099 yi = yt2;
4100 yip = yt1;
4101 }
4102 if ((yi <= ycur) && (ycur < yip)) {
4103 nbi++;
4104 if (nbi >= maxnbi) return;
4105 xli[nbi-1] = xt1;
4106 }
4107 continue;
4108 }
4109
4110 // Line segment parallel to ox
4111 if (yt1 == yt2) {
4112 if (yt1 == ycur) {
4113 nbi++;
4114 if (nbi >= maxnbi) return;
4115 xli[nbi-1] = xt1;
4116 nbi++;
4117 if (nbi >= maxnbi) return;
4118 xli[nbi-1] = xt2;
4119 }
4120 continue;
4121 }
4122
4123 // Other line segment
4124 a = (yt1-yt2)/(xt1-xt2);
4125 b = (yt2*xt1-xt2*yt1)/(xt1-xt2);
4126 if (xt1 < xt2) {
4127 xi = xt1;
4128 xip = xt2;
4129 } else {
4130 xi = xt2;
4131 xip = xt1;
4132 }
4133 xin = (ycur-b)/a;
4134 if ((xi <= xin) && (xin < xip) &&
4135 (TMath::Min(yt1,yt2) <= ycur) &&
4136 (ycur < TMath::Max(yt1,yt2))) {
4137 nbi++;
4138 if (nbi >= maxnbi) return;
4139 xli[nbi-1] = xin;
4140 }
4141 }
4142
4143 // Sorting of the x coordinates intersections
4144 inv = 0;
4145 m = nbi-1;
4146L30:
4147 for (i=1; i<=m; i++) {
4148 if (xli[i] < xli[i-1]) {
4149 inv++;
4150 ll = xli[i-1];
4151 xli[i-1] = xli[i];
4152 xli[i] = ll;
4153 }
4154 }
4155 m--;
4156 if (inv == 0) goto L50;
4157 inv = 0;
4158 goto L30;
4159
4160 // Draw the hatches
4161L50:
4162 if (nbi%2 != 0) continue;
4163
4164 for (i=1; i<=nbi; i=i+2) {
4165 // Rotate back the hatches
4166 xlh[0] = cosb*xli[i-1]-sinb*ycur;
4167 ylh[0] = sinb*xli[i-1]+cosb*ycur;
4168 xlh[1] = cosb*xli[i] -sinb*ycur;
4169 ylh[1] = sinb*xli[i] +cosb*ycur;
4170 // Convert hatches' positions from true NDC to WC
4171 xlh[0] = (xlh[0]/wndc)*(rwxmax-rwxmin)+rwxmin;
4172 ylh[0] = (ylh[0]/hndc)*(rwymax-rwymin)+rwymin;
4173 xlh[1] = (xlh[1]/wndc)*(rwxmax-rwxmin)+rwxmin;
4174 ylh[1] = (ylh[1]/hndc)*(rwymax-rwymin)+rwymin;
4175 gPad->PaintLine(xlh[0], ylh[0], xlh[1], ylh[1]);
4176 }
4177 }
4178}
4179
4180////////////////////////////////////////////////////////////////////////////////
4181/// Paint line in CurrentPad World coordinates.
4182
4184{
4185 Double_t x[2], y[2];
4186 x[0] = x1; x[1] = x2; y[0] = y1; y[1] = y2;
4187
4188 //If line is totally clipped, return
4190 if (Clip(x,y,fUxmin,fUymin,fUxmax,fUymax) == 2) return;
4191 } else {
4192 if (Clip(x,y,fX1,fY1,fX2,fY2) == 2) return;
4193 }
4194
4195 if (!gPad->IsBatch() && GetPainter())
4196 GetPainter()->DrawLine(x[0], y[0], x[1], y[1]);
4197
4198 if (gVirtualPS) {
4199 gVirtualPS->DrawPS(2, x, y);
4200 }
4201
4202 Modified();
4203}
4204
4205////////////////////////////////////////////////////////////////////////////////
4206/// Paint line in normalized coordinates.
4207
4209{
4210 static Double_t xw[2], yw[2];
4211 if (!gPad->IsBatch() && GetPainter())
4212 GetPainter()->DrawLineNDC(u1, v1, u2, v2);
4213
4214 if (gVirtualPS) {
4215 xw[0] = fX1 + u1*(fX2 - fX1);
4216 xw[1] = fX1 + u2*(fX2 - fX1);
4217 yw[0] = fY1 + v1*(fY2 - fY1);
4218 yw[1] = fY1 + v2*(fY2 - fY1);
4219 gVirtualPS->DrawPS(2, xw, yw);
4220 }
4221
4222 Modified();
4223}
4224
4225////////////////////////////////////////////////////////////////////////////////
4226/// Paint 3-D line in the CurrentPad.
4227
4229{
4230 if (!fView) return;
4231
4232 // convert from 3-D to 2-D pad coordinate system
4233 Double_t xpad[6];
4234 Double_t temp[3];
4235 Int_t i;
4236 for (i=0;i<3;i++) temp[i] = p1[i];
4237 fView->WCtoNDC(temp, &xpad[0]);
4238 for (i=0;i<3;i++) temp[i] = p2[i];
4239 fView->WCtoNDC(temp, &xpad[3]);
4240 PaintLine(xpad[0],xpad[1],xpad[3],xpad[4]);
4241}
4242
4243////////////////////////////////////////////////////////////////////////////////
4244/// Paint 3-D line in the CurrentPad.
4245
4247{
4248 //take into account perspective view
4249 if (!fView) return;
4250 // convert from 3-D to 2-D pad coordinate system
4251 Double_t xpad[6];
4252 Double_t temp[3];
4253 Int_t i;
4254 for (i=0;i<3;i++) temp[i] = p1[i];
4255 fView->WCtoNDC(temp, &xpad[0]);
4256 for (i=0;i<3;i++) temp[i] = p2[i];
4257 fView->WCtoNDC(temp, &xpad[3]);
4258 PaintLine(xpad[0],xpad[1],xpad[3],xpad[4]);
4259}
4260
4261////////////////////////////////////////////////////////////////////////////////
4262/// Paint polyline in CurrentPad World coordinates.
4263
4265{
4266 if (n < 2) return;
4267
4271 } else {
4272 xmin = fX1; ymin = fY1; xmax = fX2; ymax = fY2;
4273 }
4274 Int_t i, i1=-1,np=1;
4275 for (i=0; i<n-1; i++) {
4276 Double_t x1=x[i];
4277 Double_t y1=y[i];
4278 Double_t x2=x[i+1];
4279 Double_t y2=y[i+1];
4280 Int_t iclip = Clip(&x[i],&y[i],xmin,ymin,xmax,ymax);
4281 if (iclip == 2) {
4282 i1 = -1;
4283 continue;
4284 }
4285 np++;
4286 if (i1 < 0) i1 = i;
4287 if (iclip == 0 && i < n-2) continue;
4288 if (!gPad->IsBatch() && GetPainter())
4289 GetPainter()->DrawPolyLine(np, &x[i1], &y[i1]);
4290 if (gVirtualPS) {
4291 gVirtualPS->DrawPS(np, &x[i1], &y[i1]);
4292 }
4293 if (iclip) {
4294 x[i] = x1;
4295 y[i] = y1;
4296 x[i+1] = x2;
4297 y[i+1] = y2;
4298 }
4299 i1 = -1;
4300 np = 1;
4301 }
4302
4303 Modified();
4304}
4305
4306////////////////////////////////////////////////////////////////////////////////
4307/// Paint polyline in CurrentPad World coordinates.
4308///
4309/// If option[0] == 'C' no clipping
4310
4312{
4313 if (n < 2) return;
4314
4316 Bool_t mustClip = kTRUE;
4319 } else {
4320 xmin = fX1; ymin = fY1; xmax = fX2; ymax = fY2;
4321 if (option && (option[0] == 'C')) mustClip = kFALSE;
4322 }
4323
4324 Int_t i, i1=-1, np=1, iclip=0;
4325
4326 for (i=0; i < n-1; i++) {
4327 Double_t x1=x[i];
4328 Double_t y1=y[i];
4329 Double_t x2=x[i+1];
4330 Double_t y2=y[i+1];
4331 if (mustClip) {
4332 iclip = Clip(&x[i],&y[i],xmin,ymin,xmax,ymax);
4333 if (iclip == 2) {
4334 i1 = -1;
4335 continue;
4336 }
4337 }
4338 np++;
4339 if (i1 < 0) i1 = i;
4340 if (iclip == 0 && i < n-2) continue;
4341 if (!gPad->IsBatch() && GetPainter())
4342 GetPainter()->DrawPolyLine(np, &x[i1], &y[i1]);
4343 if (gVirtualPS) {
4344 gVirtualPS->DrawPS(np, &x[i1], &y[i1]);
4345 }
4346 if (iclip) {
4347 x[i] = x1;
4348 y[i] = y1;
4349 x[i+1] = x2;
4350 y[i+1] = y2;
4351 }
4352 i1 = -1;
4353 np = 1;
4354 }
4355
4356 Modified();
4357}
4358
4359////////////////////////////////////////////////////////////////////////////////
4360/// Paint polyline in CurrentPad NDC coordinates.
4361
4363{
4364 if (n <=0) return;
4365
4366 if (!gPad->IsBatch() && GetPainter())
4368
4369 if (gVirtualPS) {
4370 Double_t *xw = new Double_t[n];
4371 Double_t *yw = new Double_t[n];
4372 for (Int_t i=0; i<n; i++) {
4373 xw[i] = fX1 + x[i]*(fX2 - fX1);
4374 yw[i] = fY1 + y[i]*(fY2 - fY1);
4375 }
4376 gVirtualPS->DrawPS(n, xw, yw);
4377 delete [] xw;
4378 delete [] yw;
4379 }
4380 Modified();
4381}
4382
4383////////////////////////////////////////////////////////////////////////////////
4384/// Paint 3-D polyline in the CurrentPad.
4385
4387{
4388 if (!fView) return;
4389
4390 // Loop on each individual line
4391 for (Int_t i = 1; i < n; i++)
4392 PaintLine3D(&p[3*i-3], &p[3*i]);
4393
4394 Modified();
4395}
4396
4397////////////////////////////////////////////////////////////////////////////////
4398/// Paint polymarker in CurrentPad World coordinates.
4399
4401{
4402 Int_t n = TMath::Abs(nn);
4404 if (nn > 0 || TestBit(TGraph::kClipFrame)) {
4406 } else {
4407 xmin = fX1; ymin = fY1; xmax = fX2; ymax = fY2;
4408 }
4409 Int_t i,i1=-1,np=0;
4410 for (i=0; i<n; i++) {
4411 if (x[i] >= xmin && x[i] <= xmax && y[i] >= ymin && y[i] <= ymax) {
4412 np++;
4413 if (i1 < 0) i1 = i;
4414 if (i < n-1) continue;
4415 }
4416 if (np == 0) continue;
4417 if (!gPad->IsBatch() && GetPainter())
4418 GetPainter()->DrawPolyMarker(np, &x[i1], &y[i1]);
4419 if (gVirtualPS) {
4420 gVirtualPS->DrawPolyMarker(np, &x[i1], &y[i1]);
4421 }
4422 i1 = -1;
4423 np = 0;
4424 }
4425 Modified();
4426}
4427
4428////////////////////////////////////////////////////////////////////////////////
4429/// Paint polymarker in CurrentPad World coordinates.
4430
4432{
4433 Int_t n = TMath::Abs(nn);
4435 if (nn > 0 || TestBit(TGraph::kClipFrame)) {
4437 } else {
4438 xmin = fX1; ymin = fY1; xmax = fX2; ymax = fY2;
4439 }
4440 Int_t i,i1=-1,np=0;
4441 for (i=0; i<n; i++) {
4442 if (x[i] >= xmin && x[i] <= xmax && y[i] >= ymin && y[i] <= ymax) {
4443 np++;
4444 if (i1 < 0) i1 = i;
4445 if (i < n-1) continue;
4446 }
4447 if (np == 0) continue;
4448 if (!gPad->IsBatch() && GetPainter())
4449 GetPainter()->DrawPolyMarker(np, &x[i1], &y[i1]);
4450 if (gVirtualPS) {
4451 gVirtualPS->DrawPolyMarker(np, &x[i1], &y[i1]);
4452 }
4453 i1 = -1;
4454 np = 0;
4455 }
4456 Modified();
4457}
4458
4459////////////////////////////////////////////////////////////////////////////////
4460/// Paint text in CurrentPad World coordinates.
4461
4463{
4464 Modified();
4465
4466 if (!gPad->IsBatch() && GetPainter())
4468
4469 if (gVirtualPS) gVirtualPS->Text(x, y, text);
4470}
4471
4472////////////////////////////////////////////////////////////////////////////////
4473/// Paint text in CurrentPad World coordinates.
4474
4475void TPad::PaintText(Double_t x, Double_t y, const wchar_t *text)
4476{
4477 Modified();
4478
4479 if (!gPad->IsBatch() && GetPainter())
4481
4482 if (gVirtualPS) gVirtualPS->Text(x, y, text);
4483}
4484
4485////////////////////////////////////////////////////////////////////////////////
4486/// Paint text in CurrentPad NDC coordinates.
4487
4489{
4490 Modified();
4491
4492 if (!gPad->IsBatch() && GetPainter())
4494
4495 if (gVirtualPS) {
4496 Double_t x = fX1 + u*(fX2 - fX1);
4497 Double_t y = fY1 + v*(fY2 - fY1);
4498 gVirtualPS->Text(x, y, text);
4499 }
4500}
4501
4502////////////////////////////////////////////////////////////////////////////////
4503/// Paint text in CurrentPad NDC coordinates.
4504
4506{
4507 Modified();
4508
4509 if (!gPad->IsBatch() && GetPainter())
4511
4512 if (gVirtualPS) {
4513 Double_t x = fX1 + u*(fX2 - fX1);
4514 Double_t y = fY1 + v*(fY2 - fY1);
4515 gVirtualPS->Text(x, y, text);
4516 }
4517}
4518
4519////////////////////////////////////////////////////////////////////////////////
4520/// Search for an object at pixel position px,py.
4521///
4522/// Check if point is in this pad.
4523///
4524/// If yes, check if it is in one of the sub-pads
4525///
4526/// If found in the pad, compute closest distance of approach
4527/// to each primitive.
4528///
4529/// If one distance of approach is found to be within the limit Distancemaximum
4530/// the corresponding primitive is selected and the routine returns.
4531
4533{
4534 //the two following statements are necessary under NT (multithreaded)
4535 //when a TCanvas object is being created and a thread calling TPad::Pick
4536 //before the TPad constructor has completed in the other thread
4537 if (gPad == 0) return 0; //Andy Haas
4538 if (GetListOfPrimitives() == 0) return 0; //Andy Haas
4539
4540 Int_t dist;
4541 // Search if point is in pad itself
4542 Double_t x = AbsPixeltoX(px);
4543 Double_t y = AbsPixeltoY(py);
4544 if (this != gPad->GetCanvas()) {
4545 if (!((x >= fX1 && x <= fX2) && (y >= fY1 && y <= fY2))) return 0;
4546 }
4547
4548 // search for a primitive in this pad or its sub-pads
4549 static TObjOptLink dummyLink(0,""); //place holder for when no link available
4550 TPad *padsav = (TPad*)gPad;
4551 gPad = this; // since no drawing will be done, don't use cd() for efficiency reasons
4552 TPad *pick = 0;
4553 TPad *picked = this;
4554 pickobj = 0;
4556 dummyLink.SetObject(this);
4557 pickobj = &dummyLink;
4558 }
4559
4560 // Loop backwards over the list of primitives. The first non-pad primitive
4561 // found is the selected one. However, we have to keep going down the
4562 // list to see if there is maybe a pad overlaying the primitive. In that
4563 // case look into the pad for a possible primitive. Once a pad has been
4564 // found we can terminate the loop.
4565 Bool_t gotPrim = kFALSE; // true if found a non pad primitive
4567
4568 //We can have 3d stuff in pad. If canvas prefers to draw
4569 //such stuff with OpenGL, the selection of 3d objects is
4570 //a gl viewer business so, in first cycle we do not
4571 //call DistancetoPrimitive for TAtt3D descendants.
4572 //In case of gl we first try to select 2d object first.
4573
4574 while (lnk) {
4575 TObject *obj = lnk->GetObject();
4576
4577 //If canvas prefers GL, all 3d objects must be drawn/selected by
4578 //gl viewer
4579 if (obj->InheritsFrom(TAtt3D::Class()) && fEmbeddedGL) {
4580 lnk = lnk->Prev();
4581 continue;
4582 }
4583
4584 fPadPointer = obj;
4585 if (obj->InheritsFrom(TPad::Class())) {
4586 pick = ((TPad*)obj)->Pick(px, py, pickobj);
4587 if (pick) {
4588 picked = pick;
4589 break;
4590 }
4591 } else if (!gROOT->GetEditorMode()) {
4592 if (!gotPrim) {
4593 if (!obj->TestBit(kCannotPick)) {
4594 dist = obj->DistancetoPrimitive(px, py);
4595 if (dist < fgMaxPickDistance) {
4596 pickobj = lnk;
4597 gotPrim = kTRUE;
4598 if (dist == 0) break;
4599 }
4600 }
4601 }
4602 }
4603
4604 lnk = lnk->Prev();
4605 }
4606
4607 //if no primitive found, check if we have a TView
4608 //if yes, return the view except if you are in the lower or upper X range
4609 //of the pad.
4610 //In case canvas prefers gl, fView existence
4611 //automatically means viewer3d existence. (?)
4612
4613 if (fView && !gotPrim) {
4614 Double_t dx = 0.05*(fUxmax-fUxmin);
4615 if ((x > fUxmin + dx) && (x < fUxmax-dx)) {
4616
4617 if (fEmbeddedGL) {
4618 //No 2d stuff was selected, but we have gl-viewer. Let it select an object in
4619 //scene (or select itself). In any case it'll internally call
4620 //gPad->SetSelected(ptr) as, for example, hist painter does.
4621 py -= Int_t((1 - GetHNDC() - GetYlowNDC()) * GetWh());
4622 px -= Int_t(GetXlowNDC() * GetWw());
4624 }
4625 else
4626 dummyLink.SetObject(fView);
4627 }
4628 }
4629
4630 if (picked->InheritsFrom(TButton::Class())) {
4631 TButton *button = (TButton*)picked;
4632 if (!button->IsEditable()) pickobj = 0;
4633 }
4634
4635 if (TestBit(kCannotPick)) {
4636
4637 if (picked == this) {
4638 // cannot pick pad itself!
4639 picked = 0;
4640 }
4641
4642 }
4643
4644 gPad = padsav;
4645 return picked;
4646}
4647
4648////////////////////////////////////////////////////////////////////////////////
4649/// Pop pad to the top of the stack.
4650
4652{
4653 if (!fMother) return;
4654 if (!fMother->TestBit(kNotDeleted)) return;
4655 if (!fPrimitives) fPrimitives = new TList;
4656 if (this == fMother->GetListOfPrimitives()->Last()) return;
4657
4659 TObject *obj;
4660 while ((obj = next()))
4661 if (obj == this) {
4662 char *opt = StrDup(next.GetOption());
4664 fMother->GetListOfPrimitives()->AddLast(this, opt);
4665 delete [] opt;
4666 return;
4667 }
4668}
4669
4670////////////////////////////////////////////////////////////////////////////////
4671/// Save Pad contents in a file in one of various formats.
4672///
4673/// - if filename is "", the file produced is padname.ps
4674/// - if filename starts with a dot, the padname is added in front
4675/// - if filename contains .eps, an Encapsulated Postscript file is produced
4676/// - if filename contains .pdf, a PDF file is produced NOTE: TMathText will be converted to TLatex; q.e.d., symbols only available in TMathText will not render properly.
4677/// - if filename contains .svg, a SVG file is produced
4678/// - if filename contains .tex, a TeX file is produced
4679/// - if filename contains .gif, a GIF file is produced
4680/// - if filename contains .gif+NN, an animated GIF file is produced See comments in TASImage::WriteImage for meaning of NN and other .gif sufix variants
4681/// - if filename contains .xpm, a XPM file is produced
4682/// - if filename contains .png, a PNG file is produced
4683/// - if filename contains .jpg, a JPEG file is produced NOTE: JPEG's lossy compression will make all sharp edges fuzzy.
4684/// - if filename contains .tiff, a TIFF file is produced
4685/// - if filename contains .C or .cxx, a C++ macro file is produced
4686/// - if filename contains .root, a Root file is produced
4687/// - if filename contains .xml, a XML file is produced
4688/// - if filename contains .json, a JSON file is produced
4689///
4690/// See comments in TPad::SaveAs or the TPad::Print function below
4691
4692void TPad::Print(const char *filename) const
4693{
4694 ((TPad*)this)->SaveAs(filename);
4695}
4696
4697////////////////////////////////////////////////////////////////////////////////
4698/// Auxiliary function. Returns kTRUE if list contains an object inherited
4699/// from TImage
4700
4702{
4703 TIter next(li);
4704 TObject *obj;
4705
4706 while ((obj = next())) {
4707 if (obj->InheritsFrom(TImage::C