Graph::Set3DPerspective()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 9
c 1
b 0
f 0
nc 1
nop 8
dl 0
loc 11
rs 9.9666

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * JPGraph v4.0.3
5
 */
6
7
namespace Amenadiel\JpGraph\Graph;
8
9
require_once __DIR__ . '/../config.inc.php';
10
11
use Amenadiel\JpGraph\Image;
12
use Amenadiel\JpGraph\Plot;
13
use Amenadiel\JpGraph\Text;
14
use Amenadiel\JpGraph\Util;
15
16
/**
17
 * @class Graph
18
 * // Description: Main class to handle graphs
19
 */
20
class Graph
21
{
22
    public $gDateLocale;
23
    public $gJpgDateLocale;
24
    public $cache; // Cache object (singleton)
25
    public $img; // Img object (singleton)
26
    public $plots   = []; // Array of all plot object in the graph (for Y 1 axis)
27
    public $y2plots = []; // Array of all plot object in the graph (for Y 2 axis)
28
    public $ynplots = [];
29
    public $xscale; // X Scale object (could be instance of LinearScale or LogScale
30
    public $yscale;
31
    public $y2scale;
32
    public $ynscale = [];
33
    public $iIcons  = []; // Array of Icons to add to
34
    public $cache_name; // File name to be used for the current graph in the cache directory
35
    public $xgrid; // X Grid object (linear or logarithmic)
36
    public $ygrid;
37
    public $y2grid; //dito for Y
38
    public $doframe;
39
    public $frame_color;
40
    public $frame_weight; // Frame around graph
41
    public $boxed        = false;
42
    public $box_color    = 'black';
43
    public $box_weight   = 1; // Box around plot area
44
    public $doshadow     = false;
45
    public $shadow_width = 4;
46
    public $shadow_color = '[email protected]'; // Shadow for graph
47
    public $xaxis; // X-axis (instane of Axis class)
48
    public $yaxis;
49
    public $y2axis;
50
    public $ynaxis = []; // Y axis (instance of Axis class)
51
    public $margin_color; // Margin color of graph
52
    public $plotarea_color = [255, 255, 255]; // Plot area color
53
    public $title;
54
    public $subtitle;
55
    public $subsubtitle; // Title and subtitle(s) text object
56
    public $axtype = 'linlin'; // Type of axis
57
    public $xtick_factor;
58
    public $ytick_factor; // Factor to determine the maximum number of ticks depending on the plot width
59
    public $texts;
60
    public $y2texts; // Text object to ge shown in the graph
61
    public $lines;
62
    public $y2lines;
63
    public $bands;
64
    public $y2bands;
65
    public $text_scale_off          = 0;
66
    public $text_scale_abscenteroff = -1; // Text scale in fractions and for centering bars
67
    public $background_image        = '';
68
    public $background_image_type   = -1;
69
    public $background_image_format = 'png';
70
    public $background_image_bright = 0;
71
    public $background_image_contr  = 0;
72
    public $background_image_sat    = 0;
73
    public $background_image_xpos   = 0;
74
    public $background_image_ypos   = 0;
75
    public $image_bright            = 0;
76
    public $image_contr             = 0;
77
    public $image_sat               = 0;
78
    public $inline;
79
    public $showcsim     = 0;
80
    public $csimcolor    = 'red'; //debug stuff, draw the csim boundaris on the image if <>0
81
    public $grid_depth   = DEPTH_BACK; // Draw grid under all plots as default
82
    public $iAxisStyle   = AXSTYLE_SIMPLE;
83
    public $iCSIMdisplay = false;
84
    public $iHasStroked  = false;
85
    public $footer;
86
    public $csimcachename    = '';
87
    public $csimcachetimeout = 0;
88
    public $iCSIMImgAlt      = '';
89
    public $iDoClipping      = false;
90
    public $y2orderback      = true;
91
    public $tabtitle;
92
    public $bkg_gradtype   = -1;
93
    public $bkg_gradstyle  = BGRAD_MARGIN;
94
    public $bkg_gradfrom   = 'navy';
95
    public $bkg_gradto     = 'silver';
96
    public $plot_gradtype  = -1;
97
    public $plot_gradstyle = BGRAD_MARGIN;
98
    public $plot_gradfrom  = 'silver';
99
    public $plot_gradto    = 'navy';
100
101
    public $titlebackground       = false;
102
    public $titlebackground_color = 'lightblue';
103
    public $titlebackground_style = 1;
104
    public $titlebackground_framecolor;
105
    public $titlebackground_framestyle;
106
    public $titlebackground_frameweight;
107
    public $titlebackground_bevelheight;
108
    public $titlebkg_fillstyle = TITLEBKG_FILLSTYLE_SOLID;
109
    public $titlebkg_scolor1   = 'black';
110
    public $titlebkg_scolor2   = 'white';
111
    public $framebevel;
112
    public $framebeveldepth;
113
    public $framebevelborder;
114
    public $framebevelbordercolor;
115
    public $framebevelcolor1;
116
    public $framebevelcolor2;
117
    public $background_image_mix  = 100;
118
    public $background_cflag      = '';
119
    public $background_cflag_type = BGIMG_FILLPLOT;
120
    public $background_cflag_mix  = 100;
121
    public $iImgTrans             = false;
122
    public $iImgTransHorizon      = 100;
123
    public $iImgTransSkewDist     = 150;
124
    public $iImgTransDirection    = 1;
125
    public $iImgTransMinSize      = true;
126
    public $iImgTransFillColor    = 'white';
127
    public $iImgTransHighQ        = false;
128
    public $iImgTransBorder       = false;
129
    public $iImgTransHorizonPos   = 0.5;
130
    public $legend;
131
    public $graph_theme;
132
    protected $iYAxisDeltaPos       = 50;
133
    protected $iIconDepth           = DEPTH_BACK;
134
    protected $iAxisLblBgType       = 0;
135
    protected $iXAxisLblBgFillColor = 'lightgray';
136
    protected $iXAxisLblBgColor     = 'black';
137
    protected $iYAxisLblBgFillColor = 'lightgray';
138
    protected $iYAxisLblBgColor     = 'black';
139
    protected $iTables;
140
141
    protected $isRunningClear = false;
142
    protected $inputValues;
143
    protected $isAfterSetScale = false;
144
145
    // aWIdth   Width in pixels of image
146
    // aHeight   Height in pixels of image
147
    // aCachedName Name for image file in cache directory
148
    // aTimeOut  Timeout in minutes for image in cache
149
    // aInline  If true the image is streamed back in the call to Stroke()
150
    //   If false the image is just created in the cache
151
    public function __construct($aWidth = 300, $aHeight = 200, $aCachedName = '', $aTimeout = 0, $aInline = true)
152
    {
153
        $this->gDateLocale    = new Util\DateLocale();
154
        $this->gJpgDateLocale = new Util\DateLocale();
155
        if (!is_numeric($aWidth) || !is_numeric($aHeight)) {
156
            Util\JpGraphError::RaiseL(25008); //('Image width/height argument in Graph::Graph() must be numeric');
157
        }
158
159
        // Initialize frame and margin
160
        $this->InitializeFrameAndMargin();
161
162
        // Automatically generate the image file name based on the name of the script that
163
        // generates the graph
164
        if ($aCachedName == 'auto') {
165
            $aCachedName = Util\Helper::GenImgName();
166
        }
167
168
        // Should the image be streamed back to the browser or only to the cache?
169
        $this->inline = $aInline;
170
171
        $this->img   = new Image\RotImage($aWidth, $aHeight);
172
        $this->cache = new Image\ImgStreamCache();
173
174
        // Window doesn't like '?' in the file name so replace it with an '_'
175
        $aCachedName = str_replace('?', '_', $aCachedName);
176
        $this->SetupCache($aCachedName, $aTimeout);
177
178
        $this->title = new Text\Text();
179
        $this->title->ParagraphAlign('center');
180
        $this->title->SetFont(FF_DEFAULT, FS_NORMAL); //FF_FONT2, FS_BOLD
181
        $this->title->SetMargin(5);
182
        $this->title->SetAlign('center');
183
184
        $this->subtitle = new Text\Text();
185
        $this->subtitle->ParagraphAlign('center');
186
        $this->subtitle->SetMargin(3);
187
        $this->subtitle->SetAlign('center');
188
189
        $this->subsubtitle = new Text\Text();
190
        $this->subsubtitle->ParagraphAlign('center');
191
        $this->subsubtitle->SetMargin(3);
192
        $this->subsubtitle->SetAlign('center');
193
194
        $this->legend = new Legend();
195
        $this->footer = new Image\Footer();
196
197
        // If the cached version exist just read it directly from the
198
        // cache, stream it back to browser and exit
199
        if ($aCachedName != '' && READ_CACHE && $aInline) {
200
            if ($this->cache->GetAndStream($this->img, $aCachedName)) {
201
                exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
202
            }
203
        }
204
205
        $this->SetTickDensity(); // Normal density
206
207
        $this->tabtitle = new Text\GraphTabTitle();
208
209
        if (!$this->isRunningClear) {
210
            $this->inputValues                = [];
211
            $this->inputValues['aWidth']      = $aWidth;
212
            $this->inputValues['aHeight']     = $aHeight;
213
            $this->inputValues['aCachedName'] = $aCachedName;
214
            $this->inputValues['aTimeout']    = $aTimeout;
215
            $this->inputValues['aInline']     = $aInline;
216
217
            $theme_class = '\Amenadiel\JpGraph\Themes\\' . DEFAULT_THEME_CLASS;
218
219
            if (class_exists($theme_class)) {
220
                $this->graph_theme = new $theme_class();
221
            }
222
        }
223
    }
224
225
    public function InitializeFrameAndMargin()
226
    {
227
        $this->doframe      = true;
228
        $this->frame_color  = 'black';
229
        $this->frame_weight = 1;
230
231
        $this->titlebackground_framecolor  = 'blue';
232
        $this->titlebackground_framestyle  = 2;
233
        $this->titlebackground_frameweight = 1;
234
        $this->titlebackground_bevelheight = 3;
235
        $this->titlebkg_fillstyle          = TITLEBKG_FILLSTYLE_SOLID;
236
        $this->titlebkg_scolor1            = 'black';
237
        $this->titlebkg_scolor2            = 'white';
238
        $this->framebevel                  = false;
239
        $this->framebeveldepth             = 2;
240
        $this->framebevelborder            = false;
241
        $this->framebevelbordercolor       = 'black';
242
        $this->framebevelcolor1            = '[email protected]';
243
        $this->framebevelcolor2            = '[email protected]';
244
245
        $this->margin_color = [250, 250, 250];
246
    }
247
248
    public function SetupCache($aFilename, $aTimeout = 60)
249
    {
250
        $this->cache_name = $aFilename;
251
        $this->cache->SetTimeOut($aTimeout);
252
    }
253
254
    // Enable final image perspective transformation
255
    public function Set3DPerspective($aDir = 1, $aHorizon = 100, $aSkewDist = 120, $aQuality = false, $aFillColor = '#FFFFFF', $aBorder = false, $aMinSize = true, $aHorizonPos = 0.5)
256
    {
257
        $this->iImgTrans           = true;
258
        $this->iImgTransHorizon    = $aHorizon;
259
        $this->iImgTransSkewDist   = $aSkewDist;
260
        $this->iImgTransDirection  = $aDir;
261
        $this->iImgTransMinSize    = $aMinSize;
262
        $this->iImgTransFillColor  = $aFillColor;
263
        $this->iImgTransHighQ      = $aQuality;
264
        $this->iImgTransBorder     = $aBorder;
265
        $this->iImgTransHorizonPos = $aHorizonPos;
266
    }
267
268
    public function SetUserFont($aNormal, $aBold = '', $aItalic = '', $aBoldIt = '')
269
    {
270
        $this->img->ttf->SetUserFont($aNormal, $aBold, $aItalic, $aBoldIt);
271
    }
272
273
    public function SetUserFont1($aNormal, $aBold = '', $aItalic = '', $aBoldIt = '')
274
    {
275
        $this->img->ttf->SetUserFont1($aNormal, $aBold, $aItalic, $aBoldIt);
276
    }
277
278
    public function SetUserFont2($aNormal, $aBold = '', $aItalic = '', $aBoldIt = '')
279
    {
280
        $this->img->ttf->SetUserFont2($aNormal, $aBold, $aItalic, $aBoldIt);
281
    }
282
283
    public function SetUserFont3($aNormal, $aBold = '', $aItalic = '', $aBoldIt = '')
284
    {
285
        $this->img->ttf->SetUserFont3($aNormal, $aBold, $aItalic, $aBoldIt);
286
    }
287
288
    // Set Image format and optional quality
289
    public function SetImgFormat($aFormat, $aQuality = 75)
290
    {
291
        $this->img->SetImgFormat($aFormat, $aQuality);
292
    }
293
294
    // Should the grid be in front or back of the plot?
295
    public function SetGridDepth($aDepth)
296
    {
297
        $this->grid_depth = $aDepth;
298
    }
299
300
    public function SetIconDepth($aDepth)
301
    {
302
        $this->iIconDepth = $aDepth;
303
    }
304
305
    // Specify graph angle 0-360 degrees.
306
    public function SetAngle($aAngle)
307
    {
308
        $this->img->SetAngle($aAngle);
309
    }
310
311
    public function SetAlphaBlending($aFlg = true)
312
    {
313
        $this->img->SetAlphaBlending($aFlg);
314
    }
315
316
    // Shortcut to image margin
317
    public function SetMargin($lm, $rm, $tm, $bm)
318
    {
319
        $this->img->SetMargin($lm, $rm, $tm, $bm);
320
    }
321
322
    public function SetY2OrderBack($aBack = true)
323
    {
324
        $this->y2orderback = $aBack;
325
    }
326
327
    // Rotate the graph 90 degrees and set the margin
328
    // when we have done a 90 degree rotation
329
    public function Set90AndMargin($lm = 0, $rm = 0, $tm = 0, $bm = 0)
330
    {
331
        $lm = $lm == 0 ? floor(0.2 * $this->img->width) : $lm;
332
        $rm = $rm == 0 ? floor(0.1 * $this->img->width) : $rm;
333
        $tm = $tm == 0 ? floor(0.2 * $this->img->height) : $tm;
334
        $bm = $bm == 0 ? floor(0.1 * $this->img->height) : $bm;
335
336
        $adj = ($this->img->height - $this->img->width) / 2;
337
        $this->img->SetMargin($tm - $adj, $bm - $adj, $rm + $adj, $lm + $adj);
338
        $this->img->SetCenter(floor($this->img->width / 2), floor($this->img->height / 2));
339
        $this->SetAngle(90);
340
        if (empty($this->yaxis) || empty($this->xaxis)) {
341
            Util\JpGraphError::RaiseL(25009); //('You must specify what scale to use with a call to Graph::SetScale()');
342
        }
343
        $this->xaxis->SetLabelAlign('right', 'center');
344
        $this->yaxis->SetLabelAlign('center', 'bottom');
345
    }
346
347
    public function SetClipping($aFlg = true)
348
    {
349
        $this->iDoClipping = $aFlg;
350
    }
351
352
    // Add a plot object to the graph
353
    public function Add($aPlot)
354
    {
355
        if ($aPlot == null) {
356
            Util\JpGraphError::RaiseL(25010); //("Graph::Add() You tried to add a null plot to the graph.");
357
        }
358
        if (is_array($aPlot) && safe_count($aPlot) > 0) {
359
            $cl = $aPlot[0];
360
        } else {
361
            $cl = $aPlot;
362
        }
363
364
        if ($cl instanceof Text\Text) {
365
            $this->AddText($aPlot);
366
        } elseif (($cl instanceof Plot\PlotLine)) {
367
            $this->AddLine($aPlot);
368
        } elseif (($cl instanceof Plot\PlotBand)) {
369
            $this->AddBand($aPlot);
370
        } elseif (($cl instanceof Plot\IconPlot)) {
371
            $this->AddIcon($aPlot);
372
        } elseif (($cl instanceof Text\GTextTable)) {
373
            $this->AddTable($aPlot);
374
        } else {
375
            if (is_array($aPlot)) {
376
                $this->plots = array_merge($this->plots, $aPlot);
377
            } else {
378
                $this->plots[] = $aPlot;
379
            }
380
        }
381
382
        if ($this->graph_theme) {
383
            $this->graph_theme->SetupPlot($aPlot);
384
        }
385
    }
386
387
    public function AddTable($aTable)
388
    {
389
        if (is_array($aTable)) {
390
            for ($i = 0; $i < safe_count($aTable); ++$i) {
391
                $this->iTables[] = $aTable[$i];
392
            }
393
        } else {
394
            $this->iTables[] = $aTable;
395
        }
396
    }
397
398
    public function AddIcon($aIcon)
399
    {
400
        if (is_array($aIcon)) {
401
            for ($i = 0; $i < safe_count($aIcon); ++$i) {
402
                $this->iIcons[] = $aIcon[$i];
403
            }
404
        } else {
405
            $this->iIcons[] = $aIcon;
406
        }
407
    }
408
409
    // Add plot to second Y-scale
410
    public function AddY2($aPlot)
411
    {
412
        if ($aPlot == null) {
413
            Util\JpGraphError::RaiseL(25011); //("Graph::AddY2() You tried to add a null plot to the graph.");
414
        }
415
416
        if (is_array($aPlot) && safe_count($aPlot) > 0) {
417
            $cl = $aPlot[0];
418
        } else {
419
            $cl = $aPlot;
420
        }
421
422
        if ($cl instanceof Text\Text) {
423
            $this->AddText($aPlot, true);
424
        } elseif (($cl instanceof Plot\PlotLine)) {
425
            $this->AddLine($aPlot, true);
426
        } elseif (($cl instanceof Plot\PlotBand)) {
427
            $this->AddBand($aPlot, true);
428
        } else {
429
            $this->y2plots[] = $aPlot;
430
        }
431
432
        if ($this->graph_theme) {
433
            $this->graph_theme->SetupPlot($aPlot);
434
        }
435
    }
436
437
    // Add plot to the extra Y-axises
438
    public function AddY($aN, $aPlot)
439
    {
440
        if ($aPlot == null) {
441
            Util\JpGraphError::RaiseL(25012); //("Graph::AddYN() You tried to add a null plot to the graph.");
442
        }
443
444
        if (is_array($aPlot) && safe_count($aPlot) > 0) {
445
            $cl = $aPlot[0];
446
        } else {
447
            $cl = $aPlot;
448
        }
449
450
        if (($cl instanceof Text\Text) ||
451
            ($cl instanceof Plot\PlotLine) ||
452
            ($cl instanceof Plot\PlotBand)) {
453
            Util\JpGraphError::RaiseL(25013); //('You can only add standard plots to multiple Y-axis');
454
        } else {
455
            $this->ynplots[$aN][] = $aPlot;
456
        }
457
458
        if ($this->graph_theme) {
459
            $this->graph_theme->SetupPlot($aPlot);
460
        }
461
    }
462
463
    // Add text object to the graph
464
    public function AddText($aTxt, $aToY2 = false)
465
    {
466
        if ($aTxt == null) {
467
            Util\JpGraphError::RaiseL(25014); //("Graph::AddText() You tried to add a null text to the graph.");
468
        }
469
        if ($aToY2) {
470
            if (is_array($aTxt)) {
471
                for ($i = 0; $i < safe_count($aTxt); ++$i) {
472
                    $this->y2texts[] = $aTxt[$i];
473
                }
474
            } else {
475
                $this->y2texts[] = $aTxt;
476
            }
477
        } else {
478
            if (is_array($aTxt)) {
479
                for ($i = 0; $i < safe_count($aTxt); ++$i) {
480
                    $this->texts[] = $aTxt[$i];
481
                }
482
            } else {
483
                $this->texts[] = $aTxt;
484
            }
485
        }
486
    }
487
488
    // Add a line object (class PlotLine) to the graph
489
    public function AddLine($aLine, $aToY2 = false)
490
    {
491
        if ($aLine == null) {
492
            Util\JpGraphError::RaiseL(25015); //("Graph::AddLine() You tried to add a null line to the graph.");
493
        }
494
495
        if ($aToY2) {
496
            if (is_array($aLine)) {
497
                for ($i = 0; $i < safe_count($aLine); ++$i) {
498
                    //$this->y2lines[]=$aLine[$i];
499
                    $this->y2plots[] = $aLine[$i];
500
                }
501
            } else {
502
                //$this->y2lines[] = $aLine;
503
                $this->y2plots[] = $aLine;
504
            }
505
        } else {
506
            if (is_array($aLine)) {
507
                for ($i = 0; $i < safe_count($aLine); ++$i) {
508
                    //$this->lines[]=$aLine[$i];
509
                    $this->plots[] = $aLine[$i];
510
                }
511
            } else {
512
                //$this->lines[] = $aLine;
513
                $this->plots[] = $aLine;
514
            }
515
        }
516
    }
517
518
    // Add vertical or horizontal band
519
    public function AddBand($aBand, $aToY2 = false)
520
    {
521
        if ($aBand == null) {
522
            Util\JpGraphError::RaiseL(25016); //(" Graph::AddBand() You tried to add a null band to the graph.");
523
        }
524
525
        if ($aToY2) {
526
            if (is_array($aBand)) {
527
                for ($i = 0; $i < safe_count($aBand); ++$i) {
528
                    $this->y2bands[] = $aBand[$i];
529
                }
530
            } else {
531
                $this->y2bands[] = $aBand;
532
            }
533
        } else {
534
            if (is_array($aBand)) {
535
                for ($i = 0; $i < safe_count($aBand); ++$i) {
536
                    $this->bands[] = $aBand[$i];
537
                }
538
            } else {
539
                $this->bands[] = $aBand;
540
            }
541
        }
542
    }
543
544
    public function SetPlotGradient($aFrom = 'navy', $aTo = 'silver', $aGradType = 2)
545
    {
546
        $this->plot_gradtype = $aGradType;
547
        $this->plot_gradfrom = $aFrom;
548
        $this->plot_gradto   = $aTo;
549
    }
550
551
    public function SetBackgroundGradient($aFrom = 'navy', $aTo = 'silver', $aGradType = 2, $aStyle = BGRAD_FRAME)
552
    {
553
        $this->bkg_gradtype  = $aGradType;
554
        $this->bkg_gradstyle = $aStyle;
555
        $this->bkg_gradfrom  = $aFrom;
556
        $this->bkg_gradto    = $aTo;
557
    }
558
559
    // Set a country flag in the background
560
    public function SetBackgroundCFlag($aName, $aBgType = BGIMG_FILLPLOT, $aMix = 100)
561
    {
562
        $this->background_cflag      = $aName;
563
        $this->background_cflag_type = $aBgType;
564
        $this->background_cflag_mix  = $aMix;
565
    }
566
567
    // Alias for the above method
568
    public function SetBackgroundCountryFlag($aName, $aBgType = BGIMG_FILLPLOT, $aMix = 100)
569
    {
570
        $this->background_cflag      = $aName;
571
        $this->background_cflag_type = $aBgType;
572
        $this->background_cflag_mix  = $aMix;
573
    }
574
575
    // Specify a background image
576
    public function SetBackgroundImage($aFileName, $aBgType = BGIMG_FILLPLOT, $aImgFormat = 'auto')
577
    {
578
        // Get extension to determine image type
579
        if ($aImgFormat == 'auto') {
580
            $e = explode('.', $aFileName);
581
            if (empty($e)) {
582
                Util\JpGraphError::RaiseL(25018, $aFileName); //('Incorrect file name for Graph::SetBackgroundImage() : '.$aFileName.' Must have a valid image extension (jpg,gif,png) when using autodetection of image type');
583
            }
584
585
            $valid_formats = ['png', 'jpg', 'gif'];
586
            $aImgFormat    = strtolower($e[count($e) - 1]);
587
            if ($aImgFormat == 'jpeg') {
588
                $aImgFormat = 'jpg';
589
            } elseif (!in_array($aImgFormat, $valid_formats, true)) {
590
                Util\JpGraphError::RaiseL(25019, $aImgFormat); //('Unknown file extension ($aImgFormat) in Graph::SetBackgroundImage() for filename: '.$aFileName);
591
            }
592
        }
593
594
        $this->background_image        = $aFileName;
595
        $this->background_image_type   = $aBgType;
596
        $this->background_image_format = $aImgFormat;
597
    }
598
599
    public function SetBackgroundImageMix($aMix)
600
    {
601
        $this->background_image_mix = $aMix;
602
    }
603
604
    // Adjust background image position
605
    public function SetBackgroundImagePos($aXpos, $aYpos)
606
    {
607
        $this->background_image_xpos = $aXpos;
608
        $this->background_image_ypos = $aYpos;
609
    }
610
611
    // Specify axis style (boxed or single)
612
    public function SetAxisStyle($aStyle)
613
    {
614
        $this->iAxisStyle = $aStyle;
615
    }
616
617
    // Set a frame around the plot area
618
    public function SetBox($aDrawPlotFrame = true, $aPlotFrameColor = [0, 0, 0], $aPlotFrameWeight = 1)
619
    {
620
        $this->boxed      = $aDrawPlotFrame;
621
        $this->box_weight = $aPlotFrameWeight;
622
        $this->box_color  = $aPlotFrameColor;
623
    }
624
625
    // Specify color for the plotarea (not the margins)
626
    public function SetColor($aColor)
627
    {
628
        $this->plotarea_color = $aColor;
629
    }
630
631
    // Specify color for the margins (all areas outside the plotarea)
632
    public function SetMarginColor($aColor)
633
    {
634
        $this->margin_color = $aColor;
635
    }
636
637
    // Set a frame around the entire image
638
    public function SetFrame($aDrawImgFrame = true, $aImgFrameColor = [0, 0, 0], $aImgFrameWeight = 1)
639
    {
640
        $this->doframe      = $aDrawImgFrame;
641
        $this->frame_color  = $aImgFrameColor;
642
        $this->frame_weight = $aImgFrameWeight;
643
    }
644
645
    public function SetFrameBevel($aDepth = 3, $aBorder = false, $aBorderColor = 'black', $aColor1 = '[email protected]', $aColor2 = '[email protected]', $aFlg = true)
646
    {
647
        $this->framebevel            = $aFlg;
648
        $this->framebeveldepth       = $aDepth;
649
        $this->framebevelborder      = $aBorder;
650
        $this->framebevelbordercolor = $aBorderColor;
651
        $this->framebevelcolor1      = $aColor1;
652
        $this->framebevelcolor2      = $aColor2;
653
654
        $this->doshadow = false;
655
    }
656
657
    // Set the shadow around the whole image
658
    public function SetShadow($aShowShadow = true, $aShadowWidth = 5, $aShadowColor = 'darkgray')
659
    {
660
        $this->doshadow     = $aShowShadow;
661
        $this->shadow_color = $aShadowColor;
662
        $this->shadow_width = $aShadowWidth;
663
        $this->footer->iBottomMargin += $aShadowWidth;
664
        $this->footer->iRightMargin += $aShadowWidth;
665
    }
666
667
    // Specify x,y scale. Note that if you manually specify the scale
668
    // you must also specify the tick distance with a call to Ticks::Set()
669
    public function SetScale($aAxisType, $aYMin = 1, $aYMax = 1, $aXMin = 1, $aXMax = 1)
670
    {
671
        $this->axtype = $aAxisType;
672
673
        if ($aYMax < $aYMin || $aXMax < $aXMin) {
674
            Util\JpGraphError::RaiseL(25020); //('Graph::SetScale(): Specified Max value must be larger than the specified Min value.');
675
        }
676
677
        $yt = substr($aAxisType, -3, 3);
678
        if ($yt == 'lin') {
679
            $this->yscale = new LinearScale($aYMin, $aYMax);
680
        } elseif ($yt == 'int') {
681
            $this->yscale = new LinearScale($aYMin, $aYMax);
682
            $this->yscale->SetIntScale();
683
        } elseif ($yt == 'log') {
684
            $this->yscale = new LogScale($aYMin, $aYMax);
685
        } else {
686
            Util\JpGraphError::RaiseL(25021, $aAxisType); //("Unknown scale specification for Y-scale. ($aAxisType)");
687
        }
688
689
        $xt = substr($aAxisType, 0, 3);
690
        if ($xt == 'lin' || $xt == 'tex') {
691
            $this->xscale            = new LinearScale($aXMin, $aXMax, 'x');
692
            $this->xscale->textscale = ($xt == 'tex');
693
        } elseif ($xt == 'int') {
694
            $this->xscale = new LinearScale($aXMin, $aXMax, 'x');
695
            $this->xscale->SetIntScale();
696
        } elseif ($xt == 'dat') {
697
            $this->xscale = new DateScale($aXMin, $aXMax, 'x');
698
        } elseif ($xt == 'log') {
699
            $this->xscale = new LogScale($aXMin, $aXMax, 'x');
700
        } else {
701
            Util\JpGraphError::RaiseL(25022, $aAxisType); //(" Unknown scale specification for X-scale. ($aAxisType)");
702
        }
703
704
        $this->xaxis = new Axis($this->img, $this->xscale);
705
        $this->yaxis = new Axis($this->img, $this->yscale);
706
        $this->xgrid = new Grid($this->xaxis);
707
        $this->ygrid = new Grid($this->yaxis);
708
        $this->ygrid->Show();
709
710
        if (!$this->isRunningClear) {
711
            $this->inputValues['aAxisType'] = $aAxisType;
712
            $this->inputValues['aYMin']     = $aYMin;
713
            $this->inputValues['aYMax']     = $aYMax;
714
            $this->inputValues['aXMin']     = $aXMin;
715
            $this->inputValues['aXMax']     = $aXMax;
716
717
            if ($this->graph_theme) {
718
                $this->graph_theme->ApplyGraph($this);
719
            }
720
        }
721
722
        $this->isAfterSetScale = true;
723
    }
724
725
    // Specify secondary Y scale
726
    public function SetY2Scale($aAxisType = 'lin', $aY2Min = 1, $aY2Max = 1)
727
    {
728
        if ($aAxisType == 'lin') {
729
            $this->y2scale = new LinearScale($aY2Min, $aY2Max);
730
        } elseif ($aAxisType == 'int') {
731
            $this->y2scale = new LinearScale($aY2Min, $aY2Max);
732
            $this->y2scale->SetIntScale();
733
        } elseif ($aAxisType == 'log') {
734
            $this->y2scale = new LogScale($aY2Min, $aY2Max);
735
        } else {
736
            Util\JpGraphError::RaiseL(25023, $aAxisType); //("JpGraph: Unsupported Y2 axis type: $aAxisType\nMust be one of (lin,log,int)");
737
        }
738
739
        $this->y2axis = new Axis($this->img, $this->y2scale);
740
        $this->y2axis->scale->ticks->SetDirection(SIDE_LEFT);
741
        $this->y2axis->SetLabelSide(SIDE_RIGHT);
742
        $this->y2axis->SetPos('max');
743
        $this->y2axis->SetTitleSide(SIDE_RIGHT);
744
745
        // Deafult position is the max x-value
746
        $this->y2grid = new Grid($this->y2axis);
747
748
        if ($this->graph_theme) {
749
            $this->graph_theme->ApplyGraph($this);
750
        }
751
    }
752
753
    // Set the delta position (in pixels) between the multiple Y-axis
754
    public function SetYDeltaDist($aDist)
755
    {
756
        $this->iYAxisDeltaPos = $aDist;
757
    }
758
759
    // Specify secondary Y scale
760
    public function SetYScale($aN, $aAxisType = 'lin', $aYMin = 1, $aYMax = 1)
761
    {
762
        if ($aAxisType == 'lin') {
763
            $this->ynscale[$aN] = new LinearScale($aYMin, $aYMax);
764
        } elseif ($aAxisType == 'int') {
765
            $this->ynscale[$aN] = new LinearScale($aYMin, $aYMax);
766
            $this->ynscale[$aN]->SetIntScale();
767
        } elseif ($aAxisType == 'log') {
768
            $this->ynscale[$aN] = new LogScale($aYMin, $aYMax);
769
        } else {
770
            Util\JpGraphError::RaiseL(25024, $aAxisType); //("JpGraph: Unsupported Y axis type: $aAxisType\nMust be one of (lin,log,int)");
771
        }
772
773
        $this->ynaxis[$aN] = new Axis($this->img, $this->ynscale[$aN]);
774
        $this->ynaxis[$aN]->scale->ticks->SetDirection(SIDE_LEFT);
775
        $this->ynaxis[$aN]->SetLabelSide(SIDE_RIGHT);
776
777
        if ($this->graph_theme) {
778
            $this->graph_theme->ApplyGraph($this);
779
        }
780
    }
781
782
    // Specify density of ticks when autoscaling 'normal', 'dense', 'sparse', 'verysparse'
783
    // The dividing factor have been determined heuristically according to my aesthetic
784
    // sense (or lack off) y.m.m.v !
785
    public function SetTickDensity($aYDensity = TICKD_NORMAL, $aXDensity = TICKD_NORMAL)
786
    {
787
        $this->xtick_factor = 30;
788
        $this->ytick_factor = 25;
789
        switch ($aYDensity) {
790
            case TICKD_DENSE:
791
                $this->ytick_factor = 12;
792
793
                break;
794
            case TICKD_NORMAL:
795
                $this->ytick_factor = 25;
796
797
                break;
798
            case TICKD_SPARSE:
799
                $this->ytick_factor = 40;
800
801
                break;
802
            case TICKD_VERYSPARSE:
803
                $this->ytick_factor = 100;
804
805
                break;
806
            default:
807
                Util\JpGraphError::RaiseL(25025, $densy); //("JpGraph: Unsupported Tick density: $densy");
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $densy seems to be never defined.
Loading history...
808
        }
809
        switch ($aXDensity) {
810
            case TICKD_DENSE:
811
                $this->xtick_factor = 15;
812
813
                break;
814
            case TICKD_NORMAL:
815
                $this->xtick_factor = 30;
816
817
                break;
818
            case TICKD_SPARSE:
819
                $this->xtick_factor = 45;
820
821
                break;
822
            case TICKD_VERYSPARSE:
823
                $this->xtick_factor = 60;
824
825
                break;
826
            default:
827
                Util\JpGraphError::RaiseL(25025, $densx); //("JpGraph: Unsupported Tick density: $densx");
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $densx seems to be never defined.
Loading history...
828
        }
829
    }
830
831
    // Get a string of all image map areas
832
    public function GetCSIMareas()
833
    {
834
        if (!$this->iHasStroked) {
835
            $this->Stroke(_CSIM_SPECIALFILE);
836
        }
837
838
        $csim = $this->title->GetCSIMAreas();
839
        $csim .= $this->subtitle->GetCSIMAreas();
840
        $csim .= $this->subsubtitle->GetCSIMAreas();
841
        $csim .= $this->legend->GetCSIMAreas();
842
843
        if ($this->y2axis != null) {
844
            $csim .= $this->y2axis->title->GetCSIMAreas();
845
        }
846
847
        if ($this->texts != null) {
848
            $n = safe_count($this->texts);
849
            for ($i = 0; $i < $n; ++$i) {
850
                $csim .= $this->texts[$i]->GetCSIMAreas();
851
            }
852
        }
853
854
        if ($this->y2texts != null && $this->y2scale != null) {
855
            $n = safe_count($this->y2texts);
856
            for ($i = 0; $i < $n; ++$i) {
857
                $csim .= $this->y2texts[$i]->GetCSIMAreas();
858
            }
859
        }
860
861
        if ($this->yaxis != null && $this->xaxis != null) {
862
            $csim .= $this->yaxis->title->GetCSIMAreas();
863
            $csim .= $this->xaxis->title->GetCSIMAreas();
864
        }
865
866
        $n = safe_count($this->plots);
867
        for ($i = 0; $i < $n; ++$i) {
868
            $csim .= $this->plots[$i]->GetCSIMareas();
869
        }
870
871
        $n = safe_count($this->y2plots);
872
        for ($i = 0; $i < $n; ++$i) {
873
            $csim .= $this->y2plots[$i]->GetCSIMareas();
874
        }
875
876
        $n = safe_count($this->ynaxis);
877
        for ($i = 0; $i < $n; ++$i) {
878
            $m = safe_count($this->ynplots[$i]);
879
            for ($j = 0; $j < $m; ++$j) {
880
                $csim .= $this->ynplots[$i][$j]->GetCSIMareas();
881
            }
882
        }
883
884
        $n = safe_count($this->iTables);
885
        for ($i = 0; $i < $n; ++$i) {
886
            $csim .= $this->iTables[$i]->GetCSIMareas();
887
        }
888
889
        return $csim;
890
    }
891
892
    // Get a complete <MAP>..</MAP> tag for the final image map
893
    public function GetHTMLImageMap($aMapName)
894
    {
895
        $im = "<map name=\"${aMapName}\" id=\"${aMapName}\" >\n";
896
        $im .= $this->GetCSIMareas();
897
        $im .= '</map>';
898
899
        return $im;
900
    }
901
902
    public function CheckCSIMCache($aCacheName, $aTimeOut = 60)
903
    {
904
        global $_SERVER;
905
906
        if ($aCacheName == 'auto') {
907
            $aCacheName = basename($_SERVER['PHP_SELF']);
908
        }
909
910
        $urlarg                 = $this->GetURLArguments();
911
        $this->csimcachename    = CSIMCACHE_DIR . $aCacheName . $urlarg;
912
        $this->csimcachetimeout = $aTimeOut;
913
914
        // First determine if we need to check for a cached version
915
        // This differs from the standard cache in the sense that the
916
        // image and CSIM map HTML file is written relative to the directory
917
        // the script executes in and not the specified cache directory.
918
        // The reason for this is that the cache directory is not necessarily
919
        // accessible from the HTTP server.
920
        if ($this->csimcachename != '') {
921
            $dir      = dirname($this->csimcachename);
922
            $base     = basename($this->csimcachename);
923
            $base     = strtok($base, '.');
924
            $suffix   = strtok('.');
0 ignored issues
show
Unused Code introduced by
The assignment to $suffix is dead and can be removed.
Loading history...
925
            $basecsim = $dir . '/' . $base . '?' . $urlarg . '_csim_.html';
926
            $baseimg  = $dir . '/' . $base . '?' . $urlarg . '.' . $this->img->img_format;
927
928
            $timedout = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $timedout is dead and can be removed.
Loading history...
929
            // Does it exist at all ?
930
931
            if (file_exists($basecsim) && file_exists($baseimg)) {
932
                // Check that it hasn't timed out
933
                $diff = time() - filemtime($basecsim);
934
                if ($this->csimcachetimeout > 0 && ($diff > $this->csimcachetimeout * 60)) {
935
                    $timedout = true;
936
                    @unlink($basecsim);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

936
                    /** @scrutinizer ignore-unhandled */ @unlink($basecsim);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
937
                    @unlink($baseimg);
938
                } else {
939
                    if ($fh = @fopen($basecsim, 'r')) {
940
                        fpassthru($fh);
941
942
                        return true;
943
                    }
944
                    Util\JpGraphError::RaiseL(25027, $basecsim); //(" Can't open cached CSIM \"$basecsim\" for reading.");
945
                }
946
            }
947
        }
948
949
        return false;
950
    }
951
952
    // Build the argument string to be used with the csim images
953
    public static function GetURLArguments($aAddRecursiveBlocker = false)
954
    {
955
        if ($aAddRecursiveBlocker) {
956
            // This is a JPGRAPH internal defined that prevents
957
            // us from recursively coming here again
958
            $urlarg = _CSIM_DISPLAY . '=1';
959
        }
960
961
        // Now reconstruct any user URL argument
962
        reset($_GET);
963
        foreach ($_GET as $key => $value) {
964
            if (is_array($value)) {
965
                foreach ($value as $k => $v) {
966
                    $urlarg .= '&amp;' . $key . '%5B' . $k . '%5D=' . urlencode($v);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $urlarg does not seem to be defined for all execution paths leading up to this point.
Loading history...
967
                }
968
            } else {
969
                $urlarg .= '&amp;' . $key . '=' . urlencode($value);
970
            }
971
        }
972
973
        // It's not ideal to convert POST argument to GET arguments
974
        // but there is little else we can do. One idea for the
975
        // future might be recreate the POST header in case.
976
        reset($_POST);
977
        foreach ($_POST as $key => $value) {
978
            if (is_array($value)) {
979
                foreach ($value as $k => $v) {
980
                    $urlarg .= '&amp;' . $key . '%5B' . $k . '%5D=' . urlencode($v);
981
                }
982
            } else {
983
                $urlarg .= '&amp;' . $key . '=' . urlencode($value);
984
            }
985
        }
986
987
        return $urlarg;
988
    }
989
990
    public function SetCSIMImgAlt($aAlt)
991
    {
992
        $this->iCSIMImgAlt = $aAlt;
993
    }
994
995
    public function StrokeCSIM($aScriptName = 'auto', $aCSIMName = '', $aBorder = 0)
996
    {
997
        if ($aCSIMName == '') {
998
            // create a random map name
999
            srand((int) (microtime() * 1000000));
1000
            $r         = rand(0, 100000);
1001
            $aCSIMName = '__mapname' . $r . '__';
1002
        }
1003
1004
        if ($aScriptName == 'auto') {
1005
            $aScriptName = basename($_SERVER['PHP_SELF']);
1006
        }
1007
1008
        $urlarg = $this->GetURLArguments(true);
1009
1010
        if (empty($_GET[_CSIM_DISPLAY])) {
1011
            // First determine if we need to check for a cached version
1012
            // This differs from the standard cache in the sense that the
1013
            // image and CSIM map HTML file is written relative to the directory
1014
            // the script executes in and not the specified cache directory.
1015
            // The reason for this is that the cache directory is not necessarily
1016
            // accessible from the HTTP server.
1017
            if ($this->csimcachename != '') {
1018
                $dir      = dirname($this->csimcachename);
1019
                $base     = basename($this->csimcachename);
1020
                $base     = strtok($base, '.');
1021
                $suffix   = strtok('.');
0 ignored issues
show
Unused Code introduced by
The assignment to $suffix is dead and can be removed.
Loading history...
1022
                $basecsim = $dir . '/' . $base . '?' . $urlarg . '_csim_.html';
1023
                $baseimg  = $base . '?' . $urlarg . '.' . $this->img->img_format;
1024
1025
                // Check that apache can write to directory specified
1026
1027
                if (file_exists($dir) && !is_writeable($dir)) {
1028
                    Util\JpGraphError::RaiseL(25028, $dir); //('Apache/PHP does not have permission to write to the CSIM cache directory ('.$dir.'). Check permissions.');
1029
                }
1030
1031
                // Make sure directory exists
1032
                $this->cache->MakeDirs($dir);
1033
1034
                // Write the image file
1035
                $this->Stroke(CSIMCACHE_DIR . $baseimg);
1036
1037
                // Construct wrapper HTML and write to file and send it back to browser
1038
1039
                // In the src URL we must replace the '?' with its encoding to prevent the arguments
1040
                // to be converted to real arguments.
1041
                $tmp      = str_replace('?', '%3f', $baseimg);
1042
                $htmlwrap = $this->GetHTMLImageMap($aCSIMName) . "\n" .
1043
                '<img src="' . CSIMCACHE_HTTP_DIR . $tmp . '" ismap="ismap" usemap="#' . $aCSIMName . ' width="' . $this->img->width . '" height="' . $this->img->height . '" alt="' . $this->iCSIMImgAlt . "\" />\n";
1044
1045
                if ($fh = @fopen($basecsim, 'w')) {
1046
                    fwrite($fh, $htmlwrap);
1047
                    fclose($fh);
1048
                    echo $htmlwrap;
1049
                } else {
1050
                    Util\JpGraphError::RaiseL(25029, $basecsim); //(" Can't write CSIM \"$basecsim\" for writing. Check free space and permissions.");
1051
                }
1052
            } else {
1053
                if ($aScriptName == '') {
1054
                    Util\JpGraphError::RaiseL(25030); //('Missing script name in call to StrokeCSIM(). You must specify the name of the actual image script as the first parameter to StrokeCSIM().');
1055
                }
1056
                echo $this->GetHTMLImageMap($aCSIMName) . $this->GetCSIMImgHTML($aCSIMName, $aScriptName, $aBorder);
1057
            }
1058
        } else {
1059
            $this->Stroke();
1060
        }
1061
    }
1062
1063
    public function StrokeCSIMImage()
1064
    {
1065
        if (@$_GET[_CSIM_DISPLAY] == 1) {
1066
            $this->Stroke();
1067
        }
1068
    }
1069
1070
    public function GetCSIMImgHTML($aCSIMName, $aScriptName = 'auto', $aBorder = 0)
0 ignored issues
show
Unused Code introduced by
The parameter $aBorder is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1070
    public function GetCSIMImgHTML($aCSIMName, $aScriptName = 'auto', /** @scrutinizer ignore-unused */ $aBorder = 0)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1071
    {
1072
        if ($aScriptName == 'auto') {
1073
            $aScriptName = basename($_SERVER['PHP_SELF']);
1074
        }
1075
        $urlarg = $this->GetURLArguments(true);
1076
1077
        return '<img src="' . $aScriptName . '?' . $urlarg . '" ismap="ismap" usemap="#' . $aCSIMName . '" height="' . $this->img->height . '" alt="' . $this->iCSIMImgAlt . "\" />\n";
1078
    }
1079
1080
    public function GetTextsYMinMax($aY2 = false)
1081
    {
1082
        if ($aY2) {
1083
            $txts = $this->y2texts;
1084
        } else {
1085
            $txts = $this->texts;
1086
        }
1087
        $n   = safe_count($txts);
1088
        $min = null;
1089
        $max = null;
1090
        for ($i = 0; $i < $n; ++$i) {
1091
            if ($txts[$i]->iScalePosY !== null && $txts[$i]->iScalePosX !== null) {
1092
                if ($min === null) {
1093
                    $min = $max = $txts[$i]->iScalePosY;
1094
                } else {
1095
                    $min = min($min, $txts[$i]->iScalePosY);
1096
                    $max = max($max, $txts[$i]->iScalePosY);
1097
                }
1098
            }
1099
        }
1100
        if ($min !== null) {
1101
            return [$min, $max];
1102
        }
1103
1104
        return null;
1105
    }
1106
1107
    public function GetTextsXMinMax($aY2 = false)
1108
    {
1109
        if ($aY2) {
1110
            $txts = $this->y2texts;
1111
        } else {
1112
            $txts = $this->texts;
1113
        }
1114
        $n   = safe_count($txts);
1115
        $min = null;
1116
        $max = null;
1117
        for ($i = 0; $i < $n; ++$i) {
1118
            if ($txts[$i]->iScalePosY !== null && $txts[$i]->iScalePosX !== null) {
1119
                if ($min === null) {
1120
                    $min = $max = $txts[$i]->iScalePosX;
1121
                } else {
1122
                    $min = min($min, $txts[$i]->iScalePosX);
1123
                    $max = max($max, $txts[$i]->iScalePosX);
1124
                }
1125
            }
1126
        }
1127
        if ($min !== null) {
1128
            return [$min, $max];
1129
        }
1130
1131
        return null;
1132
    }
1133
1134
    public function GetXMinMax()
1135
    {
1136
        list($min, $ymin) = $this->plots[0]->Min();
1137
        list($max, $ymax) = $this->plots[0]->Max();
1138
1139
        $i = 0;
1140
        // Some plots, e.g. PlotLine should not affect the scale
1141
        // and will return (null,null). We should ignore those
1142
        // values.
1143
        while (($min === null || $max === null) && ($i < safe_count($this->plots) - 1)) {
1144
            ++$i;
1145
            list($min, $ymin) = $this->plots[$i]->Min();
1146
            list($max, $ymax) = $this->plots[$i]->Max();
1147
        }
1148
1149
        foreach ($this->plots as $p) {
1150
            list($xmin, $ymin) = $p->Min();
1151
            list($xmax, $ymax) = $p->Max();
1152
1153
            if ($xmin !== null && $xmax !== null) {
1154
                $min = min($xmin, $min);
1155
                $max = max($xmax, $max);
1156
            }
1157
        }
1158
1159
        if ($this->y2axis != null) {
1160
            foreach ($this->y2plots as $p) {
1161
                list($xmin, $ymin) = $p->Min();
1162
                list($xmax, $ymax) = $p->Max();
1163
                $min               = min($xmin, $min);
1164
                $max               = max($xmax, $max);
1165
            }
1166
        }
1167
1168
        $n = safe_count($this->ynaxis);
1169
        for ($i = 0; $i < $n; ++$i) {
1170
            if ($this->ynaxis[$i] != null) {
1171
                foreach ($this->ynplots[$i] as $p) {
1172
                    list($xmin, $ymin) = $p->Min();
1173
                    list($xmax, $ymax) = $p->Max();
1174
                    $min               = min($xmin, $min);
1175
                    $max               = max($xmax, $max);
1176
                }
1177
            }
1178
        }
1179
1180
        return [$min, $max];
1181
    }
1182
1183
    public function AdjustMarginsForTitles()
1184
    {
1185
        $totrequired =
1186
            ($this->title->t != ''
1187
            ? $this->title->GetTextHeight($this->img) + $this->title->margin + 5 * SUPERSAMPLING_SCALE
1188
            : 0) +
1189
            ($this->subtitle->t != ''
1190
            ? $this->subtitle->GetTextHeight($this->img) + $this->subtitle->margin + 5 * SUPERSAMPLING_SCALE
1191
            : 0) +
1192
            ($this->subsubtitle->t != ''
1193
            ? $this->subsubtitle->GetTextHeight($this->img) + $this->subsubtitle->margin + 5 * SUPERSAMPLING_SCALE
1194
            : 0);
1195
1196
        $btotrequired = 0;
1197
        if ($this->xaxis != null && !$this->xaxis->hide && !$this->xaxis->hide_labels) {
1198
            // Minimum bottom margin
1199
            if ($this->xaxis->title->t != '') {
1200
                if ($this->img->a == 90) {
1201
                    $btotrequired = $this->yaxis->title->GetTextHeight($this->img) + 7;
1202
                } else {
1203
                    $btotrequired = $this->xaxis->title->GetTextHeight($this->img) + 7;
1204
                }
1205
            } else {
1206
                $btotrequired = 0;
1207
            }
1208
1209
            if ($this->img->a == 90) {
1210
                $this->img->SetFont(
1211
                    $this->yaxis->font_family,
1212
                    $this->yaxis->font_style,
1213
                    $this->yaxis->font_size
1214
                );
1215
                $lh = $this->img->GetTextHeight('Mg', $this->yaxis->label_angle);
1216
            } else {
1217
                $this->img->SetFont(
1218
                    $this->xaxis->font_family,
1219
                    $this->xaxis->font_style,
1220
                    $this->xaxis->font_size
1221
                );
1222
                $lh = $this->img->GetTextHeight('Mg', $this->xaxis->label_angle);
1223
            }
1224
1225
            $btotrequired += $lh + 6;
1226
        }
1227
1228
        if ($this->img->a == 90) {
1229
            // DO Nothing. It gets too messy to do this properly for 90 deg...
1230
        } else {
1231
            // need more top margin
1232
            if ($this->img->top_margin < $totrequired) {
1233
                $this->SetMargin(
1234
                    $this->img->raw_left_margin,
0 ignored issues
show
Bug Best Practice introduced by
The property raw_left_margin does not exist on Amenadiel\JpGraph\Image\RotImage. Since you implemented __get, consider adding a @property annotation.
Loading history...
1235
                    $this->img->raw_right_margin,
0 ignored issues
show
Bug Best Practice introduced by
The property raw_right_margin does not exist on Amenadiel\JpGraph\Image\RotImage. Since you implemented __get, consider adding a @property annotation.
Loading history...
1236
                    $totrequired / SUPERSAMPLING_SCALE,
1237
                    $this->img->raw_bottom_margin
0 ignored issues
show
Bug Best Practice introduced by
The property raw_bottom_margin does not exist on Amenadiel\JpGraph\Image\RotImage. Since you implemented __get, consider adding a @property annotation.
Loading history...
1238
                );
1239
            }
1240
1241
            // need more bottom margin
1242
            if ($this->img->bottom_margin < $btotrequired) {
1243
                $this->SetMargin(
1244
                    $this->img->raw_left_margin,
1245
                    $this->img->raw_right_margin,
1246
                    $this->img->raw_top_margin,
0 ignored issues
show
Bug Best Practice introduced by
The property raw_top_margin does not exist on Amenadiel\JpGraph\Image\RotImage. Since you implemented __get, consider adding a @property annotation.
Loading history...
1247
                    $btotrequired / SUPERSAMPLING_SCALE
1248
                );
1249
            }
1250
        }
1251
    }
1252
1253
    public function StrokeStore($aStrokeFileName)
1254
    {
1255
        // Get the handler to prevent the library from sending the
1256
        // image to the browser
1257
        $ih = $this->Stroke(_IMG_HANDLER);
0 ignored issues
show
Unused Code introduced by
The assignment to $ih is dead and can be removed.
Loading history...
1258
1259
        // Stroke it to a file
1260
        $this->img->Stream($aStrokeFileName);
1261
1262
        // Send it back to browser
1263
        $this->img->Headers();
1264
        $this->img->Stream();
1265
    }
1266
1267
    public function doAutoscaleXAxis()
1268
    {
1269
        $aPlots = array_filter($this->plots, function ($plot) {
1270
            //\Kint::dump($plot, $plot instanceof Plot\Plot);
1271
            return $plot instanceof Plot\Plot;
1272
        });
1273
1274
        //Check if we should autoscale x-axis
1275
        if (!$this->xscale->IsSpecified()) {
1276
            if (substr($this->axtype, 0, 4) == 'text') {
1277
                $max = 0;
1278
                $n   = safe_count($aPlots);
1279
1280
                for ($i = 0; $i < $n; ++$i) {
1281
                    $p = $aPlots[$i];
1282
1283
                    // We need some unfortunate sub class knowledge here in order
1284
                    // to increase number of data points in case it is a line plot
1285
                    // which has the barcenter set. If not it could mean that the
1286
                    // last point of the data is outside the scale since the barcenter
1287
                    // settings means that we will shift the entire plot half a tick step
1288
                    // to the right in oder to align with the center of the bars.
1289
1290
                    $cl = strtolower(get_class($p));
0 ignored issues
show
Unused Code introduced by
The assignment to $cl is dead and can be removed.
Loading history...
1291
                    if (empty($p->barcenter)) {
1292
                        $max = max($max, $p->numpoints - 1);
1293
                    } else {
1294
                        $max = max($max, $p->numpoints);
1295
                    }
1296
                }
1297
                $min = 0;
1298
                if ($this->y2axis != null) {
1299
                    foreach ($this->y2plots as $p) {
1300
                        $max = max($max, $p->numpoints - 1);
1301
                    }
1302
                }
1303
                $n = safe_count($this->ynaxis);
1304
                for ($i = 0; $i < $n; ++$i) {
1305
                    if ($this->ynaxis[$i] != null) {
1306
                        foreach ($this->ynplots[$i] as $p) {
1307
                            $max = max($max, $p->numpoints - 1);
1308
                        }
1309
                    }
1310
                }
1311
1312
                $this->xscale->Update($this->img, $min, $max);
1313
                $this->xscale->ticks->Set($this->xaxis->tick_step, 1);
1314
                $this->xscale->ticks->SupressMinorTickMarks();
1315
            } else {
1316
                list($min, $max) = $this->GetXMinMax();
1317
1318
                $lres = $this->GetLinesXMinMax($this->lines);
1319
                if ($lres) {
0 ignored issues
show
introduced by
The condition $lres is always false.
Loading history...
1320
                    list($linmin, $linmax) = $lres;
1321
                    $min                   = min($min, $linmin);
1322
                    $max                   = max($max, $linmax);
1323
                }
1324
1325
                $lres = $this->GetLinesXMinMax($this->y2lines);
1326
                if ($lres) {
0 ignored issues
show
introduced by
The condition $lres is always false.
Loading history...
1327
                    list($linmin, $linmax) = $lres;
1328
                    $min                   = min($min, $linmin);
1329
                    $max                   = max($max, $linmax);
1330
                }
1331
1332
                $tres = $this->GetTextsXMinMax();
1333
                if ($tres) {
1334
                    list($tmin, $tmax) = $tres;
1335
                    $min               = min($min, $tmin);
1336
                    $max               = max($max, $tmax);
1337
                }
1338
1339
                $tres = $this->GetTextsXMinMax(true);
1340
                if ($tres) {
1341
                    list($tmin, $tmax) = $tres;
1342
                    $min               = min($min, $tmin);
1343
                    $max               = max($max, $tmax);
1344
                }
1345
1346
                $this->xscale->AutoScale($this->img, $min, $max, round($this->img->plotwidth / $this->xtick_factor));
1347
            }
1348
1349
            //Adjust position of y-axis and y2-axis to minimum/maximum of x-scale
1350
            if (!is_numeric($this->yaxis->pos) && !is_string($this->yaxis->pos)) {
1351
                $this->yaxis->SetPos($this->xscale->GetMinVal());
1352
            }
1353
        } elseif ($this->xscale->IsSpecified() &&
1354
            ($this->xscale->auto_ticks || !$this->xscale->ticks->IsSpecified())) {
1355
            // The tick calculation will use the user suplied min/max values to determine
1356
            // the ticks. If auto_ticks is false the exact user specifed min and max
1357
            // values will be used for the scale.
1358
            // If auto_ticks is true then the scale might be slightly adjusted
1359
            // so that the min and max values falls on an even major step.
1360
            $min = $this->xscale->scale[0];
1361
            $max = $this->xscale->scale[1];
1362
            $this->xscale->AutoScale($this->img, $min, $max, round($this->img->plotwidth / $this->xtick_factor), false);
1363
1364
            // Now make sure we show enough precision to accurate display the
1365
            // labels. If this is not done then the user might end up with
1366
            // a scale that might actually start with, say 13.5, butdue to rounding
1367
            // the scale label will ony show 14.
1368
            if (abs(floor($min) - $min) > 0) {
1369
                // If the user has set a format then we bail out
1370
                if ($this->xscale->ticks->label_formatstr == '' && $this->xscale->ticks->label_dateformatstr == '') {
1371
                    $this->xscale->ticks->precision = abs(floor(log10(abs(floor($min) - $min)))) + 1;
1372
                }
1373
            }
1374
        }
1375
1376
        // Position the optional Y2 and Yn axis to the rightmost position of the x-axis
1377
        if ($this->y2axis != null) {
1378
            if (!is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos)) {
1379
                $this->y2axis->SetPos($this->xscale->GetMaxVal());
1380
            }
1381
            $this->y2axis->SetTitleSide(SIDE_RIGHT);
1382
        }
1383
1384
        $n      = safe_count($this->ynaxis);
1385
        $nY2adj = $this->y2axis != null ? $this->iYAxisDeltaPos : 0;
1386
        for ($i = 0; $i < $n; ++$i) {
1387
            if ($this->ynaxis[$i] != null) {
1388
                if (!is_numeric($this->ynaxis[$i]->pos) && !is_string($this->ynaxis[$i]->pos)) {
1389
                    $this->ynaxis[$i]->SetPos($this->xscale->GetMaxVal());
1390
                    $this->ynaxis[$i]->SetPosAbsDelta($i * $this->iYAxisDeltaPos + $nY2adj);
1391
                }
1392
                $this->ynaxis[$i]->SetTitleSide(SIDE_RIGHT);
1393
            }
1394
        }
1395
    }
