Test Failed
Push — feature/move_tests_to_pest_and... ( ffa61b...387f0b )
by Felipe
04:33
created

Graph::getScale()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * JPGraph - Community Edition
5
 */
6
7
namespace Amenadiel\JpGraph\Graph;
8
9
use Amenadiel\JpGraph\Image;
10
use Amenadiel\JpGraph\Plot;
11
use Amenadiel\JpGraph\Text;
12
use Amenadiel\JpGraph\Util;
13
use function count;
14
15
/**
16
 * @class Graph
17
  *  Description: Main class to handle graphs
18
 */
19
class Graph
20
{
21
    /**
22
     * @var Util\DateLocale
23
     */
24
    public $gDateLocale;
25
26
    /**
27
     * @var Util\DateLocale
28
     */
29
    public $gJpgDateLocale;
30
31
    /**
32
     * @var Image\ImgStreamCache
33
     */
34
    public $cache; // Cache object (singleton)
35
36
    public $img; // Img object (singleton)
37
38
    public $plots = []; // Array of all plot object in the graph (for Y 1 axis)
39
40
    public $y2plots = []; // Array of all plot object in the graph (for Y 2 axis)
41
42
    public $ynplots = [];
43
44
    /**
45
     * @var Scale\DateScale|Scale\LinearScale|Scale\LogScale|null
46
     */
47
    public $xscale; // X Scale object (could be instance of LinearScale or LogScale
48
    public $scale;
49
50
    /**
51
     * @var Scale\LinearScale|Scale\LogScale|null
52
     */
53
    public $yscale;
54
55
    /**
56
     * @var Scale\LinearScale|Scale\LogScale|null
57
     */
58
    public $y2scale;
59
60
    public $ynscale = [];
61
62
    public $iIcons = []; // Array of Icons to add to
63
64
    public $cache_name; // File name to be used for the current graph in the cache directory
65
66
    /**
67
     * @var Grid|null
68
     */
69
    public $xgrid; // X Grid object (linear or logarithmic)
70
71
    /**
72
     * @var Grid|null
73
     */
74
    public $ygrid;
75
76
    /**
77
     * @var Grid|null
78
     */
79
    public $y2grid; //dito for Y
80
81
    public $doframe;
82
83
    public $frame_color;
84
85
    public $frame_weight; // Frame around graph
86
87
    public $boxed = false;
88
89
    public $box_color = 'black';
90
91
    public $box_weight = 1; // Box around plot area
92
93
    public $doshadow = false;
94
95
    public $shadow_width = 4;
96
97
    public $shadow_color = '[email protected]'; // Shadow for graph
98
99
    public $xaxis; // X-axis (instane of Axis class)
100
101
    /**
102
     * @var Axis\Axis|null
103
     */
104
    public $yaxis;
105
106
    /**
107
     * @var Axis\Axis|null
108
     */
109
    public $y2axis;
110
111
    public $ynaxis = []; // Y axis (instance of Axis class)
112
113
    public $margin_color; // Margin color of graph
114
115
    public $plotarea_color = [255, 255, 255]; // Plot area color
116
117
    /**
118
     * @var Text\Text
119
     */
120
    public $title;
121
122
    /**
123
     * @var Text\Text
124
     */
125
    public $subtitle;
126
127
    /**
128
     * @var Text\Text
129
     */
130
    public $subsubtitle; // Title and subtitle(s) text object
131
132
    public $axtype = 'linlin'; // Type of axis
133
134
    /**
135
     * @var int
136
     *
137
     * @psalm-var 15|30|45|60
138
     */
139
    public $xtick_factor;
140
141
    /**
142
     * @var int
143
     *
144
     * @psalm-var 12|25|40|100
145
     */
146
    public $ytick_factor; // Factor to determine the maximum number of ticks depending on the plot width
147
148
    public $texts;
149
150
    public $y2texts; // Text object to ge shown in the graph
151
152
    public $lines;
153
154
    public $y2lines;
155
156
    public $bands;
157
158
    public $y2bands;
159
160
    public $text_scale_off = 0;
161
162
    public $text_scale_abscenteroff = -1; // Text scale in fractions and for centering bars
163
164
    public $background_image = '';
165
166
    public $background_image_type = -1;
167
168
    public $background_image_format = 'png';
169
170
    /**
171
     * @var int
172
     */
173
    public $background_image_bright = 0;
174
175
    /**
176
     * @var int
177
     */
178
    public $background_image_contr = 0;
179
180
    /**
181
     * @var int
182
     */
183
    public $background_image_sat = 0;
184
185
    public $background_image_xpos = 0;
186
187
    public $background_image_ypos = 0;
188
189
    /**
190
     * @var int
191
     */
192
    public $image_bright = 0;
193
194
    /**
195
     * @var int
196
     */
197
    public $image_contr = 0;
198
199
    /**
200
     * @var int
201
     */
202
    public $image_sat = 0;
203
204
    public $inline;
205
206
    /**
207
     * @var int
208
     */
209
    public $showcsim = 0;
210
211
    /**
212
     * @var string
213
     */
214
    public $csimcolor = 'red'; //debug stuff, draw the csim boundaris on the image if <>0
215
216
    public $grid_depth = Configs::DEPTH_BACK; // Draw grid under all plots as default
217
218
    public $iAxisStyle = Configs::AXSTYLE_SIMPLE;
219
220
    /**
221
     * @var false
222
     */
223
    public $iCSIMdisplay = false;
224
225
    /**
226
     * @var bool
227
     */
228
    public $iHasStroked = false;
229
230
    /**
231
     * @var Image\Footer
232
     */
233
    public $footer;
234
235
    /**
236
     * @var string
237
     */
238
    public $csimcachename = '';
239
240
    public $csimcachetimeout = 0;
241
242
    public $iCSIMImgAlt = '';
243
244
    public $iDoClipping = false;
245
246
    public $y2orderback = true;
247
248
    /**
249
     * @var Text\GraphTabTitle
250
     */
251
    public $tabtitle;
252
253
    public $bkg_gradtype = -1;
254
255
    public $bkg_gradstyle = Configs::BGRAD_MARGIN;
256
257
    public $bkg_gradfrom = 'navy';
258
259
    public $bkg_gradto = 'silver';
260
261
    public $plot_gradtype = -1;
262
263
    public $plot_gradstyle = Configs::BGRAD_MARGIN;
264
265
    public $plot_gradfrom = 'silver';
266
267
    public $plot_gradto = 'navy';
268
269
    public $titlebackground = false;
270
271
    public $titlebackground_color = 'lightblue';
272
273
    public $titlebackground_style = 1;
274
275
    public $titlebackground_framecolor;
276
277
    public $titlebackground_framestyle;
278
279
    public $titlebackground_frameweight;
280
281
    public $titlebackground_bevelheight;
282
283
    public $titlebkg_fillstyle = Configs::TITLEBKG_FILLSTYLE_SOLID;
284
285
    public $titlebkg_scolor1 = 'black';
286
287
    public $titlebkg_scolor2 = 'white';
288
289
    public $framebevel;
290
291
    public $framebeveldepth;
292
293
    public $framebevelborder;
294
295
    public $framebevelbordercolor;
296
297
    public $framebevelcolor1;
298
299
    public $framebevelcolor2;
300
301
    public $background_image_mix = 100;
302
303
    public $background_cflag = '';
304
305
    public $background_cflag_type = Configs::BGIMG_FILLPLOT;
306
307
    public $background_cflag_mix = 100;
308
309
    /**
310
     * @var bool
311
     */
312
    public $iImgTrans = false;
313
314
    public $iImgTransHorizon = 100;
315
316
    public $iImgTransSkewDist = 150;
317
318
    public $iImgTransDirection = 1;
319
320
    public $iImgTransMinSize = true;
321
322
    public $iImgTransFillColor = 'white';
323
324
    public $iImgTransHighQ = false;
325
326
    public $iImgTransBorder = false;
327
328
    public $iImgTransHorizonPos = 0.5;
329
330
    /**
331
     * @var Legend
332
     */
333
    public $legend;
334
335
    public $graph_theme;
336
337
    protected $iYAxisDeltaPos = 50;
338
339
    protected $iIconDepth = Configs::DEPTH_BACK;
340
341
    protected $iAxisLblBgType = 0;
342
343
    protected $iXAxisLblBgFillColor = 'lightgray';
344
345
    protected $iXAxisLblBgColor = 'black';
346
347
    protected $iYAxisLblBgFillColor = 'lightgray';
348
349
    protected $iYAxisLblBgColor = 'black';
350
351
    protected $iTables;
352
353
    /**
354
     * @var bool
355
     */
356
    protected $isRunningClear = false;
357
358
    protected $inputValues;
359
360
    /**
361
     * @var bool
362
     */
363
    protected $isAfterSetScale = false;
364
365
    // aWIdth   Width in pixels of image
366
    // aHeight   Height in pixels of image
367
    // aCachedName Name for image file in cache directory
368
    // aTimeOut  Timeout in minutes for image in cache
369
    // aInline  If true the image is streamed back in the call to Stroke()
370
    //   If false the image is just created in the cache
371
    public function __construct($aWidth = 300, $aHeight = 200, $aCachedName = '', $aTimeout = 0, $aInline = true)
372
    {
373
        // Bootstrap configs repository if it hasn't bootstrapped already
374
        Util\Helper::bootstrapLibrary();
375
376
        $this->gDateLocale = new Util\DateLocale();
377
        $this->gJpgDateLocale = new Util\DateLocale();
378
379
        if (!\is_numeric($aWidth) || !\is_numeric($aHeight)) {
380
            Util\JpGraphError::RaiseL(25008); //('Image width/height argument in Graph::Graph() must be numeric');
381
        }
382
383
        // Initialize frame and margin
384
        $this->InitializeFrameAndMargin();
385
386
        // Automatically generate the image file name based on the name of the script that
387
        // generates the graph
388
        if ('auto' === $aCachedName) {
389
            $aCachedName = Util\Helper::GenImgName();
390
        }
391
392
        // Should the image be streamed back to the browser or only to the cache?
393
        $this->inline = $aInline;
394
395
        $this->img = new Image\RotImage($aWidth, $aHeight);
396
        $this->cache = new Image\ImgStreamCache();
397
398
        // Window doesn't like '?' in the file name so replace it with an '_'
399
        $aCachedName = \str_replace('?', '_', $aCachedName);
400
        $this->SetupCache($aCachedName, $aTimeout);
401
402
        $this->title = new Text\Text();
403
        $this->title->ParagraphAlign('center');
404
        $this->title->SetFont(Configs::getConfig('FF_DEFAULT'), Configs::getConfig('FS_NORMAL')); //Configs::getConfig('FF_FONT2'), Configs::getConfig('FS_BOLD')
405
        $this->title->SetMargin(5);
406
        $this->title->SetAlign('center');
407
408
        $this->subtitle = new Text\Text();
409
        $this->subtitle->ParagraphAlign('center');
410
        $this->subtitle->SetMargin(3);
411
        $this->subtitle->SetAlign('center');
412
413
        $this->subsubtitle = new Text\Text();
414
        $this->subsubtitle->ParagraphAlign('center');
415
        $this->subsubtitle->SetMargin(3);
416
        $this->subsubtitle->SetAlign('center');
417
418
        $this->legend = new Legend();
419
        $this->footer = new Image\Footer();
420
421
        // If the cached version exist just read it directly from the
422
        // cache, stream it back to browser and exit
423
        if ('' !== $aCachedName && Configs::getConfig('READ_CACHE') && $aInline) {
424
            if ($this->cache->GetAndStream($this->img, $aCachedName)) {
425
                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...
426
            }
427
        }
428
429
        $this->SetTickDensity(); // Normal density
430
431
        $this->tabtitle = new Text\GraphTabTitle();
432
433
        if ($this->isRunningClear) {
434
            return;
435
        }
436
437
        $this->inputValues = [];
438
        $this->inputValues['aWidth'] = $aWidth;
439
        $this->inputValues['aHeight'] = $aHeight;
440
        $this->inputValues['aCachedName'] = $aCachedName;
441
        $this->inputValues['aTimeout'] = $aTimeout;
442
        $this->inputValues['aInline'] = $aInline;
443
444
        $theme_class = '\Amenadiel\JpGraph\Themes\\' . Configs::getConfig('DEFAULT_THEME_CLASS');
445
446
        if (!\class_exists($theme_class)) {
447
            return;
448
        }
449
450
        $this->graph_theme = new $theme_class();
451
    }
452
453
    public function InitializeFrameAndMargin()
454
    {
455
        $this->doframe = true;
456
        $this->frame_color = 'black';
457
        $this->frame_weight = 1;
458
459
        $this->titlebackground_framecolor = 'blue';
460
        $this->titlebackground_framestyle = 2;
461
        $this->titlebackground_frameweight = 1;
462
        $this->titlebackground_bevelheight = 3;
463
        $this->titlebkg_fillstyle = Configs::getConfig('TITLEBKG_FILLSTYLE_SOLID');
464
        $this->titlebkg_scolor1 = 'black';
465
        $this->titlebkg_scolor2 = 'white';
466
        $this->framebevel = false;
467
        $this->framebeveldepth = 2;
468
        $this->framebevelborder = false;
469
        $this->framebevelbordercolor = 'black';
470
        $this->framebevelcolor1 = '[email protected]';
471
        $this->framebevelcolor2 = '[email protected]';
472
473
        $this->margin_color = [250, 250, 250];
474
    }
475
476
    /**
477
     * @param string $aFilename
478
     */
479
    public function SetupCache($aFilename, $aTimeout = 60)
480
    {
481
        $this->cache_name = $aFilename;
482
        $this->cache->SetTimeOut($aTimeout);
483
    }
484
485
    // Enable final image perspective transformation
486
    public function Set3DPerspective($aDir = 1, $aHorizon = 100, $aSkewDist = 120, $aQuality = false, $aFillColor = '#FFFFFF', $aBorder = false, $aMinSize = true, $aHorizonPos = 0.5)
487
    {
488
        $this->iImgTrans = true;
489
        $this->iImgTransHorizon = $aHorizon;
490
        $this->iImgTransSkewDist = $aSkewDist;
491
        $this->iImgTransDirection = $aDir;
492
        $this->iImgTransMinSize = $aMinSize;
493
        $this->iImgTransFillColor = $aFillColor;
494
        $this->iImgTransHighQ = $aQuality;
495
        $this->iImgTransBorder = $aBorder;
496
        $this->iImgTransHorizonPos = $aHorizonPos;
497
    }
498
499
    public function SetUserFont($aNormal, $aBold = '', $aItalic = '', $aBoldIt = '')
500
    {
501
        $this->img->ttf->SetUserFont($aNormal, $aBold, $aItalic, $aBoldIt);
502
    }
503
504
    public function SetUserFont1($aNormal, $aBold = '', $aItalic = '', $aBoldIt = '')
505
    {
506
        $this->img->ttf->SetUserFont1($aNormal, $aBold, $aItalic, $aBoldIt);
507
    }
508
509
    public function SetUserFont2($aNormal, $aBold = '', $aItalic = '', $aBoldIt = '')
510
    {
511
        $this->img->ttf->SetUserFont2($aNormal, $aBold, $aItalic, $aBoldIt);
512
    }
513
514
    public function SetUserFont3($aNormal, $aBold = '', $aItalic = '', $aBoldIt = '')
515
    {
516
        $this->img->ttf->SetUserFont3($aNormal, $aBold, $aItalic, $aBoldIt);
517
    }
518
519
    // Set Image format and optional quality
520
    public function SetImgFormat($aFormat, $aQuality = 75)
521
    {
522
        $this->img->SetImgFormat($aFormat, $aQuality);
523
    }
524
525
    // Should the grid be in front or back of the plot?
526
    /**
527
     * @param int $aDepth
528
     */
529
    public function SetGridDepth($aDepth)
530
    {
531
        $this->grid_depth = $aDepth;
532
    }
533
534
    public function SetIconDepth($aDepth)
535
    {
536
        $this->iIconDepth = $aDepth;
537
    }
538
539
    // Specify graph angle 0-360 degrees.
540
    /**
541
     * @param int $aAngle
542
     */
543
    public function SetAngle($aAngle)
544
    {
545
        $this->img->SetAngle($aAngle);
546
    }
547
548
    public function SetAlphaBlending($aFlg = true)
549
    {
550
        $this->img->SetAlphaBlending($aFlg);
551
    }
552
553
    // Shortcut to image margin
554
    /**
555
     * @param int $lm
556
     * @param int $rm
557
     * @param int $tm
558
     * @param int $bm
559
     */
560
    public function SetMargin($lm, $rm, $tm, $bm)
561
    {
562
        $this->img->SetMargin($lm, $rm, $tm, $bm);
563
    }
564
565
    public function SetY2OrderBack($aBack = true)
566
    {
567
        $this->y2orderback = $aBack;
568
    }
569
570
    // Rotate the graph 90 degrees and set the margin
571
    // when we have done a 90 degree rotation
572
    public function Set90AndMargin($lm = 0, $rm = 0, $tm = 0, $bm = 0)
573
    {
574
        $lm = 0 === $lm ? \floor(0.2 * $this->img->width) : $lm;
575
        $rm = 0 === $rm ? \floor(0.1 * $this->img->width) : $rm;
576
        $tm = 0 === $tm ? \floor(0.2 * $this->img->height) : $tm;
577
        $bm = 0 === $bm ? \floor(0.1 * $this->img->height) : $bm;
578
579
        $adj = ($this->img->height - $this->img->width) / 2;
580
        $this->img->SetMargin($tm - $adj, $bm - $adj, $rm + $adj, $lm + $adj);
581
        $this->img->SetCenter(\floor($this->img->width / 2), \floor($this->img->height / 2));
582
        $this->SetAngle(90);
583
584
        if (empty($this->yaxis) || empty($this->xaxis)) {
585
            Util\JpGraphError::RaiseL(25009); //('You must specify what scale to use with a call to Graph::SetScale()');
586
        }
587
        $this->xaxis->SetLabelAlign('right', 'center');
588
        $this->yaxis->SetLabelAlign('center', 'bottom');
589
    }
590
591
    public function SetClipping($aFlg = true)
592
    {
593
        $this->iDoClipping = $aFlg;
594
    }
595
596
    // Add a plot object to the graph
597
    /**
598
     * @param (Plot\LinePlot|mixed)[]|Text\Text $aPlot
599
     */
600
    public function Add($aPlot)
601
    {
602
        if (null === $aPlot) {
0 ignored issues
show
introduced by
The condition null === $aPlot is always false.
Loading history...
603
            Util\JpGraphError::RaiseL(25010); //("Graph::Add() You tried to add a null plot to the graph.");
604
        }
605
606
        if (\is_array($aPlot) && Configs::safe_count($aPlot) > 0) {
607
            $cl = $aPlot[0];
608
        } else {
609
            $cl = $aPlot;
610
        }
611
612
        if ($cl instanceof Text\Text) {
613
            $this->AddText($aPlot);
614
        } elseif (($cl instanceof Plot\PlotLine)) {
615
            $this->AddLine($aPlot);
616
        } elseif (($cl instanceof Plot\PlotBand)) {
617
            $this->AddBand($aPlot);
618
        } elseif (($cl instanceof Plot\IconPlot)) {
619
            $this->AddIcon($aPlot);
0 ignored issues
show
Bug introduced by
It seems like $aPlot can also be of type Amenadiel\JpGraph\Text\Text; however, parameter $aIcon of Amenadiel\JpGraph\Graph\Graph::AddIcon() does only seem to accept Amenadiel\JpGraph\Plot\IconPlot|array, maybe add an additional type check? ( Ignorable by Annotation )

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

619
            $this->AddIcon(/** @scrutinizer ignore-type */ $aPlot);
Loading history...
620
        } elseif (($cl instanceof Text\GTextTable)) {
621
            $this->AddTable($aPlot);
622
        } else {
623
            if (\is_array($aPlot)) {
624
                $this->plots = \array_merge($this->plots, $aPlot);
625
            } else {
626
                $this->plots[] = $aPlot;
627
            }
628
        }
629
630
        if (!$this->graph_theme) {
631
            return;
632
        }
633
634
        $this->graph_theme->SetupPlot($aPlot);
635
    }
636
637
    public function AddTable($aTable)
638
    {
639
        if (\is_array($aTable)) {
640
            for ($i = 0; Configs::safe_count($aTable) > $i; ++$i) {
641
                $this->iTables[] = $aTable[$i];
642
            }
643
        } else {
644
            $this->iTables[] = $aTable;
645
        }
646
    }
647
648
    /**
649
     * @param Plot\IconPlot|array $aIcon
650
     */
651
    public function AddIcon($aIcon)
652
    {
653
        if (\is_array($aIcon)) {
654
            for ($i = 0; Configs::safe_count($aIcon) > $i; ++$i) {
655
                $this->iIcons[] = $aIcon[$i];
656
            }
657
        } else {
658
            $this->iIcons[] = $aIcon;
659
        }
660
    }
661
662
    // Add plot to second Y-scale
663
    public function AddY2($aPlot)
664
    {
665
        if (null === $aPlot) {
666
            Util\JpGraphError::RaiseL(25011); //("Graph::AddY2() You tried to add a null plot to the graph.");
667
        }
668
669
        if (\is_array($aPlot) && Configs::safe_count($aPlot) > 0) {
670
            $cl = $aPlot[0];
671
        } else {
672
            $cl = $aPlot;
673
        }
674
675
        if ($cl instanceof Text\Text) {
676
            $this->AddText($aPlot, true);
677
        } elseif (($cl instanceof Plot\PlotLine)) {
678
            $this->AddLine($aPlot, true);
679
        } elseif (($cl instanceof Plot\PlotBand)) {
680
            $this->AddBand($aPlot, true);
681
        } else {
682
            $this->y2plots[] = $aPlot;
683
        }
684
685
        if (!$this->graph_theme) {
686
            return;
687
        }
688
689
        $this->graph_theme->SetupPlot($aPlot);
690
    }
691
692
    // Add plot to the extra Y-axises
693
    public function AddY($aN, $aPlot)
694
    {
695
        if (null === $aPlot) {
696
            Util\JpGraphError::RaiseL(25012); //("Graph::AddYN() You tried to add a null plot to the graph.");
697
        }
698
699
        if (\is_array($aPlot) && Configs::safe_count($aPlot) > 0) {
700
            $cl = $aPlot[0];
701
        } else {
702
            $cl = $aPlot;
703
        }
704
705
        if (($cl instanceof Text\Text)
706
            || ($cl instanceof Plot\PlotLine)
707
            || ($cl instanceof Plot\PlotBand)
708
        ) {
709
            Util\JpGraphError::RaiseL(25013); //('You can only add standard plots to multiple Y-axis');
710
        } else {
711
            $this->ynplots[$aN][] = $aPlot;
712
        }
713
714
        if (!$this->graph_theme) {
715
            return;
716
        }
717
718
        $this->graph_theme->SetupPlot($aPlot);
719
    }
720
721
    // Add text object to the graph
722
    /**
723
     * @param Text\Text|array $aTxt
724
     * @param bool $aToY2
725
     */
726
    public function AddText($aTxt, $aToY2 = false)
727
    {
728
        if (null === $aTxt) {
0 ignored issues
show
introduced by
The condition null === $aTxt is always false.
Loading history...
729
            Util\JpGraphError::RaiseL(25014); //("Graph::AddText() You tried to add a null text to the graph.");
730
        }
731
732
        if ($aToY2) {
733
            if (\is_array($aTxt)) {
734
                for ($i = 0; Configs::safe_count($aTxt) > $i; ++$i) {
735
                    $this->y2texts[] = $aTxt[$i];
736
                }
737
            } else {
738
                $this->y2texts[] = $aTxt;
739
            }
740
        } else {
741
            if (\is_array($aTxt)) {
742
                for ($i = 0; Configs::safe_count($aTxt) > $i; ++$i) {
743
                    $this->texts[] = $aTxt[$i];
744
                }
745
            } else {
746
                $this->texts[] = $aTxt;
747
            }
748
        }
749
    }
750
751
    // Add a line object (class PlotLine) to the graph
752
    /**
753
     * @param bool $aToY2
754
     */
755
    public function AddLine($aLine, $aToY2 = false)
756
    {
757
        if (null === $aLine) {
758
            Util\JpGraphError::RaiseL(25015); //("Graph::AddLine() You tried to add a null line to the graph.");
759
        }
760
761
        if ($aToY2) {
762
            if (\is_array($aLine)) {
763
                for ($i = 0; Configs::safe_count($aLine) > $i; ++$i) {
764
                    //$this->y2lines[]=$aLine[$i];
765
                    $this->y2plots[] = $aLine[$i];
766
                }
767
            } else {
768
                //$this->y2lines[] = $aLine;
769
                $this->y2plots[] = $aLine;
770
            }
771
        } else {
772
            if (\is_array($aLine)) {
773
                for ($i = 0; Configs::safe_count($aLine) > $i; ++$i) {
774
                    //$this->lines[]=$aLine[$i];
775
                    $this->plots[] = $aLine[$i];
776
                }
777
            } else {
778
                //$this->lines[] = $aLine;
779
                $this->plots[] = $aLine;
780
            }
781
        }
782
    }
783
784
    // Add vertical or horizontal band
785
    /**
786
     * @param bool $aToY2
787
     */
788
    public function AddBand($aBand, $aToY2 = false)
789
    {
790
        if (null === $aBand) {
791
            Util\JpGraphError::RaiseL(25016); //(" Graph::AddBand() You tried to add a null band to the graph.");
792
        }
793
794
        if ($aToY2) {
795
            if (\is_array($aBand)) {
796
                for ($i = 0; Configs::safe_count($aBand) > $i; ++$i) {
797
                    $this->y2bands[] = $aBand[$i];
798
                }
799
            } else {
800
                $this->y2bands[] = $aBand;
801
            }
802
        } else {
803
            if (\is_array($aBand)) {
804
                for ($i = 0; Configs::safe_count($aBand) > $i; ++$i) {
805
                    $this->bands[] = $aBand[$i];
806
                }
807
            } else {
808
                $this->bands[] = $aBand;
809
            }
810
        }
811
    }
812
813
    public function SetPlotGradient($aFrom = 'navy', $aTo = 'silver', $aGradType = 2)
814
    {
815
        $this->plot_gradtype = $aGradType;
816
        $this->plot_gradfrom = $aFrom;
817
        $this->plot_gradto = $aTo;
818
    }
819
820
    public function SetBackgroundGradient($aFrom = 'navy', $aTo = 'silver', $aGradType = 2, $aStyle = Configs::BGRAD_FRAME)
821
    {
822
        $this->bkg_gradtype = $aGradType;
823
        $this->bkg_gradstyle = $aStyle;
824
        $this->bkg_gradfrom = $aFrom;
825
        $this->bkg_gradto = $aTo;
826
    }
827
828
    // Set a country flag in the background
829
    public function SetBackgroundCFlag($aName, $aBgType = Configs::BGIMG_FILLPLOT, $aMix = 100)
830
    {
831
        $this->background_cflag = $aName;
832
        $this->background_cflag_type = $aBgType;
833
        $this->background_cflag_mix = $aMix;
834
    }
835
836
    // Alias for the above method
837
    public function SetBackgroundCountryFlag($aName, $aBgType = Configs::BGIMG_FILLPLOT, $aMix = 100)
838
    {
839
        $this->background_cflag = $aName;
840
        $this->background_cflag_type = $aBgType;
841
        $this->background_cflag_mix = $aMix;
842
    }
843
844
    // Specify a background image
845
    public function SetBackgroundImage($aFileName, $aBgType = Configs::BGIMG_FILLPLOT, $aImgFormat = 'auto')
846
    {
847
        // Get extension to determine image type
848
        if ('auto' === $aImgFormat) {
849
            $e = \explode('.', $aFileName);
850
851
            if (empty($e)) {
852
                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');
853
            }
854
855
            $valid_formats = ['png', 'jpg', 'gif'];
856
            $aImgFormat = \mb_strtolower($e[\count($e) - 1]);
857
858
            if ('jpeg' === $aImgFormat) {
859
                $aImgFormat = 'jpg';
860
            } elseif (!\in_array($aImgFormat, $valid_formats, true)) {
861
                Util\JpGraphError::RaiseL(25019, $aImgFormat); //('Unknown file extension ($aImgFormat) in Graph::SetBackgroundImage() for filename: '.$aFileName);
862
            }
863
        }
864
865
        $this->background_image = $aFileName;
866
        $this->background_image_type = $aBgType;
867
        $this->background_image_format = $aImgFormat;
868
    }
869
870
    public function SetBackgroundImageMix($aMix)
871
    {
872
        $this->background_image_mix = $aMix;
873
    }
874
875
    // Adjust background image position
876
    public function SetBackgroundImagePos($aXpos, $aYpos)
877
    {
878
        $this->background_image_xpos = $aXpos;
879
        $this->background_image_ypos = $aYpos;
880
    }
881
882
    // Specify axis style (boxed or single)
883
    public function SetAxisStyle($aStyle)
884
    {
885
        $this->iAxisStyle = $aStyle;
886
    }
887
888
    // Set a frame around the plot area
889
    /**
890
     * @param true $aDrawPlotFrame
891
     * @param int[]|string $aPlotFrameColor
892
     */
893
    public function SetBox($aDrawPlotFrame = true, $aPlotFrameColor = [0, 0, 0], $aPlotFrameWeight = 1)
894
    {
895
        $this->boxed = $aDrawPlotFrame;
896
        $this->box_weight = $aPlotFrameWeight;
897
        $this->box_color = $aPlotFrameColor;
898
    }
899
900
    // Specify color for the plotarea (not the margins)
901
    public function SetColor($aColor)
902
    {
903
        $this->plotarea_color = $aColor;
904
    }
905
906
    // Specify color for the margins (all areas outside the plotarea)
907
    /**
908
     * @param string $aColor
909
     */
910
    public function SetMarginColor($aColor)
911
    {
912
        $this->margin_color = $aColor;
913
    }
914
915
    // Set a frame around the entire image
916
    public function SetFrame($aDrawImgFrame = true, $aImgFrameColor = [0, 0, 0], $aImgFrameWeight = 1)
917
    {
918
        $this->doframe = $aDrawImgFrame;
919
        $this->frame_color = $aImgFrameColor;
920
        $this->frame_weight = $aImgFrameWeight;
921
    }
922
923
    public function SetFrameBevel($aDepth = 3, $aBorder = false, $aBorderColor = 'black', $aColor1 = '[email protected]', $aColor2 = '[email protected]', $aFlg = true)
924
    {
925
        $this->framebevel = $aFlg;
926
        $this->framebeveldepth = $aDepth;
927
        $this->framebevelborder = $aBorder;
928
        $this->framebevelbordercolor = $aBorderColor;
929
        $this->framebevelcolor1 = $aColor1;
930
        $this->framebevelcolor2 = $aColor2;
931
932
        $this->doshadow = false;
933
    }
934
935
    // Set the shadow around the whole image
936
    public function SetShadow($aShowShadow = true, $aShadowWidth = 5, $aShadowColor = 'darkgray')
937
    {
938
        $this->doshadow = $aShowShadow;
939
        $this->shadow_color = $aShadowColor;
940
        $this->shadow_width = $aShadowWidth;
941
        $this->footer->iBottomMargin += $aShadowWidth;
942
        $this->footer->iRightMargin += $aShadowWidth;
943
    }
944
945
    // Specify x,y scale. Note that if you manually specify the scale
946
    // you must also specify the tick distance with a call to Ticks::Set()
947
    /**
948
     * @param string $aAxisType
949
     * @param float|int $aYMin
950
     * @param float|int $aYMax
951
     * @param float|int $aXMin
952
     * @param float|int $aXMax
953
     */
954
    public function SetScale($aAxisType, $aYMin = 1, $aYMax = 1, $aXMin = 1, $aXMax = 1)
955
    {
956
        $this->axtype = $aAxisType;
957
958
        if ($aYMax < $aYMin || $aXMax < $aXMin) {
959
            Util\JpGraphError::RaiseL(25020); //('Graph::SetScale(): Specified Max value must be larger than the specified Min value.');
960
        }
961
962
        $yt = \mb_substr($aAxisType, -3, 3);
963
964
        if ('lin' === $yt) {
965
            $this->yscale = new Scale\LinearScale($aYMin, $aYMax);
966
        } elseif ('int' === $yt) {
967
            $this->yscale = new Scale\LinearScale($aYMin, $aYMax);
968
            $this->yscale->SetIntScale();
969
        } elseif ('log' === $yt) {
970
            $this->yscale = new Scale\LogScale($aYMin, $aYMax);
971
        } else {
972
            Util\JpGraphError::RaiseL(25021, $aAxisType); //("Unknown scale specification for Y-scale. ($aAxisType)");
973
        }
974
975
        $xt = \mb_substr($aAxisType, 0, 3);
976
977
        if ('lin' === $xt || 'tex' === $xt) {
978
            $this->xscale = new Scale\LinearScale($aXMin, $aXMax, 'x');
979
            $this->xscale->textscale = ('tex' === $xt);
0 ignored issues
show
Documentation Bug introduced by
The property $textscale was declared of type false, but 'tex' === $xt is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
980
        } elseif ('int' === $xt) {
981
            $this->xscale = new Scale\LinearScale($aXMin, $aXMax, 'x');
982
            $this->xscale->SetIntScale();
983
        } elseif ('dat' === $xt) {
984
            $this->xscale = new Scale\DateScale($aXMin, $aXMax, 'x');
985
        } elseif ('log' === $xt) {
986
            $this->xscale = new Scale\LogScale($aXMin, $aXMax, 'x');
987
        } else {
988
            Util\JpGraphError::RaiseL(25022, $aAxisType); //(" Unknown scale specification for X-scale. ($aAxisType)");
989
        }
990
991
        $this->xaxis = new Axis\Axis($this->img, $this->xscale);
992
        $this->yaxis = new Axis\Axis($this->img, $this->yscale);
993
        $this->xgrid = new Grid($this->xaxis);
994
        $this->ygrid = new Grid($this->yaxis);
995
        $this->ygrid->Show();
996
997
        if (!$this->isRunningClear) {
998
            $this->inputValues['aAxisType'] = $aAxisType;
999
            $this->inputValues['aYMin'] = $aYMin;
1000
            $this->inputValues['aYMax'] = $aYMax;
1001
            $this->inputValues['aXMin'] = $aXMin;
1002
            $this->inputValues['aXMax'] = $aXMax;
1003
1004
            if ($this->graph_theme) {
1005
                $this->graph_theme->ApplyGraph($this);
1006
            }
1007
        }
1008
1009
        $this->isAfterSetScale = true;
1010
    }
1011
1012
    // Specify secondary Y scale
1013
    public function SetY2Scale($aAxisType = 'lin', $aY2Min = 1, $aY2Max = 1)
1014
    {
1015
        if ('lin' === $aAxisType) {
1016
            $this->y2scale = new Scale\LinearScale($aY2Min, $aY2Max);
1017
        } elseif ('int' === $aAxisType) {
1018
            $this->y2scale = new Scale\LinearScale($aY2Min, $aY2Max);
1019
            $this->y2scale->SetIntScale();
1020
        } elseif ('log' === $aAxisType) {
1021
            $this->y2scale = new Scale\LogScale($aY2Min, $aY2Max);
1022
        } else {
1023
            Util\JpGraphError::RaiseL(25023, $aAxisType); //("JpGraph: Unsupported Y2 axis type: $aAxisType\nMust be one of (lin,log,int)");
1024
        }
1025
1026
        $this->y2axis = new Axis\Axis($this->img, $this->y2scale);
1027
        $this->y2axis->scale->ticks->SetDirection(Configs::getConfig('SIDE_LEFT'));
1028
        $this->y2axis->SetLabelSide(Configs::getConfig('SIDE_RIGHT'));
1029
        $this->y2axis->SetPos('max');
1030
        $this->y2axis->SetTitleSide(Configs::getConfig('SIDE_RIGHT'));
1031
1032
        // Deafult position is the max x-value
1033
        $this->y2grid = new Grid($this->y2axis);
1034
1035
        if (!$this->graph_theme) {
1036
            return;
1037
        }
1038
1039
        $this->graph_theme->ApplyGraph($this);
1040
    }
1041
1042
    // Set the delta position (in pixels) between the multiple Y-axis
1043
    public function SetYDeltaDist($aDist)
1044
    {
1045
        $this->iYAxisDeltaPos = $aDist;
1046
    }
1047
1048
    // Specify secondary Y scale
1049
    public function SetYScale($aN, $aAxisType = 'lin', $aYMin = 1, $aYMax = 1)
1050
    {
1051
        if ('lin' === $aAxisType) {
1052
            $this->ynscale[$aN] = new Scale\LinearScale($aYMin, $aYMax);
1053
        } elseif ('int' === $aAxisType) {
1054
            $this->ynscale[$aN] = new Scale\LinearScale($aYMin, $aYMax);
1055
            $this->ynscale[$aN]->SetIntScale();
1056
        } elseif ('log' === $aAxisType) {
1057
            $this->ynscale[$aN] = new Scale\LogScale($aYMin, $aYMax);
1058
        } else {
1059
            Util\JpGraphError::RaiseL(25024, $aAxisType); //("JpGraph: Unsupported Y axis type: $aAxisType\nMust be one of (lin,log,int)");
1060
        }
1061
1062
        $this->ynaxis[$aN] = new Axis\Axis($this->img, $this->ynscale[$aN]);
1063
        $this->ynaxis[$aN]->scale->ticks->SetDirection(Configs::getConfig('SIDE_LEFT'));
1064
        $this->ynaxis[$aN]->SetLabelSide(Configs::getConfig('SIDE_RIGHT'));
1065
1066
        if (!$this->graph_theme) {
1067
            return;
1068
        }
1069
1070
        $this->graph_theme->ApplyGraph($this);
1071
    }
1072
1073
    // Specify density of ticks when autoscaling 'normal', 'dense', 'sparse', 'verysparse'
1074
    // The dividing factor have been determined heuristically according to my aesthetic
1075
    // sense (or lack off) y.m.m.v !
1076
    /**
1077
     * @param int $aYDensity
1078
     */
1079
    public function SetTickDensity($aYDensity = Configs::TICKD_NORMAL, $aXDensity = Configs::TICKD_NORMAL)
1080
    {
1081
        $this->xtick_factor = 30;
1082
        $this->ytick_factor = 25;
1083
1084
        switch ($aYDensity) {
1085
            case Configs::getConfig('TICKD_DENSE'):
1086
                $this->ytick_factor = 12;
1087
1088
                break;
1089
            case Configs::getConfig('TICKD_NORMAL'):
1090
                $this->ytick_factor = 25;
1091
1092
                break;
1093
            case Configs::getConfig('TICKD_SPARSE'):
1094
                $this->ytick_factor = 40;
1095
1096
                break;
1097
            case Configs::getConfig('TICKD_VERYSPARSE'):
1098
                $this->ytick_factor = 100;
1099
1100
                break;
1101
1102
            default:
1103
                Util\JpGraphError::RaiseL(25025, $aYDensity); //("JpGraph: Unsupported Tick density: $densy");
1104
        }
1105
1106
        switch ($aXDensity) {
1107
            case Configs::getConfig('TICKD_DENSE'):
1108
                $this->xtick_factor = 15;
1109
1110
                break;
1111
            case Configs::getConfig('TICKD_NORMAL'):
1112
                $this->xtick_factor = 30;
1113
1114
                break;
1115
            case Configs::getConfig('TICKD_SPARSE'):
1116
                $this->xtick_factor = 45;
1117
1118
                break;
1119
            case Configs::getConfig('TICKD_VERYSPARSE'):
1120
                $this->xtick_factor = 60;
1121
1122
                break;
1123
1124
            default:
1125
                Util\JpGraphError::RaiseL(25025, $aXDensity); //("JpGraph: Unsupported Tick density: $densx");
1126
        }
1127
    }
1128
1129
    // Get a string of all image map areas
1130
    public function GetCSIMareas()
1131
    {
1132
        if (!$this->iHasStroked) {
1133
            $this->Stroke(Configs::getConfig('_CSIM_SPECIALFILE'));
1134
        }
1135
1136
        $csim = $this->title->GetCSIMAreas();
1137
        $csim .= $this->subtitle->GetCSIMAreas();
1138
        $csim .= $this->subsubtitle->GetCSIMAreas();
1139
        $csim .= $this->legend->GetCSIMAreas();
1140
1141
        if (null !== $this->y2axis) {
1142
            $csim .= $this->y2axis->title->GetCSIMAreas();
1143
        }
1144
1145
        if (null !== $this->texts) {
1146
            $n = Configs::safe_count($this->texts);
1147
1148
            for ($i = 0; $i < $n; ++$i) {
1149
                $csim .= $this->texts[$i]->GetCSIMAreas();
1150
            }
1151
        }
1152
1153
        if (null !== $this->y2texts && null !== $this->y2scale) {
1154
            $n = Configs::safe_count($this->y2texts);
1155
1156
            for ($i = 0; $i < $n; ++$i) {
1157
                $csim .= $this->y2texts[$i]->GetCSIMAreas();
1158
            }
1159
        }
1160
1161
        if (null !== $this->yaxis && null !== $this->xaxis) {
1162
            $csim .= $this->yaxis->title->GetCSIMAreas();
1163
            $csim .= $this->xaxis->title->GetCSIMAreas();
1164
        }
1165
1166
        $n = Configs::safe_count($this->plots);
1167
1168
        for ($i = 0; $i < $n; ++$i) {
1169
            $csim .= $this->plots[$i]->GetCSIMareas();
1170
        }
1171
1172
        $n = Configs::safe_count($this->y2plots);
1173
1174
        for ($i = 0; $i < $n; ++$i) {
1175
            $csim .= $this->y2plots[$i]->GetCSIMareas();
1176
        }
1177
1178
        $n = Configs::safe_count($this->ynaxis);
1179
1180
        for ($i = 0; $i < $n; ++$i) {
1181
            $m = Configs::safe_count($this->ynplots[$i]);
1182
1183
            for ($j = 0; $j < $m; ++$j) {
1184
                $csim .= $this->ynplots[$i][$j]->GetCSIMareas();
1185
            }
1186
        }
1187
1188
        $n = Configs::safe_count($this->iTables);
1189
1190
        for ($i = 0; $i < $n; ++$i) {
1191
            $csim .= $this->iTables[$i]->GetCSIMareas();
1192
        }
1193
1194
        return $csim;
1195
    }
1196
1197
    // Get a complete <MAP>..</MAP> tag for the final image map
1198
    public function GetHTMLImageMap($aMapName)
1199
    {
1200
        $im = "<map name=\"{$aMapName}\" id=\"{$aMapName}\" >\n";
1201
        $im .= $this->GetCSIMareas();
1202
        $im .= '</map>';
1203
1204
        return $im;
1205
    }
1206
1207
    public function CheckCSIMCache($aCacheName, $aTimeOut = 60)
1208
    {
1209
        global $_SERVER;
1210
1211
        if ('auto' === $aCacheName) {
1212
            $aCacheName = \basename($_SERVER['PHP_SELF']);
1213
        }
1214
1215
        $urlarg = $this->GetURLArguments();
1216
        $this->csimcachename = Configs::getConfig('CSIMCACHE_DIR') . $aCacheName . $urlarg;
0 ignored issues
show
Bug introduced by
Are you sure Amenadiel\JpGraph\Graph\...Config('CSIMCACHE_DIR') of type array can be used in concatenation? ( Ignorable by Annotation )

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

1216
        $this->csimcachename = /** @scrutinizer ignore-type */ Configs::getConfig('CSIMCACHE_DIR') . $aCacheName . $urlarg;
Loading history...
1217
        $this->csimcachetimeout = $aTimeOut;
1218
1219
        // First determine if we need to check for a cached version
1220
        // This differs from the standard cache in the sense that the
1221
        // image and Configs::getConfig('CSIM') map Configs::getConfig('HTML') file is written relative to the directory
1222
        // the script executes in and not the specified cache directory.
1223
        // The reason for this is that the cache directory is not necessarily
1224
        // accessible from the Configs::getConfig('HTTP') server.
1225
        if ('' !== $this->csimcachename) {
1226
            $dir = \dirname($this->csimcachename);
1227
            $base = \basename($this->csimcachename);
1228
            $base = \strtok($base, '.');
1229
            $suffix = \strtok('.');
0 ignored issues
show
Unused Code introduced by
The assignment to $suffix is dead and can be removed.
Loading history...
1230
            $basecsim = $dir . '/' . $base . '?' . $urlarg . '_csim_.html';
1231
            $baseimg = $dir . '/' . $base . '?' . $urlarg . '.' . $this->img->img_format;
1232
1233
            $timedout = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $timedout is dead and can be removed.
Loading history...
1234
            // Does it exist at all ?
1235
1236
            if (\file_exists($basecsim) && \file_exists($baseimg)) {
1237
                // Check that it hasn't timed out
1238
                $diff = \time() - \filemtime($basecsim);
1239
1240
                if (0 < $this->csimcachetimeout && ($this->csimcachetimeout * 60 < $diff)) {
1241
                    $timedout = true;
1242
                    \unlink($basecsim);
1243
                    \unlink($baseimg);
1244
                } else {
1245
                    if ($fh = \fopen($basecsim, 'rb')) {
1246
                        \fpassthru($fh);
1247
1248
                        return true;
1249
                    }
1250
                    Util\JpGraphError::RaiseL(25027, $basecsim); //(" Can't open cached Configs::getConfig('CSIM') \"$basecsim\" for reading.");
1251
                }
1252
            }
1253
        }
1254
1255
        return false;
1256
    }
1257
1258
    // Build the argument string to be used with the csim images
1259
    /**
1260
     * @param bool $aAddRecursiveBlocker
1261
     */
1262
    public static function GetURLArguments($aAddRecursiveBlocker = false)
1263
    {
1264
        if ($aAddRecursiveBlocker) {
1265
            // This is a Configs::getConfig('JPGRAPH') internal defined that prevents
1266
            // us from recursively coming here again
1267
            $urlarg = Configs::getConfig('_CSIM_DISPLAY') . '=1';
0 ignored issues
show
Bug introduced by
Are you sure Amenadiel\JpGraph\Graph\...Config('_CSIM_DISPLAY') of type array can be used in concatenation? ( Ignorable by Annotation )

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

1267
            $urlarg = /** @scrutinizer ignore-type */ Configs::getConfig('_CSIM_DISPLAY') . '=1';
Loading history...
1268
        }
1269
1270
        // Now reconstruct any user URL argument
1271
        \reset($_GET);
1272
1273
        foreach ($_GET as $key => $value) {
1274
            if (\is_array($value)) {
1275
                foreach ($value as $k => $v) {
1276
                    $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...
1277
                }
1278
            } else {
1279
                $urlarg .= '&amp;' . $key . '=' . \urlencode($value);
1280
            }
1281
        }
1282
1283
        // It's not ideal to convert Configs::getConfig('POST') argument to GET arguments
1284
        // but there is little else we can do. One idea for the
1285
        // future might be recreate the Configs::getConfig('POST') header in case.
1286
        \reset($_POST);
1287
1288
        foreach ($_POST as $key => $value) {
1289
            if (\is_array($value)) {
1290
                foreach ($value as $k => $v) {
1291
                    $urlarg .= '&amp;' . $key . '%5B' . $k . '%5D=' . \urlencode($v);
1292
                }
1293
            } else {
1294
                $urlarg .= '&amp;' . $key . '=' . \urlencode($value);
1295
            }
1296
        }
1297
1298
        return $urlarg;
1299
    }
1300
1301
    public function SetCSIMImgAlt($aAlt)
1302
    {
1303
        $this->iCSIMImgAlt = $aAlt;
1304
    }
1305
1306
    public function StrokeCSIM($aScriptName = 'auto', $aCSIMName = '', $aBorder = 0)
1307
    {
1308
        $microtime = \microtime(true);
1309
        $random = $microtime * 1000000;
1310
1311
        if ('' === $aCSIMName) {
1312
            // create a random map name
1313
            \mt_srand((int) $random);
1314
            $r = \mt_rand(0, 100000);
1315
            $aCSIMName = '__mapname' . $r . '__';
1316
        }
1317
1318
        if ('auto' === $aScriptName) {
1319
            $aScriptName = \basename($_SERVER['PHP_SELF']);
1320
        }
1321
1322
        $urlarg = $this->GetURLArguments(true);
1323
1324
        if (empty($_GET[Configs::getConfig('_CSIM_DISPLAY')])) {
1325
            // First determine if we need to check for a cached version
1326
            // This differs from the standard cache in the sense that the
1327
            // image and Configs::getConfig('CSIM') map Configs::getConfig('HTML') file is written relative to the directory
1328
            // the script executes in and not the specified cache directory.
1329
            // The reason for this is that the cache directory is not necessarily
1330
            // accessible from the Configs::getConfig('HTTP') server.
1331
            if ('' !== $this->csimcachename) {
1332
                $dir = \dirname($this->csimcachename);
1333
                $base = \basename($this->csimcachename);
1334
                $base = \strtok($base, '.');
1335
                $suffix = \strtok('.');
0 ignored issues
show
Unused Code introduced by
The assignment to $suffix is dead and can be removed.
Loading history...
1336
                $basecsim = $dir . '/' . $base . '?' . $urlarg . '_csim_.html';
1337
                $baseimg = $base . '?' . $urlarg . '.' . $this->img->img_format;
1338
1339
                // Check that apache can write to directory specified
1340
1341
                if (\file_exists($dir) && !\is_writable($dir)) {
1342
                    Util\JpGraphError::RaiseL(25028, $dir); //('Apache/PHP does not have permission to write to the Configs::getConfig('CSIM') cache directory ('.$dir.'). Check permissions.');
1343
                }
1344
1345
                // Make sure directory exists
1346
                $this->cache->MakeDirs($dir);
1347
1348
                // Write the image file
1349
                $this->Stroke(Configs::getConfig('CSIMCACHE_DIR') . $baseimg);
0 ignored issues
show
Bug introduced by
Are you sure Amenadiel\JpGraph\Graph\...Config('CSIMCACHE_DIR') of type array can be used in concatenation? ( Ignorable by Annotation )

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

1349
                $this->Stroke(/** @scrutinizer ignore-type */ Configs::getConfig('CSIMCACHE_DIR') . $baseimg);
Loading history...
1350
1351
                // Construct wrapper Configs::getConfig('HTML') and write to file and send it back to browser
1352
1353
                // In the src URL we must replace the '?' with its encoding to prevent the arguments
1354
                // to be converted to real arguments.
1355
                $tmp = \str_replace('?', '%3f', $baseimg);
1356
                $htmlwrap = $this->GetHTMLImageMap($aCSIMName) . "\n" .
1357
                    '<img src="' . Configs::getConfig('CSIMCACHE_HTTP_DIR') . $tmp . '" ismap="ismap" usemap="#' . $aCSIMName . ' width="' . $this->img->width . '" height="' . $this->img->height . '" alt="' . $this->iCSIMImgAlt . "\" />\n";
0 ignored issues
show
Bug introduced by
Are you sure Amenadiel\JpGraph\Graph\...g('CSIMCACHE_HTTP_DIR') of type array can be used in concatenation? ( Ignorable by Annotation )

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

1357
                    '<img src="' . /** @scrutinizer ignore-type */ Configs::getConfig('CSIMCACHE_HTTP_DIR') . $tmp . '" ismap="ismap" usemap="#' . $aCSIMName . ' width="' . $this->img->width . '" height="' . $this->img->height . '" alt="' . $this->iCSIMImgAlt . "\" />\n";
Loading history...
1358
1359
                if ($fh = \fopen($basecsim, 'wb')) {
1360
                    \fwrite($fh, $htmlwrap);
1361
                    \fclose($fh);
1362
                    echo $htmlwrap;
1363
                } else {
1364
                    Util\JpGraphError::RaiseL(25029, $basecsim); //(" Can't write Configs::getConfig('CSIM') \"$basecsim\" for writing. Check free space and permissions.");
1365
                }
1366
            } else {
1367
                if ('' === $aScriptName) {
1368
                    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().');
1369
                }
1370
                echo $this->GetHTMLImageMap($aCSIMName) . $this->GetCSIMImgHTML($aCSIMName, $aScriptName, $aBorder);
1371
            }
1372
        } else {
1373
            $this->Stroke();
1374
        }
1375
    }
1376
1377
    public function StrokeCSIMImage()
1378
    {
1379
        if (1 !== $_GET[Configs::getConfig('_CSIM_DISPLAY')]) {
1380
            return;
1381
        }
1382
1383
        $this->Stroke();
1384
    }
1385
1386
    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

1386
    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...
1387
    {
1388
        if ('auto' === $aScriptName) {
1389
            $aScriptName = \basename($_SERVER['PHP_SELF']);
1390
        }
1391
        $urlarg = $this->GetURLArguments(true);
1392
1393
        return '<img src="' . $aScriptName . '?' . $urlarg . '" ismap="ismap" usemap="#' . $aCSIMName . '" height="' . $this->img->height . '" alt="' . $this->iCSIMImgAlt . "\" />\n";
1394
    }
1395
1396
    /**
1397
     * @param bool $aY2
1398
     */
1399
    public function GetTextsYMinMax($aY2 = false)
1400
    {
1401
        if ($aY2) {
1402
            $txts = $this->y2texts;
1403
        } else {
1404
            $txts = $this->texts;
1405
        }
1406
        $n = Configs::safe_count($txts);
1407
        $min = null;
1408
        $max = null;
1409
1410
        for ($i = 0; $i < $n; ++$i) {
1411
            if (null === $txts[$i]->iScalePosY || null === $txts[$i]->iScalePosX) {
1412
                continue;
1413
            }
1414
1415
            if (null === $min) {
1416
                $min = $max = $txts[$i]->iScalePosY;
1417
            } else {
1418
                $min = \min($min, $txts[$i]->iScalePosY);
1419
                $max = \max($max, $txts[$i]->iScalePosY);
1420
            }
1421
        }
1422
1423
        if (null !== $min) {
0 ignored issues
show
introduced by
The condition null !== $min is always false.
Loading history...
1424
            return [$min, $max];
1425
        }
1426
1427
        return null;
1428
    }
1429
1430
    /**
1431
     * @param bool $aY2
1432
     */
1433
    public function GetTextsXMinMax($aY2 = false)
1434
    {
1435
        if ($aY2) {
1436
            $txts = $this->y2texts;
1437
        } else {
1438
            $txts = $this->texts;
1439
        }
1440
        $n = Configs::safe_count($txts);
1441
        $min = null;
1442
        $max = null;
1443
1444
        for ($i = 0; $i < $n; ++$i) {
1445
            if (null === $txts[$i]->iScalePosY || null === $txts[$i]->iScalePosX) {
1446
                continue;
1447
            }
1448
1449
            if (null === $min) {
1450
                $min = $max = $txts[$i]->iScalePosX;
1451
            } else {
1452
                $min = \min($min, $txts[$i]->iScalePosX);
1453
                $max = \max($max, $txts[$i]->iScalePosX);
1454
            }
1455
        }
1456
1457
        if (null !== $min) {
0 ignored issues
show
introduced by
The condition null !== $min is always false.
Loading history...
1458
            return [$min, $max];
1459
        }
1460
1461
        return null;
1462
    }
1463
1464
    public function GetXMinMax()
1465
    {
1466
        [$min, $ymin] = $this->plots[0]->Min();
1467
        [$max, $ymax] = $this->plots[0]->Max();
1468
1469
        $i = 0;
1470
        // Some plots, e.g. PlotLine should not affect the scale
1471
        // and will return (null,null). We should ignore those
1472
        // values.
1473
        while ((null === $min || null === $max) && (Configs::safe_count($this->plots) - 1 > $i)) {
1474
            ++$i;
1475
            [$min, $ymin] = $this->plots[$i]->Min();
1476
            [$max, $ymax] = $this->plots[$i]->Max();
1477
        }
1478
1479
        foreach ($this->plots as $p) {
1480
            [$xmin, $ymin] = $p->Min();
1481
            [$xmax, $ymax] = $p->Max();
1482
1483
            if (null === $xmin || null === $xmax) {
1484
                continue;
1485
            }
1486
1487
            $min = \min($xmin, $min);
1488
            $max = \max($xmax, $max);
1489
        }
1490
1491
        if (null !== $this->y2axis) {
1492
            foreach ($this->y2plots as $p) {
1493
                [$xmin, $ymin] = $p->Min();
1494
                [$xmax, $ymax] = $p->Max();
1495
                $min = \min($xmin, $min);
1496
                $max = \max($xmax, $max);
1497
            }
1498
        }
1499
1500
        $n = Configs::safe_count($this->ynaxis);
1501
1502
        for ($i = 0; $i < $n; ++$i) {
1503
            if (null === $this->ynaxis[$i]) {
1504
                continue;
1505
            }
1506
1507
            foreach ($this->ynplots[$i] as $p) {
1508
                [$xmin, $ymin] = $p->Min();
1509
                [$xmax, $ymax] = $p->Max();
1510
                $min = \min($xmin, $min);
1511
                $max = \max($xmax, $max);
1512
            }
1513
        }
1514
1515
        return [$min, $max];
1516
    }
1517
1518
    public function AdjustMarginsForTitles()
1519
    {
1520
        $totrequired =
1521
            ('' !== $this->title->t
1522
                ? $this->title->GetTextHeight($this->img) + $this->title->margin + 5 * Configs::getConfig('SUPERSAMPLING_SCALE')
1523
                : 0) +
1524
            ('' !== $this->subtitle->t
1525
                ? $this->subtitle->GetTextHeight($this->img) + $this->subtitle->margin + 5 * Configs::getConfig('SUPERSAMPLING_SCALE')
1526
                : 0) +
1527
            ('' !== $this->subsubtitle->t
1528
                ? $this->subsubtitle->GetTextHeight($this->img) + $this->subsubtitle->margin + 5 * Configs::getConfig('SUPERSAMPLING_SCALE')
1529
                : 0);
1530
1531
        $btotrequired = 0;
1532
1533
        if (null !== $this->xaxis && !$this->xaxis->hide && !$this->xaxis->hide_labels) {
1534
            // Minimum bottom margin
1535
            if ('' !== $this->xaxis->title->t) {
1536
                if (90 === $this->img->a) {
1537
                    $btotrequired = $this->yaxis->title->GetTextHeight($this->img) + 7;
1538
                } else {
1539
                    $btotrequired = $this->xaxis->title->GetTextHeight($this->img) + 7;
1540
                }
1541
            } else {
1542
                $btotrequired = 0;
1543
            }
1544
1545
            if (90 === $this->img->a) {
1546
                $this->img->SetFont(
1547
                    $this->yaxis->font_family,
1548
                    $this->yaxis->font_style,
1549
                    $this->yaxis->font_size
1550
                );
1551
                $lh = $this->img->GetTextHeight('Mg', $this->yaxis->label_angle);
1552
            } else {
1553
                $this->img->SetFont(
1554
                    $this->xaxis->font_family,
1555
                    $this->xaxis->font_style,
1556
                    $this->xaxis->font_size
1557
                );
1558
                $lh = $this->img->GetTextHeight('Mg', $this->xaxis->label_angle);
1559
            }
1560
1561
            $btotrequired += $lh + 6;
1562
        }
1563
1564
        if (90 === $this->img->a) {
1565
            // DO Nothing. It gets too messy to do this properly for 90 deg...
1566
        } else {
1567
            // need more top margin
1568
            if ($this->img->top_margin < $totrequired) {
1569
                $this->SetMargin(
1570
                    $this->img->raw_left_margin,
1571
                    $this->img->raw_right_margin,
1572
                    $totrequired / Configs::getConfig('SUPERSAMPLING_SCALE'),
1573
                    $this->img->raw_bottom_margin
1574
                );
1575
            }
1576
1577
            // need more bottom margin
1578
            if ($this->img->bottom_margin < $btotrequired) {
1579
                $this->SetMargin(
1580
                    $this->img->raw_left_margin,
1581
                    $this->img->raw_right_margin,
1582
                    $this->img->raw_top_margin,
1583
                    $btotrequired / Configs::getConfig('SUPERSAMPLING_SCALE')
1584
                );
1585
            }
1586
        }
1587
    }
1588
public function getScale()  {
1589
    return $this->scale;
1590
}
1591
    public function StrokeStore($aStrokeFileName)
1592
    {
1593
        // Get the handler to prevent the library from sending the
1594
        // image to the browser
1595
        $ih = $this->Stroke(Configs::getConfig('_IMG_HANDLER'));
0 ignored issues
show
Unused Code introduced by
The assignment to $ih is dead and can be removed.
Loading history...
1596
1597
        // Stroke it to a file
1598
        $this->img->Stream($aStrokeFileName);
1599
1600
        // Send it back to browser
1601
        $this->img->Headers();
1602
        $this->img->Stream();
1603
    }
1604
1605
    public function doAutoScaleXAxis()
1606
    {
1607
        $aPlots = \array_filter($this->plots, function ($plot) {
1608
            return $plot instanceof Plot\Plot;
1609
        });
1610
1611
        //Check if we should autoscale x-axis
1612
        if (!$this->xscale->IsSpecified()) {
0 ignored issues
show
Bug introduced by
The method IsSpecified() does not exist on null. ( Ignorable by Annotation )

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

1612
        if (!$this->xscale->/** @scrutinizer ignore-call */ IsSpecified()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1613
            if (\mb_substr($this->axtype, 0, 4) === 'text') {
1614
                $max = 0;
1615
                $n = Configs::safe_count($aPlots);
1616
1617
                for ($i = 0; $i < $n; ++$i) {
1618
                    $p = $aPlots[$i];
1619
1620
                    // We need some unfortunate sub class knowledge here in order
1621
                    // to increase number of data points in case it is a line plot
1622
                    // which has the barcenter set. If not it could mean that the
1623
                    // last point of the data is outside the scale since the barcenter
1624
                    // settings means that we will shift the entire plot half a tick step
1625
                    // to the right in oder to align with the center of the bars.
1626
1627
                    $cl = \mb_strtolower(\get_class($p));
0 ignored issues
show
Unused Code introduced by
The assignment to $cl is dead and can be removed.
Loading history...
1628
1629
                    if (empty($p->barcenter)) {
1630
                        $max = \max($max, $p->numpoints - 1);
1631
                    } else {
1632
                        $max = \max($max, $p->numpoints);
1633
                    }
1634
                }
1635
                $min = 0;
1636
1637
                if (null !== $this->y2axis) {
1638
                    foreach ($this->y2plots as $p) {
1639
                        $max = \max($max, $p->numpoints - 1);
1640
                    }
1641
                }
1642
                $n = Configs::safe_count($this->ynaxis);
1643
1644
                for ($i = 0; $i < $n; ++$i) {
1645
                    if (null === $this->ynaxis[$i]) {
1646
                        continue;
1647
                    }
1648
1649
                    foreach ($this->ynplots[$i] as $p) {
1650
                        $max = \max($max, $p->numpoints - 1);
1651
                    }
1652
                }
1653
1654
                $this->xscale->Update($this->img, $min, $max);
1655
                $this->xscale->ticks->Set($this->xaxis->tick_step, 1);
1656
                $this->xscale->ticks->SupressMinorTickMarks();
1657
            } else {
1658
                [$min, $max] = $this->GetXMinMax();
1659
1660
                $lres = $this->GetLinesXMinMax($this->lines);
1661
1662
                if ($lres) {
0 ignored issues
show
introduced by
The condition $lres is always false.
Loading history...
1663
                    [$linmin, $linmax] = $lres;
1664
                    $min = \min($min, $linmin);
1665
                    $max = \max($max, $linmax);
1666
                }
1667
1668
                $lres = $this->GetLinesXMinMax($this->y2lines);
1669
1670
                if ($lres) {
0 ignored issues
show
introduced by
The condition $lres is always false.
Loading history...
1671
                    [$linmin, $linmax] = $lres;
1672
                    $min = \min($min, $linmin);
1673
                    $max = \max($max, $linmax);
1674
                }
1675
1676
                $tres = $this->GetTextsXMinMax();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $tres is correct as $this->GetTextsXMinMax() targeting Amenadiel\JpGraph\Graph\Graph::GetTextsXMinMax() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1677
1678
                if ($tres) {
0 ignored issues
show
introduced by
$tres is of type null, thus it always evaluated to false.
Loading history...
1679
                    [$tmin, $tmax] = $tres;
1680
                    $min = \min($min, $tmin);
1681
                    $max = \max($max, $tmax);
1682
                }
1683
1684
                $tres = $this->GetTextsXMinMax(true);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $tres is correct as $this->GetTextsXMinMax(true) targeting Amenadiel\JpGraph\Graph\Graph::GetTextsXMinMax() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1685
1686
                if ($tres) {
0 ignored issues
show
introduced by
$tres is of type null, thus it always evaluated to false.
Loading history...
1687
                    [$tmin, $tmax] = $tres;
1688
                    $min = \min($min, $tmin);
1689
                    $max = \max($max, $tmax);
1690
                }
1691
1692
                $this->xscale->AutoScale($this->img, $min, $max, \round($this->img->plotwidth / $this->xtick_factor));
1693
            }
1694
1695
            //Adjust position of y-axis and y2-axis to minimum/maximum of x-scale
1696
            if (!\is_numeric($this->yaxis->pos) && !\is_string($this->yaxis->pos)) {
1697
                $this->yaxis->SetPos($this->xscale->GetMinVal());
0 ignored issues
show
Bug introduced by
The method SetPos() does not exist on null. ( Ignorable by Annotation )

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

1697
                $this->yaxis->/** @scrutinizer ignore-call */ 
1698
                              SetPos($this->xscale->GetMinVal());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1698
            }
1699
        } elseif ($this->xscale->IsSpecified()
1700
            && ($this->xscale->auto_ticks || !$this->xscale->ticks->IsSpecified())
1701
        ) {
1702
            // The tick calculation will use the user suplied min/max values to determine
1703
            // the ticks. If auto_ticks is false the exact user specifed min and max
1704
            // values will be used for the scale.
1705
            // If auto_ticks is true then the scale might be slightly adjusted
1706
            // so that the min and max values falls on an even major step.
1707
            $min = $this->xscale->scale[0];
1708
            $max = $this->xscale->scale[1];
1709
            $this->xscale->AutoScale($this->img, $min, $max, \round($this->img->plotwidth / $this->xtick_factor), false);
1710
1711
            // Now make sure we show enough precision to accurate display the
1712
            // labels. If this is not done then the user might end up with
1713
            // a scale that might actually start with, say 13.5, butdue to rounding
1714
            // the scale label will ony show 14.
1715
            if (\abs(\floor($min) - $min) > 0) {
1716
                // If the user has set a format then we bail out
1717
                if ('' === $this->xscale->ticks->label_formatstr && '' === $this->xscale->ticks->label_dateformatstr) {
1718
                    $this->xscale->ticks->precision = \abs(\floor(\log10(\abs(\floor($min) - $min)))) + 1;
1719
                }
1720
            }
1721
        }
1722
1723
        // Position the optional Y2 and Yn axis to the rightmost position of the x-axis
1724
        if (null !== $this->y2axis) {
1725
            if (!\is_numeric($this->y2axis->pos) && !\is_string($this->y2axis->pos)) {
1726
                $this->y2axis->SetPos($this->xscale->GetMaxVal());
1727
            }
1728
            $this->y2axis->SetTitleSide(Configs::getConfig('SIDE_RIGHT'));
1729
        }
1730
1731
        $n = Configs::safe_count($this->ynaxis);
1732
        $nY2adj = null !== $this->y2axis ? $this->iYAxisDeltaPos : 0;
1733
1734
        for ($i = 0; $i < $n; ++$i) {
1735
            if (null === $this->ynaxis[$i]) {
1736
                continue;
1737
            }
1738
1739
            if (!\is_numeric($this->ynaxis[$i]->pos) && !\is_string($this->ynaxis[$i]->pos)) {
1740
                $this->ynaxis[$i]->SetPos($this->xscale->GetMaxVal());
1741
                $this->ynaxis[$i]->SetPosAbsDelta($i * $this->iYAxisDeltaPos + $nY2adj);
1742
            }
1743
            $this->ynaxis[$i]->SetTitleSide(Configs::getConfig('SIDE_RIGHT'));
1744
        }
1745
    }
1746
1747
    public function doAutoScaleYnAxis()
1748
    {
1749
        if (null !== $this->y2scale) {
1750
            if (!$this->y2scale->IsSpecified() && Configs::safe_count($this->y2plots) > 0) {
1751
                [$min, $max] = $this->GetPlotsYMinMax($this->y2plots);
1752
1753
                $lres = $this->GetLinesYMinMax($this->y2lines);
1754
1755
                if (\is_array($lres)) {
0 ignored issues
show
introduced by
The condition is_array($lres) is always false.
Loading history...
1756
                    [$linmin, $linmax] = $lres;
1757
                    $min = \min($min, $linmin);
1758
                    $max = \max($max, $linmax);
1759
                }
1760
                $tres = $this->GetTextsYMinMax(true);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $tres is correct as $this->GetTextsYMinMax(true) targeting Amenadiel\JpGraph\Graph\Graph::GetTextsYMinMax() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1761
1762
                if (\is_array($tres)) {
0 ignored issues
show
introduced by
The condition is_array($tres) is always false.
Loading history...
1763
                    [$tmin, $tmax] = $tres;
1764
                    $min = \min($min, $tmin);
1765
                    $max = \max($max, $tmax);
1766
                }
1767
                $this->y2scale->AutoScale($this->img, $min, $max, $this->img->plotheight / $this->ytick_factor);
1768
            } elseif ($this->y2scale->IsSpecified() && ($this->y2scale->auto_ticks || !$this->y2scale->ticks->IsSpecified())) {
1769
                // The tick calculation will use the user suplied min/max values to determine
1770
                // the ticks. If auto_ticks is false the exact user specifed min and max
1771
                // values will be used for the scale.
1772
                // If auto_ticks is true then the scale might be slightly adjusted
1773
                // so that the min and max values falls on an even major step.
1774
                $min = $this->y2scale->scale[0];
1775
                $max = $this->y2scale->scale[1];
1776
                $this->y2scale->AutoScale(
1777
                    $this->img,
1778
                    $min,
1779
                    $max,
1780
                    $this->img->plotheight / $this->ytick_factor,
1781
                    $this->y2scale->auto_ticks
1782
                );
1783
1784
                // Now make sure we show enough precision to accurate display the
1785
                // labels. If this is not done then the user might end up with
1786
                // a scale that might actually start with, say 13.5, butdue to rounding
1787
                // the scale label will ony show 14.
1788
                if (\abs(\floor($min) - $min) > 0) {
1789
                    // If the user has set a format then we bail out
1790
                    if ('' === $this->y2scale->ticks->label_formatstr && '' === $this->y2scale->ticks->label_dateformatstr) {
1791
                        $this->y2scale->ticks->precision = \abs(\floor(\log10(\abs(\floor($min) - $min)))) + 1;
1792
                    }
1793
                }
1794
            }
1795
        }
1796
1797
        // Autoscale the extra Y-axises
1798
        $n = Configs::safe_count($this->ynaxis);
1799
1800
        for ($i = 0; $i < $n; ++$i) {
1801
            if (null === $this->ynscale[$i]) {
1802
                continue;
1803
            }
1804
1805
            if (!$this->ynscale[$i]->IsSpecified() && Configs::safe_count($this->ynplots[$i]) > 0) {
1806
                [$min, $max] = $this->GetPlotsYMinMax($this->ynplots[$i]);
1807
                $this->ynscale[$i]->AutoScale($this->img, $min, $max, $this->img->plotheight / $this->ytick_factor);
1808
            } elseif ($this->ynscale[$i]->IsSpecified() && ($this->ynscale[$i]->auto_ticks || !$this->ynscale[$i]->ticks->IsSpecified())) {
1809
                // The tick calculation will use the user suplied min/max values to determine
1810
                // the ticks. If auto_ticks is false the exact user specifed min and max
1811
                // values will be used for the scale.
1812
                // If auto_ticks is true then the scale might be slightly adjusted
1813
                // so that the min and max values falls on an even major step.
1814
                $min = $this->ynscale[$i]->scale[0];
1815
                $max = $this->ynscale[$i]->scale[1];
1816
                $this->ynscale[$i]->AutoScale(
1817
                    $this->img,
1818
                    $min,
1819
                    $max,
1820
                    $this->img->plotheight / $this->ytick_factor,
1821
                    $this->ynscale[$i]->auto_ticks
1822
                );
1823
1824
                // Now make sure we show enough precision to accurate display the
1825
                // labels. If this is not done then the user might end up with
1826
                // a scale that might actually start with, say 13.5, butdue to rounding
1827
                // the scale label will ony show 14.
1828
                if (\abs(\floor($min) - $min) > 0) {
1829
                    // If the user has set a format then we bail out
1830
                    if ('' === $this->ynscale[$i]->ticks->label_formatstr && '' === $this->ynscale[$i]->ticks->label_dateformatstr) {
1831
                        $this->ynscale[$i]->ticks->precision = \abs(\floor(\log10(\abs(\floor($min) - $min)))) + 1;
1832
                    }
1833
                }
1834
            }
1835
        }
1836
    }
1837
1838
    public function doAutoScaleYAxis()
1839
    {
1840
        //Check if we should autoscale y-axis
1841
        if (!$this->yscale->IsSpecified() && Configs::safe_count($this->plots) > 0) {
0 ignored issues
show
Bug introduced by
The method IsSpecified() does not exist on null. ( Ignorable by Annotation )

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

1841
        if (!$this->yscale->/** @scrutinizer ignore-call */ IsSpecified() && Configs::safe_count($this->plots) > 0) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1842
            [$min, $max] = $this->GetPlotsYMinMax($this->plots);
1843
            $lres = $this->GetLinesYMinMax($this->lines);
1844
1845
            if (\is_array($lres)) {
0 ignored issues
show
introduced by
The condition is_array($lres) is always false.
Loading history...
1846
                [$linmin, $linmax] = $lres;
1847
                $min = \min($min, $linmin);
1848
                $max = \max($max, $linmax);
1849
            }
1850
            $tres = $this->GetTextsYMinMax();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $tres is correct as $this->GetTextsYMinMax() targeting Amenadiel\JpGraph\Graph\Graph::GetTextsYMinMax() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1851
1852
            if (\is_array($tres)) {
0 ignored issues
show
introduced by
The condition is_array($tres) is always false.
Loading history...
1853
                [$tmin, $tmax] = $tres;
1854
                $min = \min($min, $tmin);
1855
                $max = \max($max, $tmax);
1856
            }
1857
            $this->yscale->AutoScale(
1858
                $this->img,
1859
                $min,
1860
                $max,
1861
                $this->img->plotheight / $this->ytick_factor
1862
            );
1863
        } elseif ($this->yscale->IsSpecified() && ($this->yscale->auto_ticks || !$this->yscale->ticks->IsSpecified())) {
1864
            // The tick calculation will use the user suplied min/max values to determine
1865
            // the ticks. If auto_ticks is false the exact user specifed min and max
1866
            // values will be used for the scale.
1867
            // If auto_ticks is true then the scale might be slightly adjusted
1868
            // so that the min and max values falls on an even major step.
1869
            $min = $this->yscale->scale[0];
1870
            $max = $this->yscale->scale[1];
1871
            $this->yscale->AutoScale(
1872
                $this->img,
1873
                $min,
1874
                $max,
1875
                $this->img->plotheight / $this->ytick_factor,
1876
                $this->yscale->auto_ticks
1877
            );
1878
1879
            // Now make sure we show enough precision to accurate display the
1880
            // labels. If this is not done then the user might end up with
1881
            // a scale that might actually start with, say 13.5, butdue to rounding
1882
            // the scale label will ony show 14.
1883
            if (\abs(\floor($min) - $min) > 0) {
1884
                // If the user has set a format then we bail out
1885
                if ('' === $this->yscale->ticks->label_formatstr && '' === $this->yscale->ticks->label_dateformatstr) {
1886
                    $this->yscale->ticks->precision = \abs(\floor(\log10(\abs(\floor($min) - $min)))) + 1;
1887
                }
1888
            }
1889
        }
1890
    }
1891
1892
    public function InitScaleConfigs()
1893
    {
1894
        // Setup scale constants
1895
        if ($this->yscale) {
1896
            $this->yscale->InitConfigs($this->img);
1897
        }
1898
1899
        if ($this->xscale) {
1900
            $this->xscale->InitConfigs($this->img);
1901
        }
1902
1903
        if ($this->y2scale) {
1904
            $this->y2scale->InitConfigs($this->img);
1905
        }
1906
1907
        $n = Configs::safe_count($this->ynscale);
1908
1909
        for ($i = 0; $i < $n; ++$i) {
1910
            if (!$this->ynscale[$i]) {
1911
                continue;
1912
            }
1913
1914
            $this->ynscale[$i]->InitConfigs($this->img);
1915
        }
1916
    }
1917
1918
    public function doPrestrokeAdjustments()
1919
    {
1920
        // Do any pre-stroke adjustment that is needed by the different plot types
1921
        // (i.e bar plots want's to add an offset to the x-labels etc)
1922
        for ($i = 0; Configs::safe_count($this->plots) > $i; ++$i) {
1923
            if (!($this->plots[$i] instanceof Plot\Plot)) {
1924
                continue;
1925
            }
1926
1927
            $this->plots[$i]->PreStrokeAdjust($this);
1928
            $this->plots[$i]->DoLegend($this);
1929
        }
1930
1931
        // Any plots on the second Y scale?
1932
        if (null !== $this->y2scale) {
1933
            for ($i = 0; Configs::safe_count($this->y2plots) > $i; ++$i) {
1934
                if (!($this->plots[$i] instanceof Plot\Plot)) {
1935
                    continue;
1936
                }
1937
1938
                $this->y2plots[$i]->PreStrokeAdjust($this);
1939
                $this->y2plots[$i]->DoLegend($this);
1940
            }
1941
        }
1942
1943
        // Any plots on the extra Y axises?
1944
        $n = Configs::safe_count($this->ynaxis);
1945
1946
        for ($i = 0; $i < $n; ++$i) {
1947
            if (null === $this->ynplots || null === $this->ynplots[$i]) {
1948
                Util\JpGraphError::RaiseL(25032, $i); //("No plots for Y-axis nbr:$i");
1949
            }
1950
            $m = Configs::safe_count($this->ynplots[$i]);
1951
1952
            for ($j = 0; $j < $m; ++$j) {
1953
                $this->ynplots[$i][$j]->PreStrokeAdjust($this);
1954
                $this->ynplots[$i][$j]->DoLegend($this);
1955
            }
1956
        }
1957
    }
1958
1959
    /**
1960
     * @param array $aDepth
1961
     * @param bool $aCSIM
1962
     */
1963
    public function StrokeBands($aDepth, $aCSIM)
1964
    {
1965
        // Stroke bands
1966
        if (null !== $this->bands && !$aCSIM) {
1967
            for ($i = 0; Configs::safe_count($this->bands) > $i; ++$i) {
1968
                // Stroke all bands that asks to be in the background
1969
                if ($this->bands[$i]->depth !== $aDepth) {
1970
                    // Stroke all bands that asks to be in the background
1971
                    continue;
1972
                    // Stroke all bands that asks to be in the background
1973
                }
1974
1975
                $this->bands[$i]->Stroke($this->img, $this->xscale, $this->yscale);
1976
            }
1977
        }
1978
1979
        if (null === $this->y2bands || null === $this->y2scale || $aCSIM) {
1980
            return;
1981
        }
1982
1983
        for ($i = 0; Configs::safe_count($this->y2bands) > $i; ++$i) {
1984
            // Stroke all bands that asks to be in the foreground
1985
            if ($this->y2bands[$i]->depth !== $aDepth) {
1986
                // Stroke all bands that asks to be in the foreground
1987
                continue;
1988
                // Stroke all bands that asks to be in the foreground
1989
            }
1990
1991
            $this->y2bands[$i]->Stroke($this->img, $this->xscale, $this->y2scale);
1992
        }
1993
    }
1994
1995
    // Stroke the graph
1996
    // $aStrokeFileName If != "" the image will be written to this file and NOT
1997
    // streamed back to the browser
1998
    /**
1999
     * @param array|string $aStrokeFileName
2000
     */
2001
    public function Stroke($aStrokeFileName = '')
2002
    {
2003
        // Fist make a sanity check that user has specified a scale
2004
        if (empty($this->yscale)) {
2005
            Util\JpGraphError::RaiseL(25031); //('You must specify what scale to use with a call to Graph::SetScale().');
2006
        }
2007
2008
        // Start by adjusting the margin so that potential titles will fit.
2009
        $this->AdjustMarginsForTitles();
2010
2011
        // Give the plot a chance to do any scale adjuments the individual plots
2012
        // wants to do. Right now this is only used by the contour plot to set scale
2013
        // limits
2014
        for ($i = 0; Configs::safe_count($this->plots) > $i; ++$i) {
2015
            if (!($this->plots[$i] instanceof Plot\Plot)) {
2016
                continue;
2017
            }
2018
2019
            $this->plots[$i]->PreScaleSetup($this);
2020
        }
2021
2022
        // Init scale constants that are used to calculate the transformation from
2023
        // world to pixel coordinates
2024
        $this->InitScaleConfigs();
2025
2026
        // If the filename is the predefined value = '_csim_special_'
2027
        // we assume that the call to stroke only needs to do enough
2028
        // to correctly generate the Configs::getConfig('CSIM') maps.
2029
        // We use this variable to skip things we don't strictly need
2030
        // to do to generate the image map to improve performance
2031
        // a best we can. Therefor you will see a lot of tests !$_csim in the
2032
        // code below.
2033
        $_csim = (Configs::getConfig('_CSIM_SPECIALFILE') === $aStrokeFileName);
2034
2035
        // If we are called the second time (perhaps the user has called GetHTMLImageMap()
2036
        // himself then the legends have alsready been populated once in order to get the
2037
        // Configs::getConfig('CSIM') coordinats. Since we do not want the legends to be populated a second time
2038
        // we clear the legends
2039
        $this->legend->Clear();
2040
2041
        // We need to know if we have stroked the plot in the
2042
        // GetCSIMareas. Otherwise the Configs::getConfig('CSIM') hasn't been generated
2043
        // and in the case of GetCSIM called before stroke to generate
2044
        // Configs::getConfig('CSIM') without storing an image to disk GetCSIM must call Stroke.
2045
        $this->iHasStroked = true;
2046
2047
        // Setup pre-stroked adjustments and Legends
2048
        $this->doPrestrokeAdjustments();
2049
2050
        if ($this->graph_theme) {
2051
            $this->graph_theme->PreStrokeApply($this);
2052
        }
2053
2054
        // Bail out if any of the Y-axis not been specified and
2055
        // has no plots. (This means it is impossible to do autoscaling and
2056
        // no other scale was given so we can't possible draw anything). If you use manual
2057
        // scaling you also have to supply the tick steps as well.
2058
        if ((!$this->yscale->IsSpecified() && Configs::safe_count($this->plots) === 0)
2059
            || (null !== $this->y2scale && !$this->y2scale->IsSpecified() && Configs::safe_count($this->y2plots) === 0)
2060
        ) {
2061
            //$e = "n=". Configs::safe_count($this->y2plots)."\n";
2062
            // $e = "Can't draw unspecified Y-scale.<br>\nYou have either:<br>\n";
2063
            // $e .= "1. Specified an Y axis for autoscaling but have not supplied any plots<br>\n";
2064
            // $e .= "2. Specified a scale manually but have forgot to specify the tick steps";
2065
            Util\JpGraphError::RaiseL(25026);
2066
        }
2067
2068
        // Bail out if no plots and no specified X-scale
2069
        if ((!$this->xscale->IsSpecified() && Configs::safe_count($this->plots) === 0 && Configs::safe_count($this->y2plots) === 0)) {
2070
            Util\JpGraphError::RaiseL(25034); //("<strong>JpGraph: Can't draw unspecified X-scale.</strong><br>No plots.<br>");
2071
        }
2072
2073
        // Autoscale the normal Y-axis
2074
        $this->doAutoScaleYAxis();
2075
2076
        // Autoscale all additiopnal y-axis
2077
        $this->doAutoScaleYnAxis();
2078
2079
        // Autoscale the regular x-axis and position the y-axis properly
2080
        $this->doAutoScaleXAxis();
2081
2082
        // If we have a negative values and x-axis position is at 0
2083
        // we need to supress the first and possible the last tick since
2084
        // they will be drawn on top of the y-axis (and possible y2 axis)
2085
        // The test below might seem strange the reasone being that if
2086
        // the user hasn't specified a value for position this will not
2087
        // be set until we do the stroke for the axis so as of now it
2088
        // is undefined.
2089
        // For X-text scale we ignore all this since the tick are usually
2090
        // much further in and not close to the Y-axis. Hence the test
2091
        // for 'text'
2092
        if (($this->xscale->GetMinVal() === $this->yaxis->pos || (\is_string($this->yaxis->pos) && 'min' === $this->yaxis->pos))
2093
            && !\is_numeric($this->xaxis->pos) && $this->yscale->GetMinVal() < 0
2094
            && \mb_substr($this->axtype, 0, 4) !== 'text' && 'min' !== $this->xaxis->pos
2095
        ) {
2096
            //$this->yscale->ticks->SupressZeroLabel(false);
2097
            $this->xscale->ticks->SupressFirst();
2098
2099
            if (null !== $this->y2axis) {
2100
                $this->xscale->ticks->SupressLast();
2101
            }
2102
        } elseif (!\is_numeric($this->yaxis->pos) && 'max' === $this->yaxis->pos) {
2103
            $this->xscale->ticks->SupressLast();
2104
        }
2105
2106
        if (!$_csim) {
2107
            $this->StrokePlotArea();
2108
2109
            if (Configs::getConfig('DEPTH_BACK') === $this->iIconDepth) {
0 ignored issues
show
introduced by
The condition Amenadiel\JpGraph\Graph\...) === $this->iIconDepth is always false.
Loading history...
2110
                $this->StrokeIcons();
2111
            }
2112
        }
2113
        $this->StrokeAxis(false);
2114
2115
        // Stroke colored bands
2116
        $this->StrokeBands(Configs::getConfig('DEPTH_BACK'), $_csim);
2117
2118
        if (Configs::getConfig('DEPTH_BACK') === $this->grid_depth && !$_csim) {
0 ignored issues
show
introduced by
The condition Amenadiel\JpGraph\Graph\...) === $this->grid_depth is always false.
Loading history...
2119
            $this->ygrid->Stroke();
2120
            $this->xgrid->Stroke();
2121
        }
2122
2123
        // Stroke Y2-axis
2124
        if (null !== $this->y2axis && !$_csim) {
2125
            $this->y2axis->Stroke($this->xscale);
2126
            $this->y2grid->Stroke();
0 ignored issues
show
Bug introduced by
The method Stroke() does not exist on null. ( Ignorable by Annotation )

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

2126
            $this->y2grid->/** @scrutinizer ignore-call */ 
2127
                           Stroke();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2127
        }
2128
2129
        // Stroke yn-axis
2130
        $n = Configs::safe_count($this->ynaxis);
2131
2132
        for ($i = 0; $i < $n; ++$i) {
2133
            $this->ynaxis[$i]->Stroke($this->xscale);
2134
        }
2135
2136
        $oldoff = $this->xscale->off;
2137
2138
        if (\mb_substr($this->axtype, 0, 4) === 'text') {
2139
            if (-1 < $this->text_scale_abscenteroff) {
2140
                // For a text scale the scale factor is the number of pixel per step.
2141
                // Hence we can use the scale factor as a substitute for number of pixels
2142
                // per major scale step and use that in order to adjust the offset so that
2143
                // an object of width "abscenteroff" becomes centered.
2144
                $this->xscale->off += \round($this->xscale->scale_factor / 2) - \round($this->text_scale_abscenteroff / 2);
2145
            } else {
2146
                $this->xscale->off += \ceil($this->xscale->scale_factor * $this->text_scale_off * $this->xscale->ticks->minor_step);
0 ignored issues
show
Bug introduced by
The property minor_step is declared private in Amenadiel\JpGraph\Graph\Tick\RadarLinearTicks and cannot be accessed from this context.
Loading history...
2147
            }
2148
        }
2149
2150
        if ($this->iDoClipping) {
2151
            $oldimage = $this->img->CloneCanvasH();
2152
        }
2153
2154
        if (!$this->y2orderback) {
2155
            // Stroke all plots for Y1 axis
2156
            for ($i = 0; Configs::safe_count($this->plots) > $i; ++$i) {
2157
                $this->plots[$i]->Stroke($this->img, $this->xscale, $this->yscale);
2158
2159
                if (!($this->plots[$i] instanceof Plot\Plot)) {
2160
                    continue;
2161
                }
2162
2163
                $this->plots[$i]->StrokeMargin($this->img);
2164
            }
2165
        }
2166
2167
        // Stroke all plots for Y2 axis
2168
        if (null !== $this->y2scale) {
2169
            for ($i = 0; Configs::safe_count($this->y2plots) > $i; ++$i) {
2170
                $this->y2plots[$i]->Stroke($this->img, $this->xscale, $this->y2scale);
2171
            }
2172
        }
2173
2174
        if ($this->y2orderback) {
2175
            // Stroke all plots for Y1 axis
2176
            for ($i = 0; Configs::safe_count($this->plots) > $i; ++$i) {
2177
                $this->plots[$i]->Stroke($this->img, $this->xscale, $this->yscale);
2178
2179
                if (!($this->plots[$i] instanceof Plot\Plot)) {
2180
                    continue;
2181
                }
2182
2183
                $this->plots[$i]->StrokeMargin($this->img);
2184
            }
2185
        }
2186
2187
        $n = Configs::safe_count($this->ynaxis);
2188
2189
        for ($i = 0; $i < $n; ++$i) {
2190
            $m = Configs::safe_count($this->ynplots[$i]);
2191
2192
            for ($j = 0; $j < $m; ++$j) {
2193
                $this->ynplots[$i][$j]->Stroke($this->img, $this->xscale, $this->ynscale[$i]);
2194
2195
                if (!($this->ynplots[$i][$j] instanceof Plot\Plot)) {
2196
                    continue;
2197
                }
2198
2199
                $this->ynplots[$i][$j]->StrokeMargin($this->img);
2200
            }
2201
        }
2202
2203
        if (Configs::getConfig('DEPTH_FRONT') === $this->iIconDepth) {
0 ignored issues
show
introduced by
The condition Amenadiel\JpGraph\Graph\...) === $this->iIconDepth is always false.
Loading history...
2204
            $this->StrokeIcons();
2205
        }
