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
![]() |
|||||||
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
|
|||||||
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
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
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. ![]() |
|||||||
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 |