1396
1397
    public function doAutoScaleYnAxis()
1398
    {
1399
        if ($this->y2scale != null) {
1400
            if (!$this->y2scale->IsSpecified() && safe_count($this->y2plots) > 0) {
1401
                list($min, $max) = $this->GetPlotsYMinMax($this->y2plots);
1402
1403
                $lres = $this->GetLinesYMinMax($this->y2lines);
1404
                if (is_array($lres)) {
0 ignored issues
show
introduced by
The condition is_array($lres) is always false.
Loading history...
1405
                    list($linmin, $linmax) = $lres;
1406
                    $min                   = min($min, $linmin);
1407
                    $max                   = max($max, $linmax);
1408
                }
1409
                $tres = $this->GetTextsYMinMax(true);
1410
                if (is_array($tres)) {
1411
                    list($tmin, $tmax) = $tres;
1412
                    $min               = min($min, $tmin);
1413
                    $max               = max($max, $tmax);
1414
                }
1415
                $this->y2scale->AutoScale($this->img, $min, $max, $this->img->plotheight / $this->ytick_factor);
1416
            } elseif ($this->y2scale->IsSpecified() && ($this->y2scale->auto_ticks || !$this->y2scale->ticks->IsSpecified())) {
1417
                // The tick calculation will use the user suplied min/max values to determine
1418
                // the ticks. If auto_ticks is false the exact user specifed min and max
1419
                // values will be used for the scale.
1420
                // If auto_ticks is true then the scale might be slightly adjusted
1421
                // so that the min and max values falls on an even major step.
1422
                $min = $this->y2scale->scale[0];
1423
                $max = $this->y2scale->scale[1];
1424
                $this->y2scale->AutoScale(
1425
                    $this->img,
1426
                    $min,
1427
                    $max,
1428
                    $this->img->plotheight / $this->ytick_factor,
1429
                    $this->y2scale->auto_ticks
1430
                );
1431
1432
                // Now make sure we show enough precision to accurate display the
1433
                // labels. If this is not done then the user might end up with
1434
                // a scale that might actually start with, say 13.5, butdue to rounding
1435
                // the scale label will ony show 14.
1436
                if (abs(floor($min) - $min) > 0) {
1437
                    // If the user has set a format then we bail out
1438
                    if ($this->y2scale->ticks->label_formatstr == '' && $this->y2scale->ticks->label_dateformatstr == '') {
1439
                        $this->y2scale->ticks->precision = abs(floor(log10(abs(floor($min) - $min)))) + 1;
1440
                    }
1441
                }
1442
            }
1443
        }
1444
1445
        //
1446
        // Autoscale the extra Y-axises
1447
        //
1448
        $n = safe_count($this->ynaxis);
1449
        for ($i = 0; $i < $n; ++$i) {
1450
            if ($this->ynscale[$i] != null) {
1451
                if (!$this->ynscale[$i]->IsSpecified() && safe_count($this->ynplots[$i]) > 0) {
1452
                    list($min, $max) = $this->GetPlotsYMinMax($this->ynplots[$i]);
1453
                    $this->ynscale[$i]->AutoScale($this->img, $min, $max, $this->img->plotheight / $this->ytick_factor);
1454
                } elseif ($this->ynscale[$i]->IsSpecified() && ($this->ynscale[$i]->auto_ticks || !$this->ynscale[$i]->ticks->IsSpecified())) {
1455
                    // The tick calculation will use the user suplied min/max values to determine
1456
                    // the ticks. If auto_ticks is false the exact user specifed min and max
1457
                    // values will be used for the scale.
1458
                    // If auto_ticks is true then the scale might be slightly adjusted
1459
                    // so that the min and max values falls on an even major step.
1460
                    $min = $this->ynscale[$i]->scale[0];
1461
                    $max = $this->ynscale[$i]->scale[1];
1462
                    $this->ynscale[$i]->AutoScale(
1463
                        $this->img,
1464
                        $min,
1465
                        $max,
1466
                        $this->img->plotheight / $this->ytick_factor,
1467
                        $this->ynscale[$i]->auto_ticks
1468
                    );
1469
1470
                    // Now make sure we show enough precision to accurate display the
1471
                    // labels. If this is not done then the user might end up with
1472
                    // a scale that might actually start with, say 13.5, butdue to rounding
1473
                    // the scale label will ony show 14.
1474
                    if (abs(floor($min) - $min) > 0) {
1475
                        // If the user has set a format then we bail out
1476
                        if ($this->ynscale[$i]->ticks->label_formatstr == '' && $this->ynscale[$i]->ticks->label_dateformatstr == '') {
1477
                            $this->ynscale[$i]->ticks->precision = abs(floor(log10(abs(floor($min) - $min)))) + 1;
1478
                        }
1479
                    }
1480
                }
1481
            }
1482
        }
1483
    }