2206
2207
        if ($this->iDoClipping) {
2208
            // Clipping only supports graphs at 0 and 90 degrees
2209
            if (0 === $this->img->a) {
2210
                $this->img->CopyCanvasH(
2211
                    $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...
2212
                    $this->img->img,
2213
                    $this->img->left_margin,
2214
                    $this->img->top_margin,
2215
                    $this->img->left_margin,
2216
                    $this->img->top_margin,
2217
                    $this->img->plotwidth + 1,
2218
                    $this->img->plotheight
2219
                );
2220
            } elseif (90 === $this->img->a) {
2221
                $adj = ($this->img->height - $this->img->width) / 2;
2222
                $this->img->CopyCanvasH(
2223
                    $oldimage,
2224
                    $this->img->img,
2225
                    $this->img->bottom_margin - $adj,
2226
                    $this->img->left_margin + $adj,
2227
                    $this->img->bottom_margin - $adj,
2228
                    $this->img->left_margin + $adj,
2229
                    $this->img->plotheight + 1,
2230
                    $this->img->plotwidth
2231
                );
2232
            } else {
2233
                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.');
2234
            }
2235
            $this->img->Destroy();
2236
            $this->img->SetCanvasH($oldimage);
2237
        }
2238
2239
        $this->xscale->off = $oldoff;
