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