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