2240
2241
        if (Configs::getConfig('DEPTH_FRONT') === $this->grid_depth && !$_csim) {
0 ignored issues
show
introduced by
The condition Amenadiel\JpGraph\Graph\...) === $this->grid_depth is always false.
Loading history...
2242
            $this->ygrid->Stroke();
2243
            $this->xgrid->Stroke();
2244
        }
2245
2246
        // Stroke colored bands
2247
        $this->StrokeBands(Configs::getConfig('DEPTH_FRONT'), $_csim);
2248
2249
        // Finally draw the axis again since some plots may have nagged
2250
        // the axis in the edges.
2251
        if (!$_csim) {
2252
            $this->StrokeAxis();
2253
        }
2254
2255
        if (null !== $this->y2scale && !$_csim) {
2256
            $this->y2axis->Stroke($this->xscale, false);
0 ignored issues
show
Bug introduced by
The method Stroke() does not exist on null. ( Ignorable by Annotation )

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

2256
            $this->y2axis->/** @scrutinizer ignore-call */ 
2257
                           Stroke($this->xscale, false);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2257
        }
2258
2259
        if (!$_csim) {
2260
            $this->StrokePlotBox();
2261
        }
2262
2263
        // The titles and legends never gets rotated so make sure
2264
        // that the angle is 0 before stroking them