1484
1485
    public function doAutoScaleYAxis()
1486
    {
1487
        //Check if we should autoscale y-axis
1488
        if (!$this->yscale->IsSpecified() && safe_count($this->plots) > 0) {
1489
            list($min, $max) = $this->GetPlotsYMinMax($this->plots);
1490
            $lres            = $this->GetLinesYMinMax($this->lines);
1491
            if (is_array($lres)) {
0 ignored issues
show
introduced by
The condition is_array($lres) is always false.
Loading history...
1492
                list($linmin, $linmax) = $lres;
1493
                $min                   = min($min, $linmin);
1494
                $max                   = max($max, $linmax);
1495
            }
1496
            $tres = $this->GetTextsYMinMax();
1497
            if (is_array($tres)) {
1498
                list($tmin, $tmax) = $tres;
1499
                $min               = min($min, $tmin);
1500
                $max               = max($max, $tmax);
1501
            }
1502
            $this->yscale->AutoScale(
1503
                $this->img,
1504
                $min,
1505
                $max,
1506
                $this->img->plotheight / $this->ytick_factor
1507
            );
1508
        } elseif ($this->yscale->IsSpecified() && ($this->yscale->auto_ticks || !$this->yscale->ticks->IsSpecified())) {
1509
            // The tick calculation will use the user suplied min/max values to determine
1510
            // the ticks. If auto_ticks is false the exact user specifed min and max
1511
            // values will be used for the scale.
1512
            // If auto_ticks is true then the scale might be slightly adjusted
1513
            // so that the min and max values falls on an even major step.
1514
            $min = $this->yscale->scale[0];
1515
            $max = $this->yscale->scale[1];
1516
            $this->yscale->AutoScale(
1517
                $this->img,
1518
                $min,
1519
                $max,
1520
                $this->img->plotheight / $this->ytick_factor,
1521
                $this->yscale->auto_ticks
1522
            );
1523
1524
            // Now make sure we show enough precision to accurate display the
1525
            // labels. If this is not done then the user might end up with
1526
            // a scale that might actually start with, say 13.5, butdue to rounding
1527
            // the scale label will ony show 14.
1528
            if (abs(floor($min) - $min) > 0) {
1529
                // If the user has set a format then we bail out
1530
                if ($this->yscale->ticks->label_formatstr == '' && $this->yscale->ticks->label_dateformatstr == '') {
1531
                    $this->yscale->ticks->precision = abs(floor(log10(abs(floor($min) - $min)))) + 1;
1532
                }
1533
            }
1534
        }
1535
    }
