Logo ROOT  
Reference Guide
TGraphPainter.cxx
Go to the documentation of this file.
1// @(#)root/histpainter:$Id: TGraphPainter.cxx,v 1.00
2// Author: Olivier Couet
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 "TROOT.h"
13#include "TGraphPainter.h"
14#include "TMath.h"
15#include "TGraph.h"
16#include "TPolyLine.h"
17#include "TPolyMarker.h"
18#include "TCanvas.h"
19#include "TStyle.h"
20#include "TH1.h"
21#include "TF1.h"
22#include "TPaveStats.h"
23#include "TGaxis.h"
24#include "TGraphAsymmErrors.h"
25#include "TGraphMultiErrors.h"
26#include "TGraphBentErrors.h"
27#include "TGraphPolargram.h"
28#include "TGraphPolar.h"
29#include "TGraphQQ.h"
30#include "TLatex.h"
31#include "TArrow.h"
32#include "TFrame.h"
33#include "TMarker.h"
34#include "TVirtualPadEditor.h"
35#include "TVirtualX.h"
36#include "TRegexp.h"
37#include "strlcpy.h"
38#include "snprintf.h"
39#include <memory>
40
42
43static Int_t gHighlightPoint = -1; // highlight point of graph
44static TGraph *gHighlightGraph = nullptr; // pointer to graph with highlight point
45static std::unique_ptr<TMarker> gHighlightMarker; // highlight marker
46
48
49
50////////////////////////////////////////////////////////////////////////////////
51
52/*! \class TGraphPainter
53 \ingroup Histpainter
54 \brief The graph painter class. Implements all graphs' drawing's options.
55
56- [Introduction](\ref GrP0)
57- [Graphs' plotting options](\ref GrP1)
58- [Exclusion graphs](\ref GrP2)
59- [Graphs with error bars](\ref GrP3)
60 - [TGraphErrors](\ref GrP3a)
61 - [TGraphAsymmErrors](\ref GrP3b)
62 - [TGraphBentErrors](\ref GrP3c)
63 - [TGraphMultiErrors](\ref GrP3d)
64- [TGraphPolar options](\ref GrP4)
65- [Colors automatically picked in palette](\ref GrP5)
66- [Reverse graphs' axis](\ref GrP6)
67- [Graphs in logarithmic scale](\ref GrP7)
68- [Highlight mode for graph](\ref GrP8)
69
70
71\anchor GrP0
72### Introduction
73
74Graphs are drawn via the painter `TGraphPainter` class. This class
75implements techniques needed to display the various kind of
76graphs i.e.: `TGraph`, `TGraphErrors`, `TGraphBentErrors` and `TGraphAsymmErrors`.
77
78To draw a graph `graph` it's enough to do:
79
80 graph->Draw("AL");
81
82The option `AL` in the `Draw()` method means:
83
841. The axis should be drawn (option `A`),
852. The graph should be drawn as a simple line (option `L`).
86
87 By default a graph is drawn in the current pad in the current coordinate system.
88To define a suitable coordinate system and draw the axis the option
89`A` must be specified.
90
91`TGraphPainter` offers many options to paint the various kind of graphs.
92
93It is separated from the graph classes so that one can have graphs without the
94graphics overhead, for example in a batch program.
95
96When a displayed graph is modified, there is no need to call `Draw()` again; the
97image will be refreshed the next time the pad will be updated. A pad is updated
98after one of these three actions:
99
1001. a carriage return on the ROOT command line,
1012. a click inside the pad,
1023. a call to `TPad::Update`.
103
104\anchor GrP1
105### Graphs' plotting options
106Graphs can be drawn with the following options:
107
108| Option | Description |
109|----------|-------------------------------------------------------------------|
110| "A" | Axis are drawn around the graph |
111| "I" | Combine with option 'A' it draws invisible axis |
112| "L" | A simple polyline is drawn |
113| "F" | A fill area is drawn ('CF' draw a smoothed fill area) |
114| "C" | A smooth Curve is drawn |
115| "*" | A Star is plotted at each point |
116| "P" | The current marker is plotted at each point |
117| "B" | A Bar chart is drawn |
118| "1" | When a graph is drawn as a bar chart, this option makes the bars start from the bottom of the pad. By default they start at 0. |
119| "X+" | The X-axis is drawn on the top side of the plot. |
120| "Y+" | The Y-axis is drawn on the right side of the plot. |
121| "PFC" | Palette Fill Color: graph's fill color is taken in the current palette. |
122| "PLC" | Palette Line Color: graph's line color is taken in the current palette. |
123| "PMC" | Palette Marker Color: graph's marker color is taken in the current palette. |
124| "RX" | Reverse the X axis. |
125| "RY" | Reverse the Y axis. |
126
127Drawing options can be combined. In the following example the graph
128is drawn as a smooth curve (option "C") with markers (option "P") and
129with axes (option "A").
130
131Begin_Macro(source)
132{
133 auto c1 = new TCanvas("c1","c1",200,10,600,400);
134
135 c1->SetFillColor(42);
136 c1->SetGrid();
137
138 const Int_t n = 20;
139 Double_t x[n], y[n];
140 for (Int_t i=0;i<n;i++) {
141 x[i] = i*0.1;
142 y[i] = 10*sin(x[i]+0.2);
143 }
144 gr = new TGraph(n,x,y);
145 gr->SetLineColor(2);
146 gr->SetLineWidth(4);
147 gr->SetMarkerColor(4);
148 gr->SetMarkerSize(1.5);
149 gr->SetMarkerStyle(21);
150 gr->SetTitle("Option ACP example");
151 gr->GetXaxis()->SetTitle("X title");
152 gr->GetYaxis()->SetTitle("Y title");
153 gr->Draw("ACP");
154
155 // TCanvas::Update() draws the frame, after which one can change it
156 c1->Update();
157 c1->GetFrame()->SetFillColor(21);
158 c1->GetFrame()->SetBorderSize(12);
159 c1->Modified();
160}
161End_Macro
162
163The following macro shows the option "B" usage. It can be combined with the
164option "1".
165
166The bar width is equal to:
167
168 bar_width = 0.5*delta*gStyle->GetBarWidth();
169
170Where `delta` is equal to the X maximal value minus the X minimal value divided by the
171number of points in the graph.
172
173Begin_Macro(source)
174{
175 auto c47 = new TCanvas("c47","c47",200,10,600,400);
176 c47->Divide(1,2);
177 const Int_t n = 20;
178 Double_t x[n], y[n];
179 for (Int_t i=0;i<n;i++) {
180 x[i] = i*0.1;
181 y[i] = 10*sin(x[i]+0.2)-6;
182 }
183 auto gr = new TGraph(n,x,y);
184 gr->SetFillColor(38);
185 c47->cd(1); gr->Draw("AB");
186 c47->cd(2); gr->Draw("AB1");
187}
188End_Macro
189
190\anchor GrP2
191### Exclusion graphs
192
193When a graph is painted with the option `C` or `L` it is
194possible to draw a filled area on one side of the line. This is useful to show
195exclusion zones.
196
197This drawing mode is activated when the absolute value of the graph line
198width (set by `SetLineWidth()`) is greater than 99. In that
199case the line width number is interpreted as:
200
201 100*ff+ll = ffll
202
203- The two digits number `ll` represent the normal line width
204- The two digits number `ff` represent the filled area width.
205- The sign of "ffll" allows to flip the filled area from one side of the line to the other.
206
207The current fill area attributes are used to draw the hatched zone.
208
209Begin_Macro(source)
210../../../tutorials/graphs/exclusiongraph.C
211End_Macro
212
213\anchor GrP3
214### Graphs with error bars
215Three classes are available to handle graphs with error bars:
216`TGraphErrors`, `TGraphAsymmErrors` and `TGraphBentErrors`.
217The following drawing options are specific to graphs with error bars:
218
219| Option | Description |
220|----------|-------------------------------------------------------------------|
221| "Z" | Do not draw small horizontal and vertical lines the end of the error bars. Without "Z", the default is to draw these. |
222| ">" | An arrow is drawn at the end of the error bars. The size of the arrow is set to 2/3 of the marker size. |
223| \"\|>\" | A filled arrow is drawn at the end of the error bars. The size of the arrow is set to 2/3 of the marker size. |
224| "X" | Do not draw error bars. By default, graph classes that have errors are drawn with the errors (TGraph itself has no errors, and so this option has no effect.) |
225| \"\|\|\" | Draw only the small vertical/horizontal lines at the ends of the error bars, without drawing the bars themselves. This option is interesting to superimpose statistical-only errors on top of a graph with statistical+systematic errors. |
226| "[]" | Does the same as option \"\|\|\" except that it draws additional marks at the ends of the small vertical/horizontal lines. It makes plots less ambiguous in case several graphs are drawn on the same picture. |
227| "0" | By default, when a data point is outside the visible range along the Y axis, the error bars are not drawn. This option forces error bars' drawing for the data points outside the visible range along the Y axis (see example below). |
228| "2" | Error rectangles are drawn. |
229| "3" | A filled area is drawn through the end points of the vertical error bars. |
230| "4" | A smoothed filled area is drawn through the end points of the vertical error bars. |
231| "5" | Error rectangles are drawn like option "2". In addition the contour line around the boxes is drawn. This can be useful when boxes' fill colors are very light or in gray scale mode. |
232
233
234`gStyle->SetErrorX(dx)` controls the size of the error along x.
235`dx = 0` removes the error along x.
236
237`gStyle->SetEndErrorSize(np)` controls the size of the lines
238at the end of the error bars (when option 1 is used).
239By default `np=1`. (np represents the number of pixels).
240
241\anchor GrP3a
242#### TGraphErrors
243
244A `TGraphErrors` is a `TGraph` with error bars. The errors are
245defined along X and Y and are symmetric: The left and right errors are the same
246along X and the bottom and up errors are the same along Y.
247
248Begin_Macro(source)
249{
250 auto c4 = new TCanvas("c4","c4",200,10,600,400);
251 double x[] = {0, 1, 2, 3, 4};
252 double y[] = {0, 2, 4, 1, 3};
253 double ex[] = {0.1, 0.2, 0.3, 0.4, 0.5};
254 double ey[] = {1, 0.5, 1, 0.5, 1};
255 auto ge = new TGraphErrors(5, x, y, ex, ey);
256 ge->Draw("ap");
257}
258End_Macro
259
260The option "0" shows the error bars for data points outside range.
261
262Begin_Macro(source)
263{
264 auto c48 = new TCanvas("c48","c48",200,10,600,400);
265 float x[] = {1,2,3};
266 float err_x[] = {0,0,0};
267 float err_y[] = {5,5,5};
268 float y[] = {1,4,9};
269 auto tg = new TGraphErrors(3,x,y,err_x,err_y);
270 c48->Divide(2,1);
271 c48->cd(1); gPad->DrawFrame(0,0,4,8); tg->Draw("PC");
272 c48->cd(2); gPad->DrawFrame(0,0,4,8); tg->Draw("0PC");
273}
274End_Macro
275
276The option "3" shows the errors as a band.
277
278Begin_Macro(source)
279{
280 auto c41 = new TCanvas("c41","c41",200,10,600,400);
281 double x[] = {0, 1, 2, 3, 4};
282 double y[] = {0, 2, 4, 1, 3};
283 double ex[] = {0.1, 0.2, 0.3, 0.4, 0.5};
284 double ey[] = {1, 0.5, 1, 0.5, 1};
285 auto ge = new TGraphErrors(5, x, y, ex, ey);
286 ge->SetFillColor(4);
287 ge->SetFillStyle(3010);
288 ge->Draw("a3");
289}
290End_Macro
291
292The option "4" is similar to the option "3" except that the band
293is smoothed. As the following picture shows, this option should be
294used carefully because the smoothing algorithm may show some (huge)
295"bouncing" effects. In some cases it looks nicer than option "3"
296(because it is smooth) but it can be misleading.
297
298Begin_Macro(source)
299{
300 auto c42 = new TCanvas("c42","c42",200,10,600,400);
301 double x[] = {0, 1, 2, 3, 4};
302 double y[] = {0, 2, 4, 1, 3};
303 double ex[] = {0.1, 0.2, 0.3, 0.4, 0.5};
304 double ey[] = {1, 0.5, 1, 0.5, 1};
305 auto ge = new TGraphErrors(5, x, y, ex, ey);
306 ge->SetFillColor(6);
307 ge->SetFillStyle(3005);
308 ge->Draw("a4");
309}
310End_Macro
311
312The following example shows how the option "[]" can be used to superimpose
313systematic errors on top of a graph with statistical errors.
314
315Begin_Macro(source)
316{
317 auto c43 = new TCanvas("c43","c43",200,10,600,400);
318 c43->DrawFrame(0., -0.5, 6., 2);
319
320 double x[5] = {1, 2, 3, 4, 5};
321 double zero[5] = {0, 0, 0, 0, 0};
322
323 // data set (1) with stat and sys errors
324 double py1[5] = {1.2, 1.15, 1.19, 0.9, 1.4};
325 double ey_stat1[5] = {0.2, 0.18, 0.17, 0.2, 0.4};
326 double ey_sys1[5] = {0.5, 0.71, 0.76, 0.5, 0.45};
327
328 // data set (2) with stat and sys errors
329 double y2[5] = {0.25, 0.18, 0.29, 0.2, 0.21};
330 double ey_stat2[5] = {0.2, 0.18, 0.17, 0.2, 0.4};
331 double ey_sys2[5] = {0.63, 0.19, 0.7, 0.2, 0.7};
332
333 // Now draw data set (1)
334
335 // We first have to draw it only with the stat errors
336 auto graph1 = new TGraphErrors(5, x, py1, zero, ey_stat1);
337 graph1->SetMarkerStyle(20);
338 graph1->Draw("P");
339
340 // Now we have to somehow depict the sys errors
341
342 auto graph1_sys = new TGraphErrors(5, x, py1, zero, ey_sys1);
343 graph1_sys->Draw("[]");
344
345 // Now draw data set (2)
346
347 // We first have to draw it only with the stat errors
348 auto graph2 = new TGraphErrors(5, x, y2, zero, ey_stat2);
349 graph2->SetMarkerStyle(24);
350 graph2->Draw("P");
351
352 // Now we have to somehow depict the sys errors
353
354 auto graph2_sys = new TGraphErrors(5, x, y2, zero, ey_sys2);
355 graph2_sys->Draw("[]");
356}
357End_Macro
358
359\anchor GrP3b
360#### TGraphAsymmErrors
361A `TGraphAsymmErrors` is like a `TGraphErrors` but the errors
362defined along X and Y are not symmetric: The left and right errors are
363different along X and the bottom and up errors are different along Y.
364
365Begin_Macro(source)
366{
367 auto c44 = new TCanvas("c44","c44",200,10,600,400);
368 double ax[] = {0, 1, 2, 3, 4};
369 double ay[] = {0, 2, 4, 1, 3};
370 double aexl[] = {0.1, 0.2, 0.3, 0.4, 0.5};
371 double aexh[] = {0.5, 0.4, 0.3, 0.2, 0.1};
372 double aeyl[] = {1, 0.5, 1, 0.5, 1};
373 double aeyh[] = {0.5, 1, 0.5, 1, 0.5};
374 auto gae = new TGraphAsymmErrors(5, ax, ay, aexl, aexh, aeyl, aeyh);
375 gae->SetFillColor(2);
376 gae->SetFillStyle(3001);
377 gae->Draw("a2");
378 gae->Draw("p");
379}
380End_Macro
381
382
383\anchor GrP3c
384#### TGraphBentErrors
385A `TGraphBentErrors` is like a `TGraphAsymmErrors`.
386An extra parameter allows to bend the error bars to better see them
387when several graphs are drawn on the same plot.
388
389Begin_Macro(source)
390{
391 auto c45 = new TCanvas("c45","c45",200,10,600,400);
392 const Int_t n = 10;
393 Double_t x[n] = {-0.22, 0.05, 0.25, 0.35, 0.5, 0.61,0.7,0.85,0.89,0.95};
394 Double_t y[n] = {1,2.9,5.6,7.4,9,9.6,8.7,6.3,4.5,1};
395 Double_t exl[n] = {.05,.1,.07,.07,.04,.05,.06,.07,.08,.05};
396 Double_t eyl[n] = {.8,.7,.6,.5,.4,.4,.5,.6,.7,.8};
397 Double_t exh[n] = {.02,.08,.05,.05,.03,.03,.04,.05,.06,.03};
398 Double_t eyh[n] = {.6,.5,.4,.3,.2,.2,.3,.4,.5,.6};
399 Double_t exld[n] = {.0,.0,.0,.0,.0,.0,.0,.0,.0,.0};
400 Double_t eyld[n] = {.0,.0,.05,.0,.0,.0,.0,.0,.0,.0};
401 Double_t exhd[n] = {.0,.0,.0,.0,.0,.0,.0,.0,.0,.0};
402 Double_t eyhd[n] = {.0,.0,.0,.0,.0,.0,.0,.0,.05,.0};
403 auto gr = new TGraphBentErrors(n,x,y,exl,exh,eyl,eyh,exld,exhd,eyld,eyhd);
404 gr->SetTitle("TGraphBentErrors Example");
405 gr->SetMarkerColor(4);
406 gr->SetMarkerStyle(21);
407 gr->Draw("ALP");
408}
409End_Macro
410
411
412\anchor GrP3d
413#### TGraphMultiErrors
414A `TGraphMultiErrors` works basically the same way like a `TGraphAsymmErrors`.
415It has the possibility to define more than one type / dimension of y-Errors.
416This is useful if you want to plot statistic and systematic errors at once.
417
418To be able to define different drawing options for the multiple error dimensions
419the option string can consist of multiple blocks separated by semicolons.
420The painting method assigns these blocks to the error dimensions. The first block
421is always used for the general draw options and options concerning the x-Errors.
422In case there are less than NErrorDimensions + 1 blocks in the option string
423the first block is also used for the first error dimension which is reserved for
424statistical errors. The remaining blocks are assigned to the remaining dimensions.
425
426In addition to the draw options of options of `TGraphAsymmErrors` the following are possible:
427
428| Option | Block | Description |
429|----------|----------------|-------------------------------------------------------------------|
430| "X0" | First one only | Do not draw errors for points with x = 0 |
431| "Y0" | First one only | Do not draw errors for points with y = 0 |
432| "s=%f" | Any | Scales the x-Errors with %f similar to `gStyle->SetErrorX(dx)` but does not affect them directly (Useful when used in addition with box errors to make the box only half as wide as the x-Errors e.g. s=0.5) |
433| "S" | First one only | Use individual TAttFill and TAttLine attributes for the different error dimensions instead of the global ones. |
434
435
436Per default the Fill and Line Styles of the Graph are being used for all error
437dimensions. To use the specific ones add the draw option "S" to the first block.
438
439Begin_Macro(source)
440{
441 auto c47 = new TCanvas("c47","c47",200,10,600,400);
442 double ax[] = {0, 1, 2, 3, 4};
443 double ay[] = {0, 2, 4, 1, 3};
444 double aexl[] = {0.3, 0.3, 0.3, 0.3, 0.3};
445 double aexh[] = {0.3, 0.3, 0.3, 0.3, 0.3};
446 double* aeylstat = new double[5] {1, 0.5, 1, 0.5, 1};
447 double* aeyhstat = new double[5] {0.5, 1, 0.5, 1, 0.5};
448 double* aeylsys = new double[5] {0.5, 0.4, 0.8, 0.3, 1.2};
449 double* aeyhsys = new double[5] {0.6, 0.7, 0.6, 0.4, 0.8};
450
451 TGraphMultiErrors* gme = new TGraphMultiErrors("gme", "TGraphMultiErrors Example", 5, ax, ay, aexl, aexh, aeylstat, aeyhstat);
452 gme->AddYError(5, aeylsys, aeyhsys);
453 gme->SetMarkerStyle(20);
454 gme->SetLineColor(kRed);
455 gme->GetAttLine(0)->SetLineColor(kRed);
456 gme->GetAttLine(1)->SetLineColor(kBlue);
457 gme->GetAttFill(1)->SetFillStyle(0);
458
459 gme->Draw("a p s ; ; 5 s=0.5");
460}
461End_Macro
462
463
464\anchor GrP4
465### TGraphPolar options
466
467The drawing options for the polar graphs are the following:
468
469| Option | Description |
470|----------|-------------------------------------------------------------------|
471| "O" | Polar labels are drawn orthogonally to the polargram radius. |
472| "P" | Polymarker are drawn at each point position. |
473| "E" | Draw error bars. |
474| "F" | Draw fill area (closed polygon). |
475| "A" | Force axis redrawing even if a polargram already exists. |
476| "N" | Disable the display of the polar labels. |
477
478
479Begin_Macro(source)
480{
481 auto c46 = new TCanvas("c46","c46",500,500);
482 auto grP1 = new TGraphPolar();
483 grP1->SetTitle("TGraphPolar example");
484
485 grP1->SetPoint(0, (1*TMath::Pi())/4., 0.05);
486 grP1->SetPoint(1, (2*TMath::Pi())/4., 0.10);
487 grP1->SetPoint(2, (3*TMath::Pi())/4., 0.15);
488 grP1->SetPoint(3, (4*TMath::Pi())/4., 0.20);
489 grP1->SetPoint(4, (5*TMath::Pi())/4., 0.25);
490 grP1->SetPoint(5, (6*TMath::Pi())/4., 0.30);
491 grP1->SetPoint(6, (7*TMath::Pi())/4., 0.35);
492 grP1->SetPoint(7, (8*TMath::Pi())/4., 0.40);
493
494 grP1->SetMarkerStyle(20);
495 grP1->SetMarkerSize(1.);
496 grP1->SetMarkerColor(4);
497 grP1->SetLineColor(4);
498 grP1->Draw("ALP");
499
500 // Update, otherwise GetPolargram returns 0
501 c46->Update();
502 grP1->GetPolargram()->SetToRadian();
503}
504End_Macro
505
506\anchor GrP5
507### Colors automatically picked in palette
508
509\since **ROOT version 6.09/01**
510
511When several graphs are painted in the same canvas or when a multi-graph is drawn,
512it might be useful to have an easy and automatic way to choose
513their color. The simplest way is to pick colors in the current active color
514palette. Palette coloring for histogram is activated thanks to the options `PFC`
515(Palette Fill Color), `PLC` (Palette Line Color) and `PMC` (Palette Marker Color).
516When one of these options is given to `TGraph::Draw` the graph get its color
517from the current color palette defined by `gStyle->SetPalette(...)`. The color
518is determined according to the number of objects having palette coloring in
519the current pad.
520
521Begin_Macro(source)
522../../../tutorials/graphs/graphpalettecolor.C
523End_Macro
524
525Begin_Macro(source)
526../../../tutorials/graphs/multigraphpalettecolor.C
527End_Macro
528
529\anchor GrP6
530### Reverse graphs' axis
531
532\since **ROOT version 6.09/03**
533
534When a TGraph is drawn, the X-axis is drawn with increasing values from left to
535right and the Y-axis from bottom to top. The two options `RX` and `RY` allow to
536change this order. The option `RX` allows to draw the X-axis with increasing values
537from right to left and the `RY` option allows to draw the Y-axis with increasing
538values from top to bottom. The following example illustrate how to use these options.
539
540Begin_Macro(source)
541{
542 auto c = new TCanvas();
543 c->Divide(2,1);
544 auto g = new TGraphErrors();
545 g->SetTitle("Simple Graph");
546
547 g->SetPoint(0,-4,-3);
548 g->SetPoint(1,1,1);
549 g->SetPoint(2,2,1);
550 g->SetPoint(3,3,4);
551 g->SetPoint(4,5,5);
552
553 g->SetPointError(0,1.,2.);
554 g->SetPointError(1,2,1);
555 g->SetPointError(2,2,3);
556 g->SetPointError(3,3,2);
557 g->SetPointError(4,4,5);
558
559 g->GetXaxis()->SetNdivisions(520);
560
561 g->SetMarkerStyle(21);
562 c->cd(1); gPad->SetGrid(1,1);
563 g->Draw("APL");
564
565 c->cd(2); gPad->SetGrid(1,1);
566 g->Draw("A RX RY PL");
567}
568End_Macro
569
570\anchor GrP7
571### Graphs in logarithmic scale
572
573Like histograms, graphs can be drawn in logarithmic scale along X and Y. When
574a pad is set to logarithmic scale with TPad::SetLogx() and/or with TPad::SetLogy()
575the points building the graph are converted into logarithmic scale. But **only** the
576points not the lines connecting them which stay linear. This can be clearly seen
577on the following example:
578
579Begin_Macro(source)
580{
581 // A graph with 3 points
582 Double_t xmin = 750.;
583 Double_t xmax = 1000;
584 auto g = new TGraph(3);
585 g->SetPoint(0,xmin,0.1);
586 g->SetPoint(1,845,0.06504);
587 g->SetPoint(2,xmax,0.008);
588
589 // The same graph with n points
590 Int_t n = 10000;
591 Double_t dx = (xmax-xmin)/n;
592 Double_t x = xmin;
593 auto g2 = new TGraph();
594 for (Int_t i=0; i<n; i++) {
595 g2->SetPoint(i, x, g->Eval(x));
596 x = x + dx;
597 }
598
599 auto cv = new TCanvas("cv","cv",800,600);
600 cv->SetLogy();
601 cv->SetGridx();
602 cv->SetGridy();
603 g->Draw("AL*");
604
605 g2->SetMarkerColor(kRed);
606 g2->SetMarkerStyle(1);
607 g2->Draw("P");
608}
609
610End_Macro
611
612\anchor GrP8
613#### Highlight mode for graph
614
615\since **ROOT version 6.15/01**
616
617\image html hlGraph1.gif "Highlight mode"
618
619Highlight mode is implemented for `TGraph` (and for `TH1`) class. When
620highlight mode is on, mouse movement over the point will be represented
621graphically. Point will be highlighted as "point circle" (presented by
622marker object). Moreover, any highlight (change of point) emits signal
623`TCanvas::Highlighted()` which allows the user to react and call their own
624function. For a better understanding please see also the tutorials
625`$ROOTSYS/tutorials/graphs/hlGraph*.C` files.
626
627Highlight mode is switched on/off by `TGraph::SetHighlight()` function
628or interactively from `TGraph` context menu. `TGraph::IsHighlight()` to verify
629whether the highlight mode enabled or disabled, default it is disabled.
630
631~~~ {.cpp}
632 root [0] .x $ROOTSYS/tutorials/graphs/gerrors2.C
633 root [1] // try SetHighlight() interactively from TGraph context menu
634~~~
635
636\image html hlgerrors2.gif "Highlight mode for graph"
637
638See how it is used
639<a href="classTHistPainter.html#HP30a">highlight mode and user function</a>
640(is fully equivalent as for histogram).
641
642NOTE all parameters of user function are taken from
643
644 void TCanvas::Highlighted(TVirtualPad *pad, TObject *obj, Int_t x, Int_t y)
645
646 - `pad` is pointer to pad with highlighted graph
647 - `obj` is pointer to highlighted graph
648 - `x` is highlighted x-th (i-th) point for graph
649 - `y` not in use (only for 2D histogram)
650
651For more complex demo please see for example `$ROOTSYS/tutorials/math/hlquantiles.C` file.
652
653*/
654
655
656////////////////////////////////////////////////////////////////////////////////
657/// Default constructor
658
660{
661}
662
663
664////////////////////////////////////////////////////////////////////////////////
665/// Destructor.
666
668{
669}
670
671
672////////////////////////////////////////////////////////////////////////////////
673/// Compute the logarithm of variables `gxwork` and `gywork`
674/// according to the value of Options and put the results
675/// in the variables `gxworkl` and `gyworkl`.
676///
677/// npoints : Number of points in gxwork and in gywork.
678///
679/// - opt = 1 ComputeLogs is called from PaintGrapHist
680/// - opt = 0 ComputeLogs is called from PaintGraph
681
683{
684 if (gPad->GetLogx()) {
685 for (Int_t i = 0; i < npoints; i++) {
686 gxworkl[i] = (gxwork[i] > 0.) ? TMath::Log10(gxwork[i]) : gPad->GetX1();
687 }
688 } else {
689 for (Int_t i = 0; i < npoints; i++)
690 gxworkl[i] = gxwork[i];
691 }
692 if (!opt && gPad->GetLogy()) {
693 for (Int_t i = 0; i < npoints; i++) {
694 gyworkl[i] = (gywork[i] > 0.) ? TMath::Log10(gywork[i]) : gPad->GetY1();
695 }
696 } else {
697 for (Int_t i = 0; i < npoints; i++)
698 gyworkl[i] = gywork[i];
699 }
700}
701
702
703////////////////////////////////////////////////////////////////////////////////
704/// Compute distance from point px,py to a graph.
705///
706/// Compute the closest distance of approach from point px,py to this line.
707/// The distance is computed in pixels units.
708
710{
711
712 // Are we on the axis?
713 Int_t distance;
714 if (theGraph->GetHistogram()) {
715 distance = theGraph->GetHistogram()->DistancetoPrimitive(px,py);
716 if (distance <= 5) return distance;
717 }
718
719 // Somewhere on the graph points?
720 const Int_t big = 9999;
721 const Int_t kMaxDiff = 10;
722 Int_t puxmin = gPad->XtoAbsPixel(gPad->GetUxmin());
723 Int_t puymin = gPad->YtoAbsPixel(gPad->GetUymin());
724 Int_t puxmax = gPad->XtoAbsPixel(gPad->GetUxmax());
725 Int_t puymax = gPad->YtoAbsPixel(gPad->GetUymax());
726
727 // return if point is not in the graph area
728 if (px <= puxmin) return big;
729 if (py >= puymin) return big;
730 if (px >= puxmax) return big;
731 if (py <= puymax) return big;
732
733 // check if point is near one of the graph points
734 Int_t i, pxp, pyp, d;
735 distance = big;
736
737 Int_t theNpoints = theGraph->GetN();
738 Double_t *theX, *theY;
739 if (theGraph->InheritsFrom(TGraphPolar::Class())) {
740 TGraphPolar *theGraphPolar = (TGraphPolar*) theGraph;
741 theX = theGraphPolar->GetXpol();
742 theY = theGraphPolar->GetYpol();
743 } else {
744 theX = theGraph->GetX();
745 theY = theGraph->GetY();
746 }
747
748 Int_t hpoint = -1;
749 for (i=0;i<theNpoints;i++) {
750 pxp = gPad->XtoAbsPixel(gPad->XtoPad(theX[i]));
751 pyp = gPad->YtoAbsPixel(gPad->YtoPad(theY[i]));
752 d = TMath::Abs(pxp-px) + TMath::Abs(pyp-py);
753 if (d < distance) {
754 distance = d;
755 hpoint = i;
756 }
757 }
758
759 if (theGraph->IsHighlight()) // only if highlight is enable
760 HighlightPoint(theGraph, hpoint, distance);
761 if (distance < kMaxDiff) return distance;
762
763 for (i=0;i<theNpoints-1;i++) {
764 TAttLine l;
765 d = l.DistancetoLine(px, py, gPad->XtoPad(theX[i]), gPad->YtoPad(theY[i]), gPad->XtoPad(theX[i+1]), gPad->YtoPad(theY[i+1]));
766 if (d < distance) distance = d;
767 }
768
769 // If graph has been drawn with the fill area option, check if we are inside
770 TString drawOption = theGraph->GetDrawOption();
771 drawOption.ToLower();
772 if (drawOption.Contains("f")) {
773 Double_t xp = gPad->AbsPixeltoX(px); xp = gPad->PadtoX(xp);
774 Double_t yp = gPad->AbsPixeltoY(py); yp = gPad->PadtoY(yp);
775 if (TMath::IsInside(xp,yp,theNpoints,theX,theY) != 0) distance = 1;
776 }
777
778 // Loop on the list of associated functions and user objects
779 TObject *f;
780 TList *functions = theGraph->GetListOfFunctions();
781 TIter next(functions);
782 while ((f = (TObject*) next())) {
783 Int_t dist;
784 if (f->InheritsFrom(TF1::Class())) dist = f->DistancetoPrimitive(-px,py);
785 else dist = f->DistancetoPrimitive(px,py);
786 if (dist < kMaxDiff) {
787 gPad->SetSelected(f);
788 return 0; //must be o and not dist in case of TMultiGraph
789 }
790 }
791
792 return distance;
793}
794
795
796////////////////////////////////////////////////////////////////////////////////
797/// Display a panel with all histogram drawing options.
798
800{
801
802 if (!gPad) {
803 Error("DrawPanel", "need to draw graph first");
804 return;
805 }
807 editor->Show();
808 gROOT->ProcessLine(Form("((TCanvas*)0x%zx)->Selected((TVirtualPad*)0x%zx,(TObject*)0x%zx,1)",
809 (size_t)gPad->GetCanvas(), (size_t)gPad, (size_t)theGraph));
810}
811
812
813////////////////////////////////////////////////////////////////////////////////
814/// Execute action corresponding to one event.
815///
816/// This member function is called when a graph is clicked with the locator.
817///
818/// If the left mouse button is clicked on one of the line end points, this point
819/// follows the cursor until button is released.
820///
821/// If the middle mouse button clicked, the line is moved parallel to itself
822/// until the button is released.
823
825{
826
827 if (!gPad) return;
828
829 Int_t i, d;
830 Double_t xmin, xmax, ymin, ymax, dx, dy, dxr, dyr;
831 const Int_t kMaxDiff = 10;//3;
832 static Bool_t middle, badcase;
833 static Int_t ipoint, pxp, pyp;
834 static Int_t px1,px2,py1,py2;
835 static Int_t pxold, pyold, px1old, py1old, px2old, py2old;
836 static Int_t dpx, dpy;
837 static std::vector<Int_t> x, y;
838 Bool_t opaque = gPad->OpaqueMoving();
839
840 if (!theGraph->IsEditable() || theGraph->InheritsFrom(TGraphPolar::Class())) {
841 gPad->SetCursor(kHand);
842 return;
843 }
844 if (!gPad->IsEditable()) return;
845 Int_t theNpoints = theGraph->GetN();
846 Double_t *theX = theGraph->GetX();
847 Double_t *theY = theGraph->GetY();
848
849 switch (event) {
850
851 case kButton1Down:
852 badcase = kFALSE;
853 gVirtualX->SetLineColor(-1);
854 theGraph->TAttLine::Modify(); //Change line attributes only if necessary
855 px1 = gPad->XtoAbsPixel(gPad->GetX1());
856 py1 = gPad->YtoAbsPixel(gPad->GetY1());
857 px2 = gPad->XtoAbsPixel(gPad->GetX2());
858 py2 = gPad->YtoAbsPixel(gPad->GetY2());
859 ipoint = -1;
860
861
862 if (!x.empty() || !y.empty()) break;
863 x.resize(theNpoints+1);
864 y.resize(theNpoints+1);
865 for (i=0;i<theNpoints;i++) {
866 pxp = gPad->XtoAbsPixel(gPad->XtoPad(theX[i]));
867 pyp = gPad->YtoAbsPixel(gPad->YtoPad(theY[i]));
868 if (pxp < -kMaxPixel || pxp >= kMaxPixel ||
869 pyp < -kMaxPixel || pyp >= kMaxPixel) {
870 badcase = kTRUE;
871 continue;
872 }
873 if (!opaque) {
874 gVirtualX->DrawLine(pxp-4, pyp-4, pxp+4, pyp-4);
875 gVirtualX->DrawLine(pxp+4, pyp-4, pxp+4, pyp+4);
876 gVirtualX->DrawLine(pxp+4, pyp+4, pxp-4, pyp+4);
877 gVirtualX->DrawLine(pxp-4, pyp+4, pxp-4, pyp-4);
878 }
879 x[i] = pxp;
880 y[i] = pyp;
881 d = TMath::Abs(pxp-px) + TMath::Abs(pyp-py);
882 if (d < kMaxDiff) ipoint =i;
883 }
884 dpx = 0;
885 dpy = 0;
886 pxold = px;
887 pyold = py;
888 if (ipoint < 0) return;
889 if (ipoint == 0) {
890 px1old = 0;
891 py1old = 0;
892 px2old = gPad->XtoAbsPixel(theX[1]);
893 py2old = gPad->YtoAbsPixel(theY[1]);
894 } else if (ipoint == theNpoints-1) {
895 px1old = gPad->XtoAbsPixel(gPad->XtoPad(theX[theNpoints-2]));
896 py1old = gPad->YtoAbsPixel(gPad->YtoPad(theY[theNpoints-2]));
897 px2old = 0;
898 py2old = 0;
899 } else {
900 px1old = gPad->XtoAbsPixel(gPad->XtoPad(theX[ipoint-1]));
901 py1old = gPad->YtoAbsPixel(gPad->YtoPad(theY[ipoint-1]));
902 px2old = gPad->XtoAbsPixel(gPad->XtoPad(theX[ipoint+1]));
903 py2old = gPad->YtoAbsPixel(gPad->YtoPad(theY[ipoint+1]));
904 }
905 pxold = gPad->XtoAbsPixel(gPad->XtoPad(theX[ipoint]));
906 pyold = gPad->YtoAbsPixel(gPad->YtoPad(theY[ipoint]));
907
908 break;
909
910
911 case kMouseMotion:
912
913 middle = kTRUE;
914 for (i=0;i<theNpoints;i++) {
915 pxp = gPad->XtoAbsPixel(gPad->XtoPad(theX[i]));
916 pyp = gPad->YtoAbsPixel(gPad->YtoPad(theY[i]));
917 d = TMath::Abs(pxp-px) + TMath::Abs(pyp-py);
918 if (d < kMaxDiff) middle = kFALSE;
919 }
920
921
922 // check if point is close to an axis
923 if (middle) gPad->SetCursor(kMove);
924 else gPad->SetCursor(kHand);
925 break;
926
927 case kButton1Motion:
928 if (!opaque) {
929 if (middle) {
930 for(i=0;i<theNpoints-1;i++) {
931 gVirtualX->DrawLine(x[i]+dpx, y[i]+dpy, x[i+1]+dpx, y[i+1]+dpy);
932 pxp = x[i]+dpx;
933 pyp = y[i]+dpy;
934 if (pxp < -kMaxPixel || pxp >= kMaxPixel ||
935 pyp < -kMaxPixel || pyp >= kMaxPixel) continue;
936 gVirtualX->DrawLine(pxp-4, pyp-4, pxp+4, pyp-4);
937 gVirtualX->DrawLine(pxp+4, pyp-4, pxp+4, pyp+4);
938 gVirtualX->DrawLine(pxp+4, pyp+4, pxp-4, pyp+4);
939 gVirtualX->DrawLine(pxp-4, pyp+4, pxp-4, pyp-4);
940 }
941 pxp = x[theNpoints-1]+dpx;
942 pyp = y[theNpoints-1]+dpy;
943 gVirtualX->DrawLine(pxp-4, pyp-4, pxp+4, pyp-4);
944 gVirtualX->DrawLine(pxp+4, pyp-4, pxp+4, pyp+4);
945 gVirtualX->DrawLine(pxp+4, pyp+4, pxp-4, pyp+4);
946 gVirtualX->DrawLine(pxp-4, pyp+4, pxp-4, pyp-4);
947 dpx += px - pxold;
948 dpy += py - pyold;
949 pxold = px;
950 pyold = py;
951 for(i=0;i<theNpoints-1;i++) {
952 gVirtualX->DrawLine(x[i]+dpx, y[i]+dpy, x[i+1]+dpx, y[i+1]+dpy);
953 pxp = x[i]+dpx;
954 pyp = y[i]+dpy;
955 if (pxp < -kMaxPixel || pxp >= kMaxPixel ||
956 pyp < -kMaxPixel || pyp >= kMaxPixel) continue;
957 gVirtualX->DrawLine(pxp-4, pyp-4, pxp+4, pyp-4);
958 gVirtualX->DrawLine(pxp+4, pyp-4, pxp+4, pyp+4);
959 gVirtualX->DrawLine(pxp+4, pyp+4, pxp-4, pyp+4);
960 gVirtualX->DrawLine(pxp-4, pyp+4, pxp-4, pyp-4);
961 }
962 pxp = x[theNpoints-1]+dpx;
963 pyp = y[theNpoints-1]+dpy;
964 gVirtualX->DrawLine(pxp-4, pyp-4, pxp+4, pyp-4);
965 gVirtualX->DrawLine(pxp+4, pyp-4, pxp+4, pyp+4);
966 gVirtualX->DrawLine(pxp+4, pyp+4, pxp-4, pyp+4);
967 gVirtualX->DrawLine(pxp-4, pyp+4, pxp-4, pyp-4);
968 } else {
969 if (px1old) gVirtualX->DrawLine(px1old, py1old, pxold, pyold);
970 if (px2old) gVirtualX->DrawLine(pxold, pyold, px2old, py2old);
971 gVirtualX->DrawLine(pxold-4, pyold-4, pxold+4, pyold-4);
972 gVirtualX->DrawLine(pxold+4, pyold-4, pxold+4, pyold+4);
973 gVirtualX->DrawLine(pxold+4, pyold+4, pxold-4, pyold+4);
974 gVirtualX->DrawLine(pxold-4, pyold+4, pxold-4, pyold-4);
975 pxold = px;
976 pxold = TMath::Max(pxold, px1);
977 pxold = TMath::Min(pxold, px2);
978 pyold = py;
979 pyold = TMath::Max(pyold, py2);
980 pyold = TMath::Min(pyold, py1);
981 if (px1old) gVirtualX->DrawLine(px1old, py1old, pxold, pyold);
982 if (px2old) gVirtualX->DrawLine(pxold, pyold, px2old, py2old);
983 gVirtualX->DrawLine(pxold-4, pyold-4, pxold+4, pyold-4);
984 gVirtualX->DrawLine(pxold+4, pyold-4, pxold+4, pyold+4);
985 gVirtualX->DrawLine(pxold+4, pyold+4, pxold-4, pyold+4);
986 gVirtualX->DrawLine(pxold-4, pyold+4, pxold-4, pyold-4);
987 }
988 } else {
989 xmin = gPad->GetUxmin();
990 xmax = gPad->GetUxmax();
991 ymin = gPad->GetUymin();
992 ymax = gPad->GetUymax();
993 dx = xmax-xmin;
994 dy = ymax-ymin;
995 dxr = dx/(1 - gPad->GetLeftMargin() - gPad->GetRightMargin());
996 dyr = dy/(1 - gPad->GetBottomMargin() - gPad->GetTopMargin());
997
998 if (theGraph->GetHistogram()) {
999 // Range() could change the size of the pad pixmap and therefore should
1000 // be called before the other paint routines
1001 gPad->Range(xmin - dxr*gPad->GetLeftMargin(),
1002 ymin - dyr*gPad->GetBottomMargin(),
1003 xmax + dxr*gPad->GetRightMargin(),
1004 ymax + dyr*gPad->GetTopMargin());
1005 gPad->RangeAxis(xmin, ymin, xmax, ymax);
1006 }
1007 if (middle) {
1008 dpx += px - pxold;
1009 dpy += py - pyold;
1010 pxold = px;
1011 pyold = py;
1012 for(i=0;i<theNpoints;i++) {
1013 if (badcase) continue; //do not update if big zoom and points moved
1014 if (!x.empty()) theX[i] = gPad->PadtoX(gPad->AbsPixeltoX(x[i]+dpx));
1015 if (!y.empty()) theY[i] = gPad->PadtoY(gPad->AbsPixeltoY(y[i]+dpy));
1016 }
1017 } else {
1018 pxold = px;
1019 pxold = TMath::Max(pxold, px1);
1020 pxold = TMath::Min(pxold, px2);
1021 pyold = py;
1022 pyold = TMath::Max(pyold, py2);
1023 pyold = TMath::Min(pyold, py1);
1024 theX[ipoint] = gPad->PadtoX(gPad->AbsPixeltoX(pxold));
1025 theY[ipoint] = gPad->PadtoY(gPad->AbsPixeltoY(pyold));
1026 if (theGraph->InheritsFrom("TCutG")) {
1027 //make sure first and last point are the same
1028 if (ipoint == 0) {
1029 theX[theNpoints-1] = theX[0];
1030 theY[theNpoints-1] = theY[0];
1031 }
1032 if (ipoint == theNpoints-1) {
1033 theX[0] = theX[theNpoints-1];
1034 theY[0] = theY[theNpoints-1];
1035 }
1036 }
1037 }
1038 badcase = kFALSE;
1039 gPad->Modified(kTRUE);
1040 //gPad->Update();
1041 }
1042 break;
1043
1044 case kButton1Up:
1045
1046 if (gROOT->IsEscaped()) {
1047 gROOT->SetEscape(kFALSE);
1048 x.clear();
1049 y.clear();
1050 break;
1051 }
1052
1053 // Compute x,y range
1054 xmin = gPad->GetUxmin();
1055 xmax = gPad->GetUxmax();
1056 ymin = gPad->GetUymin();
1057 ymax = gPad->GetUymax();
1058 dx = xmax-xmin;
1059 dy = ymax-ymin;
1060 dxr = dx/(1 - gPad->GetLeftMargin() - gPad->GetRightMargin());
1061 dyr = dy/(1 - gPad->GetBottomMargin() - gPad->GetTopMargin());
1062
1063 if (theGraph->GetHistogram()) {
1064 // Range() could change the size of the pad pixmap and therefore should
1065 // be called before the other paint routines
1066 gPad->Range(xmin - dxr*gPad->GetLeftMargin(),
1067 ymin - dyr*gPad->GetBottomMargin(),
1068 xmax + dxr*gPad->GetRightMargin(),
1069 ymax + dyr*gPad->GetTopMargin());
1070 gPad->RangeAxis(xmin, ymin, xmax, ymax);
1071 }
1072 if (middle) {
1073 for(i=0;i<theNpoints;i++) {
1074 if (badcase) continue; //do not update if big zoom and points moved
1075 if (!x.empty()) theX[i] = gPad->PadtoX(gPad->AbsPixeltoX(x[i]+dpx));
1076 if (!y.empty()) theY[i] = gPad->PadtoY(gPad->AbsPixeltoY(y[i]+dpy));
1077 }
1078 } else {
1079 theX[ipoint] = gPad->PadtoX(gPad->AbsPixeltoX(pxold));
1080 theY[ipoint] = gPad->PadtoY(gPad->AbsPixeltoY(pyold));
1081 if (theGraph->InheritsFrom("TCutG")) {
1082 //make sure first and last point are the same
1083 if (ipoint == 0) {
1084 theX[theNpoints-1] = theX[0];
1085 theY[theNpoints-1] = theY[0];
1086 }
1087 if (ipoint == theNpoints-1) {
1088 theX[0] = theX[theNpoints-1];
1089 theY[0] = theY[theNpoints-1];
1090 }
1091 }
1092 }
1093 badcase = kFALSE;
1094 x.clear();
1095 y.clear();
1096 gPad->Modified(kTRUE);
1097 gVirtualX->SetLineColor(-1);
1098 }
1099}
1100
1101
1102////////////////////////////////////////////////////////////////////////////////
1103
1104char *TGraphPainter::GetObjectInfoHelper(TGraph * /*theGraph*/, Int_t /*px*/, Int_t /*py*/) const
1105{
1106 return (char*)"";
1107}
1108
1109
1110////////////////////////////////////////////////////////////////////////////////
1111/// Return the highlighted point for theGraph
1112
1114{
1115 if (theGraph == gHighlightGraph) return gHighlightPoint;
1116 else return -1;
1117}
1118
1119
1120////////////////////////////////////////////////////////////////////////////////
1121/// Set highlight (enable/disable) mode for theGraph
1122
1124{
1125 gHighlightPoint = -1; // must be -1
1126 gHighlightGraph = nullptr;
1127 if (theGraph->IsHighlight()) return;
1128
1129 // delete previous highlight marker
1130 if (gHighlightMarker) gHighlightMarker.reset(nullptr);
1131 // emit Highlighted() signal (user can check on disabled)
1132 if (gPad->GetCanvas()) gPad->GetCanvas()->Highlighted(gPad, theGraph, gHighlightPoint, -1);
1133}
1134
1135
1136////////////////////////////////////////////////////////////////////////////////
1137/// Check on highlight point
1138
1139void TGraphPainter::HighlightPoint(TGraph *theGraph, Int_t hpoint, Int_t distance)
1140{
1141 // call from DistancetoPrimitiveHelper (only if highlight is enable)
1142
1143 const Int_t kHighlightRange = 50; // maybe as fgHighlightRange and Set/Get
1144 static Int_t distanceOld = kHighlightRange;
1145 if (gHighlightPoint == -1) distanceOld = kHighlightRange; // reset
1146
1147 if ((distance < kHighlightRange) && (distance < distanceOld)) { // closest point
1148 if ((gHighlightPoint != hpoint) || (gHighlightGraph != theGraph)) { // was changed
1149 // Info("HighlightPoint", "graph: %p\tpoint: %d", (void *)theGraph, hpoint);
1150 gHighlightPoint = hpoint;
1151 gHighlightGraph = theGraph;
1152
1153 // paint highlight point as marker (recursive calls PaintHighlightPoint)
1154 gPad->Modified(kTRUE);
1155 gPad->Update();
1156
1157 // emit Highlighted() signal
1158 if (gPad->GetCanvas()) gPad->GetCanvas()->Highlighted(gPad, theGraph, gHighlightPoint, -1);
1159 }
1160 }
1161 if (gHighlightGraph == theGraph) distanceOld = distance;
1162}
1163
1164
1165////////////////////////////////////////////////////////////////////////////////
1166/// Paint highlight point as TMarker object (open circle)
1167
1169{
1170 // call from PaintGraphSimple
1171
1172 if ((!theGraph->IsHighlight()) || (gHighlightGraph != theGraph)) return;
1173
1174 Double_t hx, hy;
1175 if (theGraph->GetPoint(gHighlightPoint, hx, hy) == -1) {
1176 // special case, e.g. after interactive remove last point
1177 if (gHighlightMarker) gHighlightMarker.reset(nullptr);
1178 return;
1179 }
1180 // testing specific possibility (after zoom, draw with "same", log, etc.)
1181 Double_t uxmin = gPad->GetUxmin();
1182 Double_t uxmax = gPad->GetUxmax();
1183 Double_t uymin = gPad->GetUymin();
1184 Double_t uymax = gPad->GetUymax();
1185 if (gPad->GetLogx()) {
1186 uxmin = TMath::Power(10.0, uxmin);
1187 uxmax = TMath::Power(10.0, uxmax);
1188 }
1189 if (gPad->GetLogy()) {
1190 uymin = TMath::Power(10.0, uymin);
1191 uymax = TMath::Power(10.0, uymax);
1192 }
1193 if ((hx < uxmin) || (hx > uxmax)) return;
1194 if ((hy < uymin) || (hy > uymax)) return;
1195
1196 if (!gHighlightMarker) {
1197 gHighlightMarker = std::make_unique<TMarker>(hx, hy, 24);
1199 }
1200 gHighlightMarker->SetX(hx);
1201 gHighlightMarker->SetY(hy);
1202 gHighlightMarker->SetMarkerSize(theGraph->GetMarkerSize()*2.0);
1203 if (gHighlightMarker->GetMarkerSize() < 1.0) gHighlightMarker->SetMarkerSize(1.0); // always visible
1204 gHighlightMarker->SetMarkerColor(theGraph->GetMarkerColor());
1205 gHighlightMarker->Paint();
1206 // Info("PaintHighlightPoint", "graph: %p\tpoint: %d",
1207 // (void *)gHighlightGraph, gHighlightPoint);
1208}
1209
1210
1211////////////////////////////////////////////////////////////////////////////////
1212/// Paint a any kind of TGraph
1213
1215{
1216
1217 char chopt[80];
1218 strlcpy(chopt,option,80);
1219
1220 if (theGraph) {
1221 char *l1 = strstr(chopt,"pfc"); // Automatic Fill Color
1222 char *l2 = strstr(chopt,"plc"); // Automatic Line Color
1223 char *l3 = strstr(chopt,"pmc"); // Automatic Marker Color
1224 if (l1 || l2 || l3) {
1225 Int_t i = gPad->NextPaletteColor();
1226 if (l1) {memcpy(l1," ",3); theGraph->SetFillColor(i);}
1227 if (l2) {memcpy(l2," ",3); theGraph->SetLineColor(i);}
1228 if (l3) {memcpy(l3," ",3); theGraph->SetMarkerColor(i);}
1229 }
1230
1232
1233 char *l4 = strstr(chopt,"rx"); // Reverse graph along X axis
1234 char *l5 = strstr(chopt,"ry"); // Reverse graph along Y axis
1235
1236 if (l4 || l5) {
1237 PaintGraphReverse(theGraph,chopt);
1238 return;
1239 }
1240
1241 if (theGraph->InheritsFrom(TGraphBentErrors::Class())) {
1242 PaintGraphBentErrors(theGraph,chopt);
1243 } else if (theGraph->InheritsFrom(TGraphQQ::Class())) {
1244 PaintGraphQQ(theGraph,chopt);
1245 } else if (theGraph->InheritsFrom(TGraphAsymmErrors::Class())) {
1246 PaintGraphAsymmErrors(theGraph,chopt);
1247 } else if (theGraph->InheritsFrom(TGraphMultiErrors::Class())) {
1248 PaintGraphMultiErrors(theGraph,chopt);
1249 } else if (theGraph->InheritsFrom(TGraphErrors::Class())) {
1250 if (theGraph->InheritsFrom(TGraphPolar::Class())) {
1251 PaintGraphPolar(theGraph,chopt);
1252 } else {
1253 PaintGraphErrors(theGraph,chopt);
1254 }
1255 } else {
1256 PaintGraphSimple(theGraph,chopt);
1257 }
1258
1259 // Paint the fit parameters if needed.
1260 TF1 *fit = nullptr;
1261 TList *functions = theGraph->GetListOfFunctions();
1262 TObject *f;
1263 if (functions) {
1264 f = (TF1*)functions->First();
1265 if (f) {
1266 if (f->InheritsFrom(TF1::Class())) fit = (TF1*)f;
1267 }
1268 TIter next(functions);
1269 while ((f = (TObject*) next())) {
1270 if (f->InheritsFrom(TF1::Class())) {
1271 fit = (TF1*)f;
1272 break;
1273 }
1274 }
1275 }
1276 if (fit && !theGraph->TestBit(TGraph::kNoStats)) PaintStats(theGraph, fit);
1277
1278 }
1279}
1280
1281
1282////////////////////////////////////////////////////////////////////////////////
1283/// [Control function to draw a graph.](\ref GrP1)
1284
1285void TGraphPainter::PaintGraph(TGraph *theGraph, Int_t npoints, const Double_t *x, const Double_t *y, Option_t *chopt)
1286{
1287
1288 if (theGraph->InheritsFrom("TGraphPolar"))
1289 gPad->PushSelectableObject(theGraph);
1290
1291 Int_t optionLine , optionAxis , optionCurve , optionStar , optionMark;
1292 Int_t optionBar , optionR , optionOne , optionE;
1293 Int_t optionFill , optionZ , optionCurveFill, optionIAxis;
1294 Int_t i, npt, nloop;
1295 Int_t drawtype=0;
1296 Double_t xlow, xhigh, ylow, yhigh;
1297 Double_t barxmin, barxmax, barymin, barymax;
1298 Double_t uxmin, uxmax;
1299 Double_t x1, xn, y1, yn;
1300 Double_t dbar, bdelta;
1301 Int_t theNpoints = theGraph->GetN();
1302
1303 if (npoints <= 0) {
1304 Error("PaintGraph", "illegal number of points (%d)", npoints);
1305 return;
1306 }
1307 TString opt = chopt;
1308 opt.ToUpper();
1309 opt.ReplaceAll("SAME","");
1310
1311 if (opt.Contains("L")) optionLine = 1; else optionLine = 0;
1312 if (opt.Contains("A")) optionAxis = 1; else optionAxis = 0;
1313 if (opt.Contains("C")) optionCurve = 1; else optionCurve = 0;
1314 if (opt.Contains("*")) optionStar = 1; else optionStar = 0;
1315 if (opt.Contains("P")) optionMark = 1; else optionMark = 0;
1316 if (opt.Contains("B")) optionBar = 1; else optionBar = 0;
1317 if (opt.Contains("R")) optionR = 1; else optionR = 0;
1318 if (opt.Contains("1")) optionOne = 1; else optionOne = 0;
1319 if (opt.Contains("F")) optionFill = 1; else optionFill = 0;
1320 if (opt.Contains("I")) optionIAxis = 1; else optionIAxis = 0;
1321 if (opt.Contains("2") || opt.Contains("3") ||
1322 opt.Contains("4") || opt.Contains("5")) optionE = 1; else optionE = 0;
1323 optionZ = 0;
1324
1325 // If no "drawing" option is selected and if chopt<>' ' nothing is done.
1326 if (optionLine+optionFill+optionCurve+optionStar+optionMark+optionBar+optionE == 0) {
1327 if (!chopt[0]) optionLine=1;
1328 else return;
1329 }
1330
1331 if (optionStar) theGraph->SetMarkerStyle(3);
1332
1333 optionCurveFill = 0;
1334 if (optionCurve && optionFill) {
1335 optionCurveFill = 1;
1336 optionFill = 0;
1337 }
1338
1339 // Draw the Axis.
1340 Double_t rwxmin,rwxmax, rwymin, rwymax, maximum, minimum, dx, dy;
1341 if (optionAxis) {
1342 if (theGraph->GetHistogram()) {
1343 rwxmin = gPad->GetUxmin();
1344 rwxmax = gPad->GetUxmax();
1345 rwymin = gPad->GetUymin();
1346 rwymax = gPad->GetUymax();
1347 minimum = theGraph->GetHistogram()->GetMinimumStored();
1348 maximum = theGraph->GetHistogram()->GetMaximumStored();
1349 if (minimum == -1111) { //this can happen after unzooming
1350 minimum = theGraph->GetHistogram()->GetYaxis()->GetXmin();
1351 theGraph->GetHistogram()->SetMinimum(minimum);
1352 }
1353 if (maximum == -1111) {
1354 maximum = theGraph->GetHistogram()->GetYaxis()->GetXmax();
1355 theGraph->GetHistogram()->SetMaximum(maximum);
1356 }
1357 uxmin = gPad->PadtoX(rwxmin);
1358 uxmax = gPad->PadtoX(rwxmax);
1359 } else {
1360
1361 theGraph->ComputeRange(rwxmin, rwymin, rwxmax, rwymax); //this is redefined in TGraphErrors
1362
1363 if (rwxmin == rwxmax) rwxmax += 1.;
1364 if (rwymin == rwymax) rwymax += 1.;
1365 dx = 0.1*(rwxmax-rwxmin);
1366 dy = 0.1*(rwymax-rwymin);
1367 uxmin = rwxmin - dx;
1368 uxmax = rwxmax + dx;
1369 minimum = rwymin - dy;
1370 maximum = rwymax + dy;
1371 }
1372 if (theGraph->GetMinimum() != -1111) rwymin = minimum = theGraph->GetMinimum();
1373 if (theGraph->GetMaximum() != -1111) rwymax = maximum = theGraph->GetMaximum();
1374 if (uxmin < 0 && rwxmin >= 0) uxmin = 0.9*rwxmin;
1375 if (uxmax > 0 && rwxmax <= 0) {
1376 if (gPad->GetLogx()) uxmax = 1.1*rwxmax;
1377 else uxmax = 0;
1378 }
1379 if (minimum < 0 && rwymin >= 0) minimum = 0.9*rwymin;
1380 if (maximum > 0 && rwymax <= 0) {
1381 //if(gPad->GetLogy()) maximum = 1.1*rwymax;
1382 //else maximum = 0;
1383 }
1384 if (minimum <= 0 && gPad->GetLogy()) minimum = 0.001*maximum;
1385 if (uxmin <= 0 && gPad->GetLogx()) {
1386 if (uxmax > 1000) uxmin = 1;
1387 else uxmin = 0.001*uxmax;
1388 }
1389 rwymin = minimum;
1390 rwymax = maximum;
1391
1392 // Create a temporary histogram and fill each bin with the
1393 // function value.
1394 char chopth[8] = " ";
1395 if (strstr(chopt,"x+")) strncat(chopth, "x+",3);
1396 if (strstr(chopt,"y+")) strncat(chopth, "y+",3);
1397 if (optionIAxis) strncat(chopth, "A",2);
1398 if (!theGraph->GetHistogram()) {
1399 // the graph is created with at least as many bins as there are
1400 // points to permit zooming on the full range.
1401 rwxmin = uxmin;
1402 rwxmax = uxmax;
1403 npt = 100;
1404 if (theNpoints > npt) npt = theNpoints;
1405 TH1F *h = new TH1F(Form("%s_h",GetName()),GetTitle(),npt,rwxmin,rwxmax);
1406 theGraph->SetHistogram(h);
1407 if (!theGraph->GetHistogram()) return;
1408 theGraph->GetHistogram()->SetMinimum(rwymin);
1409 theGraph->GetHistogram()->SetMaximum(rwymax);
1410 theGraph->GetHistogram()->GetYaxis()->SetLimits(rwymin,rwymax);
1411 theGraph->GetHistogram()->SetBit(TH1::kNoStats);
1412 theGraph->GetHistogram()->SetDirectory(0);
1413 theGraph->GetHistogram()->Sumw2(kFALSE);
1414 theGraph->GetHistogram()->Paint(chopth); // Draw histogram axis, title and grid
1415 } else {
1416 if (gPad->GetLogy()) {
1417 theGraph->GetHistogram()->SetMinimum(rwymin);
1418 theGraph->GetHistogram()->SetMaximum(rwymax);
1419 theGraph->GetHistogram()->GetYaxis()->SetLimits(rwymin,rwymax);
1420 }
1421 theGraph->GetHistogram()->Sumw2(kFALSE);
1422 theGraph->GetHistogram()->Paint(chopth); // Draw histogram axis, title and grid
1423 }
1424 }
1425
1426 // Set Clipping option
1428
1429 rwxmin = gPad->GetUxmin();
1430 rwxmax = gPad->GetUxmax();
1431 rwymin = gPad->GetUymin();
1432 rwymax = gPad->GetUymax();
1433 uxmin = gPad->PadtoX(rwxmin);
1434 uxmax = gPad->PadtoX(rwxmax);
1435 if (theGraph->GetHistogram() && !theGraph->InheritsFrom("TGraphPolar")) {
1436 maximum = theGraph->GetHistogram()->GetMaximum();
1437 minimum = theGraph->GetHistogram()->GetMinimum();
1438 } else {
1439 maximum = gPad->PadtoY(rwymax);
1440 minimum = gPad->PadtoY(rwymin);
1441 }
1442
1443 // Set attributes
1444 theGraph->TAttLine::Modify();
1445 theGraph->TAttFill::Modify();
1446 theGraph->TAttMarker::Modify();
1447
1448 // Draw the graph with a polyline or a fill area
1449 gxwork.resize(2*npoints+10);
1450 gywork.resize(2*npoints+10);
1451 gxworkl.resize(2*npoints+10);
1452 gyworkl.resize(2*npoints+10);
1453
1454 if (optionLine || optionFill) {
1455 x1 = x[0];
1456 xn = x[npoints-1];
1457 y1 = y[0];
1458 yn = y[npoints-1];
1459 nloop = npoints;
1460 if (optionFill && (xn != x1 || yn != y1)) nloop++;
1461 npt = 0;
1462 for (i=1;i<=nloop;i++) {
1463 if (i > npoints) {
1464 gxwork[npt] = gxwork[0]; gywork[npt] = gywork[0];
1465 } else {
1466 gxwork[npt] = x[i-1]; gywork[npt] = y[i-1];
1467 npt++;
1468 }
1469 if (i == nloop) {
1470 ComputeLogs(npt, optionZ);
1471 Int_t bord = gStyle->GetDrawBorder();
1472 if (optionR) {
1473 if (optionFill) {
1474 gPad->PaintFillArea(npt,gyworkl.data(),gxworkl.data());
1475 if (bord) gPad->PaintPolyLine(npt,gyworkl.data(),gxworkl.data());
1476 }
1477 if (optionLine) {
1478 if (TMath::Abs(theGraph->GetLineWidth())>99) PaintPolyLineHatches(theGraph, npt, gyworkl.data(), gxworkl.data());
1479 gPad->PaintPolyLine(npt,gyworkl.data(),gxworkl.data());
1480 }
1481 } else {
1482 if (optionFill) {
1483 gPad->PaintFillArea(npt,gxworkl.data(),gyworkl.data());
1484 if (bord) gPad->PaintPolyLine(npt,gxworkl.data(),gyworkl.data());
1485 }
1486 if (optionLine) {
1487 if (TMath::Abs(theGraph->GetLineWidth())>99) PaintPolyLineHatches(theGraph, npt, gxworkl.data(), gyworkl.data());
1488 gPad->PaintPolyLine(npt,gxworkl.data(),gyworkl.data());
1489 }
1490 }
1491 gxwork[0] = gxwork[npt-1]; gywork[0] = gywork[npt-1];
1492 npt = 1;
1493 }
1494 }
1495 }
1496
1497 // Draw the graph with a smooth Curve. Smoothing via Smooth
1498 if (optionCurve) {
1499 x1 = x[0];
1500 xn = x[npoints-1];
1501 y1 = y[0];
1502 yn = y[npoints-1];
1503 drawtype = 1;
1504 nloop = npoints;
1505 if (optionCurveFill) {
1506 drawtype += 1000;
1507 if (xn != x1 || yn != y1) nloop++;
1508 }
1509 if (!optionR) {
1510 npt = 0;
1511 for (i=1;i<=nloop;i++) {
1512 if (i > npoints) {
1513 gxwork[npt] = gxwork[0]; gywork[npt] = gywork[0];
1514 } else {
1515 gxwork[npt] = x[i-1]; gywork[npt] = y[i-1];
1516 npt++;
1517 }
1518 ComputeLogs(npt, optionZ);
1519 if (gyworkl[npt-1] < rwymin || gyworkl[npt-1] > rwymax) {
1520 if (npt > 2) {
1521 ComputeLogs(npt, optionZ);
1522 Smooth(theGraph, npt,gxworkl.data(),gyworkl.data(),drawtype);
1523 }
1524 gxwork[0] = gxwork[npt-1]; gywork[0] = gywork[npt-1];
1525 npt=1;
1526 continue;
1527 }
1528 }
1529 if (npt > 1) {
1530 ComputeLogs(npt, optionZ);
1531 Smooth(theGraph, npt,gxworkl.data(),gyworkl.data(),drawtype);
1532 }
1533 } else {
1534 drawtype += 10;
1535 npt = 0;
1536 for (i=1;i<=nloop;i++) {
1537 if (i > npoints) {
1538 gxwork[npt] = gxwork[0]; gywork[npt] = gywork[0];
1539 } else {
1540 if (y[i-1] < minimum || y[i-1] > maximum) continue;
1541 if (x[i-1] < uxmin || x[i-1] > uxmax) continue;
1542 gxwork[npt] = x[i-1]; gywork[npt] = y[i-1];
1543 npt++;
1544 }
1545 ComputeLogs(npt, optionZ);
1546 if (gxworkl[npt-1] < rwxmin || gxworkl[npt-1] > rwxmax) {
1547 if (npt > 2) {
1548 ComputeLogs(npt, optionZ);
1549 Smooth(theGraph, npt,gxworkl.data(),gyworkl.data(),drawtype);
1550 }
1551 gxwork[0] = gxwork[npt-1]; gywork[0] = gywork[npt-1];
1552 npt=1;
1553 continue;
1554 }
1555 }
1556 if (npt > 1) {
1557 ComputeLogs(npt, optionZ);
1558 Smooth(theGraph, npt,gxworkl.data(),gyworkl.data(),drawtype);
1559 }
1560 }
1561 }
1562
1563 // Draw the graph with a '*' on every points
1564 if (optionStar) {
1565 theGraph->SetMarkerStyle(3);
1566 npt = 0;
1567 for (i=1;i<=npoints;i++) {
1568 gxwork[npt] = x[i-1]; gywork[npt] = y[i-1];
1569 npt++;
1570 if (i == npoints) {
1571 ComputeLogs(npt, optionZ);
1572 if (optionR) gPad->PaintPolyMarker(npt,gyworkl.data(),gxworkl.data());
1573 else gPad->PaintPolyMarker(npt,gxworkl.data(),gyworkl.data());
1574 npt = 0;
1575 }
1576 }
1577 }
1578
1579 // Draw the graph with the current polymarker on every points
1580 if (optionMark) {
1581 npt = 0;
1582 for (i=1;i<=npoints;i++) {
1583 gxwork[npt] = x[i-1]; gywork[npt] = y[i-1];
1584 npt++;
1585 if (i == npoints) {
1586 ComputeLogs(npt, optionZ);
1587 if (optionR) gPad->PaintPolyMarker(npt,gyworkl.data(),gxworkl.data());
1588 else gPad->PaintPolyMarker(npt,gxworkl.data(),gyworkl.data());
1589 npt = 0;
1590 }
1591 }
1592 }
1593
1594 // Draw the graph as a bar chart
1595 if (optionBar) {
1596 Int_t FillSave = theGraph->GetFillColor();
1597 if(FillSave == gPad->GetFrameFillColor()) {
1598 // make sure the bars' color is different from the frame background
1599 if (gPad->GetFrameFillColor()==1) {
1600 theGraph->SetFillColor(0);
1601 theGraph->TAttFill::Modify();
1602 } else {
1603 theGraph->SetFillColor(1);
1604 theGraph->TAttFill::Modify();
1605 }
1606 }
1607 if (!optionR) {
1608 barxmin = x[0];
1609 barxmax = x[0];
1610 for (i=1;i<npoints;i++) {
1611 if (x[i] < barxmin) barxmin = x[i];
1612 if (x[i] > barxmax) barxmax = x[i];
1613 }
1614 bdelta = (barxmax-barxmin)/Double_t(npoints);
1615 } else {
1616 barymin = y[0];
1617 barymax = y[0];
1618 for (i=1;i<npoints;i++) {
1619 if (y[i] < barymin) barymin = y[i];
1620 if (y[i] > barymax) barymax = y[i];
1621 }
1622 bdelta = (barymax-barymin)/Double_t(npoints);
1623 }
1624 dbar = 0.5*bdelta*gStyle->GetBarWidth();
1625 if (!optionR) {
1626 for (i=1;i<=npoints;i++) {
1627 xlow = x[i-1] - dbar;
1628 xhigh = x[i-1] + dbar;
1629 yhigh = y[i-1];
1630 if (xlow < uxmin && xhigh < uxmin) continue;
1631 if (xhigh > uxmax && xlow > uxmax) continue;
1632 if (xlow < uxmin) xlow = uxmin;
1633 if (xhigh > uxmax) xhigh = uxmax;
1634 if (!optionOne) ylow = TMath::Max((Double_t)0,gPad->GetUymin());
1635 else ylow = gPad->GetUymin();
1636 gxwork[0] = xlow;
1637 gywork[0] = ylow;
1638 gxwork[1] = xhigh;
1639 gywork[1] = yhigh;
1640 ComputeLogs(2, optionZ);
1641 if (gyworkl[0] < gPad->GetUymin()) gyworkl[0] = gPad->GetUymin();
1642 if (gyworkl[1] < gPad->GetUymin()) continue;
1643 if (gyworkl[1] > gPad->GetUymax()) gyworkl[1] = gPad->GetUymax();
1644 if (gyworkl[0] > gPad->GetUymax()) continue;
1645
1646 gPad->PaintBox(gxworkl[0],gyworkl[0],gxworkl[1],gyworkl[1]);
1647 }
1648 } else {
1649 for (i=1;i<=npoints;i++) {
1650 xhigh = x[i-1];
1651 ylow = y[i-1] - dbar;
1652 yhigh = y[i-1] + dbar;
1653 xlow = TMath::Max((Double_t)0, gPad->GetUxmin());
1654 gxwork[0] = xlow;
1655 gywork[0] = ylow;
1656 gxwork[1] = xhigh;
1657 gywork[1] = yhigh;
1658 ComputeLogs(2, optionZ);
1659 gPad->PaintBox(gxworkl[0],gyworkl[0],gxworkl[1],gyworkl[1]);
1660 }
1661 }
1662 theGraph->SetFillColor(FillSave);
1663 theGraph->TAttFill::Modify();
1664 }
1665 gPad->ResetBit(TGraph::kClipFrame);
1666
1667 gxwork.clear();
1668 gywork.clear();
1669 gxworkl.clear();
1670 gyworkl.clear();
1671}
1672
1673
1674////////////////////////////////////////////////////////////////////////////////
1675/// This is a service method used by `THistPainter`
1676/// to paint 1D histograms. It is not used to paint TGraph.
1677///
1678/// Input parameters:
1679///
1680/// - npoints : Number of points in X or in Y.
1681/// - x[npoints] or x[0] : x coordinates or (xmin,xmax).
1682/// - y[npoints] or y[0] : y coordinates or (ymin,ymax).
1683/// - chopt : Option.
1684///
1685/// The aspect of the histogram is done according to the value of the chopt.
1686///
1687/// | Option | Description |
1688/// |--------|-----------------------------------------------------------------|
1689/// |"R" | Graph is drawn horizontally, parallel to X axis. (default is vertically, parallel to Y axis).If option R is selected the user must give 2 values for Y (y[0]=YMIN and y[1]=YMAX) or N values for X, one for each channel. Otherwise the user must give, N values for Y, one for each channel or 2 values for X (x[0]=XMIN and x[1]=XMAX) |
1690/// |"L" | A simple polyline between every points is drawn.|
1691/// |"H" | An Histogram with equidistant bins is drawn as a polyline.|
1692/// |"F" | An histogram with equidistant bins is drawn as a fill area. Contour is not drawn unless chopt='H' is also selected..|
1693/// |"N" | Non equidistant bins (default is equidistant). If N is the number of channels array X and Y must be dimensioned as follow: If option R is not selected (default) then the user must give (N+1) values for X (limits of channels) or N values for Y, one for each channel. Otherwise the user must give (N+1) values for Y (limits of channels). or N values for X, one for each channel |
1694/// |"F1" | Idem as 'F' except that fill area base line is the minimum of the pad instead of Y=0.|
1695/// |"F2" | Draw a Fill area polyline connecting the center of bins|
1696/// |"C" | A smooth Curve is drawn.|
1697/// |"*" | A Star is plotted at the center of each bin.|
1698/// |"P" | Idem with the current marker.|
1699/// |"P0" | Idem with the current marker. Empty bins also drawn.|
1700/// |"B" | A Bar chart with equidistant bins is drawn as fill areas (Contours are drawn).|
1701/// |"][" | "Cutoff" style. When this option is selected together with H option, the first and last vertical lines of the histogram are not drawn.|
1702
1703void TGraphPainter::PaintGrapHist(TGraph *theGraph, Int_t npoints, const Double_t *x,
1704 const Double_t *y, Option_t *chopt)
1705{
1706
1707 const char *where = "PaintGrapHist";
1708
1709 Int_t optionLine , optionAxis , optionCurve, optionStar, optionMark;
1710 Int_t optionBar , optionRot , optionOne , optionOff ;
1711 Int_t optionFill , optionZ;
1712 Int_t optionHist , optionBins , optionMarker;
1713 Int_t i, j, npt;
1714 Int_t drawtype=0, drawborder, drawbordersav;
1715 Double_t xlow, xhigh, ylow, yhigh;
1717 Double_t dbar, offset, wminstep;
1718 Double_t delta = 0;
1719 Double_t ylast = 0;
1720 Double_t xi, xi1, xj, xj1, yi1, yi, yj, yj1, xwmin, ywmin;
1721 Int_t first, last, nbins;
1722 Int_t fillarea;
1723
1724 char choptaxis[10] = " ";
1725
1726 if (npoints <= 0) {
1727 Error(where, "illegal number of points (%d)", npoints);
1728 return;
1729 }
1730 TString opt = chopt;
1731 opt.ToUpper();
1732 if (opt.Contains("H")) optionHist = 1; else optionHist = 0;
1733 if (opt.Contains("F")) optionFill = 1; else optionFill = 0;
1734 if (opt.Contains("C")) optionCurve= 1; else optionCurve= 0;
1735 if (opt.Contains("*")) optionStar = 1; else optionStar = 0;
1736 if (opt.Contains("R")) optionRot = 1; else optionRot = 0;
1737 if (opt.Contains("1")) optionOne = 1; else optionOne = 0;
1738 if (opt.Contains("B")) optionBar = 1; else optionBar = 0;
1739 if (opt.Contains("N")) optionBins = 1; else optionBins = 0;
1740 if (opt.Contains("L")) optionLine = 1; else optionLine = 0;
1741 if (opt.Contains("P")) optionMark = 1; else optionMark = 0;
1742 if (opt.Contains("A")) optionAxis = 1; else optionAxis = 0;
1743 if (opt.Contains("][")) optionOff = 1; else optionOff = 0;
1744 if (opt.Contains("P0")) optionMark = 10;
1745
1746 Int_t optionFill2 = 0;
1747 if (opt.Contains("F") && opt.Contains("2")) {
1748 optionFill = 0; optionFill2 = 1;
1749 }
1750
1751 // Set Clipping option
1752 Option_t *noClip;
1753 if (theGraph->TestBit(TGraph::kClipFrame)) noClip = "";
1754 else noClip = "C";
1756
1757 optionZ = 1;
1758
1759 if (optionStar) theGraph->SetMarkerStyle(3);
1760
1761 first = 1;
1762 last = npoints;
1763 nbins = last - first + 1;
1764
1765 // Draw the Axis with a fixed number of division: 510
1766
1767 Double_t baroffset = gStyle->GetBarOffset();
1768 Double_t barwidth = gStyle->GetBarWidth();
1769 Double_t rwxmin = gPad->GetUxmin();
1770 Double_t rwxmax = gPad->GetUxmax();
1771 Double_t rwymin = gPad->GetUymin();
1772 Double_t rwymax = gPad->GetUymax();
1773 Double_t uxmin = gPad->PadtoX(rwxmin);
1774 Double_t uxmax = gPad->PadtoX(rwxmax);
1775 Double_t rounding = (uxmax-uxmin)*1.e-5;
1776 drawborder = gStyle->GetDrawBorder();
1777 if (optionAxis) {
1778 Int_t nx1, nx2, ndivx, ndivy, ndiv;
1779 choptaxis[0] = 0;
1780 Double_t rwmin = rwxmin;
1781 Double_t rwmax = rwxmax;
1782 ndivx = gStyle->GetNdivisions("X");
1783 ndivy = gStyle->GetNdivisions("Y");
1784 if (ndivx > 1000) {
1785 nx2 = ndivx/100;
1786 nx1 = TMath::Max(1, ndivx%100);
1787 ndivx = 100*nx2 + Int_t(Double_t(nx1)*gPad->GetAbsWNDC());
1788 }
1789 ndiv =TMath::Abs(ndivx);
1790 // coverity [Calling risky function]
1791 if (ndivx < 0) strlcat(choptaxis, "N",10);
1792 if (gPad->GetGridx()) {
1793 // coverity [Calling risky function]
1794 strlcat(choptaxis, "W",10);
1795 }
1796 if (gPad->GetLogx()) {
1797 rwmin = TMath::Power(10,rwxmin);
1798 rwmax = TMath::Power(10,rwxmax);
1799 // coverity [Calling risky function]
1800 strlcat(choptaxis, "G",10);
1801 }
1802 TGaxis axis;
1803 axis.SetLineColor(gStyle->GetAxisColor("X"));
1804 axis.SetTextColor(gStyle->GetLabelColor("X"));
1805 axis.SetTextFont(gStyle->GetLabelFont("X"));
1806 axis.SetLabelSize(gStyle->GetLabelSize("X"));
1808 axis.SetTickSize(gStyle->GetTickLength("X"));
1809
1810 axis.PaintAxis(rwxmin,rwymin,rwxmax,rwymin,rwmin,rwmax,ndiv,choptaxis);
1811
1812 choptaxis[0] = 0;
1813 rwmin = rwymin;
1814 rwmax = rwymax;
1815 if (ndivy < 0) {
1816 nx2 = ndivy/100;
1817 nx1 = TMath::Max(1, ndivy%100);
1818 ndivy = 100*nx2 + Int_t(Double_t(nx1)*gPad->GetAbsHNDC());
1819 // coverity [Calling risky function]
1820 strlcat(choptaxis, "N",10);
1821 }
1822 ndiv =TMath::Abs(ndivy);
1823 if (gPad->GetGridy()) {
1824 // coverity [Calling risky function]
1825 strlcat(choptaxis, "W",10);
1826 }
1827 if (gPad->GetLogy()) {
1828 rwmin = TMath::Power(10,rwymin);
1829 rwmax = TMath::Power(10,rwymax);
1830 // coverity [Calling risky function]
1831 strlcat(choptaxis,"G",10);
1832 }
1833 axis.SetLineColor(gStyle->GetAxisColor("Y"));
1834 axis.SetTextColor(gStyle->GetLabelColor("Y"));
1835 axis.SetTextFont(gStyle->GetLabelFont("Y"));
1836 axis.SetLabelSize(gStyle->GetLabelSize("Y"));
1838 axis.SetTickSize(gStyle->GetTickLength("Y"));
1839
1840 axis.PaintAxis(rwxmin,rwymin,rwxmin,rwymax,rwmin,rwmax,ndiv,choptaxis);
1841 }
1842
1843
1844 // Set attributes
1845 theGraph->TAttLine::Modify();
1846 theGraph->TAttFill::Modify();
1847 theGraph->TAttMarker::Modify();
1848
1849 // Min-Max scope
1850
1851 if (!optionRot) {wmin = x[0]; wmax = x[1];}
1852 else {wmin = y[0]; wmax = y[1];}
1853
1854 if (!optionBins) delta = (wmax - wmin)/ Double_t(nbins);
1855
1856 Int_t fwidth = gPad->GetFrameLineWidth();
1857 TFrame *frame = gPad->GetFrame();
1858 if (frame) fwidth = frame->GetLineWidth();
1859 if (optionOff) fwidth = 1;
1860 Double_t dxframe = gPad->AbsPixeltoX(fwidth/2) - gPad->AbsPixeltoX(0);
1861 Double_t vxmin = gPad->PadtoX(gPad->GetUxmin() + dxframe);
1862 Double_t vxmax = gPad->PadtoX(gPad->GetUxmax() - dxframe);
1863 Double_t dyframe = -gPad->AbsPixeltoY(fwidth/2) + gPad->AbsPixeltoY(0);
1864 Double_t vymin = gPad->GetUymin() + dyframe; //y already in log scale
1865 vxmin = TMath::Max(vxmin,wmin);
1866 vxmax = TMath::Min(vxmax,wmax);
1867
1868 // Draw the histogram with a fill area
1869
1870 gxwork.resize(2*npoints+10);
1871 gywork.resize(2*npoints+10);
1872 gxworkl.resize(2*npoints+10);
1873 gyworkl.resize(2*npoints+10);
1874
1875 if (optionFill && !optionCurve) {
1876 fillarea = kTRUE;
1877 if (!optionRot) {
1878 gxwork[0] = vxmin;
1879 if (!optionOne) gywork[0] = TMath::Min(TMath::Max((Double_t)0,gPad->GetUymin())
1880 ,gPad->GetUymax());
1881 else gywork[0] = gPad->GetUymin();
1882 npt = 2;
1883 for (j=first; j<=last;j++) {
1884 if (!optionBins) {
1885 gxwork[npt-1] = gxwork[npt-2];
1886 gxwork[npt] = wmin+((j-first+1)*delta);
1887 if (gxwork[npt] < gxwork[0]) gxwork[npt] = gxwork[0];
1888
1889 } else {
1890 xj1 = x[j]; xj = x[j-1];
1891 if (xj1 < xj) {
1892 if (j != last) Error(where, "X must be in increasing order");
1893 else Error(where, "X must have N+1 values with option N");
1894 goto do_cleanup;
1895 }
1896 gxwork[npt-1] = x[j-1]; gxwork[npt] = x[j];
1897 }
1898 gywork[npt-1] = y[j-1];
1899 gywork[npt] = y[j-1];
1900 if (gywork[npt] < vymin) {gywork[npt] = vymin; gywork[npt-1] = vymin;}
1901 if ((gxwork[npt-1] >= uxmin-rounding && gxwork[npt-1] <= uxmax+rounding) ||
1902 (gxwork[npt] >= uxmin-rounding && gxwork[npt] <= uxmax+rounding)) npt += 2;
1903 if (j == last) {
1904 gxwork[npt-1] = gxwork[npt-2];
1905 gywork[npt-1] = gywork[0];
1906 //make sure that the fill area does not overwrite the frame
1907 //take into account the frame line width
1908 if (gxwork[0 ] < vxmin) {gxwork[0 ] = vxmin; gxwork[1 ] = vxmin;}
1909 if (gywork[0] < vymin) {gywork[0] = vymin; gywork[npt-1] = vymin;}
1910
1911 //transform to log ?
1912 ComputeLogs(npt, optionZ);
1913 gPad->PaintFillArea(npt,gxworkl.data(),gyworkl.data());
1914 if (drawborder) {
1915 if (!fillarea) gyworkl[0] = ylast;
1916 gPad->PaintPolyLine(npt-1,gxworkl.data(),gyworkl.data(),noClip);
1917 }
1918 continue;
1919 }
1920 } //endfor (j=first; j<=last;j++) {
1921 } else {
1922 gywork[0] = wmin;
1923 if (!optionOne) gxwork[0] = TMath::Max((Double_t)0,gPad->GetUxmin());
1924 else gxwork[0] = gPad->GetUxmin();
1925 npt = 2;
1926 for (j=first; j<=last;j++) {
1927 if (!optionBins) {
1928 gywork[npt-1] = gywork[npt-2];
1929 gywork[npt] = wmin+((j-first+1)*delta);
1930 } else {
1931 yj1 = y[j]; yj = y[j-1];
1932 if (yj1 < yj) {
1933 if (j != last) Error(where, "Y must be in increasing order");
1934 else Error(where, "Y must have N+1 values with option N");
1935 return;
1936 }
1937 gywork[npt-1] = y[j-1]; gywork[npt] = y[j];
1938 }
1939 gxwork[npt-1] = x[j-1]; gxwork[npt] = x[j-1];
1940 if ((gxwork[npt-1] >= uxmin-rounding && gxwork[npt-1] <= uxmax+rounding) ||
1941 (gxwork[npt] >= uxmin-rounding && gxwork[npt] <= uxmax+rounding)) npt += 2;
1942 if (j == last) {
1943 gywork[npt-1] = gywork[npt-2];
1944 gxwork[npt-1] = gxwork[0];
1945 ComputeLogs(npt, optionZ);
1946 gPad->PaintFillArea(npt,gxworkl.data(),gyworkl.data());
1947 if (drawborder) {
1948 if (!fillarea) gyworkl[0] = ylast;
1949 gPad->PaintPolyLine(npt-1,gxworkl.data(),gyworkl.data(),noClip);
1950 }
1951 continue;
1952 }
1953 } //endfor (j=first; j<=last;j++)
1954 }
1955 theGraph->TAttLine::Modify();
1956 theGraph->TAttFill::Modify();
1957 }
1958
1959 // Draw a standard Histogram (default)
1960
1961 if ((optionHist) || !chopt[0]) {
1962 if (!optionRot) {
1963 gxwork[0] = wmin;
1964 gywork[0] = TMath::Min(TMath::Max((Double_t)0,gPad->GetUymin())
1965 ,gPad->GetUymax());
1966 ywmin = gywork[0];
1967 npt = 2;
1968 for (i=first; i<=last;i++) {
1969 if (!optionBins) {
1970 gxwork[npt-1] = gxwork[npt-2];
1971 gxwork[npt] = wmin+((i-first+1)*delta);
1972 } else {
1973 xi1 = x[i]; xi = x[i-1];
1974 if (xi1 < xi) {
1975 if (i != last) Error(where, "X must be in increasing order");
1976 else Error(where, "X must have N+1 values with option N");
1977 goto do_cleanup;
1978 }
1979 gxwork[npt-1] = x[i-1]; gxwork[npt] = x[i];
1980 }
1981 gywork[npt-1] = y[i-1];
1982 gywork[npt] = y[i-1];
1983 if (gywork[npt] < vymin) {gywork[npt] = vymin; gywork[npt-1] = vymin;}
1984 if ((gxwork[npt-1] >= uxmin-rounding && gxwork[npt-1] <= uxmax+rounding) ||
1985 (gxwork[npt] >= uxmin-rounding && gxwork[npt] <= uxmax+rounding)) npt += 2;
1986 if (i == last) {
1987 gxwork[npt-1] = gxwork[npt-2];
1988 gywork[npt-1] = gywork[0];
1989 //make sure that the fill area does not overwrite the frame
1990 //take into account the frame line width
1991 if (gxwork[0] < vxmin) {gxwork[0] = vxmin; gxwork[1 ] = vxmin;}
1992 if (gywork[0] < vymin) {gywork[0] = vymin; gywork[npt-1] = vymin;}
1993
1994 ComputeLogs(npt, optionZ);
1995
1996 // do not draw the two vertical lines on the edges
1997 Int_t nbpoints = npt-2;
1998 Int_t point1 = 1;
1999
2000 if (optionOff) {
2001 // remove points before the low cutoff
2002 Int_t ip;
2003 for (ip=point1; ip<=nbpoints; ip++) {
2004 if (gyworkl[ip] != ywmin) {
2005 point1 = ip;
2006 break;
2007 }
2008 }
2009 // remove points after the high cutoff
2010 Int_t point2 = nbpoints;
2011 for (ip=point2; ip>=point1; ip--) {
2012 if (gyworkl[ip] != ywmin) {
2013 point2 = ip;
2014 break;
2015 }
2016 }
2017 nbpoints = point2-point1+1;
2018 } else {
2019 // if the 1st or last bin are not on the pad limits the
2020 // the two vertical lines on the edges are added.
2021 if (gxwork[0] > gPad->GetUxmin()) { nbpoints++; point1 = 0; }
2022 if (gxwork[nbpoints] < gPad->GetUxmax()) nbpoints++;
2023 }
2024
2025 gPad->PaintPolyLine(nbpoints,gxworkl.data() + point1, gyworkl.data() + point1, noClip);
2026 continue;
2027 }
2028 } //endfor (i=first; i<=last;i++)
2029 } else {
2030 gywork[0] = wmin;
2031 gxwork[0] = TMath::Max((Double_t)0,gPad->GetUxmin());
2032 xwmin = gxwork[0];
2033 npt = 2;
2034 for (i=first; i<=last;i++) {
2035 if (!optionBins) {
2036 gywork[npt-1] = gywork[npt-2];
2037 gywork[npt] = wmin+((i-first+1)*delta);
2038 } else {
2039 yi1 = y[i]; yi = y[i-1];
2040 if (yi1 < yi) {
2041 if (i != last) Error(where, "Y must be in increasing order");
2042 else Error(where, "Y must have N+1 values with option N");
2043 goto do_cleanup;
2044 }
2045 gywork[npt-1] = y[i-1]; gywork[npt] = y[i];
2046 }
2047 gxwork[npt-1] = x[i-1]; gxwork[npt] = x[i-1];
2048 if ((gxwork[npt-1] >= uxmin-rounding && gxwork[npt-1] <= uxmax+rounding) ||
2049 (gxwork[npt] >= uxmin-rounding && gxwork[npt] <= uxmax+rounding)) npt += 2;
2050 if (i == last) {
2051 gywork[npt-1] = gywork[npt-2];
2052 gxwork[npt-1] = xwmin;
2053 ComputeLogs(npt, optionZ);
2054 gPad->PaintPolyLine(npt,gxworkl.data(),gyworkl.data(),noClip);
2055 continue;
2056 }
2057 } //endfor (i=first; i<=last;i++)
2058 }
2059 }
2060
2061 // Draw the histogram with a smooth Curve.
2062 // The smoothing is done by the method Smooth()
2063
2064 if (optionCurve) {
2065 if (!optionFill) {
2066 drawtype = 1;
2067 } else {
2068 if (!optionOne) drawtype = 2;
2069 else drawtype = 3;
2070 }
2071 if (!optionRot) {
2072 npt = 0;
2073 for (i=first; i<=last;i++) {
2074 npt++;
2075 if (!optionBins) {
2076 gxwork[npt-1] = wmin+(i-first)*delta+0.5*delta;
2077 } else {
2078 xi1 = x[i]; xi = x[i-1];
2079 if (xi1 < xi) {
2080 if (i != last) Error(where, "X must be in increasing order");
2081 else Error(where, "X must have N+1 values with option N");
2082 goto do_cleanup;
2083 }
2084 gxwork[npt-1] = x[i-1] + 0.5*(x[i]-x[i-1]);
2085 }
2086 if (gxwork[npt-1] < uxmin || gxwork[npt-1] > uxmax) {
2087 npt--;
2088 continue;
2089 }
2090 gywork[npt-1] = y[i-1];
2091 ComputeLogs(npt, optionZ);
2092 if ((gyworkl[npt-1] < rwymin) || (gyworkl[npt-1] > rwymax)) {
2093 if (npt > 2) {
2094 ComputeLogs(npt, optionZ);
2095 Smooth(theGraph, npt,gxworkl.data(),gyworkl.data(),drawtype);
2096 }
2097 gxwork[0] = gxwork[npt-1];
2098 gywork[0] = gywork[npt-1];
2099 npt = 1;
2100 continue;
2101 }
2102 if (npt >= fgMaxPointsPerLine) {
2104 Smooth(theGraph, fgMaxPointsPerLine,gxworkl.data(),gyworkl.data(),drawtype);
2105 gxwork[0] = gxwork[npt-1];
2106 gywork[0] = gywork[npt-1];
2107 npt = 1;
2108 }
2109 } //endfor (i=first; i<=last;i++)
2110 if (npt > 1) {
2111 ComputeLogs(npt, optionZ);
2112 Smooth(theGraph, npt,gxworkl.data(),gyworkl.data(),drawtype);
2113 }
2114 } else {
2115 drawtype = drawtype+10;
2116 npt = 0;
2117 for (i=first; i<=last;i++) {
2118 npt++;
2119 if (!optionBins) {
2120 gywork[npt-1] = wmin+(i-first)*delta+0.5*delta;
2121 } else {
2122 yi1 = y[i]; yi = y[i-1];
2123 if (yi1 < yi) {
2124 if (i != last) Error(where, "Y must be in increasing order");
2125 else Error(where, "Y must have N+1 values with option N");
2126 return;
2127 }
2128 gywork[npt-1] = y[i-1] + 0.5*(y[i]-y[i-1]);
2129 }
2130 gxwork[npt-1] = x[i-1];
2131 ComputeLogs(npt, optionZ);
2132 if ((gxworkl[npt] < uxmin) || (gxworkl[npt] > uxmax)) {
2133 if (npt > 2) {
2134 ComputeLogs(npt, optionZ);
2135 Smooth(theGraph, npt,gxworkl.data(),gyworkl.data(),drawtype);
2136 }
2137 gxwork[0] = gxwork[npt-1];
2138 gywork[0] = gywork[npt-1];
2139 npt = 1;
2140 continue;
2141 }
2142 if (npt >= fgMaxPointsPerLine) {
2144 Smooth(theGraph, fgMaxPointsPerLine,gxworkl.data(),gyworkl.data(),drawtype);
2145 gxwork[0] = gxwork[npt-1];
2146 gywork[0] = gywork[npt-1];
2147 npt = 1;
2148 }
2149 } //endfor (i=first; i<=last;i++)
2150 if (npt > 1) {
2151 ComputeLogs(npt, optionZ);
2152 Smooth(theGraph, npt,gxworkl.data(),gyworkl.data(),drawtype);
2153 }
2154 }
2155 }
2156
2157 // Draw the histogram with a simple line
2158
2159 if (optionLine) {
2160 gPad->SetBit(TGraph::kClipFrame);
2161 wminstep = wmin + 0.5*delta;
2162 Axis_t ax1,ax2,ay1,ay2;
2163 gPad->GetRangeAxis(ax1,ay1,ax2,ay2);
2164
2165 if (!optionRot) {
2166 npt = 0;
2167 for (i=first; i<=last;i++) {
2168 npt++;
2169 if (!optionBins) {
2170 gxwork[npt-1] = wmin+(i-first)*delta+0.5*delta;
2171 } else {
2172 xi1 = x[i]; xi = x[i-1];
2173 if (xi1 < xi) {
2174 if (i != last) Error(where, "X must be in increasing order");
2175 else Error(where, "X must have N+1 values with option N");
2176 return;
2177 }
2178 gxwork[npt-1] = x[i-1] + 0.5*(x[i]-x[i-1]);
2179 }
2180 if (gxwork[npt-1] < uxmin || gxwork[npt-1] > uxmax) { npt--; continue;}
2181 gywork[npt-1] = y[i-1];
2182 gywork[npt] = y[i-1]; //new
2183 if ((gywork[npt-1] < rwymin) || ((gywork[npt-1] > rwymax) && !optionFill2)) {
2184 if (npt > 2) {
2185 ComputeLogs(npt, optionZ);
2186 gPad->PaintPolyLine(npt,gxworkl.data(),gyworkl.data());
2187 }
2188 gxwork[0] = gxwork[npt-1];
2189 gywork[0] = gywork[npt-1];
2190 npt = 1;
2191 continue;
2192 }
2193
2194 if (npt >= fgMaxPointsPerLine) {
2195 if (optionLine) {
2197 if (optionFill2) {
2198 gxworkl[npt] = gxworkl[npt-1]; gyworkl[npt] = rwymin;
2199 gxworkl[npt+1] = gxworkl[0]; gyworkl[npt+1] = rwymin;
2200 gPad->PaintFillArea(fgMaxPointsPerLine+2,gxworkl.data(),gyworkl.data());
2201 }
2202 gPad->PaintPolyLine(npt,gxworkl.data(),gyworkl.data());
2203 }
2204 gxwork[0] = gxwork[npt-1];
2205 gywork[0] = gywork[npt-1];
2206 npt = 1;
2207 }
2208 } //endfor (i=first; i<=last;i++)
2209 if (npt > 1) {
2210 ComputeLogs(npt, optionZ);
2211 if (optionFill2) {
2212 gxworkl[npt] = gxworkl[npt-1]; gyworkl[npt] = rwymin;
2213 gxworkl[npt+1] = gxworkl[0]; gyworkl[npt+1] = rwymin;
2214 gPad->PaintFillArea(npt+2,gxworkl.data(),gyworkl.data());
2215 }
2216 gPad->PaintPolyLine(npt,gxworkl.data(),gyworkl.data());
2217 }
2218 } else {
2219 npt = 0;
2220 for (i=first; i<=last;i++) {
2221 npt++;
2222 if (!optionBins) {
2223 gywork[npt-1] = wminstep+(i-first)*delta+0.5*delta;
2224 } else {
2225 yi1 = y[i]; yi = y[i-1];
2226 if (yi1 < yi) {
2227 if (i != last) Error(where, "Y must be in increasing order");
2228 else Error(where, "Y must have N+1 values with option N");
2229 goto do_cleanup;
2230 }
2231 gywork[npt-1] = y[i-1] + 0.5*(y[i]-y[i-1]);
2232 }
2233 gxwork[npt-1] = x[i-1];
2234 if ((gxwork[npt-1] < uxmin) || (gxwork[npt-1] > uxmax)) {
2235 if (npt > 2) {
2236 if (optionLine) {
2237 ComputeLogs(npt, optionZ);
2238 gPad->PaintPolyLine(npt,gxworkl.data(),gyworkl.data(),noClip);
2239 }
2240 }
2241 gxwork[0] = gxwork[npt-1];
2242 gywork[0] = gywork[npt-1];
2243 npt = 1;
2244 continue;
2245 }
2246 if (npt >= fgMaxPointsPerLine) {
2247 if (optionLine) {
2249 gPad->PaintPolyLine(fgMaxPointsPerLine,gxworkl.data(),gyworkl.data());
2250 }
2251 gxwork[0] = gxwork[npt-1];
2252 gywork[0] = gywork[npt-1];
2253 npt = 1;
2254 }
2255 } //endfor (i=first; i<=last;i++)
2256 if (optionLine != 0 && npt > 1) {
2257 ComputeLogs(npt, optionZ);
2258 gPad->PaintPolyLine(npt,gxworkl.data(),gyworkl.data(),noClip);
2259 }
2260 }
2261 }
2262
2263 // Draw the histogram as a bar chart
2264
2265 if (optionBar) {
2266 if (!optionBins) {
2267 offset = delta*baroffset; dbar = delta*barwidth;
2268 } else {
2269 if (!optionRot) {
2270 offset = (x[1]-x[0])*baroffset;
2271 dbar = (x[1]-x[0])*barwidth;
2272 } else {
2273 offset = (y[1]-y[0])*baroffset;
2274 dbar = (y[1]-y[0])*barwidth;
2275 }
2276 }
2277 drawbordersav = drawborder;
2279 if (!optionRot) {
2280 xlow = wmin+offset;
2281 xhigh = wmin+offset+dbar;
2282 if (!optionOne) ylow = TMath::Min(TMath::Max((Double_t)0,gPad->GetUymin())
2283 ,gPad->GetUymax());
2284 else ylow = gPad->GetUymin();
2285
2286 for (i=first; i<=last;i++) {
2287 yhigh = y[i-1];
2288 gxwork[0] = xlow;
2289 gywork[0] = ylow;
2290 gxwork[1] = xhigh;
2291 gywork[1] = yhigh;
2292 ComputeLogs(2, optionZ);
2293 if (xlow < rwxmax && xhigh > rwxmin)
2294 gPad->PaintBox(gxworkl[0],gyworkl[0],gxworkl[1],gyworkl[1]);
2295 if (!optionBins) {
2296 xlow = xlow+delta;
2297 xhigh = xhigh+delta;
2298 } else {
2299 if (i < last) {
2300 xi1 = x[i]; xi = x[i-1];
2301 if (xi1 < xi) {
2302 Error(where, "X must be in increasing order");
2303 goto do_cleanup;
2304 }
2305 offset = (x[i+1]-x[i])*baroffset;
2306 dbar = (x[i+1]-x[i])*barwidth;
2307 xlow = x[i] + offset;
2308 xhigh = x[i] + offset + dbar;
2309 }
2310 }
2311 } //endfor (i=first; i<=last;i++)
2312 } else {
2313 ylow = wmin + offset;
2314 yhigh = wmin + offset + dbar;
2315 if (!optionOne) xlow = TMath::Max((Double_t)0,gPad->GetUxmin());
2316 else xlow = gPad->GetUxmin();
2317 for (i=first; i<=last;i++) {
2318 xhigh = x[i-1];
2319 gxwork[0] = xlow;
2320 gywork[0] = ylow;
2321 gxwork[1] = xhigh;
2322 gywork[1] = yhigh;
2323 ComputeLogs(2, optionZ);
2324 gPad->PaintBox(gxworkl[0],gyworkl[0],gxworkl[1],gyworkl[1]);
2325 gPad->PaintBox(xlow,ylow,xhigh,yhigh);
2326 if (!optionBins) {
2327 ylow = ylow + delta;
2328 yhigh = yhigh + delta;
2329 } else {
2330 if (i < last) {
2331 yi1 = y[i]; yi = y[i-1];
2332 if (yi1 < yi) {
2333 Error(where, "Y must be in increasing order");
2334 goto do_cleanup;
2335 }
2336 offset = (y[i+1]-y[i])*baroffset;
2337 dbar = (y[i+1]-y[i])*barwidth;
2338 ylow = y[i] + offset;
2339 yhigh = y[i] + offset + dbar;
2340 }
2341 }
2342 } //endfor (i=first; i<=last;i++)
2343 }
2344 gStyle->SetDrawBorder(drawbordersav);
2345 }
2346
2347 // Draw the histogram with a simple marker
2348
2349 optionMarker = 0;
2350 if ((optionStar) || (optionMark)) optionMarker=1;
2351
2352 if (optionMarker) {
2353 Double_t xm,ym;
2354 npt = 0;
2355 if (!optionRot) {
2356 for (i=first; i<=last;i++) {
2357 if (!optionBins) xm = wmin+(i-first)*delta+0.5*delta;
2358 else xm = x[i-1] + 0.5*(x[i]-x[i-1]);
2359 ym = y[i-1];
2360 if (optionMark != 10) {
2361 if (ym<rwymax && ym > rwymin) {
2362 npt++;
2363 gxwork[npt-1] = xm;
2364 gywork[npt-1] = ym;
2365 }
2366 } else {
2367 if (ym<rwymax && ym >= rwymin) {
2368 npt++;
2369 gxwork[npt-1] = xm;
2370 gywork[npt-1] = ym;
2371 }
2372 }
2373 if (npt >= fgMaxPointsPerLine) {
2374 ComputeLogs(npt, optionZ);
2375 gPad->PaintPolyMarker(npt,gxworkl.data(),gyworkl.data());
2376 npt = 0;
2377 }
2378 }
2379 if (npt > 0) {
2380 ComputeLogs(npt, optionZ);
2381 gPad->PaintPolyMarker(npt,gxworkl.data(),gyworkl.data());
2382 }
2383 } else {
2384 wminstep = wmin + 0.5*delta;
2385 for (i=first; i<=last;i++) {
2386 if (!optionBins) ym = wminstep+(i-first)*delta+0.5*delta;
2387 else ym = y[i-1] + 0.5*(y[i]-y[i-1]);
2388 xm = x[i-1];
2389 if (optionMark != 10) {
2390 if (xm<rwxmax && xm > rwxmin) {
2391 npt++;
2392 gxwork[npt-1] = xm;
2393 gywork[npt-1] = ym;
2394 }
2395 } else {
2396 if (xm<rwxmax && xm >= rwxmin) {
2397 npt++;
2398 gxwork[npt-1] = xm;
2399 gywork[npt-1] = ym;
2400 }
2401 }
2402 if (npt >= fgMaxPointsPerLine) {
2403 ComputeLogs(npt, optionZ);
2404 gPad->PaintPolyMarker(npt,gxworkl.data(),gyworkl.data());
2405 npt = 0;
2406 }
2407 }
2408 if (npt > 0) {
2409 ComputeLogs(npt, optionZ);
2410 gPad->PaintPolyMarker(npt,gxworkl.data(),gyworkl.data());
2411 }
2412 }
2413 }
2414
2415 gPad->ResetBit(TGraph::kClipFrame);
2416
2417do_cleanup:
2418 gxwork.clear();
2419 gywork.clear();
2420 gxworkl.clear();
2421 gyworkl.clear();
2422}
2423
2424
2425////////////////////////////////////////////////////////////////////////////////
2426/// [Paint this TGraphAsymmErrors with its current attributes.](\ref GrP3)
2427
2429{
2430
2431 std::vector<Double_t> xline, yline;
2432 Int_t if1 = 0;
2433 Int_t if2 = 0;
2434 Double_t xb[4], yb[4];
2435
2436 const Int_t kBASEMARKER=8;
2437 Double_t s2x, s2y, symbolsize, sbase;
2438 Double_t x, y, xl1, xl2, xr1, xr2, yup1, yup2, ylow1, ylow2, tx, ty;
2439 static Float_t cxx[30] = {1.0,1.0,0.5,0.5,1.0,1.0,0.5,0.6,1.0,0.5,0.5,1.0,0.5,0.6,1.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0,1.0,1.0,1.0,1.0,0.5,0.5,0.5,1.0};
2440 static Float_t cyy[30] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0,1.0,1.0,1.0,1.0,0.5,0.5,0.5,1.0};
2441 Int_t theNpoints = theGraph->GetN();
2442 Double_t *theX = theGraph->GetX();
2443 Double_t *theY = theGraph->GetY();
2444 Double_t *theEXlow = theGraph->GetEXlow(); if (!theEXlow) return;
2445 Double_t *theEYlow = theGraph->GetEYlow(); if (!theEYlow) return;
2446 Double_t *theEXhigh = theGraph->GetEXhigh(); if (!theEXhigh) return;
2447 Double_t *theEYhigh = theGraph->GetEYhigh(); if (!theEYhigh) return;
2448
2449 if (strchr(option,'X') || strchr(option,'x')) {PaintGraphSimple(theGraph, option); return;}
2450 Bool_t brackets = kFALSE;
2451 Bool_t braticks = kFALSE;
2452 if (strstr(option,"||") || strstr(option,"[]")) {
2453 brackets = kTRUE;
2454 if (strstr(option,"[]")) braticks = kTRUE;
2455 }
2456 Bool_t endLines = kTRUE;
2457 if (strchr(option,'z')) endLines = kFALSE;
2458 if (strchr(option,'Z')) endLines = kFALSE;
2459 const char *arrowOpt = nullptr;
2460 if (strchr(option,'>')) arrowOpt = ">";
2461 if (strstr(option,"|>")) arrowOpt = "|>";
2462
2463 Bool_t axis = kFALSE;
2464 if (strchr(option,'a')) axis = kTRUE;
2465 if (strchr(option,'A')) axis = kTRUE;
2466 if (axis) PaintGraphSimple(theGraph, option);
2467
2468 Bool_t option0 = kFALSE;
2469 Bool_t option2 = kFALSE;
2470 Bool_t option3 = kFALSE;
2471 Bool_t option4 = kFALSE;
2472 Bool_t option5 = kFALSE;
2473 if (strchr(option,'0')) option0 = kTRUE;
2474 if (strchr(option,'2')) option2 = kTRUE;
2475 if (strchr(option,'3')) option3 = kTRUE;
2476 if (strchr(option,'4')) {option3 = kTRUE; option4 = kTRUE;}
2477 if (strchr(option,'5')) {option2 = kTRUE; option5 = kTRUE;}
2478
2479 if (option3) {
2480 xline.resize(2*theNpoints);
2481 yline.resize(2*theNpoints);
2482 if (xline.empty() || yline.empty()) {
2483 Error("PaintGraphAsymmErrors", "too many points, out of memory");
2484 return;
2485 }
2486 if1 = 1;
2487 if2 = 2*theNpoints;
2488 }
2489
2490 theGraph->TAttLine::Modify();
2491
2492 TArrow arrow;
2493 arrow.SetLineWidth(theGraph->GetLineWidth());
2494 arrow.SetLineColor(theGraph->GetLineColor());
2495 arrow.SetFillColor(theGraph->GetFillColor());
2496
2497 TBox box;
2498 Double_t x1b,y1b,x2b,y2b;
2499 box.SetLineWidth(theGraph->GetLineWidth());
2500 box.SetLineColor(theGraph->GetLineColor());
2501 box.SetFillColor(theGraph->GetFillColor());
2502 box.SetFillStyle(theGraph->GetFillStyle());
2503
2504 symbolsize = theGraph->GetMarkerSize();
2505 sbase = symbolsize*kBASEMARKER;
2507 Double_t cx = 0;
2508 Double_t cy = 0;
2509 if (mark >= 20 && mark <= 49) {
2510 cx = cxx[mark-20];
2511 cy = cyy[mark-20];
2512 }
2513
2514 // Define the offset of the error bars due to the symbol size
2515 s2x = gPad->PixeltoX(Int_t(0.5*sbase)) - gPad->PixeltoX(0);
2516 s2y =-gPad->PixeltoY(Int_t(0.5*sbase)) + gPad->PixeltoY(0);
2517 Int_t dxend = Int_t(gStyle->GetEndErrorSize());
2518 tx = gPad->PixeltoX(dxend) - gPad->PixeltoX(0);
2519 ty =-gPad->PixeltoY(dxend) + gPad->PixeltoY(0);
2520 Float_t asize = 0.6*symbolsize*kBASEMARKER/gPad->GetWh();
2521
2523 for (Int_t i=0;i<theNpoints;i++) {
2524 x = gPad->XtoPad(theX[i]);
2525 y = gPad->YtoPad(theY[i]);
2526 if (!option0) {
2527 if (option3) {
2528 if (x < gPad->GetUxmin()) x = gPad->GetUxmin();
2529 if (x > gPad->GetUxmax()) x = gPad->GetUxmax();
2530 if (y < gPad->GetUymin()) y = gPad->GetUymin();
2531 if (y > gPad->GetUymax()) y = gPad->GetUymax();
2532 } else {
2533 if (x < gPad->GetUxmin()) continue;
2534 if (x > gPad->GetUxmax()) continue;
2535 if (y < gPad->GetUymin()) continue;
2536 if (y > gPad->GetUymax()) continue;
2537 }
2538 }
2539 xl1 = x - s2x*cx;
2540 xl2 = gPad->XtoPad(theX[i] - theEXlow[i]);
2541
2542 // draw the error rectangles
2543 if (option2) {
2544 x1b = gPad->XtoPad(theX[i] - theEXlow[i]);
2545 y1b = gPad->YtoPad(theY[i] - theEYlow[i]);
2546 x2b = gPad->XtoPad(theX[i] + theEXhigh[i]);
2547 y2b = gPad->YtoPad(theY[i] + theEYhigh[i]);
2548 if (x1b < gPad->GetUxmin()) x1b = gPad->GetUxmin();
2549 if (x1b > gPad->GetUxmax()) x1b = gPad->GetUxmax();
2550 if (y1b < gPad->GetUymin()) y1b = gPad->GetUymin();
2551 if (y1b > gPad->GetUymax()) y1b = gPad->GetUymax();
2552 if (x2b < gPad->GetUxmin()) x2b = gPad->GetUxmin();
2553 if (x2b > gPad->GetUxmax()) x2b = gPad->GetUxmax();
2554 if (y2b < gPad->GetUymin()) y2b = gPad->GetUymin();
2555 if (y2b > gPad->GetUymax()) y2b = gPad->GetUymax();
2556 if (option5) box.PaintBox(x1b, y1b, x2b, y2b, "l");
2557 else box.PaintBox(x1b, y1b, x2b, y2b);
2558 continue;
2559 }
2560
2561 // keep points for fill area drawing
2562 if (option3) {
2563 xline[if1-1] = x;
2564 xline[if2-1] = x;
2565 yline[if1-1] = gPad->YtoPad(theY[i] + theEYhigh[i]);
2566 yline[if2-1] = gPad->YtoPad(theY[i] - theEYlow[i]);
2567 if1++;
2568 if2--;
2569 continue;
2570 }
2571
2572 if (xl1 > xl2) {
2573 if (arrowOpt) {
2574 arrow.PaintArrow(xl1,y,xl2,y,asize,arrowOpt);
2575 } else {
2576 if (!brackets) gPad->PaintLine(xl1,y,xl2,y);
2577 if (endLines) {
2578 if (braticks) {
2579 xb[0] = xl2+tx; yb[0] = y-ty;
2580 xb[1] = xl2; yb[1] = y-ty;
2581 xb[2] = xl2; yb[2] = y+ty;
2582 xb[3] = xl2+tx; yb[3] = y+ty;
2583 gPad->PaintPolyLine(4, xb, yb);
2584 } else {
2585 gPad->PaintLine(xl2,y-ty,xl2,y+ty);
2586 }
2587 }
2588 }
2589 }
2590 xr1 = x + s2x*cx;
2591 xr2 = gPad->XtoPad(theX[i] + theEXhigh[i]);
2592 if (xr1 < xr2) {
2593 if (arrowOpt) {
2594 arrow.PaintArrow(xr1,y,xr2,y,asize,arrowOpt);
2595 } else {
2596 if (!brackets) gPad->PaintLine(xr1,y,xr2,y);
2597 if (endLines) {
2598 if (braticks) {
2599 xb[0] = xr2-tx; yb[0] = y-ty;
2600 xb[1] = xr2; yb[1] = y-ty;
2601 xb[2] = xr2; yb[2] = y+ty;
2602 xb[3] = xr2-tx; yb[3] = y+ty;
2603 gPad->PaintPolyLine(4, xb, yb);
2604 } else {
2605 gPad->PaintLine(xr2,y-ty,xr2,y+ty);
2606 }
2607 }
2608 }
2609 }
2610 yup1 = y + s2y*cy;
2611 yup2 = gPad->YtoPad(theY[i] + theEYhigh[i]);
2612 if (yup2 > gPad->GetUymax()) yup2 = gPad->GetUymax();
2613 if (yup2 > yup1) {
2614 if (arrowOpt) {
2615 arrow.PaintArrow(x,yup1,x,yup2,asize,arrowOpt);
2616 } else {
2617 if (!brackets) gPad->PaintLine(x,yup1,x,yup2);
2618 if (endLines) {
2619 if (braticks) {
2620 xb[0] = x-tx; yb[0] = yup2-ty;
2621 xb[1] = x-tx; yb[1] = yup2;
2622 xb[2] = x+tx; yb[2] = yup2;
2623 xb[3] = x+tx; yb[3] = yup2-ty;
2624 gPad->PaintPolyLine(4, xb, yb);
2625 } else {
2626 gPad->PaintLine(x-tx,yup2,x+tx,yup2);
2627 }
2628 }
2629 }
2630 }
2631 ylow1 = y - s2y*cy;
2632 ylow2 = gPad->YtoPad(theY[i] - theEYlow[i]);
2633 if (ylow2 < gPad->GetUymin()) ylow2 = gPad->GetUymin();
2634 if (ylow2 < ylow1) {
2635 if (arrowOpt) {
2636 arrow.PaintArrow(x,ylow1,x,ylow2,asize,arrowOpt);
2637 } else {
2638 if (!brackets) gPad->PaintLine(x,ylow1,x,ylow2);
2639 if (endLines) {
2640 if (braticks) {
2641 xb[0] = x-tx; yb[0] = ylow2+ty;
2642 xb[1] = x-tx; yb[1] = ylow2;
2643 xb[2] = x+tx; yb[2] = ylow2;
2644 xb[3] = x+tx; yb[3] = ylow2+ty;
2645 gPad->PaintPolyLine(4, xb, yb);
2646 } else {
2647 gPad->PaintLine(x-tx,ylow2,x+tx,ylow2);
2648 }
2649 }
2650 }
2651 }
2652 }
2653 if (!brackets && !axis) PaintGraphSimple(theGraph, option);
2654 gPad->ResetBit(TGraph::kClipFrame);
2655
2656 if (option3) {
2657 Int_t logx = gPad->GetLogx();
2658 Int_t logy = gPad->GetLogy();
2659 gPad->SetLogx(0);
2660 gPad->SetLogy(0);
2661 if (option4) PaintGraph(theGraph, 2*theNpoints, xline.data(), yline.data(),"FC");
2662 else PaintGraph(theGraph, 2*theNpoints, xline.data(), yline.data(),"F");
2663 gPad->SetLogx(logx);
2664 gPad->SetLogy(logy);
2665 }
2666}
2667
2668////////////////////////////////////////////////////////////////////////////////
2669/// [Paint this TGraphMultiErrors with its current attributes.](\ref GrP3)
2670
2672{
2673 if (!theGraph->InheritsFrom(TGraphMultiErrors::Class())) {
2674 PaintHelper(theGraph, option);
2675 return;
2676 }
2677
2678 auto tg = (TGraphMultiErrors *)theGraph;
2679
2680 Int_t NYErrors = tg->GetNYErrors();
2681 if (NYErrors <= 0) {
2683 return;
2684 }
2685
2686 TString tsOpt = option;
2687 tsOpt.ToLower();
2688
2689 std::vector<TString> options(NYErrors + 1);
2690 Int_t filled = 0;
2691
2692 if (tsOpt.CountChar(';') < NYErrors) {
2693 options[0] = tsOpt.Contains(";") ? tsOpt(0, tsOpt.First(';')) : tsOpt.Copy();
2694 filled++;
2695 }
2696
2697 Ssiz_t firstSemicolon;
2698 while ((firstSemicolon = tsOpt.First(';')) != kNPOS && filled <= NYErrors) {
2699 options[filled] = tsOpt(0, firstSemicolon);
2700 tsOpt = tsOpt(firstSemicolon + 1, tsOpt.Length());
2701 filled++;
2702 }
2703
2704 if (filled <= NYErrors) {
2705 options[filled] = tsOpt.Copy();
2706 filled++;
2707 }
2708
2709 for (Int_t i = filled; i <= NYErrors; i++)
2710 options[i] = "";
2711
2712 std::vector<Double_t> xline;
2713 std::vector<std::vector<Double_t>> yline(NYErrors);
2714 Int_t if1 = 0;
2715 Int_t if2 = 0;
2716 Double_t xb[4], yb[4];
2717
2718 const Int_t kBASEMARKER = 8;
2719 Double_t s2x, s2y, symbolsize, sbase;
2720 Double_t x, y, xl1, xl2, xr1, xr2, yup1, yup2, ylow1, ylow2, tx, ty;
2721 static Float_t cxx[30] = {1.0,1.0,0.5,0.5,1.0,1.0,0.5,0.6,1.0,0.5,0.5,1.0,0.5,0.6,1.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0,1.0,1.0,1.0,1.0,0.5,0.5,0.5,1.0};
2722 static Float_t cyy[30] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0,1.0,1.0,1.0,1.0,0.5,0.5,0.5,1.0};
2723 Int_t theNpoints = tg->GetN();
2724 Double_t *theX = tg->GetX();
2725 Double_t *theY = tg->GetY();
2726 Double_t *theExL = tg->GetEXlow();
2727 Double_t *theExH = tg->GetEXhigh();
2728 std::vector<Double_t *> theEyL(NYErrors);
2729 std::vector<Double_t *> theEyH(NYErrors);
2730
2731 Bool_t theEyExists = kTRUE;
2732 for (Int_t j = 0; j < NYErrors; j++) {
2733 theEyL[j] = tg->GetEYlow(j);
2734 theEyH[j] = tg->GetEYhigh(j);
2735 theEyExists &= (theEyL[j] && theEyH[j]);
2736 }
2737
2738 if (!theX || !theY || !theExL || !theExH || !theEyExists)
2739 return;
2740
2741 std::vector<Bool_t> DrawErrors(NYErrors);
2742 Bool_t AnyErrors = kFALSE;
2743 Bool_t NoErrorsX = kTRUE;
2744 Bool_t Option0X = kFALSE;
2746 std::vector<Bool_t> Braticks(NYErrors);
2747 std::vector<Bool_t> Brackets(NYErrors);
2748 std::vector<Bool_t> EndLines(NYErrors);
2749 std::vector<Char_t *> ArrowOpt(NYErrors);
2750 std::vector<Bool_t> Option5(NYErrors);
2751 std::vector<Bool_t> Option4(NYErrors);
2752 std::vector<Bool_t> Option3(NYErrors);
2753 Bool_t AnyOption3 = kFALSE;
2754 std::vector<Bool_t> Option2(NYErrors);
2755 std::vector<Bool_t> Option0(NYErrors);
2756 Bool_t AnyOption0 = kFALSE;
2757 std::vector<Double_t> Scale(NYErrors);
2758
2759 const TRegexp ScaleRegExp("s=*[0-9]\\.*[0-9]");
2760
2761 for (Int_t j = 0; j < NYErrors; j++) {
2762 if (options[j + 1].Contains("s=")) {
2763 sscanf(strstr(options[j + 1].Data(), "s="), "s=%lf", &Scale[j]);
2764 options[j + 1].ReplaceAll(options[j + 1](ScaleRegExp), "");
2765 } else
2766 Scale[j] = 1.;
2767
2768 DrawErrors[j] = !options[j + 1].Contains("x");
2769 AnyErrors |= DrawErrors[j];
2770 Braticks[j] = options[j + 1].Contains("[]");
2771 Brackets[j] = options[j + 1].Contains("||") || Braticks[j];
2772 EndLines[j] = !options[j + 1].Contains("z");
2773
2774 if (options[j + 1].Contains("|>"))
2775 ArrowOpt[j] = (Char_t *)"|>";
2776 else if (options[j + 1].Contains(">"))
2777 ArrowOpt[j] = (Char_t *)">";
2778 else
2779 ArrowOpt[j] = nullptr;
2780
2781 Option5[j] = options[j + 1].Contains("5");
2782 Option4[j] = options[j + 1].Contains("4");
2783 Option3[j] = options[j + 1].Contains("3") || Option4[j];
2784 AnyOption3 |= Option3[j];
2785 Option2[j] = options[j + 1].Contains("2") || Option5[j];
2786 Option0[j] = options[j + 1].Contains("0");
2787 AnyOption0 |= Option0[j];
2788
2789 NoErrorsX &= (Option3[j] || Option2[j]);
2790 Option0X |= !(Option3[j] || Option2[j]) && Option0[j];
2791 DrawMarker |= !(Brackets[j] || Option3[j] || Option2[j]);
2792 }
2793
2794 Bool_t Draw0PointsX = !options[0].Contains("x0") && (gPad->GetLogx() == 0);
2795 Bool_t Draw0PointsY = !options[0].Contains("y0") && (gPad->GetLogy() == 0);
2796 options[0].ReplaceAll("x0", "");
2797 options[0].ReplaceAll("y0", "");
2798
2799 Bool_t DrawErrorsX = !options[0].Contains("x");
2800 Bool_t BraticksX = options[0].Contains("[]");
2801 Bool_t BracketsX = options[0].Contains("||") || BraticksX;
2802 Bool_t EndLinesX = !options[0].Contains("z");
2803
2804 Char_t *ArrowOptX = nullptr;
2805 if (options[0].Contains("|>"))
2806 ArrowOptX = (Char_t *)"|>";
2807 else if (options[0].Contains(">"))
2808 ArrowOptX = (Char_t *)">";
2809
2810 Double_t ScaleX = 1.;
2811 if (options[0].Contains("s=")) {
2812 sscanf(strstr(options[0].Data(), "s="), "s=%lf", &ScaleX);
2813 options[0].ReplaceAll(options[0](ScaleRegExp), "");
2814 }
2815
2816 if (!AnyErrors && !DrawErrorsX) {
2817 PaintGraphSimple(tg, options[0].Data());
2818 return;
2819 }
2820
2821 Bool_t DrawAxis = options[0].Contains("a");
2822 Bool_t IndividualStyles = options[0].Contains("s");
2823
2824 if (DrawAxis)
2825 PaintGraphSimple(tg, options[0].Data());
2826
2827 Int_t NPointsInside = AnyOption0 ? theNpoints : 0;
2828
2829 for (Int_t i = 0; i < theNpoints && !AnyOption0; i++) {
2830 x = gPad->XtoPad(theX[i]);
2831 y = gPad->YtoPad(theY[i]);
2832
2833 if ((x >= gPad->GetUxmin()) && (x <= gPad->GetUxmax()) && (y >= gPad->GetUymin()) && (y <= gPad->GetUymax()) &&
2834 (Draw0PointsX || theX[i] != 0.) && (Draw0PointsY || theY[i] != 0.))
2835 NPointsInside++;
2836 }
2837
2838 if (AnyOption3) {
2839 xline.resize(2 * NPointsInside);
2840
2841 if (xline.empty()) {
2842 Error("PaintGraphMultiErrors", "too many points, out of memory");
2843 return;
2844 }
2845
2846 if1 = 1;
2847 if2 = 2 * NPointsInside;
2848 }
2849
2850 for (Int_t j = 0; j < NYErrors; j++) {
2851 if (Option3[j] && DrawErrors[j]) {
2852 yline[j].resize(2 * NPointsInside);
2853
2854 if (yline[j].empty()) {
2855 Error("PaintGraphMultiErrors", "too many points, out of memory");
2856 return;
2857 }
2858 }
2859 }
2860
2861 tg->TAttLine::Modify();
2862
2863 TArrow arrow;
2864 arrow.SetLineWidth(tg->GetLineWidth());
2865 arrow.SetLineColor(tg->GetLineColor());
2866 arrow.SetFillColor(tg->GetFillColor());
2867
2868 TBox box;
2869 Double_t x1b, y1b, x2b, y2b;
2870 box.SetLineWidth(tg->GetLineWidth());
2871 box.SetLineColor(tg->GetLineColor());
2872 box.SetFillColor(tg->GetFillColor());
2873 box.SetFillStyle(tg->GetFillStyle());
2874
2875 symbolsize = tg->GetMarkerSize();
2876 sbase = symbolsize * kBASEMARKER;
2877 Int_t mark = TAttMarker::GetMarkerStyleBase(tg->GetMarkerStyle());
2878 Double_t cx = 0.;
2879 Double_t cy = 0.;
2880
2881 if (mark >= 20 && mark <= 49) {
2882 cx = cxx[mark - 20];
2883 cy = cyy[mark - 20];
2884 }
2885
2886 // Define the offset of the error bars due to the symbol size
2887 s2x = gPad->PixeltoX(Int_t(0.5 * sbase)) - gPad->PixeltoX(0);
2888 s2y = -gPad->PixeltoY(Int_t(0.5 * sbase)) + gPad->PixeltoY(0);
2889 auto dxend = Int_t(gStyle->GetEndErrorSize());
2890 tx = gPad->PixeltoX(dxend) - gPad->PixeltoX(0);
2891 ty = -gPad->PixeltoY(dxend) + gPad->PixeltoY(0);
2892 Float_t asize = 0.6 * symbolsize * kBASEMARKER / gPad->GetWh();
2893
2894 gPad->SetBit(TGraph::kClipFrame, tg->TestBit(TGraph::kClipFrame));
2895
2896 for (Int_t i = 0; i < theNpoints; i++) {
2897 x = gPad->XtoPad(theX[i]);
2898 y = gPad->YtoPad(theY[i]);
2899
2900 Bool_t isOutside =
2901 (x < gPad->GetUxmin()) || (x > gPad->GetUxmax()) || (y < gPad->GetUymin()) || (y > gPad->GetUymax());
2902
2903 if ((isOutside && !AnyOption0) || (!Draw0PointsX && theX[i] == 0.) || (!Draw0PointsY && theY[i] == 0.))
2904 continue;
2905
2906 if (AnyOption3) {
2907 if (isOutside) {
2908 if (x < gPad->GetUxmin())
2909 x = gPad->GetUxmin();
2910 if (x > gPad->GetUxmax())
2911 x = gPad->GetUxmax();
2912 if (y < gPad->GetUymin())
2913 y = gPad->GetUymin();
2914 if (y > gPad->GetUymax())
2915 y = gPad->GetUymax();
2916 }
2917
2918 xline[if1 - 1] = x;
2919 xline[if2 - 1] = x;
2920
2921 if1++;
2922 if2--;
2923 }
2924
2925 for (Int_t j = 0; j < NYErrors; j++) {
2926 if (!DrawErrors[j])
2927 continue;
2928
2929 // draw the error rectangles
2930 if (Option2[j] && (!isOutside || Option0[j])) {
2931 if (IndividualStyles) {
2932 box.SetLineWidth(tg->GetLineWidth(j));
2933 box.SetLineColor(tg->GetLineColor(j));
2934 box.SetFillColor(tg->GetFillColor(j));
2935 box.SetFillStyle(tg->GetFillStyle(j));
2936 }
2937
2938 x1b = gPad->XtoPad(theX[i] - Scale[j] * theExL[i]);
2939 y1b = gPad->YtoPad(theY[i] - theEyL[j][i]);
2940 x2b = gPad->XtoPad(theX[i] + Scale[j] * theExH[i]);
2941 y2b = gPad->YtoPad(theY[i] + theEyH[j][i]);
2942 if (x1b < gPad->GetUxmin())
2943 x1b = gPad->GetUxmin();
2944 if (x1b > gPad->GetUxmax())
2945 x1b = gPad->GetUxmax();
2946 if (y1b < gPad->GetUymin())
2947 y1b = gPad->GetUymin();
2948 if (y1b > gPad->GetUymax())
2949 y1b = gPad->GetUymax();
2950 if (x2b < gPad->GetUxmin())
2951 x2b = gPad->GetUxmin();
2952 if (x2b > gPad->GetUxmax())
2953 x2b = gPad->GetUxmax();
2954 if (y2b < gPad->GetUymin())
2955 y2b = gPad->GetUymin();
2956 if (y2b > gPad->GetUymax())
2957 y2b = gPad->GetUymax();
2958 if (Option5[j])
2959 box.PaintBox(x1b, y1b, x2b, y2b, "l");
2960 else
2961 box.PaintBox(x1b, y1b, x2b, y2b);
2962 }
2963
2964 // keep points for fill area drawing
2965 if (Option3[j]) {
2966 if (!isOutside || Option0[j]) {
2967 yline[j][if1 - 2] = gPad->YtoPad(theY[i] + theEyH[j][i]);
2968 yline[j][if2] = gPad->YtoPad(theY[i] - theEyL[j][i]);
2969 } else {
2970 yline[j][if1 - 2] = gPad->GetUymin();
2971 yline[j][if2] = gPad->GetUymin();
2972 }
2973 }
2974
2975 if (IndividualStyles) {
2976 tg->GetAttLine(j)->Modify();
2977
2978 arrow.SetLineWidth(tg->GetLineWidth(j));
2979 arrow.SetLineColor(tg->GetLineColor(j));
2980 arrow.SetFillColor(tg->GetFillColor(j));
2981 }
2982
2983 ylow1 = y - s2y * cy;
2984 ylow2 = gPad->YtoPad(theY[i] - theEyL[j][i]);
2985 if (ylow2 < gPad->GetUymin())
2986 ylow2 = gPad->GetUymin();
2987 if (ylow2 < ylow1 && DrawErrors[j] && !Option2[j] && !Option3[j] && (!isOutside || Option0[j])) {
2988 if (ArrowOpt[j])
2989 arrow.PaintArrow(x, ylow1, x, ylow2, asize, ArrowOpt[j]);
2990 else {
2991 if (!Brackets[j])
2992 gPad->PaintLine(x, ylow1, x, ylow2);
2993 if (EndLines[j]) {
2994 if (Braticks[j]) {
2995 xb[0] = x - tx;
2996 yb[0] = ylow2 + ty;
2997 xb[1] = x - tx;
2998 yb[1] = ylow2;
2999 xb[2] = x + tx;
3000 yb[2] = ylow2;
3001 xb[3] = x + tx;
3002 yb[3] = ylow2 + ty;
3003 gPad->PaintPolyLine(4, xb, yb);
3004 } else
3005 gPad->PaintLine(x - tx, ylow2, x + tx, ylow2);
3006 }
3007 }
3008 }
3009
3010 yup1 = y + s2y * cy;
3011 yup2 = gPad->YtoPad(theY[i] + theEyH[j][i]);
3012 if (yup2 > gPad->GetUymax())
3013 yup2 = gPad->GetUymax();
3014 if (yup2 > yup1 && DrawErrors[j] && !Option2[j] && !Option3[j] && (!isOutside || Option0[j])) {
3015 if (ArrowOpt[j])
3016 arrow.PaintArrow(x, yup1, x, yup2, asize, ArrowOpt[j]);
3017 else {
3018 if (!Brackets[j])
3019 gPad->PaintLine(x, yup1, x, yup2);
3020 if (EndLines[j]) {
3021 if (Braticks[j]) {
3022 xb[0] = x - tx;
3023 yb[0] = yup2 - ty;
3024 xb[1] = x - tx;
3025 yb[1] = yup2;
3026 xb[2] = x + tx;
3027 yb[2] = yup2;
3028 xb[3] = x + tx;
3029 yb[3] = yup2 - ty;
3030 gPad->PaintPolyLine(4, xb, yb);
3031 } else
3032 gPad->PaintLine(x - tx, yup2, x + tx, yup2);
3033 }
3034 }
3035 }
3036 }
3037
3038 if (DrawErrorsX) {
3039 if (IndividualStyles) {
3040 tg->TAttLine::Modify();
3041
3042 arrow.SetLineWidth(tg->GetLineWidth());
3043 arrow.SetLineColor(tg->GetLineColor());
3044 arrow.SetFillColor(tg->GetFillColor());
3045 }
3046
3047 xl1 = x - s2x * cx;
3048 xl2 = gPad->XtoPad(theX[i] - ScaleX * theExL[i]);
3049 if (xl1 > xl2 && !NoErrorsX && (!isOutside || Option0X)) {
3050 if (ArrowOptX)
3051 arrow.PaintArrow(xl1, y, xl2, y, asize, ArrowOptX);
3052 else {
3053 if (!BracketsX)
3054 gPad->PaintLine(xl1, y, xl2, y);
3055 if (EndLinesX) {
3056 if (BraticksX) {
3057 xb[0] = xl2 + tx;
3058 yb[0] = y - ty;
3059 xb[1] = xl2;
3060 yb[1] = y - ty;
3061 xb[2] = xl2;
3062 yb[2] = y + ty;
3063 xb[3] = xl2 + tx;
3064 yb[3] = y + ty;
3065 gPad->PaintPolyLine(4, xb, yb);
3066 } else
3067 gPad->PaintLine(xl2, y - ty, xl2, y + ty);
3068 }
3069 }
3070 }
3071
3072 xr1 = x + s2x * cx;
3073 xr2 = gPad->XtoPad(theX[i] + ScaleX * theExH[i]);
3074 if (xr1 < xr2 && !NoErrorsX && (!isOutside || Option0X)) {
3075 if (ArrowOptX)
3076 arrow.PaintArrow(xr1, y, xr2, y, asize, ArrowOptX);
3077 else {
3078 if (!BracketsX)
3079 gPad->PaintLine(xr1, y, xr2, y);
3080 if (EndLinesX) {
3081 if (BraticksX) {
3082 xb[0] = xr2 - tx;
3083 yb[0] = y - ty;
3084 xb[1] = xr2;
3085 yb[1] = y - ty;
3086 xb[2] = xr2;
3087 yb[2] = y + ty;
3088 xb[3] = xr2 - tx;
3089 yb[3] = y + ty;
3090 gPad->PaintPolyLine(4, xb, yb);
3091 } else
3092 gPad->PaintLine(xr2, y - ty, xr2, y + ty);
3093 }
3094 }
3095 }
3096 }
3097 }
3098
3099 if (DrawMarker && !DrawAxis)
3100 PaintGraphSimple(tg, options[0].Data());
3101 gPad->ResetBit(TGraph::kClipFrame);
3102
3103 TGraph tgDummy;
3104 tg->TAttFill::Copy(tgDummy);
3105 tg->TAttLine::Copy(tgDummy);
3106 tg->TAttMarker::Copy(tgDummy);
3107
3108 for (Int_t j = 0; j < NYErrors; j++)
3109 if (Option3[j] && DrawErrors[j]) {
3110 if (IndividualStyles) {
3111 tg->GetAttFill(j)->Copy(tgDummy);
3112 tg->GetAttLine(j)->Copy(tgDummy);
3113 }
3114
3115 Int_t logx = gPad->GetLogx();
3116 Int_t logy = gPad->GetLogy();
3117 gPad->SetLogx(0);
3118 gPad->SetLogy(0);
3119 if (Option4[j])
3120 PaintGraph(&tgDummy, 2 * NPointsInside, xline.data(), yline[j].data(), "FC");
3121 else
3122 PaintGraph(&tgDummy, 2 * NPointsInside, xline.data(), yline[j].data(), "F");
3123 gPad->SetLogx(logx);
3124 gPad->SetLogy(logy);
3125 }
3126
3127}
3128
3129////////////////////////////////////////////////////////////////////////////////
3130/// [Paint this TGraphBentErrors with its current attributes.](\ref GrP3)
3131
3133{
3134
3135 std::vector<Double_t> xline, yline;
3136 Int_t if1 = 0;
3137 Int_t if2 = 0;
3138 Double_t xb[4], yb[4];
3139
3140 const Int_t kBASEMARKER=8;
3141 Double_t s2x, s2y, symbolsize, sbase;
3142 Double_t x, y, xl1, xl2, xr1, xr2, yup1, yup2, ylow1, ylow2, tx, ty;
3143 Double_t bxl, bxh, byl, byh;
3144 static Float_t cxx[30] = {1.0,1.0,0.5,0.5,1.0,1.0,0.5,0.6,1.0,0.5,0.5,1.0,0.5,0.6,1.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0,1.0,1.0,1.0,1.0,0.5,0.5,0.5,1.0};
3145 static Float_t cyy[30] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0,1.0,1.0,1.0,1.0,0.5,0.5,0.5,1.0};
3146 Int_t theNpoints = theGraph->GetN();
3147 Double_t *theX = theGraph->GetX();
3148 Double_t *theY = theGraph->GetY();
3149 Double_t *theEXlow = theGraph->GetEXlow(); if (!theEXlow) return;
3150 Double_t *theEYlow = theGraph->GetEYlow(); if (!theEYlow) return;
3151 Double_t *theEXhigh = theGraph->GetEXhigh(); if (!theEXhigh) return;
3152 Double_t *theEYhigh = theGraph->GetEYhigh(); if (!theEYhigh) return;
3153 Double_t *theEXlowd = theGraph->GetEXlowd(); if (!theEXlowd) return;
3154 Double_t *theEXhighd = theGraph->GetEXhighd(); if (!theEXhighd) return;
3155 Double_t *theEYlowd = theGraph->GetEYlowd(); if (!theEYlowd) return;
3156 Double_t *theEYhighd = theGraph->GetEYhighd(); if (!theEYhighd) return;
3157
3158 if (strchr(option,'X') || strchr(option,'x')) {PaintGraphSimple(theGraph, option); return;}
3159 Bool_t brackets = kFALSE;
3160 Bool_t braticks = kFALSE;
3161 if (strstr(option,"||") || strstr(option,"[]")) {
3162 brackets = kTRUE;
3163 if (strstr(option,"[]")) braticks = kTRUE;
3164 }
3165 Bool_t endLines = kTRUE;
3166 if (strchr(option,'z')) endLines = kFALSE;
3167 if (strchr(option,'Z')) endLines = kFALSE;
3168 const char *arrowOpt = nullptr;
3169 if (strchr(option,'>')) arrowOpt = ">";
3170 if (strstr(option,"|>")) arrowOpt = "|>";
3171
3172 Bool_t axis = kFALSE;
3173 if (strchr(option,'a')) axis = kTRUE;
3174 if (strchr(option,'A')) axis = kTRUE;
3175 if (axis) PaintGraphSimple(theGraph,option);
3176
3177 Bool_t option0 = kFALSE;
3178 Bool_t option2 = kFALSE;
3179 Bool_t option3 = kFALSE;
3180 Bool_t option4 = kFALSE;
3181 Bool_t option5 = kFALSE;
3182 if (strchr(option,'0')) option0 = kTRUE;
3183 if (strchr(option,'2')) option2 = kTRUE;
3184 if (strchr(option,'3')) option3 = kTRUE;
3185 if (strchr(option,'4')) {option3 = kTRUE; option4 = kTRUE;}
3186 if (strchr(option,'5')) {option2 = kTRUE; option5 = kTRUE;}
3187
3188 if (option3) {
3189 xline.resize(2*theNpoints);
3190 yline.resize(2*theNpoints);
3191 if (xline.empty() || yline.empty()) {
3192 Error("PaintGraphBentErrors", "too many points, out of memory");
3193 return;
3194 }
3195 if1 = 1;
3196 if2 = 2*theNpoints;
3197 }
3198
3199 theGraph->TAttLine::Modify();
3200
3201 TArrow arrow;
3202 arrow.SetLineWidth(theGraph->GetLineWidth());
3203 arrow.SetLineColor(theGraph->GetLineColor());
3204 arrow.SetFillColor(theGraph->GetFillColor());
3205
3206 TBox box;
3207 Double_t x1b,y1b,x2b,y2b;
3208 box.SetLineWidth(theGraph->GetLineWidth());
3209 box.SetLineColor(theGraph->GetLineColor());
3210 box.SetFillColor(theGraph->GetFillColor());
3211 box.SetFillStyle(theGraph->GetFillStyle());
3212
3213 symbolsize = theGraph->GetMarkerSize();
3214 sbase = symbolsize*kBASEMARKER;
3216 Double_t cx = 0;
3217 Double_t cy = 0;
3218 if (mark >= 20 && mark <= 49) {
3219 cx = cxx[mark-20];
3220 cy = cyy[mark-20];
3221 }
3222
3223 // define the offset of the error bars due to the symbol size
3224 s2x = gPad->PixeltoX(Int_t(0.5*sbase)) - gPad->PixeltoX(0);
3225 s2y =-gPad->PixeltoY(Int_t(0.5*sbase)) + gPad->PixeltoY(0);
3226 Int_t dxend = Int_t(gStyle->GetEndErrorSize());
3227 tx = gPad->PixeltoX(dxend) - gPad->PixeltoX(0);
3228 ty =-gPad->PixeltoY(dxend) + gPad->PixeltoY(0);
3229 Float_t asize = 0.6*symbolsize*kBASEMARKER/gPad->GetWh();
3230
3232 for (Int_t i=0;i<theNpoints;i++) {
3233 x = gPad->XtoPad(theX[i]);
3234 y = gPad->YtoPad(theY[i]);
3235 bxl = gPad->YtoPad(theY[i]+theEXlowd[i]);
3236 bxh = gPad->YtoPad(theY[i]+theEXhighd[i]);
3237 byl = gPad->XtoPad(theX[i]+theEYlowd[i]);
3238 byh = gPad->XtoPad(theX[i]+theEYhighd[i]);
3239 if (!option0) {
3240 if (option3) {
3241 if (x < gPad->GetUxmin()) x = gPad->GetUxmin();
3242 if (x > gPad->GetUxmax()) x = gPad->GetUxmax();
3243 if (y < gPad->GetUymin()) y = gPad->GetUymin();
3244 if (y > gPad->GetUymax()) y = gPad->GetUymax();
3245 } else {
3246 if (x < gPad->GetUxmin()) continue;
3247 if (x > gPad->GetUxmax()) continue;
3248 if (y < gPad->GetUymin()) continue;
3249 if (y > gPad->GetUymax()) continue;
3250 }
3251 }
3252
3253 // draw the error rectangles
3254 if (option2) {
3255 x1b = gPad->XtoPad(theX[i] - theEXlow[i]);
3256 y1b = gPad->YtoPad(theY[i] - theEYlow[i]);
3257 x2b = gPad->XtoPad(theX[i] + theEXhigh[i]);
3258 y2b = gPad->YtoPad(theY[i] + theEYhigh[i]);
3259 if (x1b < gPad->GetUxmin()) x1b = gPad->GetUxmin();
3260 if (x1b > gPad->GetUxmax()) x1b = gPad->GetUxmax();
3261 if (y1b < gPad->GetUymin()) y1b = gPad->GetUymin();
3262 if (y1b > gPad->GetUymax()) y1b = gPad->GetUymax();
3263 if (x2b < gPad->GetUxmin()) x2b = gPad->GetUxmin();
3264 if (x2b > gPad->GetUxmax()) x2b = gPad->GetUxmax();
3265 if (y2b < gPad->GetUymin()) y2b = gPad->GetUymin();
3266 if (y2b > gPad->GetUymax()) y2b = gPad->GetUymax();
3267 if (option5) box.PaintBox(x1b, y1b, x2b, y2b, "l");
3268 else box.PaintBox(x1b, y1b, x2b, y2b);
3269 continue;
3270 }
3271
3272 // keep points for fill area drawing
3273 if (option3) {
3274 xline[if1-1] = byh;
3275 xline[if2-1] = byl;
3276 yline[if1-1] = gPad->YtoPad(theY[i] + theEYhigh[i]);
3277 yline[if2-1] = gPad->YtoPad(theY[i] - theEYlow[i]);
3278 if1++;
3279 if2--;
3280 continue;
3281 }
3282
3283 xl1 = x - s2x*cx;
3284 xl2 = gPad->XtoPad(theX[i] - theEXlow[i]);
3285 if (xl1 > xl2) {
3286 if (arrowOpt) {
3287 arrow.PaintArrow(xl1,y,xl2,bxl,asize,arrowOpt);
3288 } else {
3289 if (!brackets) gPad->PaintLine(xl1,y,xl2,bxl);
3290 if (endLines) {
3291 if (braticks) {
3292 xb[0] = xl2+tx; yb[0] = bxl-ty;
3293 xb[1] = xl2; yb[1] = bxl-ty;
3294 xb[2] = xl2; yb[2] = bxl+ty;
3295 xb[3] = xl2+tx; yb[3] = bxl+ty;
3296 gPad->PaintPolyLine(4, xb, yb);
3297 } else {
3298 gPad->PaintLine(xl2,bxl-ty,xl2,bxl+ty);
3299 }
3300 }
3301 }
3302 }
3303 xr1 = x + s2x*cx;
3304 xr2 = gPad->XtoPad(theX[i] + theEXhigh[i]);
3305 if (xr1 < xr2) {
3306 if (arrowOpt) {
3307 arrow.PaintArrow(xr1,y,xr2,bxh,asize,arrowOpt);
3308 } else {
3309 if (!brackets) gPad->PaintLine(xr1,y,xr2,bxh);
3310 if (endLines) {
3311 if (braticks) {
3312 xb[0] = xr2-tx; yb[0] = bxh-ty;
3313 xb[1] = xr2; yb[1] = bxh-ty;
3314 xb[2] = xr2; yb[2] = bxh+ty;
3315 xb[3] = xr2-tx; yb[3] = bxh+ty;
3316 gPad->PaintPolyLine(4, xb, yb);
3317 } else {
3318 gPad->PaintLine(xr2,bxh-ty,xr2,bxh+ty);
3319 }
3320 }
3321 }
3322 }
3323 yup1 = y + s2y*cy;
3324 yup2 = gPad->YtoPad(theY[i] + theEYhigh[i]);
3325 if (yup2 > gPad->GetUymax()) yup2 = gPad->GetUymax();
3326 if (yup2 > yup1) {
3327 if (arrowOpt) {
3328 arrow.PaintArrow(x,yup1,byh,yup2,asize,arrowOpt);
3329 } else {
3330 if (!brackets) gPad->PaintLine(x,yup1,byh,yup2);
3331 if (endLines) {
3332 if (braticks) {
3333 xb[0] = byh-tx; yb[0] = yup2-ty;
3334 xb[1] = byh-tx; yb[1] = yup2;
3335 xb[2] = byh+tx; yb[2] = yup2;
3336 xb[3] = byh+tx; yb[3] = yup2-ty;
3337 gPad->PaintPolyLine(4, xb, yb);
3338 } else {
3339 gPad->PaintLine(byh-tx,yup2,byh+tx,yup2);
3340 }
3341 }
3342 }
3343 }
3344 ylow1 = y - s2y*cy;
3345 ylow2 = gPad->YtoPad(theY[i] - theEYlow[i]);
3346 if (ylow2 < gPad->GetUymin()) ylow2 = gPad->GetUymin();
3347 if (ylow2 < ylow1) {
3348 if (arrowOpt) {
3349 arrow.PaintArrow(x,ylow1,byl,ylow2,asize,arrowOpt);
3350 } else {
3351 if (!brackets) gPad->PaintLine(x,ylow1,byl,ylow2);
3352 if (endLines) {
3353 if (braticks) {
3354 xb[0] = byl-tx; yb[0] = ylow2+ty;
3355 xb[1] = byl-tx; yb[1] = ylow2;
3356 xb[2] = byl+tx; yb[2] = ylow2;
3357 xb[3] = byl+tx; yb[3] = ylow2+ty;
3358 gPad->PaintPolyLine(4, xb, yb);
3359 } else {
3360 gPad->PaintLine(byl-tx,ylow2,byl+tx,ylow2);
3361 }
3362 }
3363 }
3364 }
3365 }
3366 if (!brackets && !axis) PaintGraphSimple(theGraph, option);
3367 gPad->ResetBit(TGraph::kClipFrame);
3368
3369 if (option3) {
3370 Int_t logx = gPad->GetLogx();
3371 Int_t logy = gPad->GetLogy();
3372 gPad->SetLogx(0);
3373 gPad->SetLogy(0);
3374 if (option4) PaintGraph(theGraph, 2*theNpoints, xline.data(), yline.data(),"FC");
3375 else PaintGraph(theGraph, 2*theNpoints, xline.data(), yline.data(),"F");
3376 gPad->SetLogx(logx);
3377 gPad->SetLogy(logy);
3378 }
3379}
3380
3381
3382////////////////////////////////////////////////////////////////////////////////
3383/// [Paint this TGraphErrors with its current attributes.](\ref GrP3)
3384
3386{
3387
3388 std::vector<Double_t> xline, yline;
3389 Int_t if1 = 0;
3390 Int_t if2 = 0;
3391 Double_t xb[4], yb[4];
3392
3393 const Int_t kBASEMARKER=8;
3394 Double_t s2x, s2y, symbolsize, sbase;
3395 Double_t x, y, ex, ey, xl1, xl2, xr1, xr2, yup1, yup2, ylow1, ylow2, tx, ty;
3396 static Float_t cxx[30] = {1.0,1.0,0.5,0.5,1.0,1.0,0.5,0.6,1.0,0.5,0.5,1.0,0.5,0.6,1.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0,1.0,1.0,1.0,1.0,0.5,0.5,0.5,1.0};
3397 static Float_t cyy[30] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0,1.0,1.0,1.0,1.0,0.5,0.5,0.5,1.0};
3398 Int_t theNpoints = theGraph->GetN();
3399 Double_t *theX = theGraph->GetX();
3400 Double_t *theY = theGraph->GetY();
3401 Double_t *theEX = theGraph->GetEX(); if (!theEX) return;
3402 Double_t *theEY = theGraph->GetEY(); if (!theEY) return;
3403
3404 if (strchr(option,'X') || strchr(option,'x')) {PaintGraphSimple(theGraph, option); return;}
3405 Bool_t brackets = kFALSE;
3406 Bool_t braticks = kFALSE;
3407 if (strstr(option,"||") || strstr(option,"[]")) {
3408 brackets = kTRUE;
3409 if (strstr(option,"[]")) braticks = kTRUE;
3410 }
3411 Bool_t endLines = kTRUE;
3412 if (strchr(option,'z')) endLines = kFALSE;
3413 if (strchr(option,'Z')) endLines = kFALSE;
3414 const char *arrowOpt = nullptr;
3415 if (strchr(option,'>')) arrowOpt = ">";
3416 if (strstr(option,"|>")) arrowOpt = "|>";
3417
3418 Bool_t axis = kFALSE;
3419 if (strchr(option,'a')) axis = kTRUE;
3420 if (strchr(option,'A')) axis = kTRUE;
3421 if (axis) PaintGraphSimple(theGraph, option);
3422
3423 Bool_t option0 = kFALSE;
3424 Bool_t option2 = kFALSE;
3425 Bool_t option3 = kFALSE;
3426 Bool_t option4 = kFALSE;
3427 Bool_t option5 = kFALSE;
3428 if (strchr(option,'0')) option0 = kTRUE;
3429 if (strchr(option,'2')) option2 = kTRUE;
3430 if (strchr(option,'3')) option3 = kTRUE;
3431 if (strchr(option,'4')) {option3 = kTRUE; option4 = kTRUE;}
3432 if (strchr(option,'5')) {option2 = kTRUE; option5 = kTRUE;}
3433
3434 if (option3) {
3435 xline.resize(2*theNpoints);
3436 yline.resize(2*theNpoints);
3437 if (xline.empty() || yline.empty()) {
3438 Error("Paint", "too many points, out of memory");
3439 return;
3440 }
3441 if1 = 1;
3442 if2 = 2*theNpoints;
3443 }
3444
3445 theGraph->TAttLine::Modify();
3446
3447 TArrow arrow;
3448 arrow.SetLineWidth(theGraph->GetLineWidth());
3449 arrow.SetLineColor(theGraph->GetLineColor());
3450 arrow.SetFillColor(theGraph->GetFillColor());
3451
3452 TBox box;
3453 Double_t x1b,y1b,x2b,y2b;
3454 box.SetLineWidth(theGraph->GetLineWidth());
3455 box.SetLineColor(theGraph->GetLineColor());
3456 box.SetFillColor(theGraph->GetFillColor());
3457 box.SetFillStyle(theGraph->GetFillStyle());
3458
3459 symbolsize = theGraph->GetMarkerSize();
3460 sbase = symbolsize*kBASEMARKER;
3462 Double_t cx = 0;
3463 Double_t cy = 0;
3464 if (mark >= 20 && mark <= 49) {
3465 cx = cxx[mark-20];
3466 cy = cyy[mark-20];
3467 }
3468
3469 // define the offset of the error bars due to the symbol size
3470 s2x = gPad->PixeltoX(Int_t(0.5*sbase)) - gPad->PixeltoX(0);
3471 s2y =-gPad->PixeltoY(Int_t(0.5*sbase)) + gPad->PixeltoY(0);
3472 Int_t dxend = Int_t(gStyle->GetEndErrorSize());
3473 tx = gPad->PixeltoX(dxend) - gPad->PixeltoX(0);
3474 ty =-gPad->PixeltoY(dxend) + gPad->PixeltoY(0);
3475 Float_t asize = 0.6*symbolsize*kBASEMARKER/gPad->GetWh();
3476
3478 for (Int_t i=0;i<theNpoints;i++) {
3479 x = gPad->XtoPad(theX[i]);
3480 y = gPad->YtoPad(theY[i]);
3481 if (!option0) {
3482 if (option3) {
3483 if (x < gPad->GetUxmin()) x = gPad->GetUxmin();
3484 if (x > gPad->GetUxmax()) x = gPad->GetUxmax();
3485 if (y < gPad->GetUymin()) y = gPad->GetUymin();
3486 if (y > gPad->GetUymax()) y = gPad->GetUymax();
3487 } else {
3488 if (x < gPad->GetUxmin()) continue;
3489 if (x > gPad->GetUxmax()) continue;
3490 if (y < gPad->GetUymin()) continue;
3491 if (y > gPad->GetUymax()) continue;
3492 }
3493 }
3494 ex = theEX[i];
3495 ey = theEY[i];
3496
3497 // draw the error rectangles
3498 if (option2) {
3499 x1b = gPad->XtoPad(theX[i] - ex);
3500 y1b = gPad->YtoPad(theY[i] - ey);
3501 x2b = gPad->XtoPad(theX[i] + ex);
3502 y2b = gPad->YtoPad(theY[i] + ey);
3503 if (x1b < gPad->GetUxmin()) x1b = gPad->GetUxmin();
3504 if (x1b > gPad->GetUxmax()) x1b = gPad->GetUxmax();
3505 if (y1b < gPad->GetUymin()) y1b = gPad->GetUymin();
3506 if (y1b > gPad->GetUymax()) y1b = gPad->GetUymax();
3507 if (x2b < gPad->GetUxmin()) x2b = gPad->GetUxmin();
3508 if (x2b > gPad->GetUxmax()) x2b = gPad->GetUxmax();
3509 if (y2b < gPad->GetUymin()) y2b = gPad->GetUymin();
3510 if (y2b > gPad->GetUymax()) y2b = gPad->GetUymax();
3511 if (option5) box.PaintBox(x1b, y1b, x2b, y2b, "l");
3512 else box.PaintBox(x1b, y1b, x2b, y2b);
3513 continue;
3514 }
3515
3516 // keep points for fill area drawing
3517 if (option3) {
3518 xline[if1-1] = x;
3519 xline[if2-1] = x;
3520 yline[if1-1] = gPad->YtoPad(theY[i] + ey);
3521 yline[if2-1] = gPad->YtoPad(theY[i] - ey);
3522 if1++;
3523 if2--;
3524 continue;
3525 }
3526
3527 xl1 = x - s2x*cx;
3528 xl2 = gPad->XtoPad(theX[i] - ex);
3529 if (xl1 > xl2) {
3530 if (arrowOpt) {
3531 arrow.PaintArrow(xl1,y,xl2,y,asize,arrowOpt);
3532 } else {
3533 if (!brackets) gPad->PaintLine(xl1,y,xl2,y);
3534 if (endLines) {
3535 if (braticks) {
3536 xb[0] = xl2+tx; yb[0] = y-ty;
3537 xb[1] = xl2; yb[1] = y-ty;
3538 xb[2] = xl2; yb[2] = y+ty;
3539 xb[3] = xl2+tx; yb[3] = y+ty;
3540 gPad->PaintPolyLine(4, xb, yb);
3541 } else {
3542 gPad->PaintLine(xl2,y-ty,xl2,y+ty);
3543 }
3544 }
3545 }
3546 }
3547 xr1 = x + s2x*cx;
3548 xr2 = gPad->XtoPad(theX[i] + ex);
3549 if (xr1 < xr2) {
3550 if (arrowOpt) {
3551 arrow.PaintArrow(xr1,y,xr2,y,asize,arrowOpt);
3552 } else {
3553 if (!brackets) gPad->PaintLine(xr1,y,xr2,y);
3554 if (endLines) {
3555 if (braticks) {
3556 xb[0] = xr2-tx; yb[0] = y-ty;
3557 xb[1] = xr2; yb[1] = y-ty;
3558 xb[2] = xr2; yb[2] = y+ty;
3559 xb[3] = xr2-tx; yb[3] = y+ty;
3560 gPad->PaintPolyLine(4, xb, yb);
3561 } else {
3562 gPad->PaintLine(xr2,y-ty,xr2,y+ty);
3563 }
3564 }
3565 }
3566 }
3567 yup1 = y + s2y*cy;
3568 yup2 = gPad->YtoPad(theY[i] + ey);
3569 if (yup2 > gPad->GetUymax()) yup2 = gPad->GetUymax();
3570 if (yup2 > yup1) {
3571 if (arrowOpt) {
3572 arrow.PaintArrow(x,yup1,x,yup2,asize,arrowOpt);
3573 } else {
3574 if (!brackets) gPad->PaintLine(x,yup1,x,yup2);
3575 if (endLines) {
3576 if (braticks) {
3577 xb[0] = x-tx; yb[0] = yup2-ty;
3578 xb[1] = x-tx; yb[1] = yup2;
3579 xb[2] = x+tx; yb[2] = yup2;
3580 xb[3] = x+tx; yb[3] = yup2-ty;
3581 gPad->PaintPolyLine(4, xb, yb);
3582 } else {
3583 gPad->PaintLine(x-tx,yup2,x+tx,yup2);
3584 }
3585 }
3586 }
3587 }
3588 ylow1 = y - s2y*cy;
3589 ylow2 = gPad->YtoPad(theY[i] - ey);
3590 if (ylow2 < gPad->GetUymin()) ylow2 = gPad->GetUymin();
3591 if (ylow2 < ylow1) {
3592 if (arrowOpt) {
3593 arrow.PaintArrow(x,ylow1,x,ylow2,asize,arrowOpt);
3594 } else {
3595 if (!brackets) gPad->PaintLine(x,ylow1,x,ylow2);
3596 if (endLines) {
3597 if (braticks) {
3598 xb[0] = x-tx; yb[0] = ylow2+ty;
3599 xb[1] = x-tx; yb[1] = ylow2;
3600 xb[2] = x+tx; yb[2] = ylow2;
3601 xb[3] = x+tx; yb[3] = ylow2+ty;
3602 gPad->PaintPolyLine(4, xb, yb);
3603 } else {
3604 gPad->PaintLine(x-tx,ylow2,x+tx,ylow2);
3605 }
3606 }
3607 }
3608 }
3609 }
3610 if (!brackets && !axis) PaintGraphSimple(theGraph, option);
3611 gPad->ResetBit(TGraph::kClipFrame);
3612
3613 if (option3) {
3614 Int_t logx = gPad->GetLogx();
3615 Int_t logy = gPad->GetLogy();
3616 gPad->SetLogx(0);
3617 gPad->SetLogy(0);
3618 if (option4) PaintGraph(theGraph, 2*theNpoints, xline.data(), yline.data(),"FC");
3619 else PaintGraph(theGraph, 2*theNpoints, xline.data(), yline.data(),"F");
3620 gPad->SetLogx(logx);
3621 gPad->SetLogy(logy);
3622 }
3623}
3624
3625
3626////////////////////////////////////////////////////////////////////////////////
3627/// [Paint this TGraphPolar with its current attributes.](\ref GrP4)
3628
3630{
3631
3632 Int_t ipt, i;
3633 Double_t rwrmin, rwrmax, rwtmin, rwtmax;
3634
3635 TGraphPolar *theGraphPolar = (TGraphPolar*) theGraph;
3636
3637 Int_t theNpoints = theGraphPolar->GetN();
3638 Double_t *theX = theGraphPolar->GetX();
3639 Double_t *theY = theGraphPolar->GetY();
3640 Double_t *theEX = theGraphPolar->GetEX();
3641 Double_t *theEY = theGraphPolar->GetEY();
3642
3643 if (theNpoints<1) return;
3644 TString opt = options;
3645 opt.ToUpper();
3646
3647 Bool_t nolabel = kFALSE;
3648 if (opt.Contains("N")){
3649 nolabel = kTRUE;
3650 opt.ReplaceAll("N","");
3651 }
3652
3653 TGraphPolargram *thePolargram = theGraphPolar->GetPolargram();
3654
3655 // Check for existing TGraphPolargram in the Pad
3656 if (gPad) {
3657 // Existing polargram
3658 if (thePolargram) if (!gPad->FindObject(thePolargram->GetName())) thePolargram=0;
3659 if (!thePolargram) {
3660 // Find any other Polargram in the Pad
3661 TListIter padObjIter(gPad->GetListOfPrimitives());
3662 while (TObject* AnyObj = padObjIter.Next()) {
3663 if (TString(AnyObj->ClassName()).CompareTo("TGraphPolargram",
3664 TString::kExact)==0)
3665 thePolargram = (TGraphPolargram*)AnyObj;
3666 theGraphPolar->SetPolargram(thePolargram);
3667 }
3668 }
3669 }
3670
3671 // Get new polargram range if necessary.
3672 if (!thePolargram) {
3673 // Get range, initialize with first/last value
3674 rwrmin = theY[0]; rwrmax = theY[theNpoints-1];
3675 rwtmin = theX[0]; rwtmax = theX[theNpoints-1];
3676
3677 for (ipt = 0; ipt < theNpoints; ipt++) {
3678 // Check for errors if available
3679 if (theEX) {
3680 if (theX[ipt] -theEX[ipt] < rwtmin) rwtmin = theX[ipt]-theEX[ipt];
3681 if (theX[ipt] +theEX[ipt] > rwtmax) rwtmax = theX[ipt]+theEX[ipt];
3682 } else {
3683 if (theX[ipt] < rwtmin) rwtmin=theX[ipt];
3684 if (theX[ipt] > rwtmax) rwtmax=theX[ipt];
3685 }
3686 if (theEY) {
3687 if (theY[ipt] -theEY[ipt] < rwrmin) rwrmin = theY[ipt]-theEY[ipt];
3688 if (theY[ipt] +theEY[ipt] > rwrmax) rwrmax = theY[ipt]+theEY[ipt];
3689 } else {
3690 if (theY[ipt] < rwrmin) rwrmin=theY[ipt];
3691 if (theY[ipt] > rwrmax) rwrmax=theY[ipt];
3692 }
3693 }
3694 // Add radial and Polar margins.
3695 if (rwrmin == rwrmax) rwrmax += 1.;
3696 if (rwtmin == rwtmax) rwtmax += 1.;
3697 Double_t dr = (rwrmax-rwrmin);
3698 Double_t dt = (rwtmax-rwtmin);
3699 rwrmax += 0.1*dr;
3700 rwrmin -= 0.1*dr;
3701
3702 // Assume equally spaced points for full 2*Pi.
3703 rwtmax += dt/theNpoints;
3704 } else {
3705 rwrmin = thePolargram->GetRMin();
3706 rwrmax = thePolargram->GetRMax();
3707 rwtmin = thePolargram->GetTMin();
3708 rwtmax = thePolargram->GetTMax();
3709 }
3710
3711 if ((!thePolargram) || theGraphPolar->GetOptionAxis()) {
3712 // Draw Polar coord system
3713 thePolargram = new TGraphPolargram("Polargram",rwrmin,rwrmax,rwtmin,rwtmax);
3714 theGraphPolar->SetPolargram(thePolargram);
3715 if (opt.Contains("O")) thePolargram->SetBit(TGraphPolargram::kLabelOrtho);
3716 else thePolargram->ResetBit(TGraphPolargram::kLabelOrtho);
3717 if (nolabel) thePolargram->Draw("N");
3718 else thePolargram->Draw("");
3719 theGraphPolar->SetOptionAxis(kFALSE); //Prevent redrawing
3720 }
3721
3722 // Convert points to polar.
3723 Double_t *theXpol = theGraphPolar->GetXpol();
3724 Double_t *theYpol = theGraphPolar->GetYpol();
3725
3726 // Project theta in [0,2*Pi] and radius in [0,1].
3727 Double_t radiusNDC = rwrmax-rwrmin;
3728 Double_t thetaNDC = (rwtmax-rwtmin)/(2*TMath::Pi());
3729
3730 // Draw the error bars.
3731 // Y errors are lines, but X errors are pieces of circles.
3732 if (opt.Contains("E")) {
3733 Double_t c=1;
3734 if (thePolargram->IsDegree()) {c=180/TMath::Pi();}
3735 if (thePolargram->IsGrad()) {c=100/TMath::Pi();}
3736 if (theEY) {
3737 for (i=0; i<theNpoints; i++) {
3738 Double_t eymin, eymax, exmin,exmax;
3739 exmin = (theY[i]-theEY[i]-rwrmin)/radiusNDC*
3740 TMath::Cos(c*(theX[i]-rwtmin)/thetaNDC);
3741 eymin = (theY[i]-theEY[i]-rwrmin)/radiusNDC*
3742 TMath::Sin(c*(theX[i]-rwtmin)/thetaNDC);
3743 exmax = (theY[i]+theEY[i]-rwrmin)/radiusNDC*
3744 TMath::Cos(c*(theX[i]-rwtmin)/thetaNDC);
3745 eymax = (theY[i]+theEY[i]-rwrmin)/radiusNDC*
3746 TMath::Sin(c*(theX[i]-rwtmin)/thetaNDC);
3747 theGraphPolar->TAttLine::Modify();
3748 if (exmin != exmax || eymin != eymax) gPad->PaintLine(exmin,eymin,exmax,eymax);
3749 }
3750 }
3751 if (theEX) {
3752 for (i=0; i<theNpoints; i++) {
3753 Double_t rad = (theY[i]-rwrmin)/radiusNDC;
3754 Double_t phimin = c*(theX[i]-theEX[i]-rwtmin)/thetaNDC*180/TMath::Pi();
3755 Double_t phimax = c*(theX[i]+theEX[i]-rwtmin)/thetaNDC*180/TMath::Pi();
3756 theGraphPolar->TAttLine::Modify();
3757 if (phimin != phimax) thePolargram->PaintCircle(0,0,rad,phimin,phimax,0);
3758 }
3759 }
3760 }
3761
3762 // Draw the graph itself.
3763 if (!(gPad->GetLogx()) && !(gPad->GetLogy())) {
3764 Double_t a, b, c=1, x1, x2, y1, y2, discr, norm1, norm2, xts, yts;
3765 Bool_t previouspointin = kFALSE;
3766 Double_t norm = 0;
3767 Double_t xt = 0;
3768 Double_t yt = 0 ;
3769 Int_t j = -1;
3770 if (thePolargram->IsDegree()) {c=180/TMath::Pi();}
3771 if (thePolargram->IsGrad()) {c=100/TMath::Pi();}
3772 for (i=0; i<theNpoints; i++) {
3773 xts = xt;
3774 yts = yt;
3775 xt = (theY[i]-rwrmin)/radiusNDC*TMath::Cos(c*(theX[i]-rwtmin)/thetaNDC);
3776 yt = (theY[i]-rwrmin)/radiusNDC*TMath::Sin(c*(theX[i]-rwtmin)/thetaNDC);
3777 norm = sqrt(xt*xt+yt*yt);
3778 // Check if points are in the main circle.
3779 if ( norm <= 1) {
3780 // We check that the previous point was in the circle too.
3781 // We record new point position.
3782 if (!previouspointin) {
3783 j++;
3784 theXpol[j] = xt;
3785 theYpol[j] = yt;
3786 } else {
3787 a = (yt-yts)/(xt-xts);
3788 b = yts-a*xts;
3789 discr = 4*(a*a-b*b+1);
3790 x1 = (-2*a*b+sqrt(discr))/(2*(a*a+1));
3791 x2 = (-2*a*b-sqrt(discr))/(2*(a*a+1));
3792 y1 = a*x1+b;
3793 y2 = a*x2+b;
3794 norm1 = sqrt((x1-xt)*(x1-xt)+(y1-yt)*(y1-yt));
3795 norm2 = sqrt((x2-xt)*(x2-xt)+(y2-yt)*(y2-yt));
3796 previouspointin = kFALSE;
3797 j = 0;
3798 if (norm1 < norm2) {
3799 theXpol[j] = x1;
3800 theYpol[j] = y1;
3801 } else {
3802 theXpol[j] = x2;
3803 theYpol[j] = y2;
3804 }
3805 j++;
3806 theXpol[j] = xt;
3807 theYpol[j] = yt;
3808 PaintGraph(theGraphPolar, j+1, theXpol, theYpol, opt);
3809 }
3810 } else {
3811 // We check that the previous point was in the circle.
3812 // We record new point position
3813 if (j>=1 && !previouspointin) {
3814 a = (yt-theYpol[j])/(xt-theXpol[j]);
3815 b = theYpol[j]-a*theXpol[j];
3816 previouspointin = kTRUE;
3817 discr = 4*(a*a-b*b+1);
3818 x1 = (-2*a*b+sqrt(discr))/(2*(a*a+1));
3819 x2 = (-2*a*b-sqrt(discr))/(2*(a*a+1));
3820 y1 = a*x1+b;
3821 y2 = a*x2+b;
3822 norm1 = sqrt((x1-xt)*(x1-xt)+(y1-yt)*(y1-yt));
3823 norm2 = sqrt((x2-xt)*(x2-xt)+(y2-yt)*(y2-yt));
3824 j++;
3825 if (norm1 < norm2) {
3826 theXpol[j] = x1;
3827 theYpol[j] = y1;
3828 } else {
3829 theXpol[j] = x2;
3830 theYpol[j] = y2;
3831 }
3832 PaintGraph(theGraphPolar, j+1, theXpol, theYpol, opt);
3833 }
3834 j=-1;
3835 }
3836 }
3837 if (j>=1) {
3838 // If the last point is in the circle, we draw the last serie of point.
3839 PaintGraph(theGraphPolar, j+1, theXpol, theYpol, opt);
3840 }
3841 } else {
3842 for (i=0; i<theNpoints; i++) {
3843 theXpol[i] = TMath::Abs((theY[i]-rwrmin)/radiusNDC*TMath::Cos((theX[i]-rwtmin)/thetaNDC)+1);
3844 theYpol[i] = TMath::Abs((theY[i]-rwrmin)/radiusNDC*TMath::Sin((theX[i]-rwtmin)/thetaNDC)+1);
3845 }
3846 PaintGraph(theGraphPolar, theNpoints, theXpol, theYpol,opt);
3847 }
3848
3849 // Paint the title.
3850
3851 if (TestBit(TH1::kNoTitle)) return;
3852 Int_t nt = strlen(theGraph->GetTitle());
3853 TPaveText *title = nullptr;
3854 TIter next(gPad->GetListOfPrimitives());
3855 while (auto obj = next()) {
3856 if (!obj->InheritsFrom(TPaveText::Class())) continue;
3857 title = (TPaveText*)obj;
3858 if (title->GetName())
3859 if (strcmp(title->GetName(),"title")) {title = nullptr; continue;}
3860 break;
3861 }
3862 if (nt == 0 || gStyle->GetOptTitle() <= 0) {
3863 if (title) delete title;
3864 return;
3865 }
3866 Double_t ht = gStyle->GetTitleH();
3867 Double_t wt = gStyle->GetTitleW();
3868 if (ht <= 0) ht = 1.1*gStyle->GetTitleFontSize();
3869 if (ht <= 0) ht = 0.05;
3870 if (wt <= 0) {
3871 TLatex l;
3872 l.SetTextSize(ht);
3873 l.SetTitle(theGraph->GetTitle());
3874 // Adjustment in case the title has several lines (#splitline)
3875 ht = TMath::Max(ht, 1.2*l.GetYsize()/(gPad->GetY2() - gPad->GetY1()));
3876 Double_t wndc = l.GetXsize()/(gPad->GetX2() - gPad->GetX1());
3877 wt = TMath::Min(0.7, 0.02+wndc);
3878 }
3879 if (title) {
3880 TText *t0 = (TText*)title->GetLine(0);
3881 if (t0) {
3882 if (!strcmp(t0->GetTitle(),theGraph->GetTitle())) return;
3883 t0->SetTitle(theGraph->GetTitle());
3884 if (wt > 0) title->SetX2NDC(title->GetX1NDC()+wt);
3885 }
3886 return;
3887 }
3888
3889 Int_t talh = gStyle->GetTitleAlign()/10;
3890 if (talh < 1) talh = 1; else if (talh > 3) talh = 3;
3891 Int_t talv = gStyle->GetTitleAlign()%10;
3892 if (talv < 1) talv = 1; else if (talv > 3) talv = 3;
3893
3895 xpos = gStyle->GetTitleX();
3896 ypos = gStyle->GetTitleY();
3897
3898 if (talh == 2) xpos = xpos-wt/2.;
3899 if (talh == 3) xpos = xpos-wt;
3900 if (talv == 2) ypos = ypos+ht/2.;
3901 if (talv == 1) ypos = ypos+ht;
3902
3903 TPaveText *ptitle = new TPaveText(xpos, ypos-ht, xpos+wt, ypos,"blNDC");
3904
3905 // Box with the histogram title.
3907 ptitle->SetFillStyle(gStyle->GetTitleStyle());
3908 ptitle->SetName("title");
3911 ptitle->SetTextFont(gStyle->GetTitleFont(""));
3912 if (gStyle->GetTitleFont("")%10 > 2)
3914 ptitle->AddText(theGraph->GetTitle());
3915 ptitle->SetBit(kCanDelete);
3916 ptitle->Draw();
3917 ptitle->Paint();
3918}
3919
3920
3921////////////////////////////////////////////////////////////////////////////////
3922/// Paint this graphQQ. No options for the time being.
3923
3925{
3926
3927 TGraphQQ *theGraphQQ = (TGraphQQ*) theGraph;
3928
3929 Double_t *theX = theGraphQQ->GetX();
3930 Double_t theXq1 = theGraphQQ->GetXq1();
3931 Double_t theXq2 = theGraphQQ->GetXq2();
3932 Double_t theYq1 = theGraphQQ->GetYq1();
3933 Double_t theYq2 = theGraphQQ->GetYq2();
3934 TF1 *theF = theGraphQQ->GetF();
3935
3936 if (!theX){
3937 Error("TGraphQQ::Paint", "2nd dataset or theoretical function not specified");
3938 return;
3939 }
3940
3941 if (theF){
3942 theGraphQQ->GetXaxis()->SetTitle("theoretical quantiles");
3943 theGraphQQ->GetYaxis()->SetTitle("data quantiles");
3944 }
3945
3946 PaintGraphSimple(theGraph,option);
3947
3948 Double_t xmin = gPad->GetUxmin();
3949 Double_t xmax = gPad->GetUxmax();
3950 Double_t ymin = gPad->GetUymin();
3951 Double_t ymax = gPad->GetUymax();
3952 Double_t yxmin, xymin, yxmax, xymax;
3953 Double_t xqmin = TMath::Max(xmin, theXq1);
3954 Double_t xqmax = TMath::Min(xmax, theXq2);
3955 Double_t yqmin = TMath::Max(ymin, theYq1);
3956 Double_t yqmax = TMath::Min(ymax, theYq2);
3957
3958 TLine line1, line2, line3;
3959 line1.SetLineStyle(2);
3960 line3.SetLineStyle(2);
3961 yxmin = (theYq2-theYq1)*(xmin-theXq1)/(theXq2-theXq1) + theYq1;
3962 if (yxmin < ymin){
3963 xymin = (theXq2-theXq1)*(ymin-theYq1)/(theYq2-theYq1) + theXq1;
3964 line1.PaintLine(xymin, ymin, xqmin, yqmin);
3965 }
3966 else
3967 line1.PaintLine(xmin, yxmin, xqmin, yqmin);
3968
3969 line2.PaintLine(xqmin, yqmin, xqmax, yqmax);
3970
3971 yxmax = (theYq2-theYq1)*(xmax-theXq1)/(theXq2-theXq1) + theYq1;
3972 if (yxmax > ymax){
3973 xymax = (theXq2-theXq1)*(ymax-theYq1)/(theYq2-theYq1) + theXq1;
3974 line3.PaintLine(xqmax, yqmax, xymax, ymax);
3975 }
3976 else
3977 line3.PaintLine(xqmax, yqmax, xmax, yxmax);
3978}
3979
3980
3981////////////////////////////////////////////////////////////////////////////////
3982/// Paint theGraph reverting values along X and/or Y axis. a new graph is created.
3983
3985{
3986 TString opt = option;
3987 opt.ToLower();
3988 TH1F *theHist = (TH1F *)theGraph->GetHistogram();
3989
3990 Bool_t lrx = opt.Contains("rx");
3991 Bool_t lry = opt.Contains("ry");
3992 Bool_t lxp = opt.Contains("x+");
3993 Bool_t lyp = opt.Contains("y+");
3994 Bool_t axis = opt.Contains("a");
3995 opt.ReplaceAll("a", "");
3996
3997 Double_t LOX = theHist->GetXaxis()->GetLabelOffset();
3998 Double_t TLX = theHist->GetXaxis()->GetTickLength();
3999 Double_t LOY = theHist->GetYaxis()->GetLabelOffset();
4000 Double_t TLY = theHist->GetYaxis()->GetTickLength();
4001 Int_t XACOL = theHist->GetXaxis()->GetAxisColor();
4002 Int_t YACOL = theHist->GetYaxis()->GetAxisColor();
4003
4004 if (axis) {
4005 if (lrx) {
4006 theHist->GetXaxis()->SetTickLength(0.);
4007 theHist->GetXaxis()->SetLabelOffset(999.);
4008 theHist->GetXaxis()->SetAxisColor(gPad->GetFrameFillColor());
4009 }
4010 if (lry) {
4011 theHist->GetYaxis()->SetTickLength(0.);
4012 theHist->GetYaxis()->SetLabelOffset(999.);
4013 theHist->GetYaxis()->SetAxisColor(gPad->GetFrameFillColor());
4014 }
4015 TString opth = "0";
4016 if (lxp) opth.Append("x+");
4017 if (lyp) opth.Append("y+");
4018 theHist->Paint(opth.Data());
4019 }
4020
4021 Int_t N = theGraph->GetN();
4022 Double_t *X = theGraph->GetX();
4023 Double_t *Y = theGraph->GetY();
4024 Double_t XA1, XA2, YA1, YA2;
4025 if (axis) {
4026 XA1 = theGraph->GetXaxis()->GetXmin();
4027 XA2 = theGraph->GetXaxis()->GetXmax();
4028 YA1 = theGraph->GetYaxis()->GetXmin();
4029 YA2 = theGraph->GetYaxis()->GetXmax();
4030 } else {
4031 XA1 = gPad->GetUxmin();
4032 XA2 = gPad->GetUxmax();
4033 YA1 = gPad->GetUymin();
4034 YA2 = gPad->GetUymax();
4035 }
4036 Double_t dX = XA1+XA2;
4037 Double_t dY = YA1+YA2;
4038
4039 std::vector<Double_t> newX(N);
4040 std::vector<Double_t> newY(N);
4041
4042 if (lrx) {
4043 opt.ReplaceAll("rx", "");
4044 if (axis) {
4045 Double_t GL = 0.;
4046 theHist->GetXaxis()->SetTickLength(0.);
4047 theHist->GetXaxis()->SetLabelOffset(999.);
4048
4049 // Redraw the new X axis
4050 gPad->Update();
4051 TString optax = "-SDH";
4052 if (gPad->GetGridx()) {
4053 GL = (YA2 - YA1) / (gPad->GetY2() - gPad->GetY1());
4054 optax.Append("W");
4055 }
4056 Double_t ypos;
4057 if (lxp) ypos = gPad->GetUymax();
4058 else ypos = gPad->GetUymin();
4059 auto *theNewAxis = new TGaxis(gPad->GetUxmax(),
4060 ypos,
4061 gPad->GetUxmin(),
4062 ypos,
4063 theGraph->GetXaxis()->GetXmin(),
4064 theGraph->GetXaxis()->GetXmax(),
4065 theHist->GetNdivisions("X"),
4066 optax.Data(), -GL);
4067 theNewAxis->SetLabelFont(theGraph->GetXaxis()->GetLabelFont());
4068 theNewAxis->SetLabelSize(theGraph->GetXaxis()->GetLabelSize());
4069 theNewAxis->SetLabelColor(theGraph->GetXaxis()->GetLabelColor());
4070 theNewAxis->SetTickLength(TLX);
4071 theNewAxis->SetLabelOffset(LOX - theGraph->GetXaxis()->GetLabelSize());
4072 theNewAxis->Paint();
4073 }
4074 // Reverse X coordinates
4075 for (Int_t i=0; i<N; i++) newX[i] = dX-X[i];
4076 } else {
4077 for (Int_t i=0; i<N; i++) newX[i] = X[i];
4078 }
4079
4080 if (lry) {
4081 opt.ReplaceAll("ry", "");
4082 if (axis) {
4083 Double_t GL = 0.;
4084 // Redraw the new Y axis
4085 gPad->Update();
4086 TString optax = "-SDH";
4087
4088 if (gPad->GetGridy()) {
4089 GL = (XA2 - XA1) / (gPad->GetX2() - gPad->GetX1());
4090 optax.Append("W");
4091 }
4092 Double_t xpos;
4093 if (lyp) xpos = gPad->GetUxmax();
4094 else xpos = gPad->GetUxmin();
4095 auto *theNewAxis = new TGaxis(xpos,
4096 gPad->GetUymax(),
4097 xpos,
4098 gPad->GetUymin(),
4099 theGraph->GetYaxis()->GetXmin(),
4100 theGraph->GetYaxis()->GetXmax(),
4101 theHist->GetNdivisions("Y"),
4102 optax.Data(), GL);
4103 theNewAxis->SetLabelFont(theGraph->GetYaxis()->GetLabelFont());
4104 theNewAxis->SetLabelSize(theGraph->GetYaxis()->GetLabelSize());
4105 theNewAxis->SetLabelColor(theGraph->GetYaxis()->GetLabelColor());
4106 theNewAxis->SetTickLength(-TLY);
4107 theNewAxis->SetLabelOffset(LOY-TLY);
4108 theNewAxis->Paint();
4109 }
4110 // Reverse Y coordinates
4111 for (Int_t i=0; i<N; i++) newY[i] = dY-Y[i];
4112 } else {
4113 for (Int_t i=0; i<N; i++) newY[i] = Y[i];
4114 }
4115
4116 // Create the new reversed graph
4117 TGraph *theNewGraph = (TGraph*)theGraph->Clone();
4118 for (Int_t i=0; i<N; i++) theNewGraph->SetPoint(i, newX[i], newY[i]);
4119 theNewGraph->SetMarkerStyle(theGraph->GetMarkerStyle());
4120 theNewGraph->SetMarkerColor(theGraph->GetMarkerColor());
4121 theNewGraph->SetLineStyle(theGraph->GetLineStyle());
4122 theNewGraph->SetLineColor(theGraph->GetLineColor());
4123
4124 PaintHelper(theNewGraph,opt.Data());
4125
4126 theHist->GetXaxis()->SetLabelOffset(LOX);
4127 theHist->GetXaxis()->SetTickLength(TLX);
4128 theHist->GetYaxis()->SetLabelOffset(LOY);
4129 theHist->GetYaxis()->SetTickLength(TLY);
4130 theHist->GetXaxis()->SetAxisColor(XACOL);
4131 theHist->GetYaxis()->SetAxisColor(YACOL);
4132}
4133
4134
4135////////////////////////////////////////////////////////////////////////////////
4136/// Paint a simple graph, without errors bars.
4137
4139{
4140
4141 if (strstr(option,"H") || strstr(option,"h")) {
4142 PaintGrapHist(theGraph, theGraph->GetN(), theGraph->GetX(), theGraph->GetY(), option);
4143 } else {
4144 PaintGraph(theGraph, theGraph->GetN(), theGraph->GetX(), theGraph->GetY(), option);
4145 }
4146
4147 PaintHighlightPoint(theGraph, option);
4148
4149 // Paint associated objects in the list of functions (for instance
4150 // the fit function).
4151 TList *functions = theGraph->GetListOfFunctions();
4152 if (!functions) return;
4153 TObjOptLink *lnk = (TObjOptLink*)functions->FirstLink();
4154 TObject *obj;
4155
4156 while (lnk) {
4157 obj = lnk->GetObject();
4158 TVirtualPad *padsave = gPad;
4159 if (obj->InheritsFrom(TF1::Class())) {
4160 if (obj->TestBit(TF1::kNotDraw) == 0) obj->Paint("lsame");
4161 } else {
4162 obj->Paint(lnk->GetOption());
4163 }
4164 lnk = (TObjOptLink*)lnk->Next();
4165 padsave->cd();
4166 }
4167 return;
4168}
4169
4170
4171////////////////////////////////////////////////////////////////////////////////
4172/// Paint a polyline with hatches on one side showing an exclusion zone. x and y
4173/// are the the vectors holding the polyline and n the number of points in the
4174/// polyline and `w` the width of the hatches. `w` can be negative.
4175/// This method is not meant to be used directly. It is called automatically
4176/// according to the line style convention.
4177
4179{
4180
4181 Int_t i,j,nf;
4182 Double_t w = (theGraph->GetLineWidth()/100)*0.005;
4183
4184 std::vector<Double_t> xf(2*n);
4185 std::vector<Double_t> yf(2*n);
4186 std::vector<Double_t> xt(n);
4187 std::vector<Double_t> yt(n);
4188 Double_t x1, x2, y1, y2, x3, y3, xm, ym, a, a1, a2, a3;
4189
4190 // Compute the gPad coordinates in TRUE normalized space (NDC)
4191 Int_t ix1,iy1,ix2,iy2;
4192 Int_t iw = gPad->GetWw();
4193 Int_t ih = gPad->GetWh();
4194 Double_t x1p,y1p,x2p,y2p;
4195 gPad->GetPadPar(x1p,y1p,x2p,y2p);
4196 ix1 = (Int_t)(iw*x1p);
4197 iy1 = (Int_t)(ih*y1p);
4198 ix2 = (Int_t)(iw*x2p);
4199 iy2 = (Int_t)(ih*y2p);
4200 Double_t wndc = TMath::Min(1.,(Double_t)iw/(Double_t)ih);
4201 Double_t hndc = TMath::Min(1.,(Double_t)ih/(Double_t)iw);
4202 Double_t rh = hndc/(Double_t)ih;
4203 Double_t rw = wndc/(Double_t)iw;
4204 Double_t x1ndc = (Double_t)ix1*rw;
4205 Double_t y1ndc = (Double_t)iy1*rh;
4206 Double_t x2ndc = (Double_t)ix2*rw;
4207 Double_t y2ndc = (Double_t)iy2*rh;
4208
4209 // Ratios to convert user space in TRUE normalized space (NDC)
4210 Double_t rx1,ry1,rx2,ry2;
4211 gPad->GetRange(rx1,ry1,rx2,ry2);
4212 Double_t rx = (x2ndc-x1ndc)/(rx2-rx1);
4213 Double_t ry = (y2ndc-y1ndc)/(ry2-ry1);
4214
4215 // The first part of the filled area is made of the graph points.
4216 // Make sure that two adjacent points are different.
4217 xf[0] = rx*(x[0]-rx1)+x1ndc;
4218 yf[0] = ry*(y[0]-ry1)+y1ndc;
4219 nf = 0;
4220 for (i=1; i<n; i++) {
4221 if (x[i]==x[i-1] && y[i]==y[i-1]) continue;
4222 nf++;
4223 xf[nf] = rx*(x[i]-rx1)+x1ndc;
4224 if (xf[i]==xf[i-1]) xf[i] += 0.000001; // add an epsilon to avoid exact vertical lines.
4225 yf[nf] = ry*(y[i]-ry1)+y1ndc;
4226 }
4227
4228 // For each graph points a shifted points is computed to build up
4229 // the second part of the filled area. First and last points are
4230 // treated as special cases, outside of the loop.
4231 if (xf[1]==xf[0]) {
4232 a = TMath::PiOver2();
4233 } else {
4234 a = TMath::ATan((yf[1]-yf[0])/(xf[1]-xf[0]));
4235 }
4236 if (xf[0]<=xf[1]) {
4237 xt[0] = xf[0]-w*TMath::Sin(a);
4238 yt[0] = yf[0]+w*TMath::Cos(a);
4239 } else {
4240 xt[0] = xf[0]+w*TMath::Sin(a);
4241 yt[0] = yf[0]-w*TMath::Cos(a);
4242 }
4243
4244 if (xf[nf]==xf[nf-1]) {
4245 a = TMath::PiOver2();
4246 } else {
4247 a = TMath::ATan((yf[nf]-yf[nf-1])/(xf[nf]-xf[nf-1]));
4248 }
4249 if (xf[nf]>=xf[nf-1]) {
4250 xt[nf] = xf[nf]-w*TMath::Sin(a);
4251 yt[nf] = yf[nf]+w*TMath::Cos(a);
4252 } else {
4253 xt[nf] = xf[nf]+w*TMath::Sin(a);
4254 yt[nf] = yf[nf]-w*TMath::Cos(a);
4255 }
4256
4257 Double_t xi0,yi0,xi1,yi1,xi2,yi2;
4258 for (i=1; i<nf; i++) {
4259 xi0 = xf[i];
4260 yi0 = yf[i];
4261 xi1 = xf[i+1];
4262 yi1 = yf[i+1];
4263 xi2 = xf[i-1];
4264 yi2 = yf[i-1];
4265 if (xi1==xi0) {
4266 a1 = TMath::PiOver2();
4267 } else {
4268 a1 = TMath::ATan((yi1-yi0)/(xi1-xi0));
4269 }
4270 if (xi1<xi0) a1 = a1+3.14159;
4271 if (xi2==xi0) {
4272 a2 = TMath::PiOver2();
4273 } else {
4274 a2 = TMath::ATan((yi0-yi2)/(xi0-xi2));
4275 }
4276 if (xi0<xi2) a2 = a2+3.14159;
4277 x1 = xi0-w*TMath::Sin(a1);
4278 y1 = yi0+w*TMath::Cos(a1);
4279 x2 = xi0-w*TMath::Sin(a2);
4280 y2 = yi0+w*TMath::Cos(a2);
4281 xm = (x1+x2)*0.5;
4282 ym = (y1+y2)*0.5;
4283 if (xm==xi0) {
4284 a3 = TMath::PiOver2();
4285 } else {
4286 a3 = TMath::ATan((ym-yi0)/(xm-xi0));
4287 }
4288 x3 = xi0-w*TMath::Sin(a3+1.57079);
4289 y3 = yi0+w*TMath::Cos(a3+1.57079);
4290 // Rotate (x3,y3) by PI around (xi0,yi0) if it is not on the (xm,ym) side.
4291 if ((xm-xi0)*(x3-xi0)<0 && (ym-yi0)*(y3-yi0)<0) {
4292 x3 = 2*xi0-x3;
4293 y3 = 2*yi0-y3;
4294 }
4295 if ((xm==x1) && (ym==y1)) {
4296 x3 = xm;
4297 y3 = ym;
4298 }
4299 xt[i] = x3;
4300 yt[i] = y3;
4301 }
4302
4303 // Close the polygon if the first and last points are the same
4304 if (xf[nf]==xf[0] && yf[nf]==yf[0]) {
4305 xm = (xt[nf]+xt[0])*0.5;
4306 ym = (yt[nf]+yt[0])*0.5;
4307 if (xm==xf[0]) {
4308 a3 = TMath::PiOver2();
4309 } else {
4310 a3 = TMath::ATan((ym-yf[0])/(xm-xf[0]));
4311 }
4312 x3 = xf[0]+w*TMath::Sin(a3+1.57079);
4313 y3 = yf[0]-w*TMath::Cos(a3+1.57079);
4314 if ((xm-xf[0])*(x3-xf[0])<0 && (ym-yf[0])*(y3-yf[0])<0) {
4315 x3 = 2*xf[0]-x3;
4316 y3 = 2*yf[0]-y3;
4317 }
4318 xt[nf] = x3;
4319 xt[0] = x3;
4320 yt[nf] = y3;
4321 yt[0] = y3;
4322 }
4323
4324 // Find the crossing segments and remove the useless ones
4325 Double_t xc, yc, c1, b1, c2, b2;
4326 Bool_t cross = kFALSE;
4327 Int_t nf2 = nf;
4328 for (i=nf2; i>0; i--) {
4329 for (j=i-1; j>0; j--) {
4330 if (xt[i-1]==xt[i] || xt[j-1]==xt[j]) continue;
4331 c1 = (yt[i-1]-yt[i])/(xt[i-1]-xt[i]);
4332 b1 = yt[i]-c1*xt[i];
4333 c2 = (yt[j-1]-yt[j])/(xt[j-1]-xt[j]);
4334 b2 = yt[j]-c2*xt[j];
4335 if (c1 != c2) {
4336 xc = (b2-b1)/(c1-c2);
4337 yc = c1*xc+b1;
4338 if (xc>TMath::Min(xt[i],xt[i-1]) && xc<TMath::Max(xt[i],xt[i-1]) &&
4339 xc>TMath::Min(xt[j],xt[j-1]) && xc<TMath::Max(xt[j],xt[j-1]) &&
4340 yc>TMath::Min(yt[i],yt[i-1]) && yc<TMath::Max(yt[i],yt[i-1]) &&
4341 yc>TMath::Min(yt[j],yt[j-1]) && yc<TMath::Max(yt[j],yt[j-1])) {
4342 nf++; xf[nf] = xt[i]; yf[nf] = yt[i];
4343 nf++; xf[nf] = xc ; yf[nf] = yc;
4344 i = j;
4345 cross = kTRUE;
4346 break;
4347 } else {
4348 continue;
4349 }
4350 } else {
4351 continue;
4352 }
4353 }
4354 if (!cross) {
4355 nf++;
4356 xf[nf] = xt[i];
4357 yf[nf] = yt[i];
4358 }
4359 cross = kFALSE;
4360 }
4361 nf++; xf[nf] = xt[0]; yf[nf] = yt[0];
4362
4363 // NDC to user coordinates
4364 for (i=0; i<nf+1; i++) {
4365 xf[i] = (1/rx)*(xf[i]-x1ndc)+rx1;
4366 yf[i] = (1/ry)*(yf[i]-y1ndc)+ry1;
4367 }
4368
4369 // Draw filled area
4370 gPad->PaintFillArea(nf+1,xf.data(),yf.data());
4371 theGraph->TAttLine::Modify(); // In case of PaintFillAreaHatches
4372}
4373
4374
4375////////////////////////////////////////////////////////////////////////////////
4376/// Paint the statistics box with the fit info.
4377
4379{
4380
4381 Int_t dofit;
4382 TPaveStats *stats = nullptr;
4383 TList *functions = theGraph->GetListOfFunctions();
4384 TIter next(functions);
4385 while (auto obj = next()) {
4386 if (obj->InheritsFrom(TPaveStats::Class())) {
4387 stats = (TPaveStats*)obj;
4388 break;
4389 }
4390 }
4391
4392 if (stats) dofit = stats->GetOptFit();
4393 else dofit = gStyle->GetOptFit();
4394
4395 if (!dofit) fit = 0;
4396 if (!fit) return;
4397 if (dofit == 1) dofit = 111;
4398 Int_t nlines = 0;
4399 Int_t print_fval = dofit%10;
4400 Int_t print_ferrors = (dofit/10)%10;
4401 Int_t print_fchi2 = (dofit/100)%10;
4402 Int_t print_fprob = (dofit/1000)%10;
4403 Int_t nlinesf = print_fval + print_fchi2 + print_fprob;
4404 if (fit) {
4405 if (print_fval < 2) nlinesf += fit->GetNumberFreeParameters();
4406 else nlinesf += fit->GetNpar();
4407 }
4408 Bool_t done = kFALSE;
4409 Double_t statw = 1.8*gStyle->GetStatW();
4410 Double_t stath = 0.25*(nlines+nlinesf)*gStyle->GetStatH();
4411 if (stats) {
4412 stats->Clear();
4413 done = kTRUE;
4414 } else {
4415 stats = new TPaveStats(
4416 gStyle->GetStatX()-statw,
4417 gStyle->GetStatY()-stath,
4418 gStyle->GetStatX(),
4419 gStyle->GetStatY(),"brNDC");
4420
4421 stats->SetParent(functions);
4422 stats->SetOptFit(dofit);
4423 stats->SetOptStat(0);
4424 stats->SetFillColor(gStyle->GetStatColor());
4425 stats->SetFillStyle(gStyle->GetStatStyle());
4427 stats->SetTextFont(gStyle->GetStatFont());
4428 if (gStyle->GetStatFont()%10 > 2)
4430 stats->SetFitFormat(gStyle->GetFitFormat());
4432 stats->SetName("stats");
4433
4435 stats->SetTextAlign(12);
4436 stats->SetBit(kCanDelete);
4437 stats->SetBit(kMustCleanup);
4438 }
4439
4440 char t[64];
4441 char textstats[50];
4442 Int_t ndf = fit->GetNDF();
4443 snprintf(textstats,50,"#chi^{2} / ndf = %s%s / %d","%",stats->GetFitFormat(),ndf);
4444 snprintf(t,64,textstats,(Float_t)fit->GetChisquare());
4445 if (print_fchi2) stats->AddText(t);
4446 if (print_fprob) {
4447 snprintf(textstats,50,"Prob = %s%s","%",stats->GetFitFormat());
4448 snprintf(t,64,textstats,(Float_t)TMath::Prob(fit->GetChisquare(),ndf));
4449 stats->AddText(t);
4450 }
4451 if (print_fval || print_ferrors) {
4452 Double_t parmin,parmax;
4453 for (Int_t ipar=0;ipar<fit->GetNpar();ipar++) {
4454 fit->GetParLimits(ipar,parmin,parmax);
4455 if (print_fval < 2 && parmin*parmax != 0 && parmin >= parmax) continue;
4456 if (print_ferrors) {
4457 snprintf(textstats,50,"%-8s = %s%s #pm %s%s ",fit->GetParName(ipar),"%",stats->GetFitFormat(),"%",stats->GetFitFormat());
4458 snprintf(t,64,textstats,(Float_t)fit->GetParameter(ipar)
4459 ,(Float_t)fit->GetParError(ipar));
4460 } else {
4461 snprintf(textstats,50,"%-8s = %s%s ",fit->GetParName(ipar),"%",stats->GetFitFormat());
4462 snprintf(t,64,textstats,(Float_t)fit->GetParameter(ipar));
4463 }
4464 t[63] = 0;
4465 stats->AddText(t);
4466 }
4467 }
4468
4469 if (!done) functions->Add(stats);
4470 stats->Paint();
4471}
4472
4473
4474////////////////////////////////////////////////////////////////////////////////
4475/// Smooth a curve given by N points.
4476///
4477/// The original code is from an underlaying routine for Draw based on the
4478/// CERN GD3 routine TVIPTE:
4479///
4480/// Author - Marlow etc. Modified by - P. Ward Date - 3.10.1973
4481///
4482/// This method draws a smooth tangentially continuous curve through
4483/// the sequence of data points P(I) I=1,N where P(I)=(X(I),Y(I)).
4484/// The curve is approximated by a polygonal arc of short vectors.
4485/// The data points can represent open curves, P(1) != P(N) or closed
4486/// curves P(2) == P(N). If a tangential discontinuity at P(I) is
4487/// required, then set P(I)=P(I+1). Loops are also allowed.
4488///
4489/// Reference Marlow and Powell, Harwell report No.R.7092.1972
4490/// MCCONALOGUE, Computer Journal VOL.13, NO4, NOV1970P p392 6
4491///
4492/// - npoints : Number of data points.
4493/// - x : Abscissa
4494/// - y : Ordinate
4495
4496void TGraphPainter::Smooth(TGraph *theGraph, Int_t npoints, Double_t *x, Double_t *y, Int_t drawtype)
4497{
4498
4499 Int_t i, k, kp, km, npointsMax, banksize, n2, npt;
4500 Int_t maxiterations, finished;
4501 Int_t jtype, ktype, closed;
4502 Double_t sxmin, sxmax, symin, symax;
4503 Double_t delta;
4504 Double_t xorg, yorg;
4505 Double_t ratio_signs, xratio, yratio;
4506 Int_t flgic, flgis;
4507 Int_t iw, loptx;
4508 Double_t p1, p2, p3, p4, p5, p6;
4509 Double_t w1, w2, w3;
4510 Double_t a, b, c, r, s=0.0, t, z;
4511 Double_t co, so, ct, st, ctu, stu, xnt;
4512 Double_t dx1, dy1, dx2, dy2, dk1, dk2;
4513 Double_t xo, yo, dx, dy, xt, yt;
4514 Double_t xa, xb, ya, yb;
4515 Double_t u1, u2, u3, tj;
4516 Double_t cc, err;
4517 Double_t sb, sth;
4518 Double_t wsign, tsquare, tcube;
4519 c = t = co = so = ct = st = ctu = stu = dx1 = dy1 = dx2 = dy2 = 0;
4520 xt = yt = xa = xb = ya = yb = u1 = u2 = u3 = tj = sb = 0;
4521
4522 npointsMax = npoints*10;
4523 n2 = npointsMax-2;
4524 banksize = n2;
4525
4526 std::vector<Double_t> qlx(npointsMax);
4527 std::vector<Double_t> qly(npointsMax);
4528 if (qlx.empty() || qly.empty()) {
4529 Error("Smooth", "not enough space in memory");
4530 return;
4531 }
4532
4533 // Decode the type of curve (draw type).
4534
4535 loptx = kFALSE;
4536 jtype = (drawtype%1000)-10;
4537 if (jtype > 0) { ktype = jtype; loptx = kTRUE; }
4538 else ktype = drawtype%1000;
4539
4540 Double_t ruxmin = gPad->GetUxmin();
4541 Double_t ruymin = gPad->GetUymin();
4542 if (ktype == 3) {
4543 xorg = ruxmin;
4544 yorg = ruymin;
4545 } else {
4546 xorg = TMath::Max((Double_t)0,ruxmin);
4547 yorg = TMath::Min(TMath::Max((Double_t)0,ruymin),gPad->GetUymax());
4548 }
4549
4550 // delta is the accuracy required in constructing the curve.
4551 // If it is zero then the routine calculates a value otherwise
4552 // it uses this value. (default is 0.0)
4553
4554 delta = 0.00055;
4555 maxiterations = 20;
4556
4557 // Scale data to the range 0-ratio_signs in X, 0-1 in Y
4558 // where ratio_signs is the ratio between the number of changes
4559 // of sign in Y divided by the number of changes of sign in X
4560
4561 sxmin = x[0];
4562 sxmax = x[0];
4563 symin = y[0];
4564 symax = y[0];
4565 Double_t six = 1;
4566 Double_t siy = 1;
4567 for (i=1;i<npoints;i++) {
4568 if (i > 1) {
4569 if ((x[i]-x[i-1])*(x[i-1]-x[i-2]) < 0) six++;
4570 if ((y[i]-y[i-1])*(y[i-1]-y[i-2]) < 0) siy++;
4571 }
4572 if (x[i] < sxmin) sxmin = x[i];
4573 if (x[i] > sxmax) sxmax = x[i];
4574 if (y[i] < symin) symin = y[i];
4575 if (y[i] > symax) symax = y[i];
4576 }
4577 closed = 0;
4578 Double_t dx1n = TMath::Abs(x[npoints-1]-x[0]);
4579 Double_t dy1n = TMath::Abs(y[npoints-1]-y[0]);
4580 if (dx1n < 0.01*(sxmax-sxmin) && dy1n < 0.01*(symax-symin)) closed = 1;
4581 if (sxmin == sxmax) {
4582 xratio = 1;
4583 } else {
4584 if (six > 1) ratio_signs = siy/six;
4585 else ratio_signs = 20;
4586 xratio = ratio_signs/(sxmax-sxmin);
4587 }
4588 if (symin == symax) yratio = 1;
4589 else yratio = 1/(symax-symin);
4590
4591 qlx[0] = x[0];
4592 qly[0] = y[0];
4593 for (i=0;i<npoints;i++) {
4594 x[i] = (x[i]-sxmin)*xratio;
4595 y[i] = (y[i]-symin)*yratio;
4596 }
4597
4598 // "finished" is minus one if we must draw a straight line from P(k-1)
4599 // to P(k). "finished" is one if the last call to PaintPolyLine has < n2
4600 // points. "finished" is zero otherwise. npt counts the X and Y
4601 // coordinates in work . When npt=n2 a call to IPL is made.
4602
4603 finished = 0;
4604 npt = 1;
4605 k = 1;
4606
4607 // Convert coordinates back to original system
4608
4609 // Separate the set of data points into arcs P(k-1),P(k).
4610 // Calculate the direction cosines. first consider whether
4611 // there is a continuous tangent at the endpoints.
4612
4613 if (!closed) {
4614 if (x[0] != x[npoints-1] || y[0] != y[npoints-1]) goto L40;
4615 if (x[npoints-2] == x[npoints-1] && y[npoints-2] == y[npoints-1]) goto L40;
4616 if (x[0] == x[1] && y[0] == y[1]) goto L40;
4617 }
4618 flgic = kFALSE;
4619 flgis = kTRUE;
4620
4621 // flgic is true if the curve is open and false if it is closed.
4622 // flgis is true in the main loop, but is false if there is
4623 // a deviation from the main loop.
4624
4625 km = npoints - 1;
4626
4627 // Calculate direction cosines at P(1) using P(N-1),P(1),P(2).
4628
4629 goto L100;
4630L40:
4631 flgic = kTRUE;
4632 flgis = kFALSE;
4633
4634 // Skip excessive consecutive equal points.
4635
4636L50:
4637 if (k >= npoints) {
4638 finished = 1; // Prepare to clear out remaining short vectors before returning
4639 if (npt > 1) goto L310;
4640 goto L390;
4641 }
4642 k++;
4643 if (x[k-1] == x[k-2] && y[k-1] == y[k-2]) goto L50;
4644L60:
4645 km = k-1;
4646 if (k > npoints) {
4647 finished = 1; // Prepare to clear out remaining short vectors before returning
4648 if (npt > 1) goto L310;
4649 goto L390;
4650 }
4651 if (k < npoints) goto L90;
4652 if (!flgic) { kp = 2; goto L130;}
4653
4654L80:
4655 if (flgis) goto L150;
4656
4657 // Draw a straight line from P(k-1) to P(k).
4658
4659 finished = -1;
4660 goto L170;
4661
4662 // Test whether P(k) is a cusp.
4663
4664L90:
4665 if (x[k-1] == x[k] && y[k-1] == y[k]) goto L80;
4666L100:
4667 kp = k+1;
4668 goto L130;
4669
4670 // Branch if the next section of the curve begins at a cusp.
4671
4672L110:
4673 if (!flgis) goto L50;
4674
4675 // Carry forward the direction cosines from the previous arc.
4676
4677L120:
4678 co = ct;
4679 so = st;
4680 k++;
4681 goto L60;
4682
4683 // Calculate the direction cosines at P(k). If k=1 then
4684 // N-1 is used for k-1. If k=N then 2 is used for k+1.
4685 // direction cosines at P(k) obtained from P(k-1),P(k),P(k+1).
4686
4687L130:
4688 dx1 = x[k-1] - x[km-1];
4689 dy1 = y[k-1] - y[km-1];
4690 dk1 = dx1*dx1 + dy1*dy1;
4691 dx2 = x[kp-1] - x[k-1];
4692 dy2 = y[kp-1] - y[k-1];
4693 dk2 = dx2*dx2 + dy2*dy2;
4694 ctu = dx1*dk2 + dx2*dk1;
4695 stu = dy1*dk2 + dy2*dk1;
4696 xnt = ctu*ctu + stu*stu;
4697
4698 // If both ctu and stu are zero,then default.This can
4699 // occur when P(k)=P(k+1). I.E. A loop.
4700
4701 if (xnt < 1.E-25) {
4702 ctu = dy1;
4703 stu =-dx1;
4704 xnt = dk1;
4705 }
4706 // Normalise direction cosines.
4707
4708 ct = ctu/TMath::Sqrt(xnt);
4709 st = stu/TMath::Sqrt(xnt);
4710 if (flgis) goto L160;
4711
4712 // Direction cosines at P(k-1) obtained from P(k-1),P(k),P(k+1).
4713
4714 w3 = 2*(dx1*dy2-dx2*dy1);
4715 co = ctu+w3*dy1;
4716 so = stu-w3*dx1;
4717 xnt = 1/TMath::Sqrt(co*co+so*so);
4718 co = co*xnt;
4719 so = so*xnt;
4720 flgis = kTRUE;
4721 goto L170;
4722
4723 // Direction cosines at P(k) obtained from P(k-2),P(k-1),P(k).
4724
4725L150:
4726 w3 = 2*(dx1*dy2-dx2*dy1);
4727 ct = ctu-w3*dy2;
4728 st = stu+w3*dx2;
4729 xnt = 1/TMath::Sqrt(ct*ct+st*st);
4730 ct = ct*xnt;
4731 st = st*xnt;
4732 flgis = kFALSE;
4733 goto L170;
4734L160:
4735 if (k <= 1) goto L120;
4736
4737 // For the arc between P(k-1) and P(k) with direction cosines co,
4738 // so and ct,st respectively, calculate the coefficients of the
4739 // parametric cubic represented by X(t) and Y(t) where
4740 // X(t)=xa*t**3 + xb*t**2 + co*t + xo
4741 // Y(t)=ya*t**3 + yb*t**2 + so*t + yo
4742
4743L170:
4744 xo = x[k-2];
4745 yo = y[k-2];
4746 dx = x[k-1] - xo;
4747 dy = y[k-1] - yo;
4748
4749 // Initialise the values of X(TI),Y(TI) in xt and yt respectively.
4750
4751 xt = xo;
4752 yt = yo;
4753 if (finished < 0) { // Draw a straight line between (xo,yo) and (xt,yt)
4754 xt += dx;
4755 yt += dy;
4756 goto L300;
4757 }
4758 c = dx*dx+dy*dy;
4759 a = co+ct;
4760 b = so+st;
4761 r = dx*a+dy*b;
4762 t = c*6/(TMath::Sqrt(r*r+2*(7-co*ct-so*st)*c)+r);
4763 tsquare = t*t;
4764 tcube = t*tsquare;
4765 xa = (a*t-2*dx)/tcube;
4766 xb = (3*dx-(co+a)*t)/tsquare;
4767 ya = (b*t-2*dy)/tcube;
4768 yb = (3*dy-(so+b)*t)/tsquare;
4769
4770 // If the curve is close to a straight line then use a straight
4771 // line between (xo,yo) and (xt,yt).
4772
4773 if (.75*TMath::Max(TMath::Abs(dx*so-dy*co),TMath::Abs(dx*st-dy*ct)) <= delta) {
4774 finished = -1;
4775 xt += dx;
4776 yt += dy;
4777 goto L300;
4778 }
4779
4780 // Calculate a set of values 0 == t(0).LTCT(1) < ... < t(M)=TC
4781 // such that polygonal arc joining X(t(J)),Y(t(J)) (J=0,1,..M)
4782 // is within the required accuracy of the curve
4783
4784 tj = 0;
4785 u1 = ya*xb-yb*xa;
4786 u2 = yb*co-xb*so;
4787 u3 = so*xa-ya*co;
4788
4789 // Given t(J), calculate t(J+1). The values of X(t(J)),
4790 // Y(t(J)) t(J) are contained in xt,yt and tj respectively.
4791
4792L180:
4793 s = t - tj;
4794 iw = -2;
4795
4796 // Define iw here later.
4797
4798 p1 = (2*u1)*tj-u3;
4799 p2 = (u1*tj-u3)*3*tj+u2;
4800 p3 = 3*tj*ya+yb;
4801 p4 = (p3+yb)*tj+so;
4802 p5 = 3*tj*xa+xb;
4803 p6 = (p5+xb)*tj+co;
4804
4805 // Test D(tj,THETA). A is set to (Y(tj+s)-Y(tj))/s.b is
4806 // set to (X(tj+s)-X(tj))/s.
4807
4808 cc = 0.8209285;
4809 err = 0.1209835;
4810L190:
4811 iw -= 2;
4812L200:
4813 a = (s*ya+p3)*s+p4;
4814 b = (s*xa+p5)*s+p6;
4815
4816 // Set z to PSI(D/delta)-cc.
4817
4818 w1 = -s*(s*u1+p1);
4819 w2 = s*s*u1-p2;
4820 w3 = 1.5*w1+w2;
4821
4822 // Set the estimate of (THETA-tj)/s.Then set the numerator
4823 // of the expression (EQUATION 4.4)/s. Then set the square
4824 // of D(tj,tj+s)/delta. Then replace z by PSI(D/delta)-cc.
4825
4826 if (w3 > 0) wsign = TMath::Abs(w1);
4827 else wsign = -TMath::Abs(w1);
4828 sth = 0.5+wsign/(3.4*TMath::Abs(w1)+5.2*TMath::Abs(w3));
4829 z = s*sth*(s-s*sth)*(w1*sth+w1+w2);
4830 z = z*z/((a*a+b*b)*(delta*delta));
4831 z = (z+2.642937)*z/((.3715652*z+3.063444)*z+.2441889)-cc;
4832
4833 // Branch if z has been calculated
4834
4835 if (iw > 0) goto L250;
4836 if (z > err) goto L240;
4837 goto L220;
4838L210:
4839 iw -= 2;
4840L220:
4841 if (iw+2 == 0) goto L190;
4842 if (iw+2 > 0) goto L290;
4843
4844 // Last part of arc.
4845
4846L230:
4847 xt = x[k-1];
4848 yt = y[k-1];
4849 s = 0;
4850 goto L300;
4851
4852 // z(s). find a value of s where 0 <= s <= sb such that
4853 // TMath::Abs(z(s)) < err
4854
4855L240:
4856 kp = 0;
4857 c = z;
4858 sb = s;
4859L250:
4860 theGraph->Zero(kp,0,sb,err,s,z,maxiterations);
4861 if (kp == 2) goto L210;
4862 if (kp > 2) {
4863 Error("Smooth", "Attempt to plot outside plot limits");
4864 goto L230;
4865 }
4866 if (iw > 0) goto L200;
4867
4868 // Set z=z(s) for s=0.
4869
4870 if (iw < 0) {
4871 z = -cc;
4872 iw = 0;
4873 goto L250;
4874 }
4875
4876 // Set z=z(s) for s=sb.
4877
4878 z = c;
4879 iw = 1;
4880 goto L250;
4881
4882 // Update tj,xt and yt.
4883
4884L290:
4885 xt = xt + s*b;
4886 yt = yt + s*a;
4887 tj = s + tj;
4888
4889 // Convert coordinates to original system
4890
4891L300:
4892 qlx[npt] = sxmin + xt/xratio;
4893 qly[npt] = symin + yt/yratio;
4894 npt++;
4895
4896 // If a fill area must be drawn and if the banks LX and
4897 // LY are too small they are enlarged in order to draw
4898 // the filled area in one go.
4899
4900 if (npt < banksize) goto L320;
4901 if (drawtype >= 1000 || ktype > 1) {
4902 Int_t newsize = banksize + n2;
4903 std::vector<Double_t> qtemp(banksize);
4904 for (i=0;i<banksize;i++) qtemp[i] = qlx[i];
4905 qlx.resize(newsize);
4906 for (i=0;i<banksize;i++) qlx[i] = qtemp[i];
4907 for (i=0;i<banksize;i++) qtemp[i] = qly[i];
4908 qly.resize(newsize);
4909 for (i=0;i<banksize;i++) qly[i] = qtemp[i];
4910 banksize = newsize;
4911 goto L320;
4912 }
4913
4914 // Draw the graph
4915
4916L310:
4917 if (drawtype >= 1000) {
4918 gPad->PaintFillArea(npt,qlx.data(),qly.data(), "B");
4919 } else {
4920 if (ktype > 1) {
4921 if (!loptx) {
4922 qlx[npt] = qlx[npt-1];
4923 qlx[npt+1] = qlx[0];
4924 qly[npt] = yorg;
4925 qly[npt+1] = yorg;
4926 } else {
4927 qlx[npt] = xorg;
4928 qlx[npt+1] = xorg;
4929 qly[npt] = qly[npt-1];
4930 qly[npt+1] = qly[0];
4931 }
4932 gPad->PaintFillArea(npt+2,qlx.data(),qly.data());
4933 }
4934 if (TMath::Abs(theGraph->GetLineWidth())>99) PaintPolyLineHatches(theGraph, npt, qlx.data(), qly.data());
4935 gPad->PaintPolyLine(npt,qlx.data(),qly.data());
4936 }
4937 npt = 1;
4938 qlx[0] = sxmin + xt/xratio;
4939 qly[0] = symin + yt/yratio;
4940L320:
4941 if (finished > 0) goto L390;
4942 if (finished < 0) { finished = 0; goto L110;}
4943 if (s > 0) goto L180;
4944 goto L110;
4945
4946 // Convert coordinates back to original system
4947
4948L390:
4949 for (i=0;i<npoints;i++) {
4950 x[i] = sxmin + x[i]/xratio;
4951 y[i] = symin + y[i]/yratio;
4952 }
4953
4954}
4955
4956////////////////////////////////////////////////////////////////////////////////
4957/// Static function to set `fgMaxPointsPerLine` for graph painting. When graphs
4958/// are painted with lines, they are split into chunks of length `fgMaxPointsPerLine`.
4959/// This allows to paint line with an "infinite" number of points. In some case
4960/// this "chunks painting" technic may create artefacts at the chunk's boundaries.
4961/// For instance when zooming deeply in a PDF file. To avoid this effect it might
4962/// be necessary to increase the chunks' size using this function:
4963/// `TGraphPainter::SetMaxPointsPerLine(20000)`.
4964
4966{
4967 fgMaxPointsPerLine = maxp;
4968 if (maxp < 50) fgMaxPointsPerLine = 50;
4969}
@ kMouseMotion
Definition: Buttons.h:23
@ kButton1Motion
Definition: Buttons.h:20
@ kButton1Up
Definition: Buttons.h:19