2265
        $aa = $this->img->SetAngle(0);
2266
        $this->StrokeTitles();
2267
        $this->footer->Stroke($this->img);
2268
        $this->legend->Stroke($this->img);
2269
        $this->img->SetAngle($aa);
2270
        $this->StrokeTexts();
2271
        $this->StrokeTables();
2272
2273
        if ($_csim) {
2274
            return;
2275
        }
2276
2277
        $this->img->SetAngle($aa);
2278
2279
        // Draw an outline around the image map
2280
        if (Configs::_JPG_DEBUG) {
2281
            $this->DisplayClientSideaImageMapAreas();
2282
        }
2283
2284
        // Should we do any final image transformation
2285
        if ($this->iImgTrans) {
2286
            $tform = new Image\ImgTrans($this->img->img);
2287
            $this->img->img = $tform->Skew3D(
2288
                $this->iImgTransHorizon,
2289
                $this->iImgTransSkewDist,
2290
                $this->iImgTransDirection,
2291
                $this->iImgTransHighQ,
2292
                $this->iImgTransMinSize,
2293
                $this->iImgTransFillColor,
2294
                $this->iImgTransBorder
2295
            );
2296
        }
2297
2298
        // If the filename is given as the special "__handle"
2299
        // then the image handler is returned and the image is NOT
2300
        // streamed back
2301
        if (Configs::getConfig('_IMG_HANDLER') === $aStrokeFileName) {
2302
            return $this->img->img;
2303
        }
2304
        // Finally stream the generated picture
2305
        $this->cache->PutAndStream($this->img, $this->cache_name, $this->inline, $aStrokeFileName);
2306
    }
2307
2308
    public function SetAxisLabelBackground($aType, $aXFColor = 'lightgray', $aXColor = 'black', $aYFColor = 'lightgray', $aYColor = 'black')
2309
    {
2310
        $this->iAxisLblBgType = $aType;
2311
        $this->iXAxisLblBgFillColor = $aXFColor;
2312
        $this->iXAxisLblBgColor = $aXColor;
2313
        $this->iYAxisLblBgFillColor = $aYFColor;
2314
        $this->iYAxisLblBgColor = $aYColor;
2315
    }
2316
2317
    public function StrokeAxisLabelBackground()
2318
    {
2319
        // Types
2320
        // 0 = No background
2321
        // 1 = Only X-labels, length of axis
2322
        // 2 = Only Y-labels, length of axis
2323
        // 3 = As 1 but extends to width of graph
2324
        // 4 = As 2 but extends to height of graph
2325
        // 5 = Combination of 3 & 4
2326
        // 6 = Combination of 1 & 2
2327
2328
        $t = $this->iAxisLblBgType;
2329
2330
        if (1 > $t) {
2331
            return;
2332
        }
2333
2334
        // Stroke optional X-axis label background color
2335
        if (1 === $t || 3 === $t || 5 === $t || 6 === $t) {
2336
            $this->img->PushColor($this->iXAxisLblBgFillColor);
2337
2338
            if (1 === $t || 6 === $t) {
2339
                $xl = $this->img->left_margin;
2340
                $yu = $this->img->height - $this->img->bottom_margin + 1;
2341
                $xr = $this->img->width - $this->img->right_margin;
2342
                $yl = $this->img->height - 1 - $this->frame_weight;
2343
            } else {
2344
                // t==3 || t==5
2345
                $xl = $this->frame_weight;
2346
                $yu = $this->img->height - $this->img->bottom_margin + 1;
2347
                $xr = $this->img->width - 1 - $this->frame_weight;
2348
                $yl = $this->img->height - 1 - $this->frame_weight;
2349
            }
2350
2351
            $this->img->FilledRectangle($xl, $yu, $xr, $yl);
2352
            $this->img->PopColor();
2353
2354
            // Check if we should add the vertical lines at left and right edge
2355
            if ('' !== $this->iXAxisLblBgColor) {
2356
                // Hardcode to one pixel wide
2357
                $this->img->SetLineWeight(1);
2358
                $this->img->PushColor($this->iXAxisLblBgColor);
2359
2360
                if (1 === $t || 6 === $t) {
2361
                    $this->img->Line($xl, $yu, $xl, $yl);
2362
                    $this->img->Line($xr, $yu, $xr, $yl);
2363
                } else {
2364
                    $xl = $this->img->width - $this->img->right_margin;
2365
                    $this->img->Line($xl, $yu - 1, $xr, $yu - 1);
2366
                }
2367
                $this->img->PopColor();
2368
            }
2369
        }
2370
2371
        if (2 !== $t && 4 !== $t && 5 !== $t && 6 !== $t) {
2372
            return;
2373
        }
2374
2375
        $this->img->PushColor($this->iYAxisLblBgFillColor);
2376
2377
        if (2 === $t || 6 === $t) {
2378
            $xl = $this->frame_weight;
2379
            $yu = $this->frame_weight + $this->img->top_margin;
2380
            $xr = $this->img->left_margin - 1;
2381
            $yl = $this->img->height - $this->img->bottom_margin + 1;
2382
        } else {
2383
            $xl = $this->frame_weight;
2384
            $yu = $this->frame_weight;
2385
            $xr = $this->img->left_margin - 1;
2386
            $yl = $this->img->height - 1 - $this->frame_weight;
2387
        }
2388
2389
        $this->img->FilledRectangle($xl, $yu, $xr, $yl);
2390
        $this->img->PopColor();
2391
2392
        // Check if we should add the vertical lines at left and right edge
2393
        if ('' === $this->iXAxisLblBgColor) {
2394
            // Check if we should add the vertical lines at left and right edge
2395
            return;
2396
            // Check if we should add the vertical lines at left and right edge
2397
        }
2398
2399
        $this->img->PushColor($this->iXAxisLblBgColor);
2400
2401
        if (2 === $t || 6 === $t) {
2402
            $this->img->Line($xl, $yu - 1, $xr, $yu - 1);
2403
            $this->img->Line($xl, $yl - 1, $xr, $yl - 1);
2404
        } else {
2405
            $this->img->Line($xr + 1, $yu, $xr + 1, $this->img->top_margin);
2406
        }
2407
        $this->img->PopColor();
2408
    }
2409
2410
    /**
2411
     * @param bool $aStrokeLabels
2412
     */
2413
    public function StrokeAxis($aStrokeLabels = true)
2414
    {
2415
        if ($aStrokeLabels) {
2416
            $this->StrokeAxisLabelBackground();
2417
        }
2418
2419
        // Stroke axis
2420
        if (Configs::getConfig('AXSTYLE_SIMPLE') !== $this->iAxisStyle) {
0 ignored issues
show
introduced by
The condition Amenadiel\JpGraph\Graph\...) !== $this->iAxisStyle is always true.
Loading history...
2421
            switch ($this->iAxisStyle) {
2422
                case Configs::getConfig('AXSTYLE_BOXIN'):
2423
                    $toppos = Configs::getConfig('SIDE_DOWN');
2424
                    $bottompos = Configs::getConfig('SIDE_UP');
2425
                    $leftpos = Configs::getConfig('SIDE_RIGHT');
2426
                    $rightpos = Configs::getConfig('SIDE_LEFT');
2427
2428
                    break;
2429
                case Configs::getConfig('AXSTYLE_BOXOUT'):
2430
                    $toppos = Configs::getConfig('SIDE_UP');
2431
                    $bottompos = Configs::getConfig('SIDE_DOWN');
2432
                    $leftpos = Configs::getConfig('SIDE_LEFT');
2433
                    $rightpos = Configs::getConfig('SIDE_RIGHT');
2434
2435
                    break;
2436
                case Configs::getConfig('AXSTYLE_YBOXIN'):
2437
                    $toppos = false;
2438
                    $bottompos = Configs::getConfig('SIDE_UP');
2439
                    $leftpos = Configs::getConfig('SIDE_RIGHT');
2440
                    $rightpos = Configs::getConfig('SIDE_LEFT');
2441
2442
                    break;
2443
                case Configs::getConfig('AXSTYLE_YBOXOUT'):
2444
                    $toppos = false;
2445
                    $bottompos = Configs::getConfig('SIDE_DOWN');
2446
                    $leftpos = Configs::getConfig('SIDE_LEFT');
2447
                    $rightpos = Configs::getConfig('SIDE_RIGHT');
2448
2449
                    break;
2450
2451
                default:
2452
                    Util\JpGraphError::RaiseL(25036, $this->iAxisStyle); //('Unknown AxisStyle() : '.$this->iAxisStyle);
2453
2454
                    break;
2455
            }
2456
2457
            // By default we hide the first label so it doesn't cross the
2458
            // Y-axis in case the positon hasn't been set by the user.
2459
            // However, if we use a box we always want the first value
2460
            // displayed so we make sure it will be displayed.
2461
            $this->xscale->ticks->SupressFirst(false);
2462
2463
            // Now draw the bottom X-axis
2464
            $this->xaxis->SetPos('min');
2465
            $this->xaxis->SetLabelSide(Configs::getConfig('SIDE_DOWN'));
2466
            $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...
2467
            $this->xaxis->Stroke($this->yscale, $aStrokeLabels);
2468
2469
            if (false !== $toppos) {
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...
2470
                // We also want a top X-axis
2471
                $this->xaxis = $this->xaxis;
2472
                $this->xaxis->SetPos('max');
2473
                $this->xaxis->SetLabelSide(Configs::getConfig('SIDE_UP'));
2474
                // No title for the top X-axis
2475
                if ($aStrokeLabels) {
2476
                    $this->xaxis->title->Set('');
2477
                }
2478
                $this->xaxis->scale->ticks->SetSide($toppos);
2479
                $this->xaxis->Stroke($this->yscale, $aStrokeLabels);
2480
            }
2481
2482
            // Stroke the left Y-axis
2483
            $this->yaxis->SetPos('min');
2484
            $this->yaxis->SetLabelSide(Configs::getConfig('SIDE_LEFT'));
2485
            $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...
2486
            $this->yaxis->Stroke($this->xscale, $aStrokeLabels);
2487
2488
            // Stroke the  right Y-axis
2489
            $this->yaxis->SetPos('max');
2490
            // No title for the right side
2491
            if ($aStrokeLabels) {
2492
                $this->yaxis->title->Set('');
2493
            }
2494
            $this->yaxis->SetLabelSide(Configs::getConfig('SIDE_RIGHT'));
2495
            $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...
2496
            $this->yaxis->Stroke($this->xscale, $aStrokeLabels);
2497
        } else {
2498
            $this->xaxis->Stroke($this->yscale, $aStrokeLabels);
2499
            $this->yaxis->Stroke($this->xscale, $aStrokeLabels);
2500
        }
2501
    }
2502
2503
    // Private helper function for backgound image
2504
    /**
2505
     * @param string $aImgFormat
2506
     * @param string $aFile
2507
     */
2508
    public static function LoadBkgImage($aImgFormat = '', $aFile = '', $aImgStr = '')
2509
    {
2510
        if ('' !== $aImgStr) {
2511
            return Image\Image::CreateFromString($aImgStr);
2512
        }
2513
2514
        // Remove case sensitivity and setup appropriate function to create image
2515
        // Get file extension. This should be the Configs::getConfig('LAST') '.' separated part of the filename
2516
        $e = \explode('.', $aFile);
2517
        $ext = \mb_strtolower($e[\count($e) - 1]);
2518
2519
        if ('jpeg' === $ext) {
2520
            $ext = 'jpg';
2521
        }
2522
2523
        if (\trim($ext) === '') {
2524
            $ext = 'png'; // Assume PNG if no extension specified
2525
        }
2526
2527
        if ('' === $aImgFormat) {
2528
            $imgtag = $ext;
2529
        } else {
2530
            $imgtag = $aImgFormat;
2531
        }
2532
2533
        $supported = \imagetypes();
2534
2535
        if (('jpg' === $ext && !($supported & \IMG_JPG))
2536
            || ('gif' === $ext && !($supported & \IMG_GIF))
2537
            || ('png' === $ext && !($supported & \IMG_PNG))
2538
            || ('bmp' === $ext && !($supported & \IMG_WBMP))
2539
            || ('xpm' === $ext && !($supported & \IMG_XPM))
2540
        ) {
2541
            Util\JpGraphError::RaiseL(25037, $aFile); //('The image format of your background image ('.$aFile.') is not supported in your system configuration. ');
2542
        }
2543
2544
        if ('jpg' === $imgtag || 'jpeg' === $imgtag) {
2545
            $f = 'imagecreatefromjpeg';
2546
            $imgtag = 'jpg';
2547
        } else {
2548
            $f = 'imagecreatefrom' . $imgtag;
2549
        }
2550
2551
        // Compare specified image type and file extension
2552
        if ($imgtag !== $ext) {
2553
            //$t = "Background image seems to be of different type (has different file extension) than specified imagetype. Specified: '".$aImgFormat."'File: '".$aFile."'";
2554
            Util\JpGraphError::RaiseL(25038, $aImgFormat, $aFile);
2555
        }
2556
2557
        $img = $f($aFile);
2558
2559
        if (!$img) {
2560
            Util\JpGraphError::RaiseL(25039, $aFile); //(" Can't read background image: '".$aFile."'");
2561
        }
2562
2563
        return $img;
2564
    }
2565
2566
    public function StrokePlotGrad()
2567
    {
2568
        if (0 > $this->plot_gradtype) {
2569
            return;
2570
        }
2571
2572
        $grad = new Plot\Gradient($this->img);
2573
        $xl = $this->img->left_margin;
2574
        $yt = $this->img->top_margin;
2575
        $xr = $xl + $this->img->plotwidth + 1;
2576
        $yb = $yt + $this->img->plotheight;
2577
        $grad->FilledRectangle($xl, $yt, $xr, $yb, $this->plot_gradfrom, $this->plot_gradto, $this->plot_gradtype);
2578
    }
2579
2580
    public function StrokeBackgroundGrad()
2581
    {
2582
        if (0 > $this->bkg_gradtype) {
2583
            return;
2584
        }
2585
2586
        $grad = new Plot\Gradient($this->img);
2587
2588
        if (Configs::getConfig('BGRAD_PLOT') === $this->bkg_gradstyle) {
0 ignored issues
show
introduced by
The condition Amenadiel\JpGraph\Graph\...== $this->bkg_gradstyle is always false.
Loading history...
2589
            $xl = $this->img->left_margin;
2590
            $yt = $this->img->top_margin;
2591
            $xr = $xl + $this->img->plotwidth + 1;
2592
            $yb = $yt + $this->img->plotheight;
2593
            $grad->FilledRectangle($xl, $yt, $xr, $yb, $this->bkg_gradfrom, $this->bkg_gradto, $this->bkg_gradtype);
2594
        } else {
2595
            $xl = 0;
2596
            $yt = 0;
2597
            $xr = $xl + $this->img->width - 1;
2598
            $yb = $yt + $this->img->height - 1;
2599
2600
            if ($this->doshadow) {
2601
                $xr -= $this->shadow_width;
2602
                $yb -= $this->shadow_width;
2603
            }
2604
2605
            if ($this->doframe) {
2606
                $yt += $this->frame_weight;
2607
                $yb -= $this->frame_weight;
2608
                $xl += $this->frame_weight;
2609
                $xr -= $this->frame_weight;
2610
            }
2611
            $aa = $this->img->SetAngle(0);
2612
            $grad->FilledRectangle($xl, $yt, $xr, $yb, $this->bkg_gradfrom, $this->bkg_gradto, $this->bkg_gradtype);
2613
            $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...
2614
        }
2615
    }
2616
2617
    public function StrokeFrameBackground()
2618
    {
2619
        if ('' !== $this->background_image && '' !== $this->background_cflag) {
2620
            Util\JpGraphError::RaiseL(25040); //('It is not possible to specify both a background image and a background country flag.');
2621
        }
2622
2623
        if ('' !== $this->background_image) {
2624
            $bkgimg = $this->LoadBkgImage($this->background_image_format, $this->background_image);
2625
        } elseif ('' !== $this->background_cflag) {
2626
            $fobj = new Image\FlagImages(Configs::getConfig('FLAGSIZE4'));
2627
            $dummy = '';
2628
            $bkgimg = $fobj->GetImgByName($this->background_cflag, $dummy);
2629
            $this->background_image_mix = $this->background_cflag_mix;
2630
            $this->background_image_type = $this->background_cflag_type;
2631
        } else {
2632
            return;
2633
        }
2634
2635
        $bw = \imagesx($bkgimg);
2636
        $bh = \imagesy($bkgimg);
2637
2638
        // No matter what the angle is we always stroke the image and frame
2639
        // assuming it is 0 degree
2640
        $aa = $this->img->SetAngle(0);
2641
2642
        switch ($this->background_image_type) {
2643
            case Configs::getConfig('BGIMG_FILLPLOT'): // Resize to just fill the plotarea
2644
                $this->FillMarginArea();
2645
                $this->StrokeFrame();
2646
                // Special case to hande 90 degree rotated graph corectly
2647
                if (90 === $aa) {
2648
                    $this->img->SetAngle(90);
2649
                    $this->FillPlotArea();
2650
                    $aa = $this->img->SetAngle(0);
2651
                    $adj = ($this->img->height - $this->img->width) / 2;
2652
                    $this->img->CopyMerge(
2653
                        $bkgimg,
2654
                        $this->img->bottom_margin - $adj,
2655
                        $this->img->left_margin + $adj,
2656
                        0,
2657
                        0,
2658
                        $this->img->plotheight + 1,
2659
                        $this->img->plotwidth,
2660
                        $bw,
2661
                        $bh,
2662
                        $this->background_image_mix
2663
                    );
2664
                } else {
2665
                    $this->FillPlotArea();
2666
                    $this->img->CopyMerge(
2667
                        $bkgimg,
2668
                        $this->img->left_margin,
2669
                        $this->img->top_margin + 1,
2670
                        0,
2671
                        0,
2672
                        $this->img->plotwidth + 1,
2673
                        $this->img->plotheight,
2674
                        $bw,
2675
                        $bh,
2676
                        $this->background_image_mix
2677
                    );
2678
                }
2679
2680
                break;
2681
            case Configs::getConfig('BGIMG_FILLFRAME'): // Fill the whole area from upper left corner, resize to just fit
2682
                $hadj = 0;
2683
                $vadj = 0;
2684
2685
                if ($this->doshadow) {
2686
                    $hadj = $this->shadow_width;
2687
                    $vadj = $this->shadow_width;
2688
                }
2689
                $this->FillMarginArea();
2690
                $this->FillPlotArea();
2691
                $this->img->CopyMerge(
2692
                    $bkgimg,
2693
                    0,
2694
                    0,
2695
                    0,
2696
                    0,
2697
                    $this->img->width - $hadj,
2698
                    $this->img->height - $vadj,
2699
                    $bw,
2700
                    $bh,
2701
                    $this->background_image_mix
2702
                );
2703
                $this->StrokeFrame();
2704
2705
                break;
2706
            case Configs::getConfig('BGIMG_COPY'): // Just copy the image from left corner, no resizing
2707
                $this->FillMarginArea();
2708
                $this->FillPlotArea();
2709
                $this->img->CopyMerge(
2710
                    $bkgimg,
2711
                    0,
2712
                    0,
2713
                    0,
2714
                    0,
2715
                    $bw,
2716
                    $bh,
2717
                    $bw,
2718
                    $bh,
2719
                    $this->background_image_mix
2720
                );
2721
                $this->StrokeFrame();
2722
2723
                break;
2724
            case Configs::getConfig('BGIMG_CENTER'): // Center original image in the plot area
2725
                $this->FillMarginArea();
2726
                $this->FillPlotArea();
2727
                $centerx = \round($this->img->plotwidth / 2 + $this->img->left_margin - $bw / 2);
2728
                $centery = \round($this->img->plotheight / 2 + $this->img->top_margin - $bh / 2);
2729
                $this->img->CopyMerge(
2730
                    $bkgimg,
2731
                    $centerx,
2732
                    $centery,
2733
                    0,
2734
                    0,
2735
                    $bw,
2736
                    $bh,
2737
                    $bw,
2738
                    $bh,
2739
                    $this->background_image_mix
2740
                );
2741
                $this->StrokeFrame();
2742
2743
                break;
2744
            case Configs::getConfig('BGIMG_FREE'): // Just copy the image to the specified location
2745
                $this->img->CopyMerge(
2746
                    $bkgimg,
2747
                    $this->background_image_xpos,
2748
                    $this->background_image_ypos,
2749
                    0,
2750
                    0,
2751
                    $bw,
2752
                    $bh,
2753
                    $bw,
2754
                    $bh,
2755
                    $this->background_image_mix
2756
                );
2757
                $this->StrokeFrame(); // New
2758
2759
                break;
2760
2761
            default:
2762
                Util\JpGraphError::RaiseL(25042); //(" Unknown background image layout");
2763
        }
2764
        $this->img->SetAngle($aa);
2765
    }
2766
2767
    // Private
2768
    // Draw a frame around the image
2769
    public function StrokeFrame()
2770
    {
2771
        if (!$this->doframe) {
2772
            return;
2773
        }
2774
2775
        if (1 >= $this->background_image_type && (0 > $this->bkg_gradtype || (0 < $this->bkg_gradtype && Configs::getConfig('BGRAD_PLOT') === $this->bkg_gradstyle))) {
2776
            $c = $this->margin_color;
2777
        } else {
2778
            $c = false;
2779
        }
2780
2781
        if ($this->doshadow) {
2782
            $this->img->SetColor($this->frame_color);
2783
            $this->img->ShadowRectangle(
2784
                0,
2785
                0,
2786
                $this->img->width,
2787
                $this->img->height,
2788
                $c,
2789
                $this->shadow_width,
2790
                $this->shadow_color
2791
            );
2792
        } elseif ($this->framebevel) {
2793
            if ($c) {
2794
                $this->img->SetColor($this->margin_color);
2795
                $this->img->FilledRectangle(0, 0, $this->img->width - 1, $this->img->height - 1);
2796
            }
2797
            $this->img->Bevel(
2798
                1,
2799
                1,
2800
                $this->img->width - 2,
2801
                $this->img->height - 2,
2802
                $this->framebeveldepth,
2803
                $this->framebevelcolor1,
2804
                $this->framebevelcolor2
2805
            );
2806
2807
            if ($this->framebevelborder) {
2808
                $this->img->SetColor($this->framebevelbordercolor);
2809
                $this->img->Rectangle(0, 0, $this->img->width - 1, $this->img->height - 1);
2810
            }
2811
        } else {
2812
            $this->img->SetLineWeight($this->frame_weight);
2813
2814
            if ($c) {
2815
                $this->img->SetColor($this->margin_color);
2816
                $this->img->FilledRectangle(0, 0, $this->img->width - 1, $this->img->height - 1);
2817
            }
2818
            $this->img->SetColor($this->frame_color);
2819
            $this->img->Rectangle(0, 0, $this->img->width - 1, $this->img->height - 1);
2820
        }
2821
    }
2822
2823
    public function FillMarginArea()
2824
    {
2825
        $hadj = 0;
2826
        $vadj = 0;
2827
2828
        if ($this->doshadow) {
2829
            $hadj = $this->shadow_width;
2830
            $vadj = $this->shadow_width;
2831
        }
2832
2833
        $this->img->SetColor($this->margin_color);
2834
        $this->img->FilledRectangle(0, 0, $this->img->width - 1 - $hadj, $this->img->height - 1 - $vadj);
2835
2836
        $this->img->FilledRectangle(0, 0, $this->img->width - 1 - $hadj, $this->img->top_margin);
2837
        $this->img->FilledRectangle(0, $this->img->top_margin, $this->img->left_margin, $this->img->height - 1 - $hadj);
2838
        $this->img->FilledRectangle(
2839
            $this->img->left_margin + 1,
2840
            $this->img->height - $this->img->bottom_margin,
2841
            $this->img->width - 1 - $hadj,
2842
            $this->img->height - 1 - $hadj
2843
        );
2844
        $this->img->FilledRectangle(
2845
            $this->img->width - $this->img->right_margin,
2846
            $this->img->top_margin + 1,
2847
            $this->img->width - 1 - $hadj,
2848
            $this->img->height - $this->img->bottom_margin - 1
2849
        );
2850
    }
2851
2852
    public function FillPlotArea()
2853
    {
2854
        $this->img->PushColor($this->plotarea_color);
2855
        $this->img->FilledRectangle(
2856
            $this->img->left_margin,
2857
            $this->img->top_margin,
2858
            $this->img->width - $this->img->right_margin,
2859
            $this->img->height - $this->img->bottom_margin
2860
        );
2861
        $this->img->PopColor();
2862
    }
2863
2864
    // Stroke the plot area with either a solid color or a background image
2865
    public function StrokePlotArea()
2866
    {
2867
        // Note: To be consistent we really should take a possible shadow
2868
        // into account. However, that causes some problem for the LinearScale class
2869
        // since in the current design it does not have any links to class Graph which
2870
        // means it has no way of compensating for the adjusted plotarea in case of a
2871
        // shadow. So, until I redesign LinearScale we can't compensate for this.
2872
        // So just set the two adjustment parameters to zero for now.
2873
        $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...
2874
        $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...
2875
2876
        if ('' !== $this->background_image || '' !== $this->background_cflag) {
2877
            $this->StrokeFrameBackground();
2878
        } else {
2879
            $aa = $this->img->SetAngle(0);
2880
            $this->StrokeFrame();
2881
            $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...
2882
            $this->StrokeBackgroundGrad();
2883
2884
            if (0 > $this->bkg_gradtype || (0 < $this->bkg_gradtype && Configs::getConfig('BGRAD_MARGIN') === $this->bkg_gradstyle)) {
2885
                $this->FillPlotArea();
2886
            }
2887
            $this->StrokePlotGrad();
2888
        }
2889
    }
2890
2891
    public function StrokeIcons()
2892
    {
2893
        $n = Configs::safe_count($this->iIcons);
2894
2895
        for ($i = 0; $i < $n; ++$i) {
2896
            $this->iIcons[$i]->StrokeWithScale($this->img, $this->xscale, $this->yscale);
2897
        }
2898
    }
2899
2900
    public function StrokePlotBox()
2901
    {
2902
        // Should we draw a box around the plot area?
2903
        if (!$this->boxed) {
2904
            // Should we draw a box around the plot area?
2905
            return;
2906
            // Should we draw a box around the plot area?
2907
        }
2908
2909
        $this->img->SetLineWeight(1);
2910
        $this->img->SetLineStyle('solid');
2911
        $this->img->SetColor($this->box_color);
2912
2913
        for ($i = 0; $i < $this->box_weight; ++$i) {
2914
            $this->img->Rectangle(
2915
                $this->img->left_margin - $i,
2916
                $this->img->top_margin - $i,
2917
                $this->img->width - $this->img->right_margin + $i,
2918
                $this->img->height - $this->img->bottom_margin + $i
2919
            );
2920
        }
2921
    }
2922
2923
    public function SetTitleBackgroundFillStyle($aStyle, $aColor1 = 'black', $aColor2 = 'white')
2924
    {
2925
        $this->titlebkg_fillstyle = $aStyle;
2926
        $this->titlebkg_scolor1 = $aColor1;
2927
        $this->titlebkg_scolor2 = $aColor2;
2928
    }
2929
2930
    public function SetTitleBackground(
2931
        $aBackColor = 'gray',
2932
        $aStyle = Configs::TITLEBKG_STYLE1,
2933
        $aFrameStyle = Configs::TITLEBKG_FRAME_NONE,
2934
        $aFrameColor = 'black',
2935
        $aFrameWeight = 1,
2936
        $aBevelHeight = 3,
2937
        $aEnable = true
2938
    ) {
2939
        $this->titlebackground = $aEnable;
2940
        $this->titlebackground_color = $aBackColor;
2941
        $this->titlebackground_style = $aStyle;
2942
        $this->titlebackground_framecolor = $aFrameColor;
2943
        $this->titlebackground_framestyle = $aFrameStyle;
2944
        $this->titlebackground_frameweight = $aFrameWeight;
2945
        $this->titlebackground_bevelheight = $aBevelHeight;
2946
    }
2947
2948
    public function StrokeTitles()
2949
    {
2950
        $margin = 3;
2951
2952
        if ($this->titlebackground) {
2953
            // Find out height
2954
            $this->title->margin += 2;
2955
            $h = $this->title->GetTextHeight($this->img) + $this->title->margin + $margin;
2956
2957
            if ('' !== $this->subtitle->t && !$this->subtitle->hide) {
2958
                $h += $this->subtitle->GetTextHeight($this->img) + $margin +
2959
                    $this->subtitle->margin;
2960
                $h += 2;
2961
            }
2962
2963
            if ('' !== $this->subsubtitle->t && !$this->subsubtitle->hide) {
2964
                $h += $this->subsubtitle->GetTextHeight($this->img) + $margin +
2965
                    $this->subsubtitle->margin;
2966
                $h += 2;
2967
            }
2968
            $this->img->PushColor($this->titlebackground_color);
2969
2970
            if (Configs::getConfig('TITLEBKG_STYLE1') === $this->titlebackground_style) {
0 ignored issues
show
introduced by
The condition Amenadiel\JpGraph\Graph\...->titlebackground_style is always false.
Loading history...
2971
                // Inside the frame
2972
                if ($this->framebevel) {
2973
                    $x1 = $y1 = $this->framebeveldepth + 1;
2974
                    $x2 = $this->img->width - $this->framebeveldepth - 2;
2975
                    $this->title->margin += $this->framebeveldepth + 1;
2976
                    $h += $y1;
2977
                    $h += 2;
2978
                } else {
2979
                    $x1 = $y1 = $this->frame_weight;
2980
                    $x2 = $this->img->width - $this->frame_weight - 1;
2981
                }
2982
            } elseif (Configs::getConfig('TITLEBKG_STYLE2') === $this->titlebackground_style) {
0 ignored issues
show
introduced by
The condition Amenadiel\JpGraph\Graph\...->titlebackground_style is always false.
Loading history...
2983
                // Cover the frame as well
2984
                $x1 = $y1 = 0;
2985
                $x2 = $this->img->width - 1;
2986
            } elseif (Configs::getConfig('TITLEBKG_STYLE3') === $this->titlebackground_style) {
0 ignored issues
show
introduced by
The condition Amenadiel\JpGraph\Graph\...->titlebackground_style is always false.
Loading history...
2987
                // Cover the frame as well (the difference is that
2988
                // for style==3 a bevel frame border is on top
2989
                // of the title background)
2990
                $x1 = $y1 = 0;
2991
                $x2 = $this->img->width - 1;
2992
                $h += $this->framebeveldepth;
2993
                $this->title->margin += $this->framebeveldepth;
2994
            } else {
2995
                Util\JpGraphError::RaiseL(25043); //('Unknown title background style.');
2996
            }
2997
2998
            if (3 === $this->titlebackground_framestyle) {
2999
                $h += $this->titlebackground_bevelheight * 2 + 1;
3000
                $this->title->margin += $this->titlebackground_bevelheight;
3001
            }
3002
3003
            if ($this->doshadow) {
3004
                $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...
3005
            }
3006
3007
            $indent = 0;
3008
3009
            if (Configs::getConfig('TITLEBKG_FRAME_BEVEL') === $this->titlebackground_framestyle) {
3010
                $indent = $this->titlebackground_bevelheight;
3011
            }
3012
3013
            if (Configs::getConfig('TITLEBKG_FILLSTYLE_HSTRIPED') === $this->titlebkg_fillstyle) {
3014
                $this->img->FilledRectangle2(
3015
                    $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...
3016
                    $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...
3017
                    $x2 - $indent,
3018
                    $h - $indent,
3019
                    $this->titlebkg_scolor1,
3020
                    $this->titlebkg_scolor2
3021
                );
3022
            } elseif (Configs::getConfig('TITLEBKG_FILLSTYLE_VSTRIPED') === $this->titlebkg_fillstyle) {
3023
                $this->img->FilledRectangle2(
3024
                    $x1 + $indent,
3025
                    $y1 + $indent,
3026
                    $x2 - $indent,
3027
                    $h - $indent,
3028
                    $this->titlebkg_scolor1,
3029
                    $this->titlebkg_scolor2,
3030
                    2
3031
                );
3032
            } else {
3033
                // Solid fill
3034
                $this->img->FilledRectangle($x1, $y1, $x2, $h);
3035
            }
3036
            $this->img->PopColor();
3037
3038
            $this->img->PushColor($this->titlebackground_framecolor);
3039
            $this->img->SetLineWeight($this->titlebackground_frameweight);
3040
3041
            if (Configs::getConfig('TITLEBKG_FRAME_FULL') === $this->titlebackground_framestyle) {
3042
                // Frame background
3043
                $this->img->Rectangle($x1, $y1, $x2, $h);
3044
            } elseif (Configs::getConfig('TITLEBKG_FRAME_BOTTOM') === $this->titlebackground_framestyle) {
3045
                // Bottom line only
3046
                $this->img->Line($x1, $h, $x2, $h);
3047
            } elseif (Configs::getConfig('TITLEBKG_FRAME_BEVEL') === $this->titlebackground_framestyle) {
3048
                $this->img->Bevel($x1, $y1, $x2, $h, $this->titlebackground_bevelheight);
3049
            }
3050
            $this->img->PopColor();
3051
3052
            // This is clumsy. But we neeed to stroke the whole graph frame if it is
3053
            // set to bevel to get the bevel shading on top of the text background
3054
            if ($this->framebevel && $this->doframe && 3 === $this->titlebackground_style) {
3055
                $this->img->Bevel(
3056
                    1,
3057
                    1,
3058
                    $this->img->width - 2,
3059
                    $this->img->height - 2,
3060
                    $this->framebeveldepth,
3061
                    $this->framebevelcolor1,
3062
                    $this->framebevelcolor2
3063
                );
3064
3065
                if ($this->framebevelborder) {
3066
                    $this->img->SetColor($this->framebevelbordercolor);
3067
                    $this->img->Rectangle(0, 0, $this->img->width - 1, $this->img->height - 1);
3068
                }
3069
            }
3070
        }
3071
3072
        // Stroke title
3073
        $y = $this->title->margin;
3074
3075
        if ('center' === $this->title->halign) {
3076
            $this->title->Center(0, $this->img->width, $y);
3077
        } elseif ('left' === $this->title->halign) {
3078
            $this->title->SetPos($this->title->margin + 2, $y);
3079
        } elseif ('right' === $this->title->halign) {
3080
            $indent = 0;
3081
3082
            if ($this->doshadow) {
3083
                $indent = $this->shadow_width + 2;
3084
            }
3085
            $this->title->SetPos($this->img->width - $this->title->margin - $indent, $y, 'right');
3086
        }
3087
        $this->title->Stroke($this->img);
3088
3089
        // ... and subtitle
3090
        $y += $this->title->GetTextHeight($this->img) + $margin + $this->subtitle->margin;
3091
3092
        if ('center' === $this->subtitle->halign) {
3093
            $this->subtitle->Center(0, $this->img->width, $y);
3094
        } elseif ('left' === $this->subtitle->halign) {
3095
            $this->subtitle->SetPos($this->subtitle->margin + 2, $y);
3096
        } elseif ('right' === $this->subtitle->halign) {
3097
            $indent = 0;
3098
3099
            if ($this->doshadow) {
3100
                $indent = $this->shadow_width + 2;
3101
            }
3102
3103
            $this->subtitle->SetPos($this->img->width - $this->subtitle->margin - $indent, $y, 'right');
3104
        }
3105
        $this->subtitle->Stroke($this->img);
3106
3107
        // ... and subsubtitle
3108
        $y += $this->subtitle->GetTextHeight($this->img) + $margin + $this->subsubtitle->margin;
3109
3110
        if ('center' === $this->subsubtitle->halign) {
3111
            $this->subsubtitle->Center(0, $this->img->width, $y);
3112
        } elseif ('left' === $this->subsubtitle->halign) {
3113
            $this->subsubtitle->SetPos($this->subsubtitle->margin + 2, $y);
3114
        } elseif ('right' === $this->subsubtitle->halign) {
3115
            $indent = 0;
3116
3117
            if ($this->doshadow) {
3118
                $indent = $this->shadow_width + 2;
3119
            }
3120
3121
            $this->subsubtitle->SetPos($this->img->width - $this->subsubtitle->margin - $indent, $y, 'right');
3122
        }
3123
        $this->subsubtitle->Stroke($this->img);
3124
3125
        // ... and fancy title
3126
        $this->tabtitle->Stroke($this->img);
3127
    }
3128
3129
    public function StrokeTexts()
3130
    {
3131
        // Stroke any user added text objects
3132
        if (null !== $this->texts) {
3133
            for ($i = 0; Configs::safe_count($this->texts) > $i; ++$i) {
3134
                $this->texts[$i]->StrokeWithScale($this->img, $this->xscale, $this->yscale);
3135
            }
3136
        }
3137
3138
        if (null === $this->y2texts || null === $this->y2scale) {
3139
            return;
3140
        }
3141
3142
        for ($i = 0; Configs::safe_count($this->y2texts) > $i; ++$i) {
3143
            $this->y2texts[$i]->StrokeWithScale($this->img, $this->xscale, $this->y2scale);
3144
        }
3145
    }
3146
3147
    public function StrokeTables()
3148
    {
3149
        if (null === $this->iTables) {
3150
            return;
3151
        }
3152
3153
        $n = Configs::safe_count($this->iTables);
3154
3155
        for ($i = 0; $i < $n; ++$i) {
3156
            $this->iTables[$i]->StrokeWithScale($this->img, $this->xscale, $this->yscale);
3157
        }
3158
    }
3159
3160
    public function DisplayClientSideaImageMapAreas()
3161
    {
3162
        // Debug stuff - display the outline of the image map areas
3163
        $csim = '';
3164
3165
        foreach ($this->plots as $p) {
3166
            $csim .= $p->GetCSIMareas();
3167
        }
3168
        $csim .= $this->legend->GetCSIMareas();
3169
3170
        if (!\preg_match_all('/area shape="(\\w+)" coords="([0-9\\, ]+)"/', $csim, $coords)) {
3171
            return;
3172
        }
3173
3174
        $this->img->SetColor($this->csimcolor);
3175
        $n = Configs::safe_count($coords[0]);
3176
3177
        for ($i = 0; $i < $n; ++$i) {
3178
            if ('poly' === $coords[1][$i]) {
3179
                \preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/', $coords[2][$i], $pts);
3180
                $this->img->SetStartPoint($pts[1][\count($pts[0]) - 1], $pts[2][\count($pts[0]) - 1]);
3181
                $m = Configs::safe_count($pts[0]);
3182
3183
                for ($j = 0; $j < $m; ++$j) {
3184
                    $this->img->LineTo($pts[1][$j], $pts[2][$j]);
3185
                }
3186
            } elseif ('rect' === $coords[1][$i]) {
3187
                $pts = \preg_split('/,/', $coords[2][$i]);
3188
                $this->img->SetStartPoint($pts[0], $pts[1]);
3189
                $this->img->LineTo($pts[2], $pts[1]);
3190
                $this->img->LineTo($pts[2], $pts[3]);
3191
                $this->img->LineTo($pts[0], $pts[3]);
3192
                $this->img->LineTo($pts[0], $pts[1]);
3193
            }
3194
        }
3195
    }
3196
3197
    // Text scale offset in world coordinates
3198
    public function SetTextScaleOff($aOff)
3199
    {
3200
        $this->text_scale_off = $aOff;
3201
        $this->xscale->text_scale_off = $aOff;
3202
    }
3203
3204
    // Text width of bar to be centered in absolute pixels
3205
    public function SetTextScaleAbsCenterOff($aOff)
3206
    {
3207
        $this->text_scale_abscenteroff = $aOff;
3208
    }
3209
3210
    // Get Y min and max values for added lines
3211
    public function GetLinesYMinMax($aLines)
3212
    {
3213
        if (null === $aLines) {
3214
            return false;
3215
        }
3216
3217
        $n = Configs::safe_count($aLines);
3218
3219
        if (0 === $n) {
3220
            return false;
3221
        }
3222
3223
        $min = $aLines[0]->scaleposition;
3224
        $max = $min;
3225
        $flg = false;
3226
3227
        for ($i = 0; $i < $n; ++$i) {
3228
            if (Configs::getConfig('HORIZONTAL') !== $aLines[$i]->direction) {
3229
                continue;
3230
            }
3231
3232
            $flg = true;
3233
            $v = $aLines[$i]->scaleposition;
3234
3235
            if ($min > $v) {
3236
                $min = $v;
3237
            }
3238
3239
            if ($max >= $v) {
3240
                continue;
3241
            }
3242
3243
            $max = $v;
3244
        }
3245
3246
        return $flg ? [$min, $max] : false;
0 ignored issues
show
introduced by
The condition $flg is always false.
Loading history...
3247
    }
3248
3249
    // Get X min and max values for added lines
3250
    public function GetLinesXMinMax($aLines)
3251
    {
3252
        $n = Configs::safe_count($aLines);
3253
3254
        if (0 === $n) {
3255
            return false;
3256
        }
3257
3258
        $min = $aLines[0]->scaleposition;
3259
        $max = $min;
3260
        $flg = false;
3261
3262
        for ($i = 0; $i < $n; ++$i) {
3263
            if (Configs::getConfig('VERTICAL') !== $aLines[$i]->direction) {
3264
                continue;
3265
            }
3266
3267
            $flg = true;
3268
            $v = $aLines[$i]->scaleposition;
3269
3270
            if ($min > $v) {
3271
                $min = $v;
3272
            }
3273
3274
            if ($max >= $v) {
3275
                continue;
3276
            }
3277
3278
            $max = $v;
3279
        }
3280
3281
        return $flg ? [$min, $max] : false;
0 ignored issues
show
introduced by
The condition $flg is always false.
Loading history...
3282
    }
3283
3284
    // Get min and max values for all included plots
3285
    public function GetPlotsYMinMax($bPlots)
3286
    {
3287
        $aPlots = \array_filter($bPlots, function ($plot) {
3288
            return $plot instanceof Plot\Plot;
3289
        });
3290
        \reset($aPlots);
3291
        $n = Configs::safe_count($aPlots);
3292
        $i = 0;
3293
3294
        do {
3295
            [$xmax, $max] = isset($aPlots[$i]) ? $aPlots[$i]->Max() : [null, null];
3296
        } while (++$i < $n && !\is_numeric($max));
3297
3298
        $i = 0;
3299
3300
        do {
3301
            [$xmin, $min] = isset($aPlots[$i]) ? $aPlots[$i]->Min() : [null, null];
3302
        } while (++$i < $n && !\is_numeric($min));
3303
3304
        if (!\is_numeric($min) || !\is_numeric($max)) {
3305
            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).');
3306
        }
3307
3308
        for ($i = 0; $i < $n; ++$i) {
3309
            [$xmax, $ymax] = isset($aPlots[$i]) ? $aPlots[$i]->Max() : [null, null];
3310
            [$xmin, $ymin] = isset($aPlots[$i]) ? $aPlots[$i]->Min() : [null, null];
3311
3312
            if (\is_numeric($ymax)) {
3313
                $max = \max($max, $ymax);
3314
            }
3315
3316
            if (!\is_numeric($ymin)) {
3317
                continue;
3318
            }
3319
3320
            $min = \min($min, $ymin);
3321
        }
3322
3323
        if ('' === $min) {
3324
            $min = 0;
3325
        }
3326
3327
        if ('' === $max) {
3328
            $max = 0;
3329
        }
3330
3331
        if (0 === $min && 0 === $max) {
3332
            // Special case if all values are 0
3333
            $min = 0;
3334
            $max = 1;
3335
        }
3336
3337
        return [$min, $max];
3338
    }
3339
3340
    public function hasLinePlotAndBarPlot()
3341
    {
3342
        $has_line = false;
3343
        $has_bar = false;
3344
3345
        foreach ($this->plots as $plot) {
3346
            if ($plot instanceof Plot\LinePlot) {
3347
                $has_line = true;
3348
            }
3349
3350
            if (!($plot instanceof Plot\BarPlot)) {
3351
                continue;
3352
            }
3353
3354
            $has_bar = true;
3355
        }
3356
3357
        if ($has_line && $has_bar) {
3358
            return true;
3359
        }
3360
3361
        return false;
3362
    }
3363
3364
    public function SetTheme($graph_theme)
3365
    {
3366
        if (!($this instanceof PieGraph)) {
3367
            if (!$this->isAfterSetScale) {
3368
                Util\JpGraphError::RaiseL(25133); //('Use Graph::SetTheme() after Graph::SetScale().');
3369
            }
3370
        }
3371
3372
        if ($this->graph_theme) {
3373
            $this->ClearTheme();
3374
        }
3375
        $this->graph_theme = $graph_theme;
3376
        $this->graph_theme->ApplyGraph($this);
3377
    }
3378
3379
    public function ClearTheme()
3380
    {
3381
        $this->graph_theme = null;
3382
3383
        $this->isRunningClear = true;
3384
3385
        $this->__construct(
3386
            $this->inputValues['aWidth'],
3387
            $this->inputValues['aHeight'],
3388
            $this->inputValues['aCachedName'],
3389
            $this->inputValues['aTimeout'],
3390
            $this->inputValues['aInline']
3391
        );
3392
3393
        if ($this->isAfterSetScale) {
3394
            $this->SetScale(
3395
                $this->inputValues['aAxisType'],
3396
                $this->inputValues['aYMin'],
3397
                $this->inputValues['aYMax'],
3398
                $this->inputValues['aXMin'],
3399
                $this->inputValues['aXMax']
3400
            );
3401
        }
3402
3403
        $this->isRunningClear = false;
3404
    }
3405
3406
    public function SetSupersampling($do = false, $scale = 2)
3407
    {
3408
        if ($do) {
3409
            Configs::setConfig('SUPERSAMPLING_SCALE', $scale);
3410
        // $this->img->scale = $scale;
3411
        } else {
3412
            Configs::setConfig('SUPERSAMPLING_SCALE', 1);
3413
            //$this->img->scale = 0;
3414
        }
3415
    }
3416
}
3417
3418
// @class
3419