1536
1537
    public function InitScaleConstants()
1538
    {
1539
        // Setup scale constants
1540
        if ($this->yscale) {
1541
            $this->yscale->InitConstants($this->img);
1542
        }
1543
1544
        if ($this->xscale) {
1545
            $this->xscale->InitConstants($this->img);
1546
        }
1547
1548
        if ($this->y2scale) {
1549
            $this->y2scale->InitConstants($this->img);
1550
        }
1551
1552
        $n = safe_count($this->ynscale);
1553
        for ($i = 0; $i < $n; ++$i) {
1554
            if ($this->ynscale[$i]) {
1555
                $this->ynscale[$i]->InitConstants($this->img);
1556
            }
1557
        }
1558
    }
1559
1560
    public function doPrestrokeAdjustments()
1561
    {
1562
        // Do any pre-stroke adjustment that is needed by the different plot types
1563
        // (i.e bar plots want's to add an offset to the x-labels etc)
1564
        for ($i = 0; $i < safe_count($this->plots); ++$i) {
1565
            if ($this->plots[$i] instanceof Plot\Plot) {
1566
                $this->plots[$i]->PreStrokeAdjust($this);
1567
                $this->plots[$i]->DoLegend($this);
1568
            }
1569
        }
1570
1571
        // Any plots on the second Y scale?
1572
        if ($this->y2scale != null) {
1573
            for ($i = 0; $i < safe_count($this->y2plots); ++$i) {
1574
                if ($this->plots[$i] instanceof Plot\Plot) {
1575
                    $this->y2plots[$i]->PreStrokeAdjust($this);
1576
                    $this->y2plots[$i]->DoLegend($this);
1577
                }
1578
            }
1579
        }
1580
1581
        // Any plots on the extra Y axises?
1582
        $n = safe_count($this->ynaxis);
1583
        for ($i = 0; $i < $n; ++$i) {
1584
            if ($this->ynplots == null || $this->ynplots[$i] == null) {
1585
                Util\JpGraphError::RaiseL(25032, $i); //("No plots for Y-axis nbr:$i");
1586
            }
1587
            $m = safe_count($this->ynplots[$i]);
1588
            for ($j = 0; $j < $m; ++$j) {
1589
                $this->ynplots[$i][$j]->PreStrokeAdjust($this);
1590
                $this->ynplots[$i][$j]->DoLegend($this);
1591
            }
1592
        }
1593
    }
1594
1595
    public function StrokeBands($aDepth, $aCSIM)
1596
    {
1597
        // Stroke bands
1598
        if ($this->bands != null && !$aCSIM) {
1599
            for ($i = 0; $i < safe_count($this->bands); ++$i) {
1600
                // Stroke all bands that asks to be in the background
1601
                if ($this->bands[$i]->depth == $aDepth) {
1602
                    $this->bands[$i]->Stroke($this->img, $this->xscale, $this->yscale);
1603
                }
1604
            }
1605
        }
1606
1607
        if ($this->y2bands != null && $this->y2scale != null && !$aCSIM) {
1608
            for ($i = 0; $i < safe_count($this->y2bands); ++$i) {
1609
                // Stroke all bands that asks to be in the foreground
1610
                if ($this->y2bands[$i]->depth == $aDepth) {
1611
                    $this->y2bands[$i]->Stroke($this->img, $this->xscale, $this->y2scale);
1612
                }
1613
            }
1614
        }
1615
    }
1616
1617
    // Stroke the graph
1618
    // $aStrokeFileName If != "" the image will be written to this file and NOT
1619
    // streamed back to the browser
1620
    public function Stroke($aStrokeFileName = '')
1621
    {
1622
        // Fist make a sanity check that user has specified a scale
1623
        if (empty($this->yscale)) {
1624
            Util\JpGraphError::RaiseL(25031); //('You must specify what scale to use with a call to Graph::SetScale().');
1625
        }
1626
1627
        // Start by adjusting the margin so that potential titles will fit.
1628
        $this->AdjustMarginsForTitles();
1629
1630
        // Give the plot a chance to do any scale adjuments the individual plots
1631
        // wants to do. Right now this is only used by the contour plot to set scale
1632
        // limits
1633
        for ($i = 0; $i < safe_count($this->plots); ++$i) {
1634
            if ($this->plots[$i] instanceof Plot\Plot) {
1635
                $this->plots[$i]->PreScaleSetup($this);
1636
            }
1637
            //\Kint::dump($this->plots[$i]);
1638
        }
1639
1640
        // Init scale constants that are used to calculate the transformation from
1641
        // world to pixel coordinates
1642
        $this->InitScaleConstants();
1643
1644
        // If the filename is the predefined value = '_csim_special_'
1645
        // we assume that the call to stroke only needs to do enough
1646
        // to correctly generate the CSIM maps.
1647
        // We use this variable to skip things we don't strictly need
1648
        // to do to generate the image map to improve performance
1649
        // a best we can. Therefor you will see a lot of tests !$_csim in the
1650
        // code below.
1651
        $_csim = ($aStrokeFileName === _CSIM_SPECIALFILE);
1652
1653
        // If we are called the second time (perhaps the user has called GetHTMLImageMap()
1654
        // himself then the legends have alsready been populated once in order to get the
1655
        // CSIM coordinats. Since we do not want the legends to be populated a second time
1656
        // we clear the legends
1657
        $this->legend->Clear();
1658
1659
        // We need to know if we have stroked the plot in the
1660
        // GetCSIMareas. Otherwise the CSIM hasn't been generated
1661
        // and in the case of GetCSIM called before stroke to generate
1662
        // CSIM without storing an image to disk GetCSIM must call Stroke.
1663
        $this->iHasStroked = true;
1664
1665
        // Setup pre-stroked adjustments and Legends
1666
        $this->doPrestrokeAdjustments();
1667
1668
        if ($this->graph_theme) {
1669
            $this->graph_theme->PreStrokeApply($this);
1670
        }
1671
1672
        // Bail out if any of the Y-axis not been specified and
1673
        // has no plots. (This means it is impossible to do autoscaling and
1674
        // no other scale was given so we can't possible draw anything). If you use manual
1675
        // scaling you also have to supply the tick steps as well.
1676
        if ((!$this->yscale->IsSpecified() && safe_count($this->plots) == 0) ||
1677
            ($this->y2scale != null && !$this->y2scale->IsSpecified() && safe_count($this->y2plots) == 0)) {
1678
            //$e = "n=". safe_count($this->y2plots)."\n";
1679
            // $e = "Can't draw unspecified Y-scale.<br>\nYou have either:<br>\n";
1680
            // $e .= "1. Specified an Y axis for autoscaling but have not supplied any plots<br>\n";
1681
            // $e .= "2. Specified a scale manually but have forgot to specify the tick steps";
1682
            Util\JpGraphError::RaiseL(25026);
1683
        }
1684
1685
        // Bail out if no plots and no specified X-scale
1686
        if ((!$this->xscale->IsSpecified() && safe_count($this->plots) == 0 && safe_count($this->y2plots) == 0)) {
1687
            Util\JpGraphError::RaiseL(25034); //("<strong>JpGraph: Can't draw unspecified X-scale.</strong><br>No plots.<br>");
1688
        }
1689
1690
        // Autoscale the normal Y-axis
1691
        $this->doAutoScaleYAxis();
1692
1693
        // Autoscale all additiopnal y-axis
1694
        $this->doAutoScaleYnAxis();
1695
1696
        // Autoscale the regular x-axis and position the y-axis properly
1697
        $this->doAutoScaleXAxis();
1698
1699
        // If we have a negative values and x-axis position is at 0
1700
        // we need to supress the first and possible the last tick since
1701
        // they will be drawn on top of the y-axis (and possible y2 axis)
1702
        // The test below might seem strange the reasone being that if
1703
        // the user hasn't specified a value for position this will not
1704
        // be set until we do the stroke for the axis so as of now it
1705
        // is undefined.
1706
        // For X-text scale we ignore all this since the tick are usually
1707
        // much further in and not close to the Y-axis. Hence the test
1708
        // for 'text'
1709
        if (($this->yaxis->pos == $this->xscale->GetMinVal() || (is_string($this->yaxis->pos) && $this->yaxis->pos == 'min')) &&
1710
            !is_numeric($this->xaxis->pos) && $this->yscale->GetMinVal() < 0 &&
1711
            substr($this->axtype, 0, 4) != 'text' && $this->xaxis->pos != 'min') {
1712
            //$this->yscale->ticks->SupressZeroLabel(false);
1713
            $this->xscale->ticks->SupressFirst();
1714
            if ($this->y2axis != null) {
1715
                $this->xscale->ticks->SupressLast();
1716
            }
1717
        } elseif (!is_numeric($this->yaxis->pos) && $this->yaxis->pos == 'max') {
1718
            $this->xscale->ticks->SupressLast();
1719
        }
1720
1721
        if (!$_csim) {
1722
            $this->StrokePlotArea();
1723
            if ($this->iIconDepth == DEPTH_BACK) {
1724
                $this->StrokeIcons();
1725
            }
1726
        }
1727
        $this->StrokeAxis(false);
1728
1729
        // Stroke colored bands
1730
        $this->StrokeBands(DEPTH_BACK, $_csim);
1731
1732
        if ($this->grid_depth == DEPTH_BACK && !$_csim) {
1733
            $this->ygrid->Stroke();
1734
            $this->xgrid->Stroke();
1735
        }
1736
1737
        // Stroke Y2-axis
1738
        if ($this->y2axis != null && !$_csim) {
1739
            $this->y2axis->Stroke($this->xscale);
1740
            $this->y2grid->Stroke();
1741
        }
1742
1743
        // Stroke yn-axis
1744
        $n = safe_count($this->ynaxis);
1745
        for ($i = 0; $i < $n; ++$i) {
1746
            $this->ynaxis[$i]->Stroke($this->xscale);
1747
        }
1748
1749
        $oldoff = $this->xscale->off;
1750
        if (substr($this->axtype, 0, 4) == 'text') {
1751
            if ($this->text_scale_abscenteroff > -1) {
1752
                // For a text scale the scale factor is the number of pixel per step.
1753
                // Hence we can use the scale factor as a substitute for number of pixels
1754
                // per major scale step and use that in order to adjust the offset so that
1755
                // an object of width "abscenteroff" becomes centered.
1756
                $this->xscale->off += round($this->xscale->scale_factor / 2) - round($this->text_scale_abscenteroff / 2);
1757
            } else {
1758
                $this->xscale->off += ceil($this->xscale->scale_factor * $this->text_scale_off * $this->xscale->ticks->minor_step);
1759
            }
1760
        }
1761
1762
        if ($this->iDoClipping) {
1763
            $oldimage = $this->img->CloneCanvasH();
1764
        }
1765
1766
        if (!$this->y2orderback) {
1767
            // Stroke all plots for Y1 axis
1768
            for ($i = 0; $i < safe_count($this->plots); ++$i) {
1769
                $this->plots[$i]->Stroke($this->img, $this->xscale, $this->yscale);
1770
                if ($this->plots[$i] instanceof Plot\Plot) {
1771
                    $this->plots[$i]->StrokeMargin($this->img);
1772
                }
1773
            }
1774
        }
1775
1776
        // Stroke all plots for Y2 axis
1777
        if ($this->y2scale != null) {
1778
            for ($i = 0; $i < safe_count($this->y2plots); ++$i) {
1779
                $this->y2plots[$i]->Stroke($this->img, $this->xscale, $this->y2scale);
1780
            }
1781
        }
1782
1783
        if ($this->y2orderback) {
1784
            // Stroke all plots for Y1 axis
1785
            for ($i = 0; $i < safe_count($this->plots); ++$i) {
1786
                $this->plots[$i]->Stroke($this->img, $this->xscale, $this->yscale);
1787
                if ($this->plots[$i] instanceof Plot\Plot) {
1788
                    $this->plots[$i]->StrokeMargin($this->img);
1789
                }
1790
            }
1791
        }
1792
1793
        $n = safe_count($this->ynaxis);
1794
        for ($i = 0; $i < $n; ++$i) {
1795
            $m = safe_count($this->ynplots[$i]);
1796
            for ($j = 0; $j < $m; ++$j) {
1797
                $this->ynplots[$i][$j]->Stroke($this->img, $this->xscale, $this->ynscale[$i]);
1798
                if ($this->ynplots[$i][$j] instanceof Plot\Plot) {
1799
                    $this->ynplots[$i][$j]->StrokeMargin($this->img);
1800
                }
1801
            }
1802
        }
1803
1804
        if ($this->iIconDepth == DEPTH_FRONT) {
1805
            $this->StrokeIcons();
1806
        }
1807
1808
        if ($this->iDoClipping) {
1809
            // Clipping only supports graphs at 0 and 90 degrees
1810
            if ($this->img->a == 0) {
1811
                $this->img->CopyCanvasH(
1812
                    $oldimage,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $oldimage does not seem to be defined for all execution paths leading up to this point.
Loading history...
1813
                    $this->img->img,
1814
                    $this->img->left_margin,
1815
                    $this->img->top_margin,
1816
                    $this->img->left_margin,
1817
                    $this->img->top_margin,
1818
                    $this->img->plotwidth + 1,
1819
                    $this->img->plotheight
1820
                );
1821
            } elseif ($this->img->a == 90) {
1822
                $adj = ($this->img->height - $this->img->width) / 2;
1823
                $this->img->CopyCanvasH(
1824
                    $oldimage,
1825
                    $this->img->img,
1826
                    $this->img->bottom_margin - $adj,
1827
                    $this->img->left_margin + $adj,
1828
                    $this->img->bottom_margin - $adj,
1829
                    $this->img->left_margin + $adj,
1830
                    $this->img->plotheight + 1,
1831
                    $this->img->plotwidth
1832
                );
1833
            } else {
1834
                Util\JpGraphError::RaiseL(25035, $this->img->a); //('You have enabled clipping. Cliping is only supported for graphs at 0 or 90 degrees rotation. Please adjust you current angle (='.$this->img->a.' degrees) or disable clipping.');
1835
            }
1836
            $this->img->Destroy();
1837
            $this->img->SetCanvasH($oldimage);
1838
        }
1839
1840
        $this->xscale->off = $oldoff;
1841
1842
        if ($this->grid_depth == DEPTH_FRONT && !$_csim) {
1843
            $this->ygrid->Stroke();
1844
            $this->xgrid->Stroke();
1845
        }
1846
1847
        // Stroke colored bands
1848
        $this->StrokeBands(DEPTH_FRONT, $_csim);
1849
1850
        // Finally draw the axis again since some plots may have nagged
1851
        // the axis in the edges.
1852
        if (!$_csim) {
1853
            $this->StrokeAxis();
1854
        }
1855
1856
        if ($this->y2scale != null && !$_csim) {
1857
            $this->y2axis->Stroke($this->xscale, false);
1858
        }
1859
1860
        if (!$_csim) {
1861
            $this->StrokePlotBox();
1862
        }
1863
1864
        // The titles and legends never gets rotated so make sure
1865
        // that the angle is 0 before stroking them
1866
        $aa = $this->img->SetAngle(0);
1867
        $this->StrokeTitles();
1868
        $this->footer->Stroke($this->img);
1869
        $this->legend->Stroke($this->img);
1870
        $this->img->SetAngle($aa);
1871
        $this->StrokeTexts();
1872
        $this->StrokeTables();
1873
1874
        if (!$_csim) {
1875
            $this->img->SetAngle($aa);
1876
1877
            // Draw an outline around the image map
1878
            if (_JPG_DEBUG) {
1879
                $this->DisplayClientSideaImageMapAreas();
1880
            }
1881
1882
            // Should we do any final image transformation
1883
            if ($this->iImgTrans) {
1884
                $tform          = new Image\ImgTrans($this->img->img);
1885
                $this->img->img = $tform->Skew3D(
1886
                    $this->iImgTransHorizon,
1887
                    $this->iImgTransSkewDist,
1888
                    $this->iImgTransDirection,
1889
                    $this->iImgTransHighQ,
1890
                    $this->iImgTransMinSize,
1891
                    $this->iImgTransFillColor,
1892
                    $this->iImgTransBorder
1893
                );
1894
            }
1895
1896
            // If the filename is given as the special "__handle"
1897
            // then the image handler is returned and the image is NOT
1898
            // streamed back
1899
            if ($aStrokeFileName == _IMG_HANDLER) {
1900
                return $this->img->img;
1901
            }
1902
            // Finally stream the generated picture
1903
            $this->cache->PutAndStream($this->img, $this->cache_name, $this->inline, $aStrokeFileName);
1904
        }
1905
    }
1906
1907
    public function SetAxisLabelBackground($aType, $aXFColor = 'lightgray', $aXColor = 'black', $aYFColor = 'lightgray', $aYColor = 'black')
1908
    {
1909
        $this->iAxisLblBgType       = $aType;
1910
        $this->iXAxisLblBgFillColor = $aXFColor;
1911
        $this->iXAxisLblBgColor     = $aXColor;
1912
        $this->iYAxisLblBgFillColor = $aYFColor;
1913
        $this->iYAxisLblBgColor     = $aYColor;
1914
    }
1915
1916
    public function StrokeAxisLabelBackground()
1917
    {
1918
        // Types
1919
        // 0 = No background
1920
        // 1 = Only X-labels, length of axis
1921
        // 2 = Only Y-labels, length of axis
1922
        // 3 = As 1 but extends to width of graph
1923
        // 4 = As 2 but extends to height of graph
1924
        // 5 = Combination of 3 & 4
1925
        // 6 = Combination of 1 & 2
1926
1927
        $t = $this->iAxisLblBgType;
1928
        if ($t < 1) {
1929
            return;
1930
        }
1931
1932
        // Stroke optional X-axis label background color
1933
        if ($t == 1 || $t == 3 || $t == 5 || $t == 6) {
1934
            $this->img->PushColor($this->iXAxisLblBgFillColor);
1935
            if ($t == 1 || $t == 6) {
1936
                $xl = $this->img->left_margin;
1937
                $yu = $this->img->height - $this->img->bottom_margin + 1;
1938
                $xr = $this->img->width - $this->img->right_margin;
1939
                $yl = $this->img->height - 1 - $this->frame_weight;
1940
            } else {
1941
                // t==3 || t==5
1942
                $xl = $this->frame_weight;
1943
                $yu = $this->img->height - $this->img->bottom_margin + 1;
1944
                $xr = $this->img->width - 1 - $this->frame_weight;
1945
                $yl = $this->img->height - 1 - $this->frame_weight;
1946
            }
1947
1948
            $this->img->FilledRectangle($xl, $yu, $xr, $yl);
1949
            $this->img->PopColor();
1950
1951
            // Check if we should add the vertical lines at left and right edge
1952
            if ($this->iXAxisLblBgColor !== '') {
1953
                // Hardcode to one pixel wide
1954
                $this->img->SetLineWeight(1);
1955
                $this->img->PushColor($this->iXAxisLblBgColor);
1956
                if ($t == 1 || $t == 6) {
1957
                    $this->img->Line($xl, $yu, $xl, $yl);
1958
                    $this->img->Line($xr, $yu, $xr, $yl);
1959
                } else {
1960
                    $xl = $this->img->width - $this->img->right_margin;
1961
                    $this->img->Line($xl, $yu - 1, $xr, $yu - 1);
1962
                }
1963
                $this->img->PopColor();
1964
            }
1965
        }
1966
1967
        if ($t == 2 || $t == 4 || $t == 5 || $t == 6) {
1968
            $this->img->PushColor($this->iYAxisLblBgFillColor);
1969
            if ($t == 2 || $t == 6) {
1970
                $xl = $this->frame_weight;
1971
                $yu = $this->frame_weight + $this->img->top_margin;
1972
                $xr = $this->img->left_margin - 1;
1973
                $yl = $this->img->height - $this->img->bottom_margin + 1;
1974
            } else {
1975
                $xl = $this->frame_weight;
1976
                $yu = $this->frame_weight;
1977
                $xr = $this->img->left_margin - 1;
1978
                $yl = $this->img->height - 1 - $this->frame_weight;
1979
            }
1980
1981
            $this->img->FilledRectangle($xl, $yu, $xr, $yl);
1982
            $this->img->PopColor();
1983
1984
            // Check if we should add the vertical lines at left and right edge
1985
            if ($this->iXAxisLblBgColor !== '') {
1986
                $this->img->PushColor($this->iXAxisLblBgColor);
1987
                if ($t == 2 || $t == 6) {
1988
                    $this->img->Line($xl, $yu - 1, $xr, $yu - 1);
1989
                    $this->img->Line($xl, $yl - 1, $xr, $yl - 1);
1990
                } else {
1991
                    $this->img->Line($xr + 1, $yu, $xr + 1, $this->img->top_margin);
1992
                }
1993
                $this->img->PopColor();
1994
            }
1995
        }
1996
    }
1997
1998
    public function StrokeAxis($aStrokeLabels = true)
1999
    {
2000
        if ($aStrokeLabels) {
2001
            $this->StrokeAxisLabelBackground();
2002
        }
2003
2004
        // Stroke axis
2005
        if ($this->iAxisStyle != AXSTYLE_SIMPLE) {
2006
            switch ($this->iAxisStyle) {
2007
                case AXSTYLE_BOXIN:
2008
                    $toppos    = SIDE_DOWN;
2009
                    $bottompos = SIDE_UP;
2010
                    $leftpos   = SIDE_RIGHT;
2011
                    $rightpos  = SIDE_LEFT;
2012
2013
                    break;
2014
                case AXSTYLE_BOXOUT:
2015
                    $toppos    = SIDE_UP;
2016
                    $bottompos = SIDE_DOWN;
2017
                    $leftpos   = SIDE_LEFT;
2018
                    $rightpos  = SIDE_RIGHT;
2019
2020
                    break;
2021
                case AXSTYLE_YBOXIN:
2022
                    $toppos    = false;
2023
                    $bottompos = SIDE_UP;
2024
                    $leftpos   = SIDE_RIGHT;
2025
                    $rightpos  = SIDE_LEFT;
2026
2027
                    break;
2028
                case AXSTYLE_YBOXOUT:
2029
                    $toppos    = false;
2030
                    $bottompos = SIDE_DOWN;
2031
                    $leftpos   = SIDE_LEFT;
2032
                    $rightpos  = SIDE_RIGHT;
2033
2034
                    break;
2035
                default:
2036
                    Util\JpGraphError::RaiseL(25036, $this->iAxisStyle); //('Unknown AxisStyle() : '.$this->iAxisStyle);
2037
                    break;
2038
            }
2039
2040
            // By default we hide the first label so it doesn't cross the
2041
            // Y-axis in case the positon hasn't been set by the user.
2042
            // However, if we use a box we always want the first value
2043
            // displayed so we make sure it will be displayed.
2044
            $this->xscale->ticks->SupressFirst(false);
2045
2046
            // Now draw the bottom X-axis
2047
            $this->xaxis->SetPos('min');
2048
            $this->xaxis->SetLabelSide(SIDE_DOWN);
2049
            $this->xaxis->scale->ticks->SetSide($bottompos);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $bottompos does not seem to be defined for all execution paths leading up to this point.
Loading history...
2050
            $this->xaxis->Stroke($this->yscale, $aStrokeLabels);
2051
2052
            if ($toppos !== false) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $toppos does not seem to be defined for all execution paths leading up to this point.
Loading history...
2053
                // We also want a top X-axis
2054
                $this->xaxis = $this->xaxis;
2055
                $this->xaxis->SetPos('max');
2056
                $this->xaxis->SetLabelSide(SIDE_UP);
2057
                // No title for the top X-axis
2058
                if ($aStrokeLabels) {
2059
                    $this->xaxis->title->Set('');
2060
                }
2061
                $this->xaxis->scale->ticks->SetSide($toppos);
2062
                $this->xaxis->Stroke($this->yscale, $aStrokeLabels);
2063
            }
2064
2065
            // Stroke the left Y-axis
2066
            $this->yaxis->SetPos('min');
2067
            $this->yaxis->SetLabelSide(SIDE_LEFT);
2068
            $this->yaxis->scale->ticks->SetSide($leftpos);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $leftpos does not seem to be defined for all execution paths leading up to this point.
Loading history...
2069
            $this->yaxis->Stroke($this->xscale, $aStrokeLabels);
2070
2071
            // Stroke the  right Y-axis
2072
            $this->yaxis->SetPos('max');
2073
            // No title for the right side
2074
            if ($aStrokeLabels) {
2075
                $this->yaxis->title->Set('');
2076
            }
2077
            $this->yaxis->SetLabelSide(SIDE_RIGHT);
2078
            $this->yaxis->scale->ticks->SetSide($rightpos);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $rightpos does not seem to be defined for all execution paths leading up to this point.
Loading history...
2079
            $this->yaxis->Stroke($this->xscale, $aStrokeLabels);
2080
        } else {
2081
            $this->xaxis->Stroke($this->yscale, $aStrokeLabels);
2082
            $this->yaxis->Stroke($this->xscale, $aStrokeLabels);
2083
        }
2084
    }
2085
2086
    // Private helper function for backgound image
2087
    public static function LoadBkgImage($aImgFormat = '', $aFile = '', $aImgStr = '')
2088
    {
2089
        if ($aImgStr != '') {
2090
            return Image::CreateFromString($aImgStr);
2091
        }
2092
2093
        // Remove case sensitivity and setup appropriate function to create image
2094
        // Get file extension. This should be the LAST '.' separated part of the filename
2095
        $e   = explode('.', $aFile);
2096
        $ext = strtolower($e[count($e) - 1]);
2097
        if ($ext == 'jpeg') {
2098
            $ext = 'jpg';
2099
        }
2100
2101
        if (trim($ext) == '') {
2102
            $ext = 'png'; // Assume PNG if no extension specified
2103
        }
2104
2105
        if ($aImgFormat == '') {
2106
            $imgtag = $ext;
2107
        } else {
2108
            $imgtag = $aImgFormat;
2109
        }
2110
2111
        $supported = imagetypes();
2112
        if (($ext == 'jpg' && !($supported & IMG_JPG)) ||
2113
            ($ext == 'gif' && !($supported & IMG_GIF)) ||
2114
            ($ext == 'png' && !($supported & IMG_PNG)) ||
2115
            ($ext == 'bmp' && !($supported & IMG_WBMP)) ||
2116
            ($ext == 'xpm' && !($supported & IMG_XPM))) {
2117
            Util\JpGraphError::RaiseL(25037, $aFile); //('The image format of your background image ('.$aFile.') is not supported in your system configuration. ');
2118
        }
2119
2120
        if ($imgtag == 'jpg' || $imgtag == 'jpeg') {
2121
            $f      = 'imagecreatefromjpeg';
2122
            $imgtag = 'jpg';
2123
        } else {
2124
            $f = 'imagecreatefrom' . $imgtag;
2125
        }
2126
2127
        // Compare specified image type and file extension
2128
        if ($imgtag != $ext) {
2129
            //$t = "Background image seems to be of different type (has different file extension) than specified imagetype. Specified: '".$aImgFormat."'File: '".$aFile."'";
2130
            Util\JpGraphError::RaiseL(25038, $aImgFormat, $aFile);
2131
        }
2132
2133
        $img = @$f($aFile);
2134
        if (!$img) {
2135
            Util\JpGraphError::RaiseL(25039, $aFile); //(" Can't read background image: '".$aFile."'");
2136
        }
2137
2138
        return $img;
2139
    }
2140
2141
    public function StrokePlotGrad()
2142
    {
2143
        if ($this->plot_gradtype < 0) {
2144
            return;
2145
        }
2146
2147
        $grad = new Plot\Gradient($this->img);
2148
        $xl   = $this->img->left_margin;
2149
        $yt   = $this->img->top_margin;
2150
        $xr   = $xl + $this->img->plotwidth + 1;
2151
        $yb   = $yt + $this->img->plotheight;
2152
        $grad->FilledRectangle($xl, $yt, $xr, $yb, $this->plot_gradfrom, $this->plot_gradto, $this->plot_gradtype);
2153
    }
2154
2155
    public function StrokeBackgroundGrad()
2156
    {
2157
        if ($this->bkg_gradtype < 0) {
2158
            return;
2159
        }
2160
2161
        $grad = new Plot\Gradient($this->img);
2162
        if ($this->bkg_gradstyle == BGRAD_PLOT) {
2163
            $xl = $this->img->left_margin;
2164
            $yt = $this->img->top_margin;
2165
            $xr = $xl + $this->img->plotwidth + 1;
2166
            $yb = $yt + $this->img->plotheight;
2167
            $grad->FilledRectangle($xl, $yt, $xr, $yb, $this->bkg_gradfrom, $this->bkg_gradto, $this->bkg_gradtype);
2168
        } else {
2169
            $xl = 0;
2170
            $yt = 0;
2171
            $xr = $xl + $this->img->width - 1;
2172
            $yb = $yt + $this->img->height - 1;
2173
            if ($this->doshadow) {
2174
                $xr -= $this->shadow_width;
2175
                $yb -= $this->shadow_width;
2176
            }
2177
            if ($this->doframe) {
2178
                $yt += $this->frame_weight;
2179
                $yb -= $this->frame_weight;
2180
                $xl += $this->frame_weight;
2181
                $xr -= $this->frame_weight;
2182
            }
2183
            $aa = $this->img->SetAngle(0);
2184
            $grad->FilledRectangle($xl, $yt, $xr, $yb, $this->bkg_gradfrom, $this->bkg_gradto, $this->bkg_gradtype);
2185
            $aa = $this->img->SetAngle($aa);
0 ignored issues
show
Unused Code introduced by
The assignment to $aa is dead and can be removed.
Loading history...
2186
        }
2187
    }
2188
2189
    public function StrokeFrameBackground()
2190
    {
2191
        if ($this->background_image != '' && $this->background_cflag != '') {
2192
            Util\JpGraphError::RaiseL(25040); //('It is not possible to specify both a background image and a background country flag.');
2193
        }
2194
        if ($this->background_image != '') {
2195
            $bkgimg = $this->LoadBkgImage($this->background_image_format, $this->background_image);
2196
        } elseif ($this->background_cflag != '') {
2197
            $fobj                        = new Image\FlagImages(FLAGSIZE4);
2198
            $dummy                       = '';
2199
            $bkgimg                      = $fobj->GetImgByName($this->background_cflag, $dummy);
2200
            $this->background_image_mix  = $this->background_cflag_mix;
2201
            $this->background_image_type = $this->background_cflag_type;
2202
        } else {
2203
            return;
2204
        }
2205
2206
        $bw = imagesx($bkgimg);
2207
        $bh = imagesy($bkgimg);
2208
2209
        // No matter what the angle is we always stroke the image and frame
2210
        // assuming it is 0 degree
2211
        $aa = $this->img->SetAngle(0);
2212
2213
        switch ($this->background_image_type) {
2214
            case BGIMG_FILLPLOT: // Resize to just fill the plotarea
2215
                $this->FillMarginArea();
2216
                $this->StrokeFrame();
2217
                // Special case to hande 90 degree rotated graph corectly
2218
                if ($aa == 90) {
2219
                    $this->img->SetAngle(90);
2220
                    $this->FillPlotArea();
2221
                    $aa  = $this->img->SetAngle(0);
2222
                    $adj = ($this->img->height - $this->img->width) / 2;
2223
                    $this->img->CopyMerge(
2224
                        $bkgimg,
2225
                        $this->img->bottom_margin - $adj,
2226
                        $this->img->left_margin + $adj,
2227
                        0,
2228
                        0,
2229
                        $this->img->plotheight + 1,
2230
                        $this->img->plotwidth,
2231
                        $bw,
2232
                        $bh,
2233
                        $this->background_image_mix
2234
                    );
2235
                } else {
2236
                    $this->FillPlotArea();
2237
                    $this->img->CopyMerge(
2238
                        $bkgimg,
2239
                        $this->img->left_margin,
2240
                        $this->img->top_margin + 1,
2241
                        0,
2242
                        0,
2243
                        $this->img->plotwidth + 1,
2244
                        $this->img->plotheight,
2245
                        $bw,
2246
                        $bh,
2247
                        $this->background_image_mix
2248
                    );
2249
                }
2250
2251
                break;
2252
            case BGIMG_FILLFRAME: // Fill the whole area from upper left corner, resize to just fit
2253
                $hadj = 0;
2254
                $vadj = 0;
2255
                if ($this->doshadow) {
2256
                    $hadj = $this->shadow_width;
2257
                    $vadj = $this->shadow_width;
2258
                }
2259
                $this->FillMarginArea();
2260
                $this->FillPlotArea();
2261
                $this->img->CopyMerge(
2262
                    $bkgimg,
2263
                    0,
2264
                    0,
2265
                    0,
2266
                    0,
2267
                    $this->img->width - $hadj,
2268
                    $this->img->height - $vadj,
2269
                    $bw,
2270
                    $bh,
2271
                    $this->background_image_mix
2272
                );
2273
                $this->StrokeFrame();
2274
2275
                break;
2276
            case BGIMG_COPY: // Just copy the image from left corner, no resizing
2277
                $this->FillMarginArea();
2278
                $this->FillPlotArea();
2279
                $this->img->CopyMerge(
2280
                    $bkgimg,
2281
                    0,
2282
                    0,
2283
                    0,
2284
                    0,
2285
                    $bw,
2286
                    $bh,
2287
                    $bw,
2288
                    $bh,
2289
                    $this->background_image_mix
2290
                );
2291
                $this->StrokeFrame();
2292
2293
                break;
2294
            case BGIMG_CENTER: // Center original image in the plot area
2295
                $this->FillMarginArea();
2296
                $this->FillPlotArea();
2297
                $centerx = round($this->img->plotwidth / 2 + $this->img->left_margin - $bw / 2);
2298
                $centery = round($this->img->plotheight / 2 + $this->img->top_margin - $bh / 2);
2299
                $this->img->CopyMerge(
2300
                    $bkgimg,
2301
                    $centerx,
2302
                    $centery,
2303
                    0,
2304
                    0,
2305
                    $bw,
2306
                    $bh,
2307
                    $bw,
2308
                    $bh,
2309
                    $this->background_image_mix
2310
                );
2311
                $this->StrokeFrame();
2312
2313
                break;
2314
            case BGIMG_FREE: // Just copy the image to the specified location
2315
                $this->img->CopyMerge(
2316
                    $bkgimg,
2317
                    $this->background_image_xpos,
2318
                    $this->background_image_ypos,
2319
                    0,
2320
                    0,
2321
                    $bw,
2322
                    $bh,
2323
                    $bw,
2324
                    $bh,
2325
                    $this->background_image_mix
2326
                );
2327
                $this->StrokeFrame(); // New
2328
                break;
2329
            default:
2330
                Util\JpGraphError::RaiseL(25042); //(" Unknown background image layout");
2331
        }
2332
        $this->img->SetAngle($aa);
2333
    }
2334
2335
    // Private
2336
    // Draw a frame around the image
2337
    public function StrokeFrame()
2338
    {
2339
        if (!$this->doframe) {
2340
            return;
2341
        }
2342
2343
        if ($this->background_image_type <= 1 && ($this->bkg_gradtype < 0 || ($this->bkg_gradtype > 0 && $this->bkg_gradstyle == BGRAD_PLOT))) {
2344
            $c = $this->margin_color;
2345
        } else {
2346
            $c = false;
2347
        }
2348
2349
        if ($this->doshadow) {
2350
            $this->img->SetColor($this->frame_color);
2351
            $this->img->ShadowRectangle(
2352
                0,
2353
                0,
2354
                $this->img->width,
2355
                $this->img->height,
2356
                $c,
2357
                $this->shadow_width,
2358
                $this->shadow_color
2359
            );
2360
        } elseif ($this->framebevel) {
2361
            if ($c) {
2362
                $this->img->SetColor($this->margin_color);
2363
                $this->img->FilledRectangle(0, 0, $this->img->width - 1, $this->img->height - 1);
2364
            }
2365
            $this->img->Bevel(
2366
                1,
2367
                1,
2368
                $this->img->width - 2,
2369
                $this->img->height - 2,
2370
                $this->framebeveldepth,
2371
                $this->framebevelcolor1,
2372
                $this->framebevelcolor2
2373
            );
2374
            if ($this->framebevelborder) {
2375
                $this->img->SetColor($this->framebevelbordercolor);
2376
                $this->img->Rectangle(0, 0, $this->img->width - 1, $this->img->height - 1);
2377
            }
2378
        } else {
2379
            $this->img->SetLineWeight($this->frame_weight);
2380
            if ($c) {
2381
                $this->img->SetColor($this->margin_color);
2382
                $this->img->FilledRectangle(0, 0, $this->img->width - 1, $this->img->height - 1);
2383
            }
2384
            $this->img->SetColor($this->frame_color);
2385
            $this->img->Rectangle(0, 0, $this->img->width - 1, $this->img->height - 1);
2386
        }
2387
    }
2388
2389
    public function FillMarginArea()
2390
    {
2391
        $hadj = 0;
2392
        $vadj = 0;
2393
        if ($this->doshadow) {
2394
            $hadj = $this->shadow_width;
2395
            $vadj = $this->shadow_width;
2396
        }
2397
2398
        $this->img->SetColor($this->margin_color);
2399
        $this->img->FilledRectangle(0, 0, $this->img->width - 1 - $hadj, $this->img->height - 1 - $vadj);
2400
2401
        $this->img->FilledRectangle(0, 0, $this->img->width - 1 - $hadj, $this->img->top_margin);
2402
        $this->img->FilledRectangle(0, $this->img->top_margin, $this->img->left_margin, $this->img->height - 1 - $hadj);
2403
        $this->img->FilledRectangle(
2404
            $this->img->left_margin + 1,
2405
            $this->img->height - $this->img->bottom_margin,
2406
            $this->img->width - 1 - $hadj,
2407
            $this->img->height - 1 - $hadj
2408
        );
2409
        $this->img->FilledRectangle(
2410
            $this->img->width - $this->img->right_margin,
2411
            $this->img->top_margin + 1,
2412
            $this->img->width - 1 - $hadj,
2413
            $this->img->height - $this->img->bottom_margin - 1
2414
        );
2415
    }
2416
2417
    public function FillPlotArea()
2418
    {
2419
        $this->img->PushColor($this->plotarea_color);
2420
        $this->img->FilledRectangle(
2421
            $this->img->left_margin,
2422
            $this->img->top_margin,
2423
            $this->img->width - $this->img->right_margin,
2424
            $this->img->height - $this->img->bottom_margin
2425
        );
2426
        $this->img->PopColor();
2427
    }
2428
2429
    // Stroke the plot area with either a solid color or a background image
2430
    public function StrokePlotArea()
2431
    {
2432
        // Note: To be consistent we really should take a possible shadow
2433
        // into account. However, that causes some problem for the LinearScale class
2434
        // since in the current design it does not have any links to class Graph which
2435
        // means it has no way of compensating for the adjusted plotarea in case of a
2436
        // shadow. So, until I redesign LinearScale we can't compensate for this.
2437
        // So just set the two adjustment parameters to zero for now.
2438
        $boxadj = 0; //$this->doframe ? $this->frame_weight : 0 ;
0 ignored issues
show
Unused Code introduced by
The assignment to $boxadj is dead and can be removed.
Loading history...
2439
        $adj    = 0; //$this->doshadow ? $this->shadow_width : 0 ;
0 ignored issues
show
Unused Code introduced by
The assignment to $adj is dead and can be removed.
Loading history...
2440
2441
        if ($this->background_image != '' || $this->background_cflag != '') {
2442
            $this->StrokeFrameBackground();
2443
        } else {
2444
            $aa = $this->img->SetAngle(0);
2445
            $this->StrokeFrame();
2446
            $aa = $this->img->SetAngle($aa);
0 ignored issues
show
Unused Code introduced by
The assignment to $aa is dead and can be removed.
Loading history...
2447
            $this->StrokeBackgroundGrad();
2448
            if ($this->bkg_gradtype < 0 || ($this->bkg_gradtype > 0 && $this->bkg_gradstyle == BGRAD_MARGIN)) {
2449
                $this->FillPlotArea();
2450
            }
2451
            $this->StrokePlotGrad();
2452
        }
2453
    }
2454
2455
    public function StrokeIcons()
2456
    {
2457
        $n = safe_count($this->iIcons);
2458
        for ($i = 0; $i < $n; ++$i) {
2459
            $this->iIcons[$i]->StrokeWithScale($this->img, $this->xscale, $this->yscale);
2460
        }
2461
    }
2462
2463
    public function StrokePlotBox()
2464
    {
2465
        // Should we draw a box around the plot area?
2466
        if ($this->boxed) {
2467
            $this->img->SetLineWeight(1);
2468
            $this->img->SetLineStyle('solid');
2469
            $this->img->SetColor($this->box_color);
2470
            for ($i = 0; $i < $this->box_weight; ++$i) {
2471
                $this->img->Rectangle(
2472
                    $this->img->left_margin - $i,
2473
                    $this->img->top_margin - $i,
2474
                    $this->img->width - $this->img->right_margin + $i,
2475
                    $this->img->height - $this->img->bottom_margin + $i
2476
                );
2477
            }
2478
        }
2479
    }
2480
2481
    public function SetTitleBackgroundFillStyle($aStyle, $aColor1 = 'black', $aColor2 = 'white')
2482
    {
2483
        $this->titlebkg_fillstyle = $aStyle;
2484
        $this->titlebkg_scolor1   = $aColor1;
2485
        $this->titlebkg_scolor2   = $aColor2;
2486
    }
2487
2488
    public function SetTitleBackground($aBackColor = 'gray', $aStyle = TITLEBKG_STYLE1, $aFrameStyle = TITLEBKG_FRAME_NONE, $aFrameColor = 'black', $aFrameWeight = 1, $aBevelHeight = 3, $aEnable = true)
2489
    {
2490
        $this->titlebackground             = $aEnable;
2491
        $this->titlebackground_color       = $aBackColor;
2492
        $this->titlebackground_style       = $aStyle;
2493
        $this->titlebackground_framecolor  = $aFrameColor;
2494
        $this->titlebackground_framestyle  = $aFrameStyle;
2495
        $this->titlebackground_frameweight = $aFrameWeight;
2496
        $this->titlebackground_bevelheight = $aBevelHeight;
2497
    }
2498
2499
    public function StrokeTitles()
2500
    {
2501
        $margin = 3;
2502
2503
        if ($this->titlebackground) {
2504
            // Find out height
2505
            $this->title->margin += 2;
2506
            $h = $this->title->GetTextHeight($this->img) + $this->title->margin + $margin;
2507
            if ($this->subtitle->t != '' && !$this->subtitle->hide) {
2508
                $h += $this->subtitle->GetTextHeight($this->img) + $margin +
2509
                $this->subtitle->margin;
2510
                $h += 2;
2511
            }
2512
            if ($this->subsubtitle->t != '' && !$this->subsubtitle->hide) {
2513
                $h += $this->subsubtitle->GetTextHeight($this->img) + $margin +
2514
                $this->subsubtitle->margin;
2515
                $h += 2;
2516
            }
2517
            $this->img->PushColor($this->titlebackground_color);
2518
            if ($this->titlebackground_style === TITLEBKG_STYLE1) {
2519
                // Inside the frame
2520
                if ($this->framebevel) {
2521
                    $x1 = $y1 = $this->framebeveldepth + 1;
2522
                    $x2 = $this->img->width - $this->framebeveldepth - 2;
2523
                    $this->title->margin += $this->framebeveldepth + 1;
2524
                    $h += $y1;
2525
                    $h += 2;
2526
                } else {
2527
                    $x1 = $y1 = $this->frame_weight;
2528
                    $x2 = $this->img->width - $this->frame_weight - 1;
2529
                }
2530
            } elseif ($this->titlebackground_style === TITLEBKG_STYLE2) {
2531
                // Cover the frame as well
2532
                $x1 = $y1 = 0;
2533
                $x2 = $this->img->width - 1;
2534
            } elseif ($this->titlebackground_style === TITLEBKG_STYLE3) {
2535
                // Cover the frame as well (the difference is that
2536
                // for style==3 a bevel frame border is on top
2537
                // of the title background)
2538
                $x1 = $y1 = 0;
2539
                $x2 = $this->img->width - 1;
2540
                $h += $this->framebeveldepth;
2541
                $this->title->margin += $this->framebeveldepth;
2542
            } else {
2543
                Util\JpGraphError::RaiseL(25043); //('Unknown title background style.');
2544
            }
2545
2546
            if ($this->titlebackground_framestyle === 3) {
2547
                $h += $this->titlebackground_bevelheight * 2 + 1;
2548
                $this->title->margin += $this->titlebackground_bevelheight;
2549
            }
2550
2551
            if ($this->doshadow) {
2552
                $x2 -= $this->shadow_width;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $x2 does not seem to be defined for all execution paths leading up to this point.
Loading history...
2553
            }
2554
2555
            $indent = 0;
2556
            if ($this->titlebackground_framestyle == TITLEBKG_FRAME_BEVEL) {
2557
                $indent = $this->titlebackground_bevelheight;
2558
            }
2559
2560
            if ($this->titlebkg_fillstyle == TITLEBKG_FILLSTYLE_HSTRIPED) {
2561
                $this->img->FilledRectangle2(
2562
                    $x1 + $indent,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $x1 does not seem to be defined for all execution paths leading up to this point.
Loading history...
2563
                    $y1 + $indent,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $y1 does not seem to be defined for all execution paths leading up to this point.
Loading history...
2564
                    $x2 - $indent,
2565
                    $h - $indent,
2566
                    $this->titlebkg_scolor1,
2567
                    $this->titlebkg_scolor2
2568
                );
2569
            } elseif ($this->titlebkg_fillstyle == TITLEBKG_FILLSTYLE_VSTRIPED) {
2570
                $this->img->FilledRectangle2(
2571
                    $x1 + $indent,
2572
                    $y1 + $indent,
2573
                    $x2 - $indent,
2574
                    $h - $indent,
2575
                    $this->titlebkg_scolor1,
2576
                    $this->titlebkg_scolor2,
2577
                    2
2578
                );
2579
            } else {
2580
                // Solid fill
2581
                $this->img->FilledRectangle($x1, $y1, $x2, $h);
2582
            }
2583
            $this->img->PopColor();
2584
2585
            $this->img->PushColor($this->titlebackground_framecolor);
2586
            $this->img->SetLineWeight($this->titlebackground_frameweight);
2587
            if ($this->titlebackground_framestyle == TITLEBKG_FRAME_FULL) {
2588
                // Frame background
2589
                $this->img->Rectangle($x1, $y1, $x2, $h);
2590
            } elseif ($this->titlebackground_framestyle == TITLEBKG_FRAME_BOTTOM) {
2591
                // Bottom line only
2592
                $this->img->Line($x1, $h, $x2, $h);
2593
            } elseif ($this->titlebackground_framestyle == TITLEBKG_FRAME_BEVEL) {
2594
                $this->img->Bevel($x1, $y1, $x2, $h, $this->titlebackground_bevelheight);
2595
            }
2596
            $this->img->PopColor();
2597
2598
            // This is clumsy. But we neeed to stroke the whole graph frame if it is
2599
            // set to bevel to get the bevel shading on top of the text background
2600
            if ($this->framebevel && $this->doframe && $this->titlebackground_style === 3) {
2601
                $this->img->Bevel(
2602
                    1,
2603
                    1,
2604
                    $this->img->width - 2,
2605
                    $this->img->height - 2,
2606
                    $this->framebeveldepth,
2607
                    $this->framebevelcolor1,
2608
                    $this->framebevelcolor2
2609
                );
2610
                if ($this->framebevelborder) {
2611
                    $this->img->SetColor($this->framebevelbordercolor);
2612
                    $this->img->Rectangle(0, 0, $this->img->width - 1, $this->img->height - 1);
2613
                }
2614
            }
2615
        }
2616
2617
        // Stroke title
2618
        $y = $this->title->margin;
2619
        if ($this->title->halign == 'center') {
2620
            $this->title->Center(0, $this->img->width, $y);
2621
        } elseif ($this->title->halign == 'left') {
2622
            $this->title->SetPos($this->title->margin + 2, $y);
2623
        } elseif ($this->title->halign == 'right') {
2624
            $indent = 0;
2625
            if ($this->doshadow) {
2626
                $indent = $this->shadow_width + 2;
2627
            }
2628
            $this->title->SetPos($this->img->width - $this->title->margin - $indent, $y, 'right');
2629
        }
2630
        $this->title->Stroke($this->img);
2631
2632
        // ... and subtitle
2633
        $y += $this->title->GetTextHeight($this->img) + $margin + $this->subtitle->margin;
2634
        if ($this->subtitle->halign == 'center') {
2635
            $this->subtitle->Center(0, $this->img->width, $y);
2636
        } elseif ($this->subtitle->halign == 'left') {
2637
            $this->subtitle->SetPos($this->subtitle->margin + 2, $y);
2638
        } elseif ($this->subtitle->halign == 'right') {
2639
            $indent = 0;
2640
            if ($this->doshadow) {
2641
                $indent = $this->shadow_width + 2;
2642
            }
2643
2644
            $this->subtitle->SetPos($this->img->width - $this->subtitle->margin - $indent, $y, 'right');
2645
        }
2646
        $this->subtitle->Stroke($this->img);
2647
2648
        // ... and subsubtitle
2649
        $y += $this->subtitle->GetTextHeight($this->img) + $margin + $this->subsubtitle->margin;
2650
        if ($this->subsubtitle->halign == 'center') {
2651
            $this->subsubtitle->Center(0, $this->img->width, $y);
2652
        } elseif ($this->subsubtitle->halign == 'left') {
2653
            $this->subsubtitle->SetPos($this->subsubtitle->margin + 2, $y);
2654
        } elseif ($this->subsubtitle->halign == 'right') {
2655
            $indent = 0;
2656
            if ($this->doshadow) {
2657
                $indent = $this->shadow_width + 2;
2658
            }
2659
2660
            $this->subsubtitle->SetPos($this->img->width - $this->subsubtitle->margin - $indent, $y, 'right');
2661
        }
2662
        $this->subsubtitle->Stroke($this->img);
2663
2664
        // ... and fancy title
2665
        $this->tabtitle->Stroke($this->img);
2666
    }
2667
2668
    public function StrokeTexts()
2669
    {
2670
        // Stroke any user added text objects
2671
        if ($this->texts != null) {
2672
            for ($i = 0; $i < safe_count($this->texts); ++$i) {
2673
                $this->texts[$i]->StrokeWithScale($this->img, $this->xscale, $this->yscale);
2674
            }
2675
        }
2676
2677
        if ($this->y2texts != null && $this->y2scale != null) {
2678
            for ($i = 0; $i < safe_count($this->y2texts); ++$i) {
2679
                $this->y2texts[$i]->StrokeWithScale($this->img, $this->xscale, $this->y2scale);
2680
            }
2681
        }
2682
    }
2683
2684
    public function StrokeTables()
2685
    {
2686
        if ($this->iTables != null) {
2687
            $n = safe_count($this->iTables);
2688
            for ($i = 0; $i < $n; ++$i) {
2689
                $this->iTables[$i]->StrokeWithScale($this->img, $this->xscale, $this->yscale);
2690
            }
2691
        }
2692
    }
2693
2694
    public function DisplayClientSideaImageMapAreas()
2695
    {
2696
        // Debug stuff - display the outline of the image map areas
2697
        $csim = '';
2698
        foreach ($this->plots as $p) {
2699
            $csim .= $p->GetCSIMareas();
2700
        }
2701
        $csim .= $this->legend->GetCSIMareas();
2702
        if (preg_match_all('/area shape="(\\w+)" coords="([0-9\\, ]+)"/', $csim, $coords)) {
2703
            $this->img->SetColor($this->csimcolor);
2704
            $n = safe_count($coords[0]);
2705
            for ($i = 0; $i < $n; ++$i) {
2706
                if ($coords[1][$i] == 'poly') {
2707
                    preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/', $coords[2][$i], $pts);
2708
                    $this->img->SetStartPoint($pts[1][count($pts[0]) - 1], $pts[2][count($pts[0]) - 1]);
2709
                    $m = safe_count($pts[0]);
2710
                    for ($j = 0; $j < $m; ++$j) {
2711
                        $this->img->LineTo($pts[1][$j], $pts[2][$j]);
2712
                    }
2713
                } elseif ($coords[1][$i] == 'rect') {
2714
                    $pts = preg_split('/,/', $coords[2][$i]);
2715
                    $this->img->SetStartPoint($pts[0], $pts[1]);
2716
                    $this->img->LineTo($pts[2], $pts[1]);
2717
                    $this->img->LineTo($pts[2], $pts[3]);
2718
                    $this->img->LineTo($pts[0], $pts[3]);
2719
                    $this->img->LineTo($pts[0], $pts[1]);
2720
                }
2721
            }
2722
        }
2723
    }
2724
2725
    // Text scale offset in world coordinates
2726
    public function SetTextScaleOff($aOff)
2727
    {
2728
        $this->text_scale_off         = $aOff;
2729
        $this->xscale->text_scale_off = $aOff;
2730
    }
2731
2732
    // Text width of bar to be centered in absolute pixels
2733
    public function SetTextScaleAbsCenterOff($aOff)
2734
    {
2735
        $this->text_scale_abscenteroff = $aOff;
2736
    }
2737
2738
    // Get Y min and max values for added lines
2739
    public function GetLinesYMinMax($aLines)
2740
    {
2741
        if (is_null($aLines)) {
2742
            return false;
2743
        }
2744
2745
        $n = safe_count($aLines);
2746
        if ($n == 0) {
2747
            return false;
2748
        }
2749
2750
        $min = $aLines[0]->scaleposition;
2751
        $max = $min;
2752
        $flg = false;
2753
        for ($i = 0; $i < $n; ++$i) {
2754
            if ($aLines[$i]->direction == HORIZONTAL) {
2755
                $flg = true;
2756
                $v   = $aLines[$i]->scaleposition;
2757
                if ($min > $v) {
2758
                    $min = $v;
2759
                }
2760
2761
                if ($max < $v) {
2762
                    $max = $v;
2763
                }
2764
            }
2765
        }
2766
2767
        return $flg ? [$min, $max] : false;
0 ignored issues
show
introduced by
The condition $flg is always false.
Loading history...
2768
    }
2769
2770
    // Get X min and max values for added lines
2771
    public function GetLinesXMinMax($aLines)
2772
    {
2773
        $n = safe_count($aLines);
2774
        if ($n == 0) {
2775
            return false;
2776
        }
2777
2778
        $min = $aLines[0]->scaleposition;
2779
        $max = $min;
2780
        $flg = false;
2781
        for ($i = 0; $i < $n; ++$i) {
2782
            if ($aLines[$i]->direction == VERTICAL) {
2783
                $flg = true;
2784
                $v   = $aLines[$i]->scaleposition;
2785
                if ($min > $v) {
2786
                    $min = $v;
2787
                }
2788
2789
                if ($max < $v) {
2790
                    $max = $v;
2791
                }
2792
            }
2793
        }
2794
2795
        return $flg ? [$min, $max] : false;
0 ignored issues
show
introduced by
The condition $flg is always false.
Loading history...
2796
    }
2797
2798
    // Get min and max values for all included plots
2799
    public function GetPlotsYMinMax($bPlots)
2800
    {
2801
        $aPlots = array_filter($bPlots, function ($plot) {
2802
            //\Kint::dump($plot, $plot instanceof Plot\Plot);
2803
            return $plot instanceof Plot\Plot;
2804
        });
2805
        reset($aPlots);
2806
        $n = safe_count($aPlots);
2807
        $i = 0;
2808
        //\Kint::dump($n, $aPlots);
2809
        do {
2810
            //\Kint::dump($i, $aPlots[$i]);
2811
            list($xmax, $max) = isset($aPlots[$i]) ? $aPlots[$i]->Max() : [null, null];
2812
        } while (++$i < $n && !is_numeric($max));
2813
2814
        $i = 0;
2815
        do {
2816
            list($xmin, $min) = isset($aPlots[$i]) ? $aPlots[$i]->Min() : [null, null];
2817
        } while (++$i < $n && !is_numeric($min));
2818
2819
        if (!is_numeric($min) || !is_numeric($max)) {
2820
            Util\JpGraphError::RaiseL(25044); //('Cannot use autoscaling since it is impossible to determine a valid min/max value  of the Y-axis (only null values).');
2821
        }
2822
2823
        for ($i = 0; $i < $n; ++$i) {
2824
            list($xmax, $ymax) = isset($aPlots[$i]) ? $aPlots[$i]->Max() : [null, null];
2825
            list($xmin, $ymin) = isset($aPlots[$i]) ? $aPlots[$i]->Min() : [null, null];
2826
            if (is_numeric($ymax)) {
2827
                $max = max($max, $ymax);
2828
            }
2829
2830
            if (is_numeric($ymin)) {
2831
                $min = min($min, $ymin);
2832
            }
2833
        }
2834
        if ($min == '') {
2835
            $min = 0;
2836
        }
2837
2838
        if ($max == '') {
2839
            $max = 0;
2840
        }
2841
2842
        if ($min == 0 && $max == 0) {
2843
            // Special case if all values are 0
2844
            $min = 0;
2845
            $max = 1;
2846
        }
2847
2848
        return [$min, $max];
2849
    }
2850
2851
    public function hasLinePlotAndBarPlot()
2852
    {
2853
        $has_line = false;
2854
        $has_bar  = false;
2855
2856
        foreach ($this->plots as $plot) {
2857
            if ($plot instanceof Plot\LinePlot) {
2858
                $has_line = true;
2859
            }
2860
            if ($plot instanceof Plot\BarPlot) {
2861
                $has_bar = true;
2862
            }
2863
        }
2864
2865
        if ($has_line && $has_bar) {
2866
            return true;
2867
        }
2868
2869
        return false;
2870
    }
2871
2872
    public function SetTheme($graph_theme)
2873
    {
2874
        if (!($this instanceof PieGraph)) {
2875
            if (!$this->isAfterSetScale) {
2876
                Util\JpGraphError::RaiseL(25133); //('Use Graph::SetTheme() after Graph::SetScale().');
2877
            }
2878
        }
2879
2880
        if ($this->graph_theme) {
2881
            $this->ClearTheme();
2882
        }
2883
        $this->graph_theme = $graph_theme;
2884
        $this->graph_theme->ApplyGraph($this);
2885
    }
2886
2887
    public function ClearTheme()
2888
    {
2889
        $this->graph_theme = null;
2890
2891
        $this->isRunningClear = true;
2892
2893
        $this->__construct(
2894
            $this->inputValues['aWidth'],
2895
            $this->inputValues['aHeight'],
2896
            $this->inputValues['aCachedName'],
2897
            $this->inputValues['aTimeout'],
2898
            $this->inputValues['aInline']
2899
        );
2900
2901
        if (!($this instanceof PieGraph)) {
2902
            if ($this->isAfterSetScale) {
2903
                $this->SetScale(
2904
                    $this->inputValues['aAxisType'],
2905
                    $this->inputValues['aYMin'],
2906
                    $this->inputValues['aYMax'],
2907
                    $this->inputValues['aXMin'],
2908
                    $this->inputValues['aXMax']
2909
                );
2910
            }
2911
        }
2912
2913
        $this->isRunningClear = false;
2914
    }
2915
2916
    public function SetSupersampling($do = false, $scale = 2)
2917
    {
2918
        if ($do) {
2919
            define('SUPERSAMPLING_SCALE', $scale);
2920
        // $this->img->scale = $scale;
2921
        } else {
2922
            define('SUPERSAMPLING_SCALE', 1);
2923
            //$this->img->scale = 0;
2924
        }
2925
    }
2926
} // @class
2927