LinearTicks::SetMajTickPositions()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
3
/**
4
 * JPGraph v4.0.3
5
 */
6
7
namespace Amenadiel\JpGraph\Graph;
8
9
use Amenadiel\JpGraph\Util;
10
11
/**
12
 * @class LinearTicks
13
 * // Description: Draw linear ticks on axis
14
 */
15
class LinearTicks extends Ticks
16
{
17
    public $minor_step    = 1;
18
    public $major_step    = 2;
19
    public $xlabel_offset = 0;
20
    public $xtick_offset  = 0;
21
    private $label_offset = 0; // What offset should the displayed label have
22
    // i.e should we display 0,1,2 or 1,2,3,4 or 2,3,4 etc
23
    private $text_label_start = 0;
24
    private $iManualTickPos;
25
    private $iManualMinTickPos;
26
    private $iManualTickLabels;
27
    private $iAdjustForDST = false; // If a date falls within the DST period add one hour to the diaplyed time
28
29 19
    public function __construct()
30
    {
31 19
        $this->precision = -1;
32 19
    }
33
34
    // Return major step size in world coordinates
35 19
    public function GetMajor()
36
    {
37 19
        return $this->major_step;
38
    }
39
40
    // Return minor step size in world coordinates
41
    public function GetMinor()
42
    {
43
        return $this->minor_step;
44
    }
45
46
    // Set Minor and Major ticks (in world coordinates)
47 19
    public function Set($aMajStep, $aMinStep = false)
48
    {
49 19
        if ($aMinStep == false) {
50
            $aMinStep = $aMajStep;
51
        }
52
53 19
        if ($aMajStep <= 0 || $aMinStep <= 0) {
54
            Util\JpGraphError::RaiseL(25064);
55
            //(" Minor or major step size is 0. Check that you haven't got an accidental SetTextTicks(0) in your code. If this is not the case you might have stumbled upon a bug in JpGraph. Please report this and if possible include the data that caused the problem.");
56
        }
57
58 19
        $this->major_step = $aMajStep;
59 19
        $this->minor_step = $aMinStep;
60 19
        $this->is_set     = true;
61 19
    }
62
63
    public function SetMajTickPositions($aMajPos, $aLabels = null)
64
    {
65
        $this->SetTickPositions($aMajPos, null, $aLabels);
66
    }
67
68 2
    public function SetTickPositions($aMajPos, $aMinPos = null, $aLabels = null)
69
    {
70 2
        if (!is_array($aMajPos) || ($aMinPos !== null && !is_array($aMinPos))) {
71
            Util\JpGraphError::RaiseL(25065); //('Tick positions must be specifued as an array()');
72
            return;
73
        }
74 2
        $n = safe_count($aMajPos);
75 2
        if (is_array($aLabels) && (safe_count($aLabels) != $n)) {
76
            Util\JpGraphError::RaiseL(25066); //('When manually specifying tick positions and labels the number of labels must be the same as the number of specified ticks.');
77
        }
78 2
        $this->iManualTickPos    = $aMajPos;
79 2
        $this->iManualMinTickPos = $aMinPos;
80 2
        $this->iManualTickLabels = $aLabels;
81 2
    }
82
83 7
    public function HaveManualLabels()
84
    {
85 7
        return safe_count($this->iManualTickLabels) > 0;
86
    }
87
88
    // Specify all the tick positions manually and possible also the exact labels
89 2
    public function _doManualTickPos($aScale)
90
    {
91 2
        $n     = safe_count($this->iManualTickPos);
92 2
        $m     = safe_count($this->iManualMinTickPos);
93 2
        $doLbl = safe_count($this->iManualTickLabels) > 0;
94
95 2
        $this->maj_ticks_pos      = [];
96 2
        $this->maj_ticklabels_pos = [];
97 2
        $this->ticks_pos          = [];
98
99
        // Now loop through the supplied positions and translate them to screen coordinates
100
        // and store them in the maj_label_positions
101 2
        $minScale = $aScale->scale[0];
102 2
        $maxScale = $aScale->scale[1];
103 2
        $j        = 0;
104 2
        for ($i = 0; $i < $n; ++$i) {
105
            // First make sure that the first tick is not lower than the lower scale value
106 2
            if (!isset($this->iManualTickPos[$i]) || $this->iManualTickPos[$i] < $minScale || $this->iManualTickPos[$i] > $maxScale) {
107 2
                continue;
108
            }
109
110 2
            $this->maj_ticks_pos[$j]      = $aScale->Translate($this->iManualTickPos[$i]);
111 2
            $this->maj_ticklabels_pos[$j] = $this->maj_ticks_pos[$j];
112
113
            // Set the minor tick marks the same as major if not specified
114 2
            if ($m <= 0) {
115 2
                $this->ticks_pos[$j] = $this->maj_ticks_pos[$j];
116
            }
117 2
            if ($doLbl) {
118 1
                $this->maj_ticks_label[$j] = $this->iManualTickLabels[$i];
119
            } else {
120 1
                $this->maj_ticks_label[$j] = $this->_doLabelFormat($this->iManualTickPos[$i], $i, $n);
121
            }
122 2
            ++$j;
123
        }
124
125
        // Some sanity check
126 2
        if (safe_count($this->maj_ticks_pos) < 2) {
127
            Util\JpGraphError::RaiseL(25067); //('Your manually specified scale and ticks is not correct. The scale seems to be too small to hold any of the specified tickl marks.');
128
        }
129
130
        // Setup the minor tick marks
131 2
        $j = 0;
132 2
        for ($i = 0; $i < $m; ++$i) {
133
            if (empty($this->iManualMinTickPos[$i]) || $this->iManualMinTickPos[$i] < $minScale || $this->iManualMinTickPos[$i] > $maxScale) {
134
                continue;
135
            }
136
            $this->ticks_pos[$j] = $aScale->Translate($this->iManualMinTickPos[$i]);
137
            ++$j;
138
        }
139 2
    }
140
141 19
    public function _doAutoTickPos($aScale)
142
    {
143 19
        $maj_step_abs = $aScale->scale_factor * $this->major_step;
144 19
        $min_step_abs = $aScale->scale_factor * $this->minor_step;
145
146 19
        if ($min_step_abs == 0 || $maj_step_abs == 0) {
147
            Util\JpGraphError::RaiseL(25068); //("A plot has an illegal scale. This could for example be that you are trying to use text autoscaling to draw a line plot with only one point or that the plot area is too small. It could also be that no input data value is numeric (perhaps only '-' or 'x')");
148
        }
149
        // We need to make this an int since comparing it below
150
        // with the result from round() can give wrong result, such that
151
        // (40 < 40) == TRUE !!!
152 19
        $limit = (int) $aScale->scale_abs[1];
153
154 19
        if ($aScale->textscale) {
155
            // This can only be true for a X-scale (horizontal)
156
            // Define ticks for a text scale. This is slightly different from a
157
            // normal linear type of scale since the position might be adjusted
158
            // and the labels start at on
159 13
            $label       = (float) $aScale->GetMinVal() + $this->text_label_start + $this->label_offset;
160 13
            $start_abs   = $aScale->scale_factor * $this->text_label_start;
161 13
            $nbrmajticks = round(($aScale->GetMaxVal() - $aScale->GetMinVal() - $this->text_label_start) / $this->major_step) + 1;
162
163 13
            $x = $aScale->scale_abs[0] + $start_abs + $this->xlabel_offset * $min_step_abs;
164 13
            for ($i = 0; $label <= $aScale->GetMaxVal() + $this->label_offset; ++$i) {
165
                // Apply format to label
166 13
                $this->maj_ticks_label[$i] = $this->_doLabelFormat($label, $i, $nbrmajticks);
167 13
                $label += $this->major_step;
168
169
                // The x-position of the tick marks can be different from the labels.
170
                // Note that we record the tick position (not the label) so that the grid
171
                // happen upon tick marks and not labels.
172 13
                $xtick                        = $aScale->scale_abs[0] + $start_abs + $this->xtick_offset * $min_step_abs + $i * $maj_step_abs;
173 13
                $this->maj_ticks_pos[$i]      = $xtick;
174 13
                $this->maj_ticklabels_pos[$i] = round($x);
175 13
                $x += $maj_step_abs;
176
            }
177
        } else {
178 19
            $label   = $aScale->GetMinVal();
179 19
            $abs_pos = $aScale->scale_abs[0];
180 19
            $j       = 0;
181 19
            $i       = 0;
182 19
            $step    = round($maj_step_abs / $min_step_abs);
183 19
            if ($aScale->type == 'x') {
184
                // For a normal linear type of scale the major ticks will always be multiples
185
                // of the minor ticks. In order to avoid any rounding issues the major ticks are
186
                // defined as every "step" minor ticks and not calculated separately
187 8
                $nbrmajticks = round(($aScale->GetMaxVal() - $aScale->GetMinVal() - $this->text_label_start) / $this->major_step) + 1;
188 8
                while (round($abs_pos) <= $limit) {
189 8
                    $this->ticks_pos[]   = round($abs_pos);
190 8
                    $this->ticks_label[] = $label;
191 8
                    if ($step == 0 || $i % $step == 0 && $j < $nbrmajticks) {
192 8
                        $this->maj_ticks_pos[$j]      = round($abs_pos);
193 8
                        $this->maj_ticklabels_pos[$j] = round($abs_pos);
194 8
                        $this->maj_ticks_label[$j]    = $this->_doLabelFormat($label, $j, $nbrmajticks);
195 8
                        ++$j;
196
                    }
197 8
                    ++$i;
198 8
                    $abs_pos += $min_step_abs;
199 8
                    $label += $this->minor_step;
200
                }
201 18
            } elseif ($aScale->type == 'y') {
202
                //@todo  s=2:20,12  s=1:50,6  $this->major_step:$nbr
203
                // abs_point,limit s=1:270,80 s=2:540,160
204
                // $this->major_step = 50;
205 18
                $nbrmajticks = round(($aScale->GetMaxVal() - $aScale->GetMinVal()) / $this->major_step) + 1;
206
                //                $step = 5;
207 18
                while (round($abs_pos) >= $limit) {
208 18
                    $this->ticks_pos[$i]   = round($abs_pos);
209 18
                    $this->ticks_label[$i] = $label;
210 18
                    if ($step == 0 || $i % $step == 0 && $j < $nbrmajticks) {
211 18
                        $this->maj_ticks_pos[$j]      = round($abs_pos);
212 18
                        $this->maj_ticklabels_pos[$j] = round($abs_pos);
213 18
                        $this->maj_ticks_label[$j]    = $this->_doLabelFormat($label, $j, $nbrmajticks);
214 18
                        ++$j;
215
                    }
216 18
                    ++$i;
217 18
                    $abs_pos += $min_step_abs;
218 18
                    $label += $this->minor_step;
219
                }
220
            }
221
        }
222 19
    }
223
224
    public function AdjustForDST($aFlg = true)
225
    {
226
        $this->iAdjustForDST = $aFlg;
227
    }
228
229 19
    public function _doLabelFormat($aVal, $aIdx, $aNbrTicks)
230
    {
231
        // If precision hasn't been specified set it to a sensible value
232 19
        if ($this->precision == -1) {
233 19
            $t = log10($this->minor_step);
234 19
            if ($t > 0) {
235 16
                $precision = 0;
236
            } else {
237 19
                $precision = -floor($t);
238
            }
239
        } else {
240
            $precision = $this->precision;
241
        }
242
243 19
        if ($this->label_formfunc != '') {
244 2
            $f = $this->label_formfunc;
245 2
            if ($this->label_formatstr == '') {
246 2
                $l = call_user_func($f, $aVal);
247
            } else {
248 2
                $l = sprintf($this->label_formatstr, call_user_func($f, $aVal));
249
            }
250 19
        } elseif ($this->label_formatstr != '' || $this->label_dateformatstr != '') {
251 2
            if ($this->label_usedateformat) {
252
                // Adjust the value to take daylight savings into account
253 1
                if (date('I', $aVal) == 1 && $this->iAdjustForDST) {
254
                    // DST
255
                    $aVal += 3600;
256
                }
257
258 1
                $l = date($this->label_formatstr, $aVal);
259 1
                if ($this->label_formatstr == 'W') {
260
                    // If we use week formatting then add a single 'w' in front of the
261
                    // week number to differentiate it from dates
262 1
                    $l = 'w' . $l;
263
                }
264
            } else {
265 2
                if ($this->label_dateformatstr !== '') {
266
                    // Adjust the value to take daylight savings into account
267 1
                    if (date('I', $aVal) == 1 && $this->iAdjustForDST) {
268
                        // DST
269
                        $aVal += 3600;
270
                    }
271
272 1
                    $l = date($this->label_dateformatstr, $aVal);
273 1
                    if ($this->label_formatstr == 'W') {
274
                        // If we use week formatting then add a single 'w' in front of the
275
                        // week number to differentiate it from dates
276 1
                        $l = 'w' . $l;
277
                    }
278
                } else {
279 2
                    $l = sprintf($this->label_formatstr, $aVal);
280
                }
281
            }
282
        } else {
283
            //FIX: if negative precision  is returned "0f" , instead of formatted values
284 19
            $format = $precision > 0 ? '%01.' . $precision . 'f' : '%01.0f';
285 19
            $l      = sprintf($format, round($aVal, $precision));
0 ignored issues
show
Bug introduced by
It seems like $precision can also be of type double; however, parameter $precision of round() does only seem to accept integer, 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

285
            $l      = sprintf($format, round($aVal, /** @scrutinizer ignore-type */ $precision));
Loading history...
286
        }
287
288 19
        if (($this->supress_zerolabel && $l == 0) || ($this->supress_first && $aIdx == 0) || ($this->supress_last && $aIdx == $aNbrTicks - 1)) {
289 9
            $l = '';
290
        }
291
292 19
        return $l;
293
    }
294
295
    // Stroke ticks on either X or Y axis
296 19
    public function _StrokeTicks($aImg, $aScale, $aPos)
297
    {
298 19
        $hor = $aScale->type == 'x';
299 19
        $aImg->SetLineWeight($this->weight);
300
301
        // We need to make this an int since comparing it below
302
        // with the result from round() can give wrong result, such that
303
        // (40 < 40) == TRUE !!!
304 19
        $limit = (int) $aScale->scale_abs[1];
0 ignored issues
show
Unused Code introduced by
The assignment to $limit is dead and can be removed.
Loading history...
305
306
        // A text scale doesn't have any minor ticks
307 19
        if (!$aScale->textscale) {
308
            // Stroke minor ticks
309 19
            $yu = $aPos - $this->direction * $this->GetMinTickAbsSize();
310 19
            $xr = $aPos + $this->direction * $this->GetMinTickAbsSize();
311 19
            $n  = safe_count($this->ticks_pos);
312 19
            for ($i = 0; $i < $n; ++$i) {
313 19
                if (!$this->supress_tickmarks && !$this->supress_minor_tickmarks) {
314 3
                    if ($this->mincolor != '') {
315
                        $aImg->PushColor($this->mincolor);
316
                    }
317 3
                    if ($hor) {
318
                        //if( $this->ticks_pos[$i] <= $limit )
319 1
                        $aImg->Line($this->ticks_pos[$i], $aPos, $this->ticks_pos[$i], $yu);
320
                    } else {
321
                        //if( $this->ticks_pos[$i] >= $limit )
322 3
                        $aImg->Line($aPos, $this->ticks_pos[$i], $xr, $this->ticks_pos[$i]);
323
                    }
324 3
                    if ($this->mincolor != '') {
325
                        $aImg->PopColor();
326
                    }
327
                }
328
            }
329
        }
330
331
        // Stroke major ticks
332 19
        $yu          = $aPos - $this->direction * $this->GetMajTickAbsSize();
333 19
        $xr          = $aPos + $this->direction * $this->GetMajTickAbsSize();
334 19
        $nbrmajticks = round(($aScale->GetMaxVal() - $aScale->GetMinVal() - $this->text_label_start) / $this->major_step) + 1;
335 19
        $n           = safe_count($this->maj_ticks_pos);
336 19
        for ($i = 0; $i < $n; ++$i) {
337 19
            if (!($this->xtick_offset > 0 && $i == $nbrmajticks - 1) && !$this->supress_tickmarks) {
338 5
                if ($this->majcolor != '') {
339
                    $aImg->PushColor($this->majcolor);
340
                }
341 5
                if ($hor) {
342
                    //if( $this->maj_ticks_pos[$i] <= $limit )
343 2
                    $aImg->Line($this->maj_ticks_pos[$i], $aPos, $this->maj_ticks_pos[$i], $yu);
344
                } else {
345
                    //if( $this->maj_ticks_pos[$i] >= $limit )
346 4
                    $aImg->Line($aPos, $this->maj_ticks_pos[$i], $xr, $this->maj_ticks_pos[$i]);
347
                }
348 5
                if ($this->majcolor != '') {
349
                    $aImg->PopColor();
350
                }
351
            }
352
        }
353 19
    }
354
355
    // Draw linear ticks
356 19
    public function Stroke($aImg, $aScale, $aPos)
357
    {
358 19
        if ($this->iManualTickPos != null) {
359 2
            $this->_doManualTickPos($aScale);
360
        } else {
361 19
            $this->_doAutoTickPos($aScale);
362
        }
363 19
        $this->_StrokeTicks($aImg, $aScale, $aPos, $aScale->type == 'x');
0 ignored issues
show
Unused Code introduced by
The call to Amenadiel\JpGraph\Graph\...arTicks::_StrokeTicks() has too many arguments starting with $aScale->type == 'x'. ( Ignorable by Annotation )

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

363
        $this->/** @scrutinizer ignore-call */ 
364
               _StrokeTicks($aImg, $aScale, $aPos, $aScale->type == 'x');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
364 19
    }
365
366
    /**
367
     * PRIVATE METHODS.
368
     *
369
     * @param mixed $aLabelOff
370
     * @param mixed $aTickOff
371
     */
372
    // Spoecify the offset of the displayed tick mark with the tick "space"
373
    // Legal values for $o is [0,1] used to adjust where the tick marks and label
374
    // should be positioned within the major tick-size
375
    // $lo specifies the label offset and $to specifies the tick offset
376
    // this comes in handy for example in bar graphs where we wont no offset for the
377
    // tick but have the labels displayed halfway under the bars.
378 18
    public function SetXLabelOffset($aLabelOff, $aTickOff = -1)
379
    {
380 18
        $this->xlabel_offset = $aLabelOff;
381 18
        if ($aTickOff == -1) {
382
            // Same as label offset
383 16
            $this->xtick_offset = $aLabelOff;
384
        } else {
385 3
            $this->xtick_offset = $aTickOff;
386
        }
387 18
        if ($aLabelOff > 0) {
388 8
            $this->SupressLast(); // The last tick wont fit
389
        }
390 18
    }
391
392
    // Which tick label should we start with?
393 2
    public function SetTextLabelStart($aTextLabelOff)
394
    {
395 2
        $this->text_label_start = $aTextLabelOff;
396 2
    }
397
} // @class
398