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