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