PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.12.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.12.1
5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / vendor / szymach / c-pchart / src / Draw.php
matomo / app / vendor / szymach / c-pchart / src Last commit date
Barcode 1 year ago Chart 1 year ago BaseDraw.php 1 year ago Cache.php 1 year ago Data.php 1 year ago Draw.php 1 year ago Image.php 1 year ago
Draw.php
6047 lines
1 <?php
2
3 namespace CpChart;
4
5 use Exception;
6 /**
7 * Draw - class extension with drawing methods
8 *
9 * Version : 2.1.4
10 * Made by : Jean-Damien POGOLOTTI
11 * Last Update : 19/01/2014
12 *
13 * This file can be distributed under the license you can find at :
14 *
15 * http://www.pchart.net/license
16 *
17 * You can find the whole class documentation on the pChart web site.
18 */
19 abstract class Draw extends \CpChart\BaseDraw
20 {
21 /**
22 * Draw a polygon
23 * @param array $Points
24 * @param array $Format
25 */
26 public function drawPolygon(array $Points, array $Format = [])
27 {
28 $R = isset($Format["R"]) ? $Format["R"] : 0;
29 $G = isset($Format["G"]) ? $Format["G"] : 0;
30 $B = isset($Format["B"]) ? $Format["B"] : 0;
31 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
32 $NoFill = isset($Format["NoFill"]) ? $Format["NoFill"] : \false;
33 $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : \false;
34 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
35 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
36 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
37 $BorderAlpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $Alpha / 2;
38 $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
39 $SkipX = isset($Format["SkipX"]) ? $Format["SkipX"] : OUT_OF_SIGHT;
40 $SkipY = isset($Format["SkipY"]) ? $Format["SkipY"] : OUT_OF_SIGHT;
41 /* Calling the ImageFilledPolygon() public function over the $Points array will round it */
42 $Backup = $Points;
43 if ($Surrounding != null) {
44 $BorderR = $R + $Surrounding;
45 $BorderG = $G + $Surrounding;
46 $BorderB = $B + $Surrounding;
47 }
48 if ($SkipX != OUT_OF_SIGHT) {
49 $SkipX = floor($SkipX);
50 }
51 if ($SkipY != OUT_OF_SIGHT) {
52 $SkipY = floor($SkipY);
53 }
54 $RestoreShadow = $this->Shadow;
55 if (!$NoFill) {
56 if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
57 $this->Shadow = \false;
58 for ($i = 0; $i <= count($Points) - 1; $i = $i + 2) {
59 $Shadow[] = $Points[$i] + $this->ShadowX;
60 $Shadow[] = $Points[$i + 1] + $this->ShadowY;
61 }
62 $this->drawPolygon($Shadow, ["R" => $this->ShadowR, "G" => $this->ShadowG, "B" => $this->ShadowB, "Alpha" => $this->Shadowa, "NoBorder" => \true]);
63 }
64 $FillColor = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
65 if (count($Points) >= 6) {
66 $this->imageFilledPolygonWrapper($this->Picture, $Points, count($Points) / 2, $FillColor);
67 }
68 }
69 if (!$NoBorder) {
70 $Points = $Backup;
71 if ($NoFill) {
72 $BorderSettings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha];
73 } else {
74 $BorderSettings = ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha];
75 }
76 for ($i = 0; $i <= count($Points) - 1; $i = $i + 2) {
77 if (isset($Points[$i + 2]) && !($Points[$i] == $Points[$i + 2] && $Points[$i] == $SkipX) && !($Points[$i + 1] == $Points[$i + 3] && $Points[$i + 1] == $SkipY)) {
78 $this->drawLine($Points[$i], $Points[$i + 1], $Points[$i + 2], $Points[$i + 3], $BorderSettings);
79 } elseif (!($Points[$i] == $Points[0] && $Points[$i] == $SkipX) && !($Points[$i + 1] == $Points[1] && $Points[$i + 1] == $SkipY)) {
80 $this->drawLine($Points[$i], $Points[$i + 1], $Points[0], $Points[1], $BorderSettings);
81 }
82 }
83 }
84 $this->Shadow = $RestoreShadow;
85 }
86 /**
87 * Draw a rectangle with rounded corners
88 * @param int $X1
89 * @param int $Y1
90 * @param int $X2
91 * @param int $Y2
92 * @param int|float $Radius
93 * @param array $Format
94 * @return null|integer
95 */
96 public function drawRoundedRectangle($X1, $Y1, $X2, $Y2, $Radius, array $Format = [])
97 {
98 $R = isset($Format["R"]) ? $Format["R"] : 0;
99 $G = isset($Format["G"]) ? $Format["G"] : 0;
100 $B = isset($Format["B"]) ? $Format["B"] : 0;
101 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
102 list($X1, $Y1, $X2, $Y2) = $this->fixBoxCoordinates($X1, $Y1, $X2, $Y2);
103 if ($X2 - $X1 < $Radius) {
104 $Radius = floor(($X2 - $X1) / 2);
105 }
106 if ($Y2 - $Y1 < $Radius) {
107 $Radius = floor(($Y2 - $Y1) / 2);
108 }
109 $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "NoBorder" => \true];
110 if ($Radius <= 0) {
111 $this->drawRectangle($X1, $Y1, $X2, $Y2, $Color);
112 return 0;
113 }
114 if ($this->Antialias) {
115 $this->drawLine($X1 + $Radius, $Y1, $X2 - $Radius, $Y1, $Color);
116 $this->drawLine($X2, $Y1 + $Radius, $X2, $Y2 - $Radius, $Color);
117 $this->drawLine($X2 - $Radius, $Y2, $X1 + $Radius, $Y2, $Color);
118 $this->drawLine($X1, $Y1 + $Radius, $X1, $Y2 - $Radius, $Color);
119 } else {
120 $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
121 imageline($this->Picture, $X1 + $Radius, $Y1, $X2 - $Radius, $Y1, $Color);
122 imageline($this->Picture, $X2, $Y1 + $Radius, $X2, $Y2 - $Radius, $Color);
123 imageline($this->Picture, $X2 - $Radius, $Y2, $X1 + $Radius, $Y2, $Color);
124 imageline($this->Picture, $X1, $Y1 + $Radius, $X1, $Y2 - $Radius, $Color);
125 }
126 $Step = 360 / (2 * PI * $Radius);
127 for ($i = 0; $i <= 90; $i = $i + $Step) {
128 $X = cos(($i + 180) * PI / 180) * $Radius + $X1 + $Radius;
129 $Y = sin(($i + 180) * PI / 180) * $Radius + $Y1 + $Radius;
130 $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
131 $X = cos(($i + 90) * PI / 180) * $Radius + $X1 + $Radius;
132 $Y = sin(($i + 90) * PI / 180) * $Radius + $Y2 - $Radius;
133 $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
134 $X = cos($i * PI / 180) * $Radius + $X2 - $Radius;
135 $Y = sin($i * PI / 180) * $Radius + $Y2 - $Radius;
136 $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
137 $X = cos(($i + 270) * PI / 180) * $Radius + $X2 - $Radius;
138 $Y = sin(($i + 270) * PI / 180) * $Radius + $Y1 + $Radius;
139 $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
140 }
141 }
142 /**
143 * Draw a rectangle with rounded corners
144 * @param int $X1
145 * @param int $Y1
146 * @param int $X2
147 * @param int $Y2
148 * @param int|float $Radius
149 * @param array $Format
150 * @return null|integer
151 */
152 public function drawRoundedFilledRectangle($X1, $Y1, $X2, $Y2, $Radius, array $Format = [])
153 {
154 $R = isset($Format["R"]) ? $Format["R"] : 0;
155 $G = isset($Format["G"]) ? $Format["G"] : 0;
156 $B = isset($Format["B"]) ? $Format["B"] : 0;
157 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
158 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
159 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
160 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
161 $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
162 /* Temporary fix for AA issue */
163 $Y1 = (int) floor($Y1);
164 $Y2 = (int) floor($Y2);
165 $X1 = (int) floor($X1);
166 $X2 = (int) floor($X2);
167 if ($Surrounding != null) {
168 $BorderR = $R + $Surrounding;
169 $BorderG = $G + $Surrounding;
170 $BorderB = $B + $Surrounding;
171 }
172 if ($BorderR == -1) {
173 $BorderR = $R;
174 $BorderG = $G;
175 $BorderB = $B;
176 }
177 list($X1, $Y1, $X2, $Y2) = $this->fixBoxCoordinates($X1, $Y1, $X2, $Y2);
178 if ($X2 - $X1 < $Radius * 2) {
179 $Radius = (int) floor(($X2 - $X1) / 4);
180 }
181 if ($Y2 - $Y1 < $Radius * 2) {
182 $Radius = (int) floor(($Y2 - $Y1) / 4);
183 }
184 $RestoreShadow = $this->Shadow;
185 if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
186 $this->Shadow = \false;
187 $this->drawRoundedFilledRectangle($X1 + $this->ShadowX, $Y1 + $this->ShadowY, $X2 + $this->ShadowX, $Y2 + $this->ShadowY, $Radius, ["R" => $this->ShadowR, "G" => $this->ShadowG, "B" => $this->ShadowB, "Alpha" => $this->Shadowa]);
188 }
189 $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "NoBorder" => \true];
190 if ($Radius <= 0) {
191 $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, $Color);
192 return 0;
193 }
194 $YTop = $Y1 + $Radius;
195 $YBottom = $Y2 - $Radius;
196 $Step = 360 / (2 * PI * $Radius);
197 $Positions = [];
198 $Radius--;
199 $MinY = null;
200 $MaxY = null;
201 for ($i = 0; $i <= 90; $i = $i + $Step) {
202 $Xp1 = cos(($i + 180) * PI / 180) * $Radius + $X1 + $Radius;
203 $Xp2 = cos((90 - $i + 270) * PI / 180) * $Radius + $X2 - $Radius;
204 $Yp = (int) floor(sin(($i + 180) * PI / 180) * $Radius + $YTop);
205 if (null === $MinY || $Yp > $MinY) {
206 $MinY = $Yp;
207 }
208 if ($Xp1 <= floor($X1)) {
209 $Xp1++;
210 }
211 if ($Xp2 >= floor($X2)) {
212 $Xp2--;
213 }
214 $Xp1++;
215 if (!isset($Positions[$Yp])) {
216 $Positions[$Yp]["X1"] = $Xp1;
217 $Positions[$Yp]["X2"] = $Xp2;
218 } else {
219 $Positions[$Yp]["X1"] = ($Positions[$Yp]["X1"] + $Xp1) / 2;
220 $Positions[$Yp]["X2"] = ($Positions[$Yp]["X2"] + $Xp2) / 2;
221 }
222 $Xp1 = cos(($i + 90) * PI / 180) * $Radius + $X1 + $Radius;
223 $Xp2 = cos((90 - $i) * PI / 180) * $Radius + $X2 - $Radius;
224 $Yp = (int) floor(sin(($i + 90) * PI / 180) * $Radius + $YBottom);
225 if (null === $MaxY || $Yp < $MaxY) {
226 $MaxY = $Yp;
227 }
228 if ($Xp1 <= floor($X1)) {
229 $Xp1++;
230 }
231 if ($Xp2 >= floor($X2)) {
232 $Xp2--;
233 }
234 $Xp1++;
235 if (!isset($Positions[$Yp])) {
236 $Positions[$Yp]["X1"] = $Xp1;
237 $Positions[$Yp]["X2"] = $Xp2;
238 } else {
239 $Positions[$Yp]["X1"] = ($Positions[$Yp]["X1"] + $Xp1) / 2;
240 $Positions[$Yp]["X2"] = ($Positions[$Yp]["X2"] + $Xp2) / 2;
241 }
242 }
243 $ManualColor = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
244 foreach ($Positions as $Yp => $Bounds) {
245 $X1 = (int) $Bounds["X1"];
246 $X1Dec = $this->getFirstDecimal($X1);
247 if ($X1Dec != 0) {
248 $X1 = (int) floor($X1) + 1;
249 }
250 $X2 = (int) $Bounds["X2"];
251 $X2Dec = $this->getFirstDecimal($X2);
252 if ($X2Dec != 0) {
253 $X2 = (int) floor($X2) - 1;
254 }
255 imageline($this->Picture, $X1, $Yp, $X2, $Yp, $ManualColor);
256 }
257 $this->drawFilledRectangle($X1, $MinY + 1, (int) floor($X2), $MaxY - 1, $Color);
258 $Radius++;
259 $this->drawRoundedRectangle($X1, $Y1, $X2 + 1, $Y2 - 1, $Radius, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]);
260 $this->Shadow = $RestoreShadow;
261 }
262 /**
263 * Draw a rectangle
264 * @param int $X1
265 * @param int $Y1
266 * @param int $X2
267 * @param int $Y2
268 * @param array $Format
269 */
270 public function drawRectangle($X1, $Y1, $X2, $Y2, array $Format = [])
271 {
272 $R = isset($Format["R"]) ? $Format["R"] : 0;
273 $G = isset($Format["G"]) ? $Format["G"] : 0;
274 $B = isset($Format["B"]) ? $Format["B"] : 0;
275 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
276 $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
277 $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : \false;
278 if ($X1 > $X2) {
279 list($X1, $X2) = [$X2, $X1];
280 }
281 if ($Y1 > $Y2) {
282 list($Y1, $Y2) = [$Y2, $Y1];
283 }
284 if ($this->Antialias) {
285 if ($NoAngle) {
286 $this->drawLine($X1 + 1, $Y1, $X2 - 1, $Y1, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
287 $this->drawLine($X2, $Y1 + 1, $X2, $Y2 - 1, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
288 $this->drawLine($X2 - 1, $Y2, $X1 + 1, $Y2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
289 $this->drawLine($X1, $Y1 + 1, $X1, $Y2 - 1, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
290 } else {
291 $this->drawLine($X1 + 1, $Y1, $X2 - 1, $Y1, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
292 $this->drawLine($X2, $Y1, $X2, $Y2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
293 $this->drawLine($X2 - 1, $Y2, $X1 + 1, $Y2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
294 $this->drawLine($X1, $Y1, $X1, $Y2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
295 }
296 } else {
297 $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
298 imagerectangle($this->Picture, $X1, $Y1, $X2, $Y2, $Color);
299 }
300 }
301 /**
302 * Draw a filled rectangle
303 * @param int $X1
304 * @param int $Y1
305 * @param int $X2
306 * @param int $Y2
307 * @param array $Format
308 */
309 public function drawFilledRectangle($X1, $Y1, $X2, $Y2, array $Format = [])
310 {
311 $R = isset($Format["R"]) ? $Format["R"] : 0;
312 $G = isset($Format["G"]) ? $Format["G"] : 0;
313 $B = isset($Format["B"]) ? $Format["B"] : 0;
314 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
315 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
316 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
317 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
318 $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha;
319 $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
320 $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
321 $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : null;
322 $Dash = isset($Format["Dash"]) ? $Format["Dash"] : \false;
323 $DashStep = isset($Format["DashStep"]) ? $Format["DashStep"] : 4;
324 $DashR = isset($Format["DashR"]) ? $Format["DashR"] : 0;
325 $DashG = isset($Format["DashG"]) ? $Format["DashG"] : 0;
326 $DashB = isset($Format["DashB"]) ? $Format["DashB"] : 0;
327 $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : \false;
328 if ($Surrounding != null) {
329 $BorderR = $R + $Surrounding;
330 $BorderG = $G + $Surrounding;
331 $BorderB = $B + $Surrounding;
332 }
333 if ($X1 > $X2) {
334 list($X1, $X2) = [$X2, $X1];
335 }
336 if ($Y1 > $Y2) {
337 list($Y1, $Y2) = [$Y2, $Y1];
338 }
339 $RestoreShadow = $this->Shadow;
340 if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
341 $this->Shadow = \false;
342 $this->drawFilledRectangle($X1 + $this->ShadowX, $Y1 + $this->ShadowY, $X2 + $this->ShadowX, $Y2 + $this->ShadowY, ["R" => $this->ShadowR, "G" => $this->ShadowG, "B" => $this->ShadowB, "Alpha" => $this->Shadowa, "Ticks" => $Ticks, "NoAngle" => $NoAngle]);
343 }
344 $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
345 if ($NoAngle) {
346 imagefilledrectangle($this->Picture, ceil($X1) + 1, ceil($Y1), floor($X2) - 1, floor($Y2), $Color);
347 imageline($this->Picture, ceil($X1), ceil($Y1) + 1, ceil($X1), floor($Y2) - 1, $Color);
348 imageline($this->Picture, floor($X2), ceil($Y1) + 1, floor($X2), floor($Y2) - 1, $Color);
349 } else {
350 imagefilledrectangle($this->Picture, ceil($X1), ceil($Y1), floor($X2), floor($Y2), $Color);
351 }
352 if ($Dash) {
353 if ($BorderR != -1) {
354 $iX1 = $X1 + 1;
355 $iY1 = $Y1 + 1;
356 $iX2 = $X2 - 1;
357 $iY2 = $Y2 - 1;
358 } else {
359 $iX1 = $X1;
360 $iY1 = $Y1;
361 $iX2 = $X2;
362 $iY2 = $Y2;
363 }
364 $Color = $this->allocateColor($this->Picture, $DashR, $DashG, $DashB, $Alpha);
365 $Y = $iY1 - $DashStep;
366 for ($X = $iX1; $X <= $iX2 + ($iY2 - $iY1); $X = $X + $DashStep) {
367 $Y = $Y + $DashStep;
368 if ($X > $iX2) {
369 $Xa = $X - ($X - $iX2);
370 $Ya = $iY1 + ($X - $iX2);
371 } else {
372 $Xa = $X;
373 $Ya = $iY1;
374 }
375 if ($Y > $iY2) {
376 $Xb = $iX1 + ($Y - $iY2);
377 $Yb = $Y - ($Y - $iY2);
378 } else {
379 $Xb = $iX1;
380 $Yb = $Y;
381 }
382 imageline($this->Picture, $Xa, $Ya, $Xb, $Yb, $Color);
383 }
384 }
385 if ($this->Antialias && !$NoBorder) {
386 if ($X1 < ceil($X1)) {
387 $AlphaA = $Alpha * (ceil($X1) - $X1);
388 $Color = $this->allocateColor($this->Picture, $R, $G, $B, $AlphaA);
389 imageline($this->Picture, ceil($X1) - 1, ceil($Y1), ceil($X1) - 1, floor($Y2), $Color);
390 }
391 if ($Y1 < ceil($Y1)) {
392 $AlphaA = $Alpha * (ceil($Y1) - $Y1);
393 $Color = $this->allocateColor($this->Picture, $R, $G, $B, $AlphaA);
394 imageline($this->Picture, ceil($X1), ceil($Y1) - 1, floor($X2), ceil($Y1) - 1, $Color);
395 }
396 if ($X2 > floor($X2)) {
397 $AlphaA = $Alpha * (0.5 - ($X2 - floor($X2)));
398 $Color = $this->allocateColor($this->Picture, $R, $G, $B, $AlphaA);
399 imageline($this->Picture, floor($X2) + 1, ceil($Y1), floor($X2) + 1, floor($Y2), $Color);
400 }
401 if ($Y2 > floor($Y2)) {
402 $AlphaA = $Alpha * (0.5 - ($Y2 - floor($Y2)));
403 $Color = $this->allocateColor($this->Picture, $R, $G, $B, $AlphaA);
404 imageline($this->Picture, ceil($X1), floor($Y2) + 1, floor($X2), floor($Y2) + 1, $Color);
405 }
406 }
407 if ($BorderR != -1) {
408 $this->drawRectangle($X1, $Y1, $X2, $Y2, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha, "Ticks" => $Ticks, "NoAngle" => $NoAngle]);
409 }
410 $this->Shadow = $RestoreShadow;
411 }
412 /**
413 * Draw a rectangular marker of the specified size
414 * @param int $X
415 * @param int $Y
416 * @param array $Format
417 */
418 public function drawRectangleMarker($X, $Y, array $Format = [])
419 {
420 $Size = isset($Format["Size"]) ? $Format["Size"] : 4;
421 $HalfSize = floor($Size / 2);
422 $this->drawFilledRectangle($X - $HalfSize, $Y - $HalfSize, $X + $HalfSize, $Y + $HalfSize, $Format);
423 }
424 /**
425 * Drawn a spline based on the bezier public function
426 * @param array $Coordinates
427 * @param array $Format
428 * @return array
429 */
430 public function drawSpline(array $Coordinates, array $Format = [])
431 {
432 $R = isset($Format["R"]) ? $Format["R"] : 0;
433 $G = isset($Format["G"]) ? $Format["G"] : 0;
434 $B = isset($Format["B"]) ? $Format["B"] : 0;
435 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
436 $Force = isset($Format["Force"]) ? $Format["Force"] : 30;
437 $Forces = isset($Format["Forces"]) ? $Format["Forces"] : null;
438 $ShowC = isset($Format["ShowControl"]) ? $Format["ShowControl"] : \false;
439 $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
440 $PathOnly = isset($Format["PathOnly"]) ? $Format["PathOnly"] : \false;
441 $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null;
442 $Cpt = null;
443 $Mode = null;
444 $Result = [];
445 for ($i = 1; $i <= count($Coordinates) - 1; $i++) {
446 $X1 = $Coordinates[$i - 1][0];
447 $Y1 = $Coordinates[$i - 1][1];
448 $X2 = $Coordinates[$i][0];
449 $Y2 = $Coordinates[$i][1];
450 if ($Forces != null) {
451 $Force = $Forces[$i];
452 }
453 /* First segment */
454 if ($i == 1) {
455 $Xv1 = $X1;
456 $Yv1 = $Y1;
457 } else {
458 $Angle1 = $this->getAngle($XLast, $YLast, $X1, $Y1);
459 $Angle2 = $this->getAngle($X1, $Y1, $X2, $Y2);
460 $XOff = cos($Angle2 * PI / 180) * $Force + $X1;
461 $YOff = sin($Angle2 * PI / 180) * $Force + $Y1;
462 $Xv1 = cos($Angle1 * PI / 180) * $Force + $XOff;
463 $Yv1 = sin($Angle1 * PI / 180) * $Force + $YOff;
464 }
465 /* Last segment */
466 if ($i == count($Coordinates) - 1) {
467 $Xv2 = $X2;
468 $Yv2 = $Y2;
469 } else {
470 $Angle1 = $this->getAngle($X2, $Y2, $Coordinates[$i + 1][0], $Coordinates[$i + 1][1]);
471 $Angle2 = $this->getAngle($X1, $Y1, $X2, $Y2);
472 $XOff = cos(($Angle2 + 180) * PI / 180) * $Force + $X2;
473 $YOff = sin(($Angle2 + 180) * PI / 180) * $Force + $Y2;
474 $Xv2 = cos(($Angle1 + 180) * PI / 180) * $Force + $XOff;
475 $Yv2 = sin(($Angle1 + 180) * PI / 180) * $Force + $YOff;
476 }
477 $Path = $this->drawBezier($X1, $Y1, $X2, $Y2, $Xv1, $Yv1, $Xv2, $Yv2, $Format);
478 if ($PathOnly) {
479 $Result[] = $Path;
480 }
481 $XLast = $X1;
482 $YLast = $Y1;
483 }
484 return $Result;
485 }
486 /**
487 * Draw a bezier curve with two controls points
488 * @param int $X1
489 * @param int $Y1
490 * @param int $X2
491 * @param int $Y2
492 * @param int $Xv1
493 * @param int $Yv1
494 * @param int $Xv2
495 * @param int $Yv2
496 * @param array $Format
497 * @return array
498 */
499 public function drawBezier($X1, $Y1, $X2, $Y2, $Xv1, $Yv1, $Xv2, $Yv2, array $Format = [])
500 {
501 $R = isset($Format["R"]) ? $Format["R"] : 0;
502 $G = isset($Format["G"]) ? $Format["G"] : 0;
503 $B = isset($Format["B"]) ? $Format["B"] : 0;
504 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
505 $ShowC = isset($Format["ShowControl"]) ? $Format["ShowControl"] : \false;
506 $Segments = isset($Format["Segments"]) ? $Format["Segments"] : null;
507 $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
508 $NoDraw = isset($Format["NoDraw"]) ? $Format["NoDraw"] : \false;
509 $PathOnly = isset($Format["PathOnly"]) ? $Format["PathOnly"] : \false;
510 $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null;
511 $DrawArrow = isset($Format["DrawArrow"]) ? $Format["DrawArrow"] : \false;
512 $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 10;
513 $ArrowRatio = isset($Format["ArrowRatio"]) ? $Format["ArrowRatio"] : 0.5;
514 $ArrowTwoHeads = isset($Format["ArrowTwoHeads"]) ? $Format["ArrowTwoHeads"] : \false;
515 if ($Segments == null) {
516 $Length = $this->getLength($X1, $Y1, $X2, $Y2);
517 $Precision = $Length * 125 / 1000;
518 } else {
519 $Precision = $Segments;
520 }
521 $P[0]["X"] = $X1;
522 $P[0]["Y"] = $Y1;
523 $P[1]["X"] = $Xv1;
524 $P[1]["Y"] = $Yv1;
525 $P[2]["X"] = $Xv2;
526 $P[2]["Y"] = $Yv2;
527 $P[3]["X"] = $X2;
528 $P[3]["Y"] = $Y2;
529 /* Compute the bezier points */
530 $Q = [];
531 $ID = 0;
532 for ($i = 0; $i <= $Precision; $i = $i + 1) {
533 $u = $i / $Precision;
534 $C = [];
535 $C[0] = (1 - $u) * (1 - $u) * (1 - $u);
536 $C[1] = $u * 3 * (1 - $u) * (1 - $u);
537 $C[2] = 3 * $u * $u * (1 - $u);
538 $C[3] = $u * $u * $u;
539 for ($j = 0; $j <= 3; $j++) {
540 if (!isset($Q[$ID])) {
541 $Q[$ID] = [];
542 }
543 if (!isset($Q[$ID]["X"])) {
544 $Q[$ID]["X"] = 0;
545 }
546 if (!isset($Q[$ID]["Y"])) {
547 $Q[$ID]["Y"] = 0;
548 }
549 $Q[$ID]["X"] = $Q[$ID]["X"] + $P[$j]["X"] * $C[$j];
550 $Q[$ID]["Y"] = $Q[$ID]["Y"] + $P[$j]["Y"] * $C[$j];
551 }
552 $ID++;
553 }
554 $Q[$ID]["X"] = $X2;
555 $Q[$ID]["Y"] = $Y2;
556 if (!$NoDraw) {
557 /* Display the control points */
558 if ($ShowC && !$PathOnly) {
559 $Xv1 = floor($Xv1);
560 $Yv1 = floor($Yv1);
561 $Xv2 = floor($Xv2);
562 $Yv2 = floor($Yv2);
563 $this->drawLine($X1, $Y1, $X2, $Y2, ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 30]);
564 $MyMarkerSettings = ["R" => 255, "G" => 0, "B" => 0, "BorderR" => 255, "BorderB" => 255, "BorderG" => 255, "Size" => 4];
565 $this->drawRectangleMarker($Xv1, $Yv1, $MyMarkerSettings);
566 $this->drawText($Xv1 + 4, $Yv1, "v1");
567 $MyMarkerSettings = ["R" => 0, "G" => 0, "B" => 255, "BorderR" => 255, "BorderB" => 255, "BorderG" => 255, "Size" => 4];
568 $this->drawRectangleMarker($Xv2, $Yv2, $MyMarkerSettings);
569 $this->drawText($Xv2 + 4, $Yv2, "v2");
570 }
571 /* Draw the bezier */
572 $LastX = null;
573 $LastY = null;
574 $Cpt = null;
575 $Mode = null;
576 $ArrowS = [];
577 foreach ($Q as $Point) {
578 $X = $Point["X"];
579 $Y = $Point["Y"];
580 /* Get the first segment */
581 if (!count($ArrowS) && $LastX != null && $LastY != null) {
582 $ArrowS["X2"] = $LastX;
583 $ArrowS["Y2"] = $LastY;
584 $ArrowS["X1"] = $X;
585 $ArrowS["Y1"] = $Y;
586 }
587 if ($LastX != null && $LastY != null && !$PathOnly) {
588 list($Cpt, $Mode) = $this->drawLine($LastX, $LastY, $X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Cpt" => $Cpt, "Mode" => $Mode, "Weight" => $Weight]);
589 }
590 /* Get the last segment */
591 $ArrowE["X1"] = $LastX;
592 $ArrowE["Y1"] = $LastY;
593 $ArrowE["X2"] = $X;
594 $ArrowE["Y2"] = $Y;
595 $LastX = $X;
596 $LastY = $Y;
597 }
598 if ($DrawArrow && !$PathOnly) {
599 $ArrowSettings = ["FillR" => $R, "FillG" => $G, "FillB" => $B, "Alpha" => $Alpha, "Size" => $ArrowSize, "Ratio" => $ArrowRatio];
600 if ($ArrowTwoHeads) {
601 $this->drawArrow($ArrowS["X1"], $ArrowS["Y1"], $ArrowS["X2"], $ArrowS["Y2"], $ArrowSettings);
602 }
603 $this->drawArrow($ArrowE["X1"], $ArrowE["Y1"], $ArrowE["X2"], $ArrowE["Y2"], $ArrowSettings);
604 }
605 }
606 return $Q;
607 }
608 /**
609 * Draw a line between two points
610 * @param int|float $X1
611 * @param int|float $Y1
612 * @param int|float $X2
613 * @param int|float $Y2
614 * @param array $Format
615 * @return array|int
616 */
617 public function drawLine($X1, $Y1, $X2, $Y2, array $Format = [])
618 {
619 $R = isset($Format["R"]) ? $Format["R"] : 0;
620 $G = isset($Format["G"]) ? $Format["G"] : 0;
621 $B = isset($Format["B"]) ? $Format["B"] : 0;
622 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
623 $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
624 $Cpt = isset($Format["Cpt"]) ? $Format["Cpt"] : 1;
625 $Mode = isset($Format["Mode"]) ? $Format["Mode"] : 1;
626 $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null;
627 $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : null;
628 if ($this->Antialias == \false && $Ticks == null) {
629 if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
630 $ShadowColor = $this->allocateColor($this->Picture, $this->ShadowR, $this->ShadowG, $this->ShadowB, $this->Shadowa);
631 imageline($this->Picture, intval($X1 + $this->ShadowX), intval($Y1 + $this->ShadowY), intval($X2 + $this->ShadowX), intval($Y2 + $this->ShadowY), $ShadowColor);
632 }
633 $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
634 imageline($this->Picture, (int) $X1, (int) $Y1, (int) $X2, (int) $Y2, $Color);
635 return 0;
636 }
637 $Distance = sqrt(($X2 - $X1) * ($X2 - $X1) + ($Y2 - $Y1) * ($Y2 - $Y1));
638 if ($Distance == 0) {
639 return -1;
640 }
641 /* Derivative algorithm for overweighted lines, re-route to polygons primitives */
642 if ($Weight != null) {
643 $Angle = $this->getAngle($X1, $Y1, $X2, $Y2);
644 $PolySettings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "BorderAlpha" => $Alpha];
645 if ($Ticks == null) {
646 $Points = [];
647 $Points[] = cos(deg2rad($Angle - 90)) * $Weight + $X1;
648 $Points[] = sin(deg2rad($Angle - 90)) * $Weight + $Y1;
649 $Points[] = cos(deg2rad($Angle + 90)) * $Weight + $X1;
650 $Points[] = sin(deg2rad($Angle + 90)) * $Weight + $Y1;
651 $Points[] = cos(deg2rad($Angle + 90)) * $Weight + $X2;
652 $Points[] = sin(deg2rad($Angle + 90)) * $Weight + $Y2;
653 $Points[] = cos(deg2rad($Angle - 90)) * $Weight + $X2;
654 $Points[] = sin(deg2rad($Angle - 90)) * $Weight + $Y2;
655 $this->drawPolygon($Points, $PolySettings);
656 } else {
657 for ($i = 0; $i <= $Distance; $i = $i + $Ticks * 2) {
658 $Xa = ($X2 - $X1) / $Distance * $i + $X1;
659 $Ya = ($Y2 - $Y1) / $Distance * $i + $Y1;
660 $Xb = ($X2 - $X1) / $Distance * ($i + $Ticks) + $X1;
661 $Yb = ($Y2 - $Y1) / $Distance * ($i + $Ticks) + $Y1;
662 $Points = [];
663 $Points[] = cos(deg2rad($Angle - 90)) * $Weight + $Xa;
664 $Points[] = sin(deg2rad($Angle - 90)) * $Weight + $Ya;
665 $Points[] = cos(deg2rad($Angle + 90)) * $Weight + $Xa;
666 $Points[] = sin(deg2rad($Angle + 90)) * $Weight + $Ya;
667 $Points[] = cos(deg2rad($Angle + 90)) * $Weight + $Xb;
668 $Points[] = sin(deg2rad($Angle + 90)) * $Weight + $Yb;
669 $Points[] = cos(deg2rad($Angle - 90)) * $Weight + $Xb;
670 $Points[] = sin(deg2rad($Angle - 90)) * $Weight + $Yb;
671 $this->drawPolygon($Points, $PolySettings);
672 }
673 }
674 return 1;
675 }
676 $XStep = ($X2 - $X1) / $Distance;
677 $YStep = ($Y2 - $Y1) / $Distance;
678 for ($i = 0; $i <= $Distance; $i++) {
679 $X = $i * $XStep + $X1;
680 $Y = $i * $YStep + $Y1;
681 $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha];
682 if ($Threshold != null) {
683 foreach ($Threshold as $Key => $Parameters) {
684 if ($Y <= $Parameters["MinX"] && $Y >= $Parameters["MaxX"]) {
685 if (isset($Parameters["R"])) {
686 $RT = $Parameters["R"];
687 } else {
688 $RT = 0;
689 }
690 if (isset($Parameters["G"])) {
691 $GT = $Parameters["G"];
692 } else {
693 $GT = 0;
694 }
695 if (isset($Parameters["B"])) {
696 $BT = $Parameters["B"];
697 } else {
698 $BT = 0;
699 }
700 if (isset($Parameters["Alpha"])) {
701 $AlphaT = $Parameters["Alpha"];
702 } else {
703 $AlphaT = 0;
704 }
705 $Color = ["R" => $RT, "G" => $GT, "B" => $BT, "Alpha" => $AlphaT];
706 }
707 }
708 }
709 if ($Ticks != null) {
710 if ($Cpt % $Ticks == 0) {
711 $Cpt = 0;
712 if ($Mode == 1) {
713 $Mode = 0;
714 } else {
715 $Mode = 1;
716 }
717 }
718 if ($Mode == 1) {
719 $this->drawAntialiasPixel($X, $Y, $Color);
720 }
721 $Cpt++;
722 } else {
723 $this->drawAntialiasPixel($X, $Y, $Color);
724 }
725 }
726 return [$Cpt, $Mode];
727 }
728 /**
729 * Draw a circle
730 * @param int $Xc
731 * @param int $Yc
732 * @param int|float $Height
733 * @param int|float $Width
734 * @param array $Format
735 */
736 public function drawCircle($Xc, $Yc, $Height, $Width, array $Format = [])
737 {
738 $R = isset($Format["R"]) ? $Format["R"] : 0;
739 $G = isset($Format["G"]) ? $Format["G"] : 0;
740 $B = isset($Format["B"]) ? $Format["B"] : 0;
741 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
742 $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
743 $Height = abs($Height);
744 $Width = abs($Width);
745 if ($Height == 0) {
746 $Height = 1;
747 }
748 if ($Width == 0) {
749 $Width = 1;
750 }
751 $Xc = floor($Xc);
752 $Yc = floor($Yc);
753 $RestoreShadow = $this->Shadow;
754 if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
755 $this->Shadow = \false;
756 $this->drawCircle($Xc + $this->ShadowX, $Yc + $this->ShadowY, $Height, $Width, ["R" => $this->ShadowR, "G" => $this->ShadowG, "B" => $this->ShadowB, "Alpha" => $this->Shadowa, "Ticks" => $Ticks]);
757 }
758 if ($Width == 0) {
759 $Width = $Height;
760 }
761 if ($R < 0) {
762 $R = 0;
763 }
764 if ($R > 255) {
765 $R = 255;
766 }
767 if ($G < 0) {
768 $G = 0;
769 }
770 if ($G > 255) {
771 $G = 255;
772 }
773 if ($B < 0) {
774 $B = 0;
775 }
776 if ($B > 255) {
777 $B = 255;
778 }
779 $Step = 360 / (2 * PI * max($Width, $Height));
780 $Mode = 1;
781 $Cpt = 1;
782 for ($i = 0; $i <= 360; $i = $i + $Step) {
783 $X = cos($i * PI / 180) * $Height + $Xc;
784 $Y = sin($i * PI / 180) * $Width + $Yc;
785 if ($Ticks != null) {
786 if ($Cpt % $Ticks == 0) {
787 $Cpt = 0;
788 if ($Mode == 1) {
789 $Mode = 0;
790 } else {
791 $Mode = 1;
792 }
793 }
794 if ($Mode == 1) {
795 $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
796 }
797 $Cpt++;
798 } else {
799 $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
800 }
801 }
802 $this->Shadow = $RestoreShadow;
803 }
804 /**
805 * Draw a filled circle
806 * @param int $X
807 * @param int $Y
808 * @param int|float $Radius
809 * @param array $Format
810 */
811 public function drawFilledCircle($X, $Y, $Radius, array $Format = [])
812 {
813 $R = isset($Format["R"]) ? $Format["R"] : 0;
814 $G = isset($Format["G"]) ? $Format["G"] : 0;
815 $B = isset($Format["B"]) ? $Format["B"] : 0;
816 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
817 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
818 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
819 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
820 $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha;
821 $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
822 $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
823 if ($Radius == 0) {
824 $Radius = 1;
825 }
826 if ($Surrounding != null) {
827 $BorderR = $R + $Surrounding;
828 $BorderG = $G + $Surrounding;
829 $BorderB = $B + $Surrounding;
830 }
831 $X = (int) floor($X);
832 $Y = (int) floor($Y);
833 $Radius = abs($Radius);
834 $RestoreShadow = $this->Shadow;
835 if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
836 $this->Shadow = \false;
837 $this->drawFilledCircle($X + $this->ShadowX, $Y + $this->ShadowY, $Radius, ["R" => $this->ShadowR, "G" => $this->ShadowG, "B" => $this->ShadowB, "Alpha" => $this->Shadowa, "Ticks" => $Ticks]);
838 }
839 $this->Mask = [];
840 $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
841 for ($i = 0; $i <= $Radius * 2; $i++) {
842 $Slice = sqrt($Radius * $Radius - ($Radius - $i) * ($Radius - $i));
843 $XPos = (int) floor($Slice);
844 $YPos = (int) ($Y + $i - $Radius);
845 $this->Mask[$X - $XPos][$YPos] = \true;
846 $this->Mask[$X + $XPos][$YPos] = \true;
847 imageline($this->Picture, $X - $XPos, $YPos, $X + $XPos, $YPos, $Color);
848 }
849 if ($this->Antialias) {
850 $this->drawCircle($X, $Y, $Radius, $Radius, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
851 }
852 $this->Mask = [];
853 if ($BorderR != -1) {
854 $this->drawCircle($X, $Y, $Radius, $Radius, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha, "Ticks" => $Ticks]);
855 }
856 $this->Shadow = $RestoreShadow;
857 }
858 /**
859 * Write text
860 * @param int|float $X
861 * @param int|float $Y
862 * @param string $Text
863 * @param array $Format
864 * @return array
865 */
866 public function drawText($X, $Y, $Text, array $Format = [])
867 {
868 $R = isset($Format["R"]) ? $Format["R"] : $this->FontColorR;
869 $G = isset($Format["G"]) ? $Format["G"] : $this->FontColorG;
870 $B = isset($Format["B"]) ? $Format["B"] : $this->FontColorB;
871 $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0;
872 $Align = isset($Format["Align"]) ? $Format["Align"] : TEXT_ALIGN_BOTTOMLEFT;
873 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->FontColorA;
874 $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName;
875 $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
876 $ShowOrigine = isset($Format["ShowOrigine"]) ? $Format["ShowOrigine"] : \false;
877 $TOffset = isset($Format["TOffset"]) ? $Format["TOffset"] : 2;
878 $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : \false;
879 $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 6;
880 $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : \false;
881 $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 6;
882 $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 255;
883 $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 255;
884 $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 255;
885 $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 50;
886 $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : "";
887 $BoxBorderR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0;
888 $BoxBorderG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0;
889 $BoxBorderB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0;
890 $BoxBorderAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 50;
891 $NoShadow = isset($Format["NoShadow"]) ? $Format["NoShadow"] : \false;
892 $Shadow = $this->Shadow;
893 if ($NoShadow) {
894 $this->Shadow = \false;
895 }
896 if ($BoxSurrounding != "") {
897 $BoxBorderR = $BoxR - $BoxSurrounding;
898 $BoxBorderG = $BoxG - $BoxSurrounding;
899 $BoxBorderB = $BoxB - $BoxSurrounding;
900 $BoxBorderAlpha = $BoxAlpha;
901 }
902 if ($ShowOrigine) {
903 $MyMarkerSettings = ["R" => 255, "G" => 0, "B" => 0, "BorderR" => 255, "BorderB" => 255, "BorderG" => 255, "Size" => 4];
904 $this->drawRectangleMarker($X, $Y, $MyMarkerSettings);
905 }
906 $TxtPos = $this->getTextBox($X, $Y, $FontName, $FontSize, $Angle, $Text);
907 if ($DrawBox && ($Angle == 0 || $Angle == 90 || $Angle == 180 || $Angle == 270)) {
908 $T[0]["X"] = 0;
909 $T[0]["Y"] = 0;
910 $T[1]["X"] = 0;
911 $T[1]["Y"] = 0;
912 $T[2]["X"] = 0;
913 $T[2]["Y"] = 0;
914 $T[3]["X"] = 0;
915 $T[3]["Y"] = 0;
916 if ($Angle == 0) {
917 $T[0]["X"] = -$TOffset;
918 $T[0]["Y"] = $TOffset;
919 $T[1]["X"] = $TOffset;
920 $T[1]["Y"] = $TOffset;
921 $T[2]["X"] = $TOffset;
922 $T[2]["Y"] = -$TOffset;
923 $T[3]["X"] = -$TOffset;
924 $T[3]["Y"] = -$TOffset;
925 }
926 $X1 = min($TxtPos[0]["X"], $TxtPos[1]["X"], $TxtPos[2]["X"], $TxtPos[3]["X"]) - $BorderOffset + 3;
927 $Y1 = min($TxtPos[0]["Y"], $TxtPos[1]["Y"], $TxtPos[2]["Y"], $TxtPos[3]["Y"]) - $BorderOffset;
928 $X2 = max($TxtPos[0]["X"], $TxtPos[1]["X"], $TxtPos[2]["X"], $TxtPos[3]["X"]) + $BorderOffset + 3;
929 $Y2 = max($TxtPos[0]["Y"], $TxtPos[1]["Y"], $TxtPos[2]["Y"], $TxtPos[3]["Y"]) + $BorderOffset - 3;
930 $X1 = $X1 - $TxtPos[$Align]["X"] + $X + $T[0]["X"];
931 $Y1 = $Y1 - $TxtPos[$Align]["Y"] + $Y + $T[0]["Y"];
932 $X2 = $X2 - $TxtPos[$Align]["X"] + $X + $T[0]["X"];
933 $Y2 = $Y2 - $TxtPos[$Align]["Y"] + $Y + $T[0]["Y"];
934 $Settings = ["R" => $BoxR, "G" => $BoxG, "B" => $BoxB, "Alpha" => $BoxAlpha, "BorderR" => $BoxBorderR, "BorderG" => $BoxBorderG, "BorderB" => $BoxBorderB, "BorderAlpha" => $BoxBorderAlpha];
935 if ($BoxRounded) {
936 $this->drawRoundedFilledRectangle($X1, $Y1, $X2, $Y2, $RoundedRadius, $Settings);
937 } else {
938 $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, $Settings);
939 }
940 }
941 $X = (int) ($X - $TxtPos[$Align]["X"] + $X);
942 $Y = (int) ($Y - $TxtPos[$Align]["Y"] + $Y);
943 if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
944 $C_ShadowColor = $this->allocateColor($this->Picture, $this->ShadowR, $this->ShadowG, $this->ShadowB, $this->Shadowa);
945 imagettftext($this->Picture, $FontSize, $Angle, (int) ($X + $this->ShadowX), (int) ($Y + $this->ShadowY), (int) $C_ShadowColor, $FontName, $Text);
946 }
947 $C_TextColor = $this->AllocateColor($this->Picture, $R, $G, $B, $Alpha);
948 imagettftext($this->Picture, $FontSize, $Angle, $X, $Y, $C_TextColor, $FontName, $Text);
949 $this->Shadow = $Shadow;
950 return $TxtPos;
951 }
952 /**
953 * Draw a gradient within a defined area
954 * @param int $X1
955 * @param int $Y1
956 * @param int $X2
957 * @param int $Y2
958 * @param int $Direction
959 * @param array $Format
960 * @return null|integer
961 */
962 public function drawGradientArea($X1, $Y1, $X2, $Y2, $Direction, array $Format = [])
963 {
964 $StartR = isset($Format["StartR"]) ? $Format["StartR"] : 90;
965 $StartG = isset($Format["StartG"]) ? $Format["StartG"] : 90;
966 $StartB = isset($Format["StartB"]) ? $Format["StartB"] : 90;
967 $EndR = isset($Format["EndR"]) ? $Format["EndR"] : 0;
968 $EndG = isset($Format["EndG"]) ? $Format["EndG"] : 0;
969 $EndB = isset($Format["EndB"]) ? $Format["EndB"] : 0;
970 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
971 $Levels = isset($Format["Levels"]) ? $Format["Levels"] : null;
972 $Shadow = $this->Shadow;
973 $this->Shadow = \false;
974 if ($StartR == $EndR && $StartG == $EndG && $StartB == $EndB) {
975 $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, ["R" => $StartR, "G" => $StartG, "B" => $StartB, "Alpha" => $Alpha]);
976 return 0;
977 }
978 if ($Levels != null) {
979 $EndR = $StartR + $Levels;
980 $EndG = $StartG + $Levels;
981 $EndB = $StartB + $Levels;
982 }
983 if ($X1 > $X2) {
984 list($X1, $X2) = [$X2, $X1];
985 }
986 if ($Y1 > $Y2) {
987 list($Y1, $Y2) = [$Y2, $Y1];
988 }
989 if ($Direction == DIRECTION_VERTICAL) {
990 $Width = abs($Y2 - $Y1);
991 }
992 if ($Direction == DIRECTION_HORIZONTAL) {
993 $Width = abs($X2 - $X1);
994 }
995 $Step = max(abs($EndR - $StartR), abs($EndG - $StartG), abs($EndB - $StartB));
996 $StepSize = $Width / $Step;
997 $RStep = ($EndR - $StartR) / $Step;
998 $GStep = ($EndG - $StartG) / $Step;
999 $BStep = ($EndB - $StartB) / $Step;
1000 $R = $StartR;
1001 $G = $StartG;
1002 $B = $StartB;
1003 switch ($Direction) {
1004 case DIRECTION_VERTICAL:
1005 $StartY = $Y1;
1006 $EndY = floor($Y2) + 1;
1007 $LastY2 = $StartY;
1008 for ($i = 0; $i <= $Step; $i++) {
1009 $Y2 = floor($StartY + $i * $StepSize);
1010 if ($Y2 > $EndY) {
1011 $Y2 = $EndY;
1012 }
1013 if ($Y1 != $Y2 && $Y1 < $Y2 || $Y2 == $EndY) {
1014 $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha];
1015 $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, $Color);
1016 $LastY2 = max($LastY2, $Y2);
1017 $Y1 = $Y2 + 1;
1018 }
1019 $R = $R + $RStep;
1020 $G = $G + $GStep;
1021 $B = $B + $BStep;
1022 }
1023 if ($LastY2 < $EndY && isset($Color)) {
1024 for ($i = $LastY2 + 1; $i <= $EndY; $i++) {
1025 $this->drawLine($X1, $i, $X2, $i, $Color);
1026 }
1027 }
1028 break;
1029 case DIRECTION_HORIZONTAL:
1030 $StartX = $X1;
1031 $EndX = $X2;
1032 for ($i = 0; $i <= $Step; $i++) {
1033 $X2 = floor($StartX + $i * $StepSize);
1034 if ($X2 > $EndX) {
1035 $X2 = $EndX;
1036 }
1037 if ($X1 != $X2 && $X1 < $X2 || $X2 == $EndX) {
1038 $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha];
1039 $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, $Color);
1040 $X1 = $X2 + 1;
1041 }
1042 $R = $R + $RStep;
1043 $G = $G + $GStep;
1044 $B = $B + $BStep;
1045 }
1046 if ($X2 < $EndX && isset($Color)) {
1047 $this->drawFilledRectangle($X2, $Y1, $EndX, $Y2, $Color);
1048 }
1049 break;
1050 }
1051 $this->Shadow = $Shadow;
1052 }
1053 /**
1054 * Draw an aliased pixel
1055 * @param int $X
1056 * @param int $Y
1057 * @param array $Format
1058 * @return int|null
1059 */
1060 public function drawAntialiasPixel($X, $Y, array $Format = [])
1061 {
1062 $R = isset($Format["R"]) ? $Format["R"] : 0;
1063 $G = isset($Format["G"]) ? $Format["G"] : 0;
1064 $B = isset($Format["B"]) ? $Format["B"] : 0;
1065 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
1066 if ($X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize) {
1067 return -1;
1068 }
1069 if ($R < 0) {
1070 $R = 0;
1071 }
1072 if ($R > 255) {
1073 $R = 255;
1074 }
1075 if ($G < 0) {
1076 $G = 0;
1077 }
1078 if ($G > 255) {
1079 $G = 255;
1080 }
1081 if ($B < 0) {
1082 $B = 0;
1083 }
1084 if ($B > 255) {
1085 $B = 255;
1086 }
1087 if (!$this->Antialias) {
1088 if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
1089 $ShadowColor = $this->allocateColor($this->Picture, $this->ShadowR, $this->ShadowG, $this->ShadowB, $this->Shadowa);
1090 imagesetpixel($this->Picture, intval($X + $this->ShadowX), intval($Y + $this->ShadowY), $ShadowColor);
1091 }
1092 $PlotColor = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
1093 imagesetpixel($this->Picture, (int) $X, (int) $Y, $PlotColor);
1094 return 0;
1095 }
1096 $Xi = floor($X);
1097 $Yi = floor($Y);
1098 if ($Xi == $X && $Yi == $Y) {
1099 if ($Alpha == 100) {
1100 $this->drawAlphaPixel($X, $Y, 100, $R, $G, $B);
1101 } else {
1102 $this->drawAlphaPixel($X, $Y, $Alpha, $R, $G, $B);
1103 }
1104 } else {
1105 $Alpha1 = (1 - ($X - floor($X))) * (1 - ($Y - floor($Y))) * 100 / 100 * $Alpha;
1106 if ($Alpha1 > $this->AntialiasQuality) {
1107 $this->drawAlphaPixel($Xi, $Yi, $Alpha1, $R, $G, $B);
1108 }
1109 $Alpha2 = ($X - floor($X)) * (1 - ($Y - floor($Y))) * 100 / 100 * $Alpha;
1110 if ($Alpha2 > $this->AntialiasQuality) {
1111 $this->drawAlphaPixel($Xi + 1, $Yi, $Alpha2, $R, $G, $B);
1112 }
1113 $Alpha3 = (1 - ($X - floor($X))) * ($Y - floor($Y)) * 100 / 100 * $Alpha;
1114 if ($Alpha3 > $this->AntialiasQuality) {
1115 $this->drawAlphaPixel($Xi, $Yi + 1, $Alpha3, $R, $G, $B);
1116 }
1117 $Alpha4 = ($X - floor($X)) * ($Y - floor($Y)) * 100 / 100 * $Alpha;
1118 if ($Alpha4 > $this->AntialiasQuality) {
1119 $this->drawAlphaPixel($Xi + 1, $Yi + 1, $Alpha4, $R, $G, $B);
1120 }
1121 }
1122 }
1123 /**
1124 * Draw a semi-transparent pixel
1125 * @param int $X
1126 * @param int $Y
1127 * @param int $Alpha
1128 * @param int $R
1129 * @param int $G
1130 * @param int $B
1131 * @return null|integer
1132 */
1133 public function drawAlphaPixel($X, $Y, $Alpha, $R, $G, $B)
1134 {
1135 if (isset($this->Mask[$X]) && isset($this->Mask[$X][$Y])) {
1136 return 0;
1137 }
1138 if ($X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize) {
1139 return -1;
1140 }
1141 if ($R < 0) {
1142 $R = 0;
1143 }
1144 if ($R > 255) {
1145 $R = 255;
1146 }
1147 if ($G < 0) {
1148 $G = 0;
1149 }
1150 if ($G > 255) {
1151 $G = 255;
1152 }
1153 if ($B < 0) {
1154 $B = 0;
1155 }
1156 if ($B > 255) {
1157 $B = 255;
1158 }
1159 if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
1160 $AlphaFactor = floor($Alpha / 100 * $this->Shadowa);
1161 $ShadowColor = $this->allocateColor($this->Picture, $this->ShadowR, $this->ShadowG, $this->ShadowB, $AlphaFactor);
1162 imagesetpixel($this->Picture, (int) ($X + $this->ShadowX), (int) ($Y + $this->ShadowY), $ShadowColor);
1163 }
1164 $C_Aliased = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
1165 imagesetpixel($this->Picture, (int) $X, (int) $Y, $C_Aliased);
1166 }
1167 /**
1168 * Load a PNG file and draw it over the chart
1169 * @param int $X
1170 * @param int $Y
1171 * @param string $FileName
1172 */
1173 public function drawFromPNG($X, $Y, $FileName)
1174 {
1175 $this->drawFromPicture(1, $FileName, $X, $Y);
1176 }
1177 /**
1178 * Load a GIF file and draw it over the chart
1179 * @param int $X
1180 * @param int $Y
1181 * @param string $FileName
1182 */
1183 public function drawFromGIF($X, $Y, $FileName)
1184 {
1185 $this->drawFromPicture(2, $FileName, $X, $Y);
1186 }
1187 /**
1188 * Load a JPEG file and draw it over the chart
1189 * @param int $X
1190 * @param int $Y
1191 * @param string $FileName
1192 */
1193 public function drawFromJPG($X, $Y, $FileName)
1194 {
1195 $this->drawFromPicture(3, $FileName, $X, $Y);
1196 }
1197 /**
1198 * Generic loader public function for external pictures
1199 * @param int $PicType
1200 * @param string $FileName
1201 * @param int $X
1202 * @param int $Y
1203 * @return null|integer
1204 */
1205 public function drawFromPicture($PicType, $FileName, $X, $Y)
1206 {
1207 $X = (int) $X;
1208 $Y = (int) $Y;
1209 if (file_exists($FileName)) {
1210 list($Width, $Height) = $this->getPicInfo($FileName);
1211 if ($PicType == 1) {
1212 $Raster = imagecreatefrompng($FileName);
1213 } elseif ($PicType == 2) {
1214 $Raster = imagecreatefromgif($FileName);
1215 } elseif ($PicType == 3) {
1216 $Raster = imagecreatefromjpeg($FileName);
1217 } else {
1218 return 0;
1219 }
1220 $RestoreShadow = $this->Shadow;
1221 if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
1222 $this->Shadow = \false;
1223 if ($PicType == 3) {
1224 $this->drawFilledRectangle($X + $this->ShadowX, $Y + $this->ShadowY, $X + $Width + $this->ShadowX, $Y + $Height + $this->ShadowY, ["R" => $this->ShadowR, "G" => $this->ShadowG, "B" => $this->ShadowB, "Alpha" => $this->Shadowa]);
1225 } else {
1226 $TranparentID = imagecolortransparent($Raster);
1227 for ($Xc = 0; $Xc <= $Width - 1; $Xc++) {
1228 for ($Yc = 0; $Yc <= $Height - 1; $Yc++) {
1229 $RGBa = imagecolorat($Raster, $Xc, $Yc);
1230 $Values = imagecolorsforindex($Raster, $RGBa);
1231 if ($Values["alpha"] < 120) {
1232 $AlphaFactor = floor($this->Shadowa / 100 * (100 / 127 * (127 - $Values["alpha"])));
1233 $this->drawAlphaPixel($X + $Xc + $this->ShadowX, $Y + $Yc + $this->ShadowY, $AlphaFactor, $this->ShadowR, $this->ShadowG, $this->ShadowB);
1234 }
1235 }
1236 }
1237 }
1238 }
1239 $this->Shadow = $RestoreShadow;
1240 imagecopy($this->Picture, $Raster, $X, $Y, 0, 0, $Width, $Height);
1241 imagedestroy($Raster);
1242 }
1243 }
1244 /**
1245 * Draw an arrow
1246 * @param int $X1
1247 * @param int $Y1
1248 * @param int $X2
1249 * @param int $Y2
1250 * @param array $Format
1251 */
1252 public function drawArrow($X1, $Y1, $X2, $Y2, array $Format = [])
1253 {
1254 $FillR = isset($Format["FillR"]) ? $Format["FillR"] : 0;
1255 $FillG = isset($Format["FillG"]) ? $Format["FillG"] : 0;
1256 $FillB = isset($Format["FillB"]) ? $Format["FillB"] : 0;
1257 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $FillR;
1258 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $FillG;
1259 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $FillB;
1260 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
1261 $Size = isset($Format["Size"]) ? $Format["Size"] : 10;
1262 $Ratio = isset($Format["Ratio"]) ? $Format["Ratio"] : 0.5;
1263 $TwoHeads = isset($Format["TwoHeads"]) ? $Format["TwoHeads"] : \false;
1264 $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : \false;
1265 /* Calculate the line angle */
1266 $Angle = $this->getAngle($X1, $Y1, $X2, $Y2);
1267 /* Override Shadow support, this will be managed internally */
1268 $RestoreShadow = $this->Shadow;
1269 if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
1270 $this->Shadow = \false;
1271 $this->drawArrow($X1 + $this->ShadowX, $Y1 + $this->ShadowY, $X2 + $this->ShadowX, $Y2 + $this->ShadowY, ["FillR" => $this->ShadowR, "FillG" => $this->ShadowG, "FillB" => $this->ShadowB, "Alpha" => $this->Shadowa, "Size" => $Size, "Ratio" => $Ratio, "TwoHeads" => $TwoHeads, "Ticks" => $Ticks]);
1272 }
1273 /* Draw the 1st Head */
1274 $TailX = cos(($Angle - 180) * PI / 180) * $Size + $X2;
1275 $TailY = sin(($Angle - 180) * PI / 180) * $Size + $Y2;
1276 $Points = [];
1277 $Points[] = $X2;
1278 $Points[] = $Y2;
1279 $Points[] = cos(($Angle - 90) * PI / 180) * $Size * $Ratio + $TailX;
1280 $Points[] = sin(($Angle - 90) * PI / 180) * $Size * $Ratio + $TailY;
1281 $Points[] = cos(($Angle - 270) * PI / 180) * $Size * $Ratio + $TailX;
1282 $Points[] = sin(($Angle - 270) * PI / 180) * $Size * $Ratio + $TailY;
1283 $Points[] = $X2;
1284 $Points[] = $Y2;
1285 /* Visual correction */
1286 if ($Angle == 180 || $Angle == 360) {
1287 $Points[4] = $Points[2];
1288 }
1289 if ($Angle == 90 || $Angle == 270) {
1290 $Points[5] = $Points[3];
1291 }
1292 $ArrowColor = $this->allocateColor($this->Picture, $FillR, $FillG, $FillB, $Alpha);
1293 $this->imageFilledPolygonWrapper($this->Picture, $Points, 4, $ArrowColor);
1294 $this->drawLine($Points[0], $Points[1], $Points[2], $Points[3], ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]);
1295 $this->drawLine($Points[2], $Points[3], $Points[4], $Points[5], ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]);
1296 $this->drawLine($Points[0], $Points[1], $Points[4], $Points[5], ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]);
1297 /* Draw the second head */
1298 if ($TwoHeads) {
1299 $Angle = $this->getAngle($X2, $Y2, $X1, $Y1);
1300 $TailX2 = cos(($Angle - 180) * PI / 180) * $Size + $X1;
1301 $TailY2 = sin(($Angle - 180) * PI / 180) * $Size + $Y1;
1302 $Points = [];
1303 $Points[] = $X1;
1304 $Points[] = $Y1;
1305 $Points[] = cos(($Angle - 90) * PI / 180) * $Size * $Ratio + $TailX2;
1306 $Points[] = sin(($Angle - 90) * PI / 180) * $Size * $Ratio + $TailY2;
1307 $Points[] = cos(($Angle - 270) * PI / 180) * $Size * $Ratio + $TailX2;
1308 $Points[] = sin(($Angle - 270) * PI / 180) * $Size * $Ratio + $TailY2;
1309 $Points[] = $X1;
1310 $Points[] = $Y1;
1311 /* Visual correction */
1312 if ($Angle == 180 || $Angle == 360) {
1313 $Points[4] = $Points[2];
1314 }
1315 if ($Angle == 90 || $Angle == 270) {
1316 $Points[5] = $Points[3];
1317 }
1318 $ArrowColor = $this->allocateColor($this->Picture, $FillR, $FillG, $FillB, $Alpha);
1319 $this->imageFilledPolygonWrapper($this->Picture, $Points, 4, $ArrowColor);
1320 $this->drawLine($Points[0], $Points[1], $Points[2], $Points[3], ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]);
1321 $this->drawLine($Points[2], $Points[3], $Points[4], $Points[5], ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]);
1322 $this->drawLine($Points[0], $Points[1], $Points[4], $Points[5], ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]);
1323 $this->drawLine($TailX, $TailY, $TailX2, $TailY2, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Ticks" => $Ticks]);
1324 } else {
1325 $this->drawLine($X1, $Y1, $TailX, $TailY, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Ticks" => $Ticks]);
1326 }
1327 /* Re-enable shadows */
1328 $this->Shadow = $RestoreShadow;
1329 }
1330 /**
1331 * Draw a label with associated arrow
1332 * @param int $X1
1333 * @param int $Y1
1334 * @param string $Text
1335 * @param array $Format
1336 */
1337 public function drawArrowLabel($X1, $Y1, $Text, array $Format = [])
1338 {
1339 $FillR = isset($Format["FillR"]) ? $Format["FillR"] : 0;
1340 $FillG = isset($Format["FillG"]) ? $Format["FillG"] : 0;
1341 $FillB = isset($Format["FillB"]) ? $Format["FillB"] : 0;
1342 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $FillR;
1343 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $FillG;
1344 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $FillB;
1345 $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName;
1346 $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
1347 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
1348 $Length = isset($Format["Length"]) ? $Format["Length"] : 50;
1349 $Angle = isset($Format["Angle"]) ? (int) $Format["Angle"] : 315;
1350 $Size = isset($Format["Size"]) ? $Format["Size"] : 10;
1351 $Position = isset($Format["Position"]) ? $Format["Position"] : POSITION_TOP;
1352 $RoundPos = isset($Format["RoundPos"]) ? $Format["RoundPos"] : \false;
1353 $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
1354 $Angle = $Angle % 360;
1355 $X2 = sin(($Angle + 180) * PI / 180) * $Length + $X1;
1356 $Y2 = cos(($Angle + 180) * PI / 180) * $Length + $Y1;
1357 if ($RoundPos && $Angle > 0 && $Angle < 180) {
1358 $Y2 = ceil($Y2);
1359 }
1360 if ($RoundPos && $Angle > 180) {
1361 $Y2 = floor($Y2);
1362 }
1363 $this->drawArrow($X2, $Y2, $X1, $Y1, $Format);
1364 $Size = imagettfbbox($FontSize, 0, $FontName, $Text);
1365 $TxtWidth = max(abs($Size[2] - $Size[0]), abs($Size[0] - $Size[6]));
1366 if ($Angle > 0 && $Angle < 180) {
1367 $this->drawLine($X2, $Y2, $X2 - $TxtWidth, $Y2, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Ticks" => $Ticks]);
1368 if ($Position == POSITION_TOP) {
1369 $this->drawText($X2, $Y2 - 2, $Text, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Align" => TEXT_ALIGN_BOTTOMRIGHT]);
1370 } else {
1371 $this->drawText($X2, $Y2 + 4, $Text, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Align" => TEXT_ALIGN_TOPRIGHT]);
1372 }
1373 } else {
1374 $this->drawLine($X2, $Y2, $X2 + $TxtWidth, $Y2, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Ticks" => $Ticks]);
1375 if ($Position == POSITION_TOP) {
1376 $this->drawText($X2, $Y2 - 2, $Text, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]);
1377 } else {
1378 $this->drawText($X2, $Y2 + 4, $Text, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Align" => TEXT_ALIGN_TOPLEFT]);
1379 }
1380 }
1381 }
1382 /**
1383 * Draw a progress bar filled with specified %
1384 * @param int $X
1385 * @param int $Y
1386 * @param int|float $Percent
1387 * @param array $Format
1388 */
1389 public function drawProgress($X, $Y, $Percent, array $Format = [])
1390 {
1391 if ($Percent > 100) {
1392 $Percent = 100;
1393 }
1394 if ($Percent < 0) {
1395 $Percent = 0;
1396 }
1397 $Width = isset($Format["Width"]) ? $Format["Width"] : 200;
1398 $Height = isset($Format["Height"]) ? $Format["Height"] : 20;
1399 $Orientation = isset($Format["Orientation"]) ? $Format["Orientation"] : ORIENTATION_HORIZONTAL;
1400 $ShowLabel = isset($Format["ShowLabel"]) ? $Format["ShowLabel"] : \false;
1401 $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : LABEL_POS_INSIDE;
1402 $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 10;
1403 $R = isset($Format["R"]) ? $Format["R"] : 130;
1404 $G = isset($Format["G"]) ? $Format["G"] : 130;
1405 $B = isset($Format["B"]) ? $Format["B"] : 130;
1406 $RFade = isset($Format["RFade"]) ? $Format["RFade"] : -1;
1407 $GFade = isset($Format["GFade"]) ? $Format["GFade"] : -1;
1408 $BFade = isset($Format["BFade"]) ? $Format["BFade"] : -1;
1409 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
1410 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
1411 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
1412 $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 0;
1413 $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 0;
1414 $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 0;
1415 $BoxBackR = isset($Format["BoxBackR"]) ? $Format["BoxBackR"] : 255;
1416 $BoxBackG = isset($Format["BoxBackG"]) ? $Format["BoxBackG"] : 255;
1417 $BoxBackB = isset($Format["BoxBackB"]) ? $Format["BoxBackB"] : 255;
1418 $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
1419 $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : null;
1420 $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : \false;
1421 if ($RFade != -1 && $GFade != -1 && $BFade != -1) {
1422 $RFade = ($RFade - $R) / 100 * $Percent + $R;
1423 $GFade = ($GFade - $G) / 100 * $Percent + $G;
1424 $BFade = ($BFade - $B) / 100 * $Percent + $B;
1425 }
1426 if ($Surrounding != null) {
1427 $BorderR = $R + $Surrounding;
1428 $BorderG = $G + $Surrounding;
1429 $BorderB = $B + $Surrounding;
1430 }
1431 if ($BoxSurrounding != null) {
1432 $BoxBorderR = $BoxBackR + $Surrounding;
1433 $BoxBorderG = $BoxBackG + $Surrounding;
1434 $BoxBorderB = $BoxBackB + $Surrounding;
1435 }
1436 if ($Orientation == ORIENTATION_VERTICAL) {
1437 $InnerHeight = ($Height - 2) / 100 * $Percent;
1438 $this->drawFilledRectangle($X, $Y, $X + $Width, $Y - $Height, ["R" => $BoxBackR, "G" => $BoxBackG, "B" => $BoxBackB, "BorderR" => $BoxBorderR, "BorderG" => $BoxBorderG, "BorderB" => $BoxBorderB, "NoAngle" => $NoAngle]);
1439 $RestoreShadow = $this->Shadow;
1440 $this->Shadow = \false;
1441 if ($RFade != -1 && $GFade != -1 && $BFade != -1) {
1442 $GradientOptions = ["StartR" => $RFade, "StartG" => $GFade, "StartB" => $BFade, "EndR" => $R, "EndG" => $G, "EndB" => $B];
1443 $this->drawGradientArea($X + 1, $Y - 1, $X + $Width - 1, $Y - $InnerHeight, DIRECTION_VERTICAL, $GradientOptions);
1444 if ($Surrounding) {
1445 $this->drawRectangle($X + 1, $Y - 1, $X + $Width - 1, $Y - $InnerHeight, ["R" => 255, "G" => 255, "B" => 255, "Alpha" => $Surrounding]);
1446 }
1447 } else {
1448 $this->drawFilledRectangle($X + 1, $Y - 1, $X + $Width - 1, $Y - $InnerHeight, ["R" => $R, "G" => $G, "B" => $B, "BorderR" => $BorderR, "BorderG" => $BorderG, "BorderB" => $BorderB]);
1449 }
1450 $this->Shadow = $RestoreShadow;
1451 if ($ShowLabel && $LabelPos == LABEL_POS_BOTTOM) {
1452 $this->drawText($X + $Width / 2, $Y + $Margin, $Percent . "%", ["Align" => TEXT_ALIGN_TOPMIDDLE]);
1453 }
1454 if ($ShowLabel && $LabelPos == LABEL_POS_TOP) {
1455 $this->drawText($X + $Width / 2, $Y - $Height - $Margin, $Percent . "%", ["Align" => TEXT_ALIGN_BOTTOMMIDDLE]);
1456 }
1457 if ($ShowLabel && $LabelPos == LABEL_POS_INSIDE) {
1458 $this->drawText($X + $Width / 2, $Y - $InnerHeight - $Margin, $Percent . "%", ["Align" => TEXT_ALIGN_MIDDLELEFT, "Angle" => 90]);
1459 }
1460 if ($ShowLabel && $LabelPos == LABEL_POS_CENTER) {
1461 $this->drawText($X + $Width / 2, $Y - $Height / 2, $Percent . "%", ["Align" => TEXT_ALIGN_MIDDLEMIDDLE, "Angle" => 90]);
1462 }
1463 } else {
1464 if ($Percent == 100) {
1465 $InnerWidth = $Width - 1;
1466 } else {
1467 $InnerWidth = ($Width - 2) / 100 * $Percent;
1468 }
1469 $this->drawFilledRectangle($X, $Y, $X + $Width, $Y + $Height, ["R" => $BoxBackR, "G" => $BoxBackG, "B" => $BoxBackB, "BorderR" => $BoxBorderR, "BorderG" => $BoxBorderG, "BorderB" => $BoxBorderB, "NoAngle" => $NoAngle]);
1470 $RestoreShadow = $this->Shadow;
1471 $this->Shadow = \false;
1472 if ($RFade != -1 && $GFade != -1 && $BFade != -1) {
1473 $GradientOptions = ["StartR" => $R, "StartG" => $G, "StartB" => $B, "EndR" => $RFade, "EndG" => $GFade, "EndB" => $BFade];
1474 $this->drawGradientArea($X + 1, $Y + 1, $X + $InnerWidth, $Y + $Height - 1, DIRECTION_HORIZONTAL, $GradientOptions);
1475 if ($Surrounding) {
1476 $this->drawRectangle($X + 1, $Y + 1, $X + $InnerWidth, $Y + $Height - 1, ["R" => 255, "G" => 255, "B" => 255, "Alpha" => $Surrounding]);
1477 }
1478 } else {
1479 $this->drawFilledRectangle($X + 1, $Y + 1, $X + $InnerWidth, $Y + $Height - 1, ["R" => $R, "G" => $G, "B" => $B, "BorderR" => $BorderR, "BorderG" => $BorderG, "BorderB" => $BorderB]);
1480 }
1481 $this->Shadow = $RestoreShadow;
1482 if ($ShowLabel && $LabelPos == LABEL_POS_LEFT) {
1483 $this->drawText($X - $Margin, $Y + $Height / 2, $Percent . "%", ["Align" => TEXT_ALIGN_MIDDLERIGHT]);
1484 }
1485 if ($ShowLabel && $LabelPos == LABEL_POS_RIGHT) {
1486 $this->drawText($X + $Width + $Margin, $Y + $Height / 2, $Percent . "%", ["Align" => TEXT_ALIGN_MIDDLELEFT]);
1487 }
1488 if ($ShowLabel && $LabelPos == LABEL_POS_CENTER) {
1489 $this->drawText($X + $Width / 2, $Y + $Height / 2, $Percent . "%", ["Align" => TEXT_ALIGN_MIDDLEMIDDLE]);
1490 }
1491 if ($ShowLabel && $LabelPos == LABEL_POS_INSIDE) {
1492 $this->drawText($X + $InnerWidth + $Margin, $Y + $Height / 2, $Percent . "%", ["Align" => TEXT_ALIGN_MIDDLELEFT]);
1493 }
1494 }
1495 }
1496 /**
1497 * Draw the legend of the active series
1498 * @param int $X
1499 * @param int $Y
1500 * @param array $Format
1501 */
1502 public function drawLegend($X, $Y, array $Format = [])
1503 {
1504 $Family = isset($Format["Family"]) ? $Format["Family"] : LEGEND_FAMILY_BOX;
1505 $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName;
1506 $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
1507 $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->FontColorR;
1508 $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->FontColorG;
1509 $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->FontColorB;
1510 $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5;
1511 $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5;
1512 $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth;
1513 $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight;
1514 $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5;
1515 $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5;
1516 $R = isset($Format["R"]) ? $Format["R"] : 200;
1517 $G = isset($Format["G"]) ? $Format["G"] : 200;
1518 $B = isset($Format["B"]) ? $Format["B"] : 200;
1519 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
1520 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255;
1521 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255;
1522 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255;
1523 $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
1524 $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND;
1525 $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL;
1526 if ($Surrounding != null) {
1527 $BorderR = $R + $Surrounding;
1528 $BorderG = $G + $Surrounding;
1529 $BorderB = $B + $Surrounding;
1530 }
1531 $Data = $this->DataSet->getData();
1532 foreach ($Data["Series"] as $SerieName => $Serie) {
1533 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"] && isset($Serie["Picture"])) {
1534 list($PicWidth, $PicHeight) = $this->getPicInfo($Serie["Picture"]);
1535 if ($IconAreaWidth < $PicWidth) {
1536 $IconAreaWidth = $PicWidth;
1537 }
1538 if ($IconAreaHeight < $PicHeight) {
1539 $IconAreaHeight = $PicHeight;
1540 }
1541 }
1542 }
1543 $YStep = max($this->FontSize, $IconAreaHeight) + 5;
1544 $XStep = $IconAreaWidth + 5;
1545 $XStep = $XSpacing;
1546 $Boundaries = [];
1547 $Boundaries["L"] = $X;
1548 $Boundaries["T"] = $Y;
1549 $Boundaries["R"] = 0;
1550 $Boundaries["B"] = 0;
1551 $vY = $Y;
1552 $vX = $X;
1553 foreach ($Data["Series"] as $SerieName => $Serie) {
1554 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
1555 if ($Mode == LEGEND_VERTICAL) {
1556 $BoxArray = $this->getTextBox($vX + $IconAreaWidth + 4, $vY + $IconAreaHeight / 2, $FontName, $FontSize, 0, $Serie["Description"]);
1557 if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) {
1558 $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2;
1559 }
1560 if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) {
1561 $Boundaries["R"] = $BoxArray[1]["X"] + 2;
1562 }
1563 if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) {
1564 $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2;
1565 }
1566 $Lines = preg_split("/\n/", $Serie["Description"]);
1567 $vY = $vY + max($this->FontSize * count($Lines), $IconAreaHeight) + 5;
1568 } elseif ($Mode == LEGEND_HORIZONTAL) {
1569 $Lines = preg_split("/\n/", $Serie["Description"]);
1570 $Width = [];
1571 foreach ($Lines as $Key => $Value) {
1572 $BoxArray = $this->getTextBox($vX + $IconAreaWidth + 6, $Y + $IconAreaHeight / 2 + ($this->FontSize + 3) * $Key, $FontName, $FontSize, 0, $Value);
1573 if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) {
1574 $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2;
1575 }
1576 if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) {
1577 $Boundaries["R"] = $BoxArray[1]["X"] + 2;
1578 }
1579 if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) {
1580 $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2;
1581 }
1582 $Width[] = $BoxArray[1]["X"];
1583 }
1584 $vX = max($Width) + $XStep;
1585 }
1586 }
1587 }
1588 $vY = $vY - $YStep;
1589 $vX = $vX - $XStep;
1590 $TopOffset = $Y - $Boundaries["T"];
1591 if ($Boundaries["B"] - ($vY + $IconAreaHeight) < $TopOffset) {
1592 $Boundaries["B"] = $vY + $IconAreaHeight + $TopOffset;
1593 }
1594 if ($Style == LEGEND_ROUND) {
1595 $this->drawRoundedFilledRectangle($Boundaries["L"] - $Margin, $Boundaries["T"] - $Margin, $Boundaries["R"] + $Margin, $Boundaries["B"] + $Margin, $Margin, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "BorderR" => $BorderR, "BorderG" => $BorderG, "BorderB" => $BorderB]);
1596 } elseif ($Style == LEGEND_BOX) {
1597 $this->drawFilledRectangle($Boundaries["L"] - $Margin, $Boundaries["T"] - $Margin, $Boundaries["R"] + $Margin, $Boundaries["B"] + $Margin, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "BorderR" => $BorderR, "BorderG" => $BorderG, "BorderB" => $BorderB]);
1598 }
1599 $RestoreShadow = $this->Shadow;
1600 $this->Shadow = \false;
1601 foreach ($Data["Series"] as $SerieName => $Serie) {
1602 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
1603 $R = $Serie["Color"]["R"];
1604 $G = $Serie["Color"]["G"];
1605 $B = $Serie["Color"]["B"];
1606 $Ticks = $Serie["Ticks"];
1607 $Weight = $Serie["Weight"];
1608 if (isset($Serie["Picture"])) {
1609 $Picture = $Serie["Picture"];
1610 list($PicWidth, $PicHeight) = $this->getPicInfo($Picture);
1611 $PicX = $X + $IconAreaWidth / 2;
1612 $PicY = $Y + $IconAreaHeight / 2;
1613 $this->drawFromPNG($PicX - $PicWidth / 2, $PicY - $PicHeight / 2, $Picture);
1614 } else {
1615 if ($Family == LEGEND_FAMILY_BOX) {
1616 $XOffset = 0;
1617 if ($BoxWidth != $IconAreaWidth) {
1618 $XOffset = floor(($IconAreaWidth - $BoxWidth) / 2);
1619 }
1620 $YOffset = 0;
1621 if ($BoxHeight != $IconAreaHeight) {
1622 $YOffset = floor(($IconAreaHeight - $BoxHeight) / 2);
1623 }
1624 $this->drawFilledRectangle($X + 1 + $XOffset, $Y + 1 + $YOffset, $X + $BoxWidth + $XOffset + 1, $Y + $BoxHeight + 1 + $YOffset, ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20]);
1625 $this->drawFilledRectangle($X + $XOffset, $Y + $YOffset, $X + $BoxWidth + $XOffset, $Y + $BoxHeight + $YOffset, ["R" => $R, "G" => $G, "B" => $B, "Surrounding" => 20]);
1626 } elseif ($Family == LEGEND_FAMILY_CIRCLE) {
1627 $this->drawFilledCircle($X + 1 + $IconAreaWidth / 2, $Y + 1 + $IconAreaHeight / 2, min($IconAreaHeight / 2, $IconAreaWidth / 2), ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20]);
1628 $this->drawFilledCircle($X + $IconAreaWidth / 2, $Y + $IconAreaHeight / 2, min($IconAreaHeight / 2, $IconAreaWidth / 2), ["R" => $R, "G" => $G, "B" => $B, "Surrounding" => 20]);
1629 } elseif ($Family == LEGEND_FAMILY_LINE) {
1630 $this->drawLine($X + 1, $Y + 1 + $IconAreaHeight / 2, $X + 1 + $IconAreaWidth, $Y + 1 + $IconAreaHeight / 2, ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20, "Ticks" => $Ticks, "Weight" => $Weight]);
1631 $this->drawLine($X, $Y + $IconAreaHeight / 2, $X + $IconAreaWidth, $Y + $IconAreaHeight / 2, ["R" => $R, "G" => $G, "B" => $B, "Ticks" => $Ticks, "Weight" => $Weight]);
1632 }
1633 }
1634 if ($Mode == LEGEND_VERTICAL) {
1635 $Lines = preg_split("/\n/", $Serie["Description"]);
1636 foreach ($Lines as $Key => $Value) {
1637 $this->drawText($X + $IconAreaWidth + 4, $Y + $IconAreaHeight / 2 + ($this->FontSize + 3) * $Key, $Value, ["R" => $FontR, "G" => $FontG, "B" => $FontB, "Align" => TEXT_ALIGN_MIDDLELEFT, "FontSize" => $FontSize, "FontName" => $FontName]);
1638 }
1639 $Y = $Y + max($this->FontSize * count($Lines), $IconAreaHeight) + 5;
1640 } elseif ($Mode == LEGEND_HORIZONTAL) {
1641 $Lines = preg_split("/\n/", $Serie["Description"]);
1642 $Width = [];
1643 foreach ($Lines as $Key => $Value) {
1644 $BoxArray = $this->drawText($X + $IconAreaWidth + 4, $Y + $IconAreaHeight / 2 + ($this->FontSize + 3) * $Key, $Value, ["R" => $FontR, "G" => $FontG, "B" => $FontB, "Align" => TEXT_ALIGN_MIDDLELEFT, "FontSize" => $FontSize, "FontName" => $FontName]);
1645 $Width[] = $BoxArray[1]["X"];
1646 }
1647 $X = max($Width) + 2 + $XStep;
1648 }
1649 }
1650 }
1651 $this->Shadow = $RestoreShadow;
1652 }
1653 /**
1654 * @param array $Format
1655 * @throws Exception
1656 */
1657 public function drawScale(array $Format = [])
1658 {
1659 $FloatingOffset = 0;
1660 $Pos = isset($Format["Pos"]) ? $Format["Pos"] : SCALE_POS_LEFTRIGHT;
1661 $Floating = isset($Format["Floating"]) ? $Format["Floating"] : \false;
1662 $Mode = isset($Format["Mode"]) ? $Format["Mode"] : SCALE_MODE_FLOATING;
1663 $RemoveXAxis = isset($Format["RemoveXAxis"]) ? $Format["RemoveXAxis"] : \false;
1664 $RemoveYAxis = isset($Format["RemoveYAxis"]) ? $Format["RemoveYAxis"] : \false;
1665 $RemoveYAxiValues = isset($Format["RemoveYAxisValues"]) ? $Format["RemoveYAxisValues"] : \false;
1666 $MinDivHeight = isset($Format["MinDivHeight"]) ? $Format["MinDivHeight"] : 20;
1667 $Factors = isset($Format["Factors"]) ? $Format["Factors"] : [1, 2, 5];
1668 $ManualScale = isset($Format["ManualScale"]) ? $Format["ManualScale"] : ["0" => ["Min" => -100, "Max" => 100]];
1669 $XMargin = isset($Format["XMargin"]) ? $Format["XMargin"] : AUTO;
1670 $YMargin = isset($Format["YMargin"]) ? $Format["YMargin"] : 0;
1671 $ScaleSpacing = isset($Format["ScaleSpacing"]) ? $Format["ScaleSpacing"] : 15;
1672 $InnerTickWidth = isset($Format["InnerTickWidth"]) ? $Format["InnerTickWidth"] : 2;
1673 $OuterTickWidth = isset($Format["OuterTickWidth"]) ? $Format["OuterTickWidth"] : 2;
1674 $DrawXLines = isset($Format["DrawXLines"]) ? $Format["DrawXLines"] : \true;
1675 $DrawYLines = isset($Format["DrawYLines"]) ? $Format["DrawYLines"] : ALL;
1676 $GridTicks = isset($Format["GridTicks"]) ? $Format["GridTicks"] : 4;
1677 $GridR = isset($Format["GridR"]) ? $Format["GridR"] : 255;
1678 $GridG = isset($Format["GridG"]) ? $Format["GridG"] : 255;
1679 $GridB = isset($Format["GridB"]) ? $Format["GridB"] : 255;
1680 $GridAlpha = isset($Format["GridAlpha"]) ? $Format["GridAlpha"] : 40;
1681 $AxisRo = isset($Format["AxisR"]) ? $Format["AxisR"] : 0;
1682 $AxisGo = isset($Format["AxisG"]) ? $Format["AxisG"] : 0;
1683 $AxisBo = isset($Format["AxisB"]) ? $Format["AxisB"] : 0;
1684 $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 100;
1685 $TickRo = isset($Format["TickR"]) ? $Format["TickR"] : 0;
1686 $TickGo = isset($Format["TickG"]) ? $Format["TickG"] : 0;
1687 $TickBo = isset($Format["TickB"]) ? $Format["TickB"] : 0;
1688 $TickAlpha = isset($Format["TickAlpha"]) ? $Format["TickAlpha"] : 100;
1689 $DrawSubTicks = isset($Format["DrawSubTicks"]) ? $Format["DrawSubTicks"] : \false;
1690 $InnerSubTickWidth = isset($Format["InnerSubTickWidth"]) ? $Format["InnerSubTickWidth"] : 0;
1691 $OuterSubTickWidth = isset($Format["OuterSubTickWidth"]) ? $Format["OuterSubTickWidth"] : 2;
1692 $SubTickR = isset($Format["SubTickR"]) ? $Format["SubTickR"] : 255;
1693 $SubTickG = isset($Format["SubTickG"]) ? $Format["SubTickG"] : 0;
1694 $SubTickB = isset($Format["SubTickB"]) ? $Format["SubTickB"] : 0;
1695 $SubTickAlpha = isset($Format["SubTickAlpha"]) ? $Format["SubTickAlpha"] : 100;
1696 $AutoAxisLabels = isset($Format["AutoAxisLabels"]) ? $Format["AutoAxisLabels"] : \true;
1697 $XReleasePercent = isset($Format["XReleasePercent"]) ? $Format["XReleasePercent"] : 1;
1698 $DrawArrows = isset($Format["DrawArrows"]) ? $Format["DrawArrows"] : \false;
1699 $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 8;
1700 $CycleBackground = isset($Format["CycleBackground"]) ? $Format["CycleBackground"] : \false;
1701 $BackgroundR1 = isset($Format["BackgroundR1"]) ? $Format["BackgroundR1"] : 255;
1702 $BackgroundG1 = isset($Format["BackgroundG1"]) ? $Format["BackgroundG1"] : 255;
1703 $BackgroundB1 = isset($Format["BackgroundB1"]) ? $Format["BackgroundB1"] : 255;
1704 $BackgroundAlpha1 = isset($Format["BackgroundAlpha1"]) ? $Format["BackgroundAlpha1"] : 20;
1705 $BackgroundR2 = isset($Format["BackgroundR2"]) ? $Format["BackgroundR2"] : 230;
1706 $BackgroundG2 = isset($Format["BackgroundG2"]) ? $Format["BackgroundG2"] : 230;
1707 $BackgroundB2 = isset($Format["BackgroundB2"]) ? $Format["BackgroundB2"] : 230;
1708 $BackgroundAlpha2 = isset($Format["BackgroundAlpha2"]) ? $Format["BackgroundAlpha2"] : 20;
1709 $LabelingMethod = isset($Format["LabelingMethod"]) ? $Format["LabelingMethod"] : LABELING_ALL;
1710 $LabelSkip = isset($Format["LabelSkip"]) ? $Format["LabelSkip"] : 0;
1711 $LabelRotation = isset($Format["LabelRotation"]) ? $Format["LabelRotation"] : 0;
1712 $RemoveSkippedAxis = isset($Format["RemoveSkippedAxis"]) ? $Format["RemoveSkippedAxis"] : \false;
1713 $SkippedAxisTicks = isset($Format["SkippedAxisTicks"]) ? $Format["SkippedAxisTicks"] : $GridTicks + 2;
1714 $SkippedAxisR = isset($Format["SkippedAxisR"]) ? $Format["SkippedAxisR"] : $GridR;
1715 $SkippedAxisG = isset($Format["SkippedAxisG"]) ? $Format["SkippedAxisG"] : $GridG;
1716 $SkippedAxisB = isset($Format["SkippedAxisB"]) ? $Format["SkippedAxisB"] : $GridB;
1717 $SkippedAxisAlpha = isset($Format["SkippedAxisAlpha"]) ? $Format["SkippedAxisAlpha"] : $GridAlpha - 30;
1718 $SkippedTickR = isset($Format["SkippedTickR"]) ? $Format["SkippedTickR"] : $TickRo;
1719 $SkippedTickG = isset($Format["SkippedTickG"]) ? $Format["SkippedTickG"] : $TickGo;
1720 $SkippedTickB = isset($Format["SkippedTicksB"]) ? $Format["SkippedTickB"] : $TickBo;
1721 $SkippedTickAlpha = isset($Format["SkippedTickAlpha"]) ? $Format["SkippedTickAlpha"] : $TickAlpha - 80;
1722 $SkippedInnerTickWidth = isset($Format["SkippedInnerTickWidth"]) ? $Format["SkippedInnerTickWidth"] : 0;
1723 $SkippedOuterTickWidth = isset($Format["SkippedOuterTickWidth"]) ? $Format["SkippedOuterTickWidth"] : 2;
1724 /* Floating scale require X & Y margins to be set manually */
1725 if ($Floating && ($XMargin == AUTO || $YMargin == 0)) {
1726 $Floating = \false;
1727 }
1728 /* Skip a NOTICE event in case of an empty array */
1729 if ($DrawYLines == NONE || $DrawYLines == \false) {
1730 $DrawYLines = ["zarma" => "31"];
1731 }
1732 /* Define the color for the skipped elements */
1733 $SkippedAxisColor = ["R" => $SkippedAxisR, "G" => $SkippedAxisG, "B" => $SkippedAxisB, "Alpha" => $SkippedAxisAlpha, "Ticks" => $SkippedAxisTicks];
1734 $SkippedTickColor = ["R" => $SkippedTickR, "G" => $SkippedTickG, "B" => $SkippedTickB, "Alpha" => $SkippedTickAlpha];
1735 $Data = $this->DataSet->getData();
1736 $Abscissa = null;
1737 if (isset($Data["Abscissa"])) {
1738 $Abscissa = $Data["Abscissa"];
1739 }
1740 /* Unset the abscissa axis, needed if we display multiple charts on the same picture */
1741 if ($Abscissa != null) {
1742 foreach ($Data["Axis"] as $AxisID => $Parameters) {
1743 if ($Parameters["Identity"] == AXIS_X) {
1744 unset($Data["Axis"][$AxisID]);
1745 }
1746 }
1747 }
1748 /* Build the scale settings */
1749 $GotXAxis = \false;
1750 foreach ($Data["Axis"] as $AxisID => $AxisParameter) {
1751 if ($AxisParameter["Identity"] == AXIS_X) {
1752 $GotXAxis = \true;
1753 }
1754 if ($Pos == SCALE_POS_LEFTRIGHT && $AxisParameter["Identity"] == AXIS_Y) {
1755 $Height = $this->GraphAreaY2 - $this->GraphAreaY1 - $YMargin * 2;
1756 } elseif ($Pos == SCALE_POS_LEFTRIGHT && $AxisParameter["Identity"] == AXIS_X) {
1757 $Height = $this->GraphAreaX2 - $this->GraphAreaX1;
1758 } elseif ($Pos == SCALE_POS_TOPBOTTOM && $AxisParameter["Identity"] == AXIS_Y) {
1759 $Height = $this->GraphAreaX2 - $this->GraphAreaX1 - $YMargin * 2;
1760 } else {
1761 $Height = $this->GraphAreaY2 - $this->GraphAreaY1;
1762 }
1763 $AxisMin = ABSOLUTE_MAX;
1764 $AxisMax = OUT_OF_SIGHT;
1765 if ($Mode == SCALE_MODE_FLOATING || $Mode == SCALE_MODE_START0) {
1766 foreach ($Data["Series"] as $SerieID => $SerieParameter) {
1767 if ($SerieParameter["Axis"] == $AxisID && $Data["Series"][$SerieID]["isDrawable"] && $Data["Abscissa"] != $SerieID) {
1768 $AxisMax = max($AxisMax, $Data["Series"][$SerieID]["Max"]);
1769 $AxisMin = min($AxisMin, $Data["Series"][$SerieID]["Min"]);
1770 }
1771 }
1772 $AutoMargin = ($AxisMax - $AxisMin) / 100 * $XReleasePercent;
1773 $Data["Axis"][$AxisID]["Min"] = $AxisMin - $AutoMargin;
1774 $Data["Axis"][$AxisID]["Max"] = $AxisMax + $AutoMargin;
1775 if ($Mode == SCALE_MODE_START0) {
1776 $Data["Axis"][$AxisID]["Min"] = 0;
1777 }
1778 } elseif ($Mode == SCALE_MODE_MANUAL) {
1779 if (isset($ManualScale[$AxisID]["Min"]) && isset($ManualScale[$AxisID]["Max"])) {
1780 $Data["Axis"][$AxisID]["Min"] = $ManualScale[$AxisID]["Min"];
1781 $Data["Axis"][$AxisID]["Max"] = $ManualScale[$AxisID]["Max"];
1782 } else {
1783 throw new Exception("Manual scale boundaries not set.");
1784 }
1785 } elseif ($Mode == SCALE_MODE_ADDALL || $Mode == SCALE_MODE_ADDALL_START0) {
1786 $Series = [];
1787 foreach ($Data["Series"] as $SerieID => $SerieParameter) {
1788 if ($SerieParameter["Axis"] == $AxisID && $SerieParameter["isDrawable"] && $Data["Abscissa"] != $SerieID) {
1789 $Series[$SerieID] = count($Data["Series"][$SerieID]["Data"]);
1790 }
1791 }
1792 for ($ID = 0; $ID <= max($Series) - 1; $ID++) {
1793 $PointMin = 0;
1794 $PointMax = 0;
1795 foreach ($Series as $SerieID => $ValuesCount) {
1796 if (isset($Data["Series"][$SerieID]["Data"][$ID]) && $Data["Series"][$SerieID]["Data"][$ID] != null) {
1797 $Value = $Data["Series"][$SerieID]["Data"][$ID];
1798 if ($Value > 0) {
1799 $PointMax = $PointMax + $Value;
1800 } else {
1801 $PointMin = $PointMin + $Value;
1802 }
1803 }
1804 }
1805 $AxisMax = max($AxisMax, $PointMax);
1806 $AxisMin = min($AxisMin, $PointMin);
1807 }
1808 $AutoMargin = ($AxisMax - $AxisMin) / 100 * $XReleasePercent;
1809 $Data["Axis"][$AxisID]["Min"] = $AxisMin - $AutoMargin;
1810 $Data["Axis"][$AxisID]["Max"] = $AxisMax + $AutoMargin;
1811 }
1812 $MaxDivs = floor($Height / $MinDivHeight);
1813 if ($Mode == SCALE_MODE_ADDALL_START0) {
1814 $Data["Axis"][$AxisID]["Min"] = 0;
1815 }
1816 $Scale = $this->computeScale($Data["Axis"][$AxisID]["Min"], $Data["Axis"][$AxisID]["Max"], $MaxDivs, $Factors, $AxisID);
1817 $Data["Axis"][$AxisID]["Margin"] = $AxisParameter["Identity"] == AXIS_X ? $XMargin : $YMargin;
1818 $Data["Axis"][$AxisID]["ScaleMin"] = $Scale["XMin"];
1819 $Data["Axis"][$AxisID]["ScaleMax"] = $Scale["XMax"];
1820 $Data["Axis"][$AxisID]["Rows"] = $Scale["Rows"];
1821 $Data["Axis"][$AxisID]["RowHeight"] = $Scale["RowHeight"];
1822 if (isset($Scale["Format"])) {
1823 $Data["Axis"][$AxisID]["Format"] = $Scale["Format"];
1824 }
1825 if (!isset($Data["Axis"][$AxisID]["Display"])) {
1826 $Data["Axis"][$AxisID]["Display"] = null;
1827 }
1828 if (!isset($Data["Axis"][$AxisID]["Format"])) {
1829 $Data["Axis"][$AxisID]["Format"] = null;
1830 }
1831 if (!isset($Data["Axis"][$AxisID]["Unit"])) {
1832 $Data["Axis"][$AxisID]["Unit"] = null;
1833 }
1834 }
1835 /* Still no X axis */
1836 if ($GotXAxis == \false) {
1837 if ($Abscissa != null) {
1838 $Points = count($Data["Series"][$Abscissa]["Data"]);
1839 $AxisName = null;
1840 if ($AutoAxisLabels) {
1841 $AxisName = isset($Data["Series"][$Abscissa]["Description"]) ? $Data["Series"][$Abscissa]["Description"] : null;
1842 }
1843 } else {
1844 $Points = 0;
1845 $AxisName = isset($Data["XAxisName"]) ? $Data["XAxisName"] : null;
1846 foreach ($Data["Series"] as $SerieID => $SerieParameter) {
1847 if ($SerieParameter["isDrawable"]) {
1848 $Points = max($Points, count($SerieParameter["Data"]));
1849 }
1850 }
1851 }
1852 $AxisID = count($Data["Axis"]);
1853 $Data["Axis"][$AxisID]["Identity"] = AXIS_X;
1854 if ($Pos == SCALE_POS_LEFTRIGHT) {
1855 $Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_BOTTOM;
1856 } else {
1857 $Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_LEFT;
1858 }
1859 if (isset($Data["AbscissaName"])) {
1860 $Data["Axis"][$AxisID]["Name"] = $Data["AbscissaName"];
1861 }
1862 if ($XMargin == AUTO) {
1863 if ($Pos == SCALE_POS_LEFTRIGHT) {
1864 $Height = $this->GraphAreaX2 - $this->GraphAreaX1;
1865 } else {
1866 $Height = $this->GraphAreaY2 - $this->GraphAreaY1;
1867 }
1868 if ($Points == 0 || $Points == 1) {
1869 $Data["Axis"][$AxisID]["Margin"] = $Height / 2;
1870 } else {
1871 $Data["Axis"][$AxisID]["Margin"] = $Height / $Points / 2;
1872 }
1873 } else {
1874 $Data["Axis"][$AxisID]["Margin"] = $XMargin;
1875 }
1876 $Data["Axis"][$AxisID]["Rows"] = $Points - 1;
1877 if (!isset($Data["Axis"][$AxisID]["Display"])) {
1878 $Data["Axis"][$AxisID]["Display"] = null;
1879 }
1880 if (!isset($Data["Axis"][$AxisID]["Format"])) {
1881 $Data["Axis"][$AxisID]["Format"] = null;
1882 }
1883 if (!isset($Data["Axis"][$AxisID]["Unit"])) {
1884 $Data["Axis"][$AxisID]["Unit"] = null;
1885 }
1886 }
1887 /* Do we need to reverse the abscissa position? */
1888 if ($Pos != SCALE_POS_LEFTRIGHT) {
1889 $Data["AbsicssaPosition"] = AXIS_POSITION_RIGHT;
1890 if ($Data["AbsicssaPosition"] == AXIS_POSITION_BOTTOM) {
1891 $Data["AbsicssaPosition"] = AXIS_POSITION_LEFT;
1892 }
1893 }
1894 $Data["Axis"][$AxisID]["Position"] = $Data["AbsicssaPosition"];
1895 $this->DataSet->saveOrientation($Pos);
1896 $this->DataSet->saveAxisConfig($Data["Axis"]);
1897 $this->DataSet->saveYMargin($YMargin);
1898 $FontColorRo = $this->FontColorR;
1899 $FontColorGo = $this->FontColorG;
1900 $FontColorBo = $this->FontColorB;
1901 $AxisPos["L"] = $this->GraphAreaX1;
1902 $AxisPos["R"] = $this->GraphAreaX2;
1903 $AxisPos["T"] = $this->GraphAreaY1;
1904 $AxisPos["B"] = $this->GraphAreaY2;
1905 foreach ($Data["Axis"] as $AxisID => $Parameters) {
1906 if (isset($Parameters["Color"])) {
1907 $AxisR = $Parameters["Color"]["R"];
1908 $AxisG = $Parameters["Color"]["G"];
1909 $AxisB = $Parameters["Color"]["B"];
1910 $TickR = $Parameters["Color"]["R"];
1911 $TickG = $Parameters["Color"]["G"];
1912 $TickB = $Parameters["Color"]["B"];
1913 $this->setFontProperties(["R" => $Parameters["Color"]["R"], "G" => $Parameters["Color"]["G"], "B" => $Parameters["Color"]["B"]]);
1914 } else {
1915 $AxisR = $AxisRo;
1916 $AxisG = $AxisGo;
1917 $AxisB = $AxisBo;
1918 $TickR = $TickRo;
1919 $TickG = $TickGo;
1920 $TickB = $TickBo;
1921 $this->setFontProperties(["R" => $FontColorRo, "G" => $FontColorGo, "B" => $FontColorBo]);
1922 }
1923 $LastValue = "w00t";
1924 $ID = 1;
1925 if ($Parameters["Identity"] == AXIS_X) {
1926 if ($Pos == SCALE_POS_LEFTRIGHT) {
1927 if ($Parameters["Position"] == AXIS_POSITION_BOTTOM) {
1928 if ($LabelRotation == 0) {
1929 $LabelAlign = TEXT_ALIGN_TOPMIDDLE;
1930 $YLabelOffset = 2;
1931 }
1932 if ($LabelRotation > 0 && $LabelRotation < 190) {
1933 $LabelAlign = TEXT_ALIGN_MIDDLERIGHT;
1934 $YLabelOffset = 5;
1935 }
1936 if ($LabelRotation == 180) {
1937 $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE;
1938 $YLabelOffset = 5;
1939 }
1940 if ($LabelRotation > 180 && $LabelRotation < 360) {
1941 $LabelAlign = TEXT_ALIGN_MIDDLELEFT;
1942 $YLabelOffset = 2;
1943 }
1944 if (!$RemoveXAxis) {
1945 if ($Floating) {
1946 $FloatingOffset = $YMargin;
1947 $this->drawLine($this->GraphAreaX1 + $Parameters["Margin"], $AxisPos["B"], $this->GraphAreaX2 - $Parameters["Margin"], $AxisPos["B"], ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
1948 } else {
1949 $FloatingOffset = 0;
1950 $this->drawLine($this->GraphAreaX1, $AxisPos["B"], $this->GraphAreaX2, $AxisPos["B"], ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
1951 }
1952 if ($DrawArrows) {
1953 $this->drawArrow($this->GraphAreaX2 - $Parameters["Margin"], $AxisPos["B"], $this->GraphAreaX2 + $ArrowSize * 2, $AxisPos["B"], ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize]);
1954 }
1955 }
1956 $Width = $this->GraphAreaX2 - $this->GraphAreaX1 - $Parameters["Margin"] * 2;
1957 if ($Parameters["Rows"] == 0) {
1958 $Step = $Width;
1959 } else {
1960 $Step = $Width / $Parameters["Rows"];
1961 }
1962 $MaxBottom = $AxisPos["B"];
1963 for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
1964 $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step * $i;
1965 $YPos = $AxisPos["B"];
1966 if ($Abscissa != null) {
1967 $Value = "";
1968 if (isset($Data["Series"][$Abscissa]["Data"][$i])) {
1969 $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i], $Data["XAxisDisplay"], $Data["XAxisFormat"], $Data["XAxisUnit"]);
1970 }
1971 } else {
1972 $Value = $i;
1973 if (isset($Parameters["ScaleMin"]) && isset($Parameters["RowHeight"])) {
1974 $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, $Data["XAxisDisplay"], $Data["XAxisFormat"], $Data["XAxisUnit"]);
1975 }
1976 }
1977 $ID++;
1978 $Skipped = \true;
1979 if ($this->isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip) && !$RemoveXAxis) {
1980 $Bounds = $this->drawText($XPos, $YPos + $OuterTickWidth + $YLabelOffset, $Value, ["Angle" => $LabelRotation, "Align" => $LabelAlign]);
1981 $TxtBottom = $YPos + $OuterTickWidth + 2 + ($Bounds[0]["Y"] - $Bounds[2]["Y"]);
1982 $MaxBottom = max($MaxBottom, $TxtBottom);
1983 $LastValue = $Value;
1984 $Skipped = \false;
1985 }
1986 if ($RemoveXAxis) {
1987 $Skipped = \false;
1988 }
1989 if ($Skipped) {
1990 if ($DrawXLines && !$RemoveSkippedAxis) {
1991 $this->drawLine($XPos, $this->GraphAreaY1 + $FloatingOffset, $XPos, $this->GraphAreaY2 - $FloatingOffset, $SkippedAxisColor);
1992 }
1993 if (($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis && !$RemoveSkippedAxis) {
1994 $this->drawLine($XPos, $YPos - $SkippedInnerTickWidth, $XPos, $YPos + $SkippedOuterTickWidth, $SkippedTickColor);
1995 }
1996 } else {
1997 if ($DrawXLines && ($XPos != $this->GraphAreaX1 && $XPos != $this->GraphAreaX2)) {
1998 $this->drawLine($XPos, $this->GraphAreaY1 + $FloatingOffset, $XPos, $this->GraphAreaY2 - $FloatingOffset, ["R" => $GridR, "G" => $GridG, "B" => $GridB, "Alpha" => $GridAlpha, "Ticks" => $GridTicks]);
1999 }
2000 if (($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis) {
2001 $this->drawLine($XPos, $YPos - $InnerTickWidth, $XPos, $YPos + $OuterTickWidth, ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]);
2002 }
2003 }
2004 }
2005 if (isset($Parameters["Name"]) && !$RemoveXAxis) {
2006 $YPos = $MaxBottom + 2;
2007 $XPos = $this->GraphAreaX1 + ($this->GraphAreaX2 - $this->GraphAreaX1) / 2;
2008 $Bounds = $this->drawText($XPos, $YPos, $Parameters["Name"], ["Align" => TEXT_ALIGN_TOPMIDDLE]);
2009 $MaxBottom = $Bounds[0]["Y"];
2010 $this->DataSet->Data["GraphArea"]["Y2"] = $MaxBottom + $this->FontSize;
2011 }
2012 $AxisPos["B"] = $MaxBottom + $ScaleSpacing;
2013 } elseif ($Parameters["Position"] == AXIS_POSITION_TOP) {
2014 if ($LabelRotation == 0) {
2015 $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE;
2016 $YLabelOffset = 2;
2017 }
2018 if ($LabelRotation > 0 && $LabelRotation < 190) {
2019 $LabelAlign = TEXT_ALIGN_MIDDLELEFT;
2020 $YLabelOffset = 2;
2021 }
2022 if ($LabelRotation == 180) {
2023 $LabelAlign = TEXT_ALIGN_TOPMIDDLE;
2024 $YLabelOffset = 5;
2025 }
2026 if ($LabelRotation > 180 && $LabelRotation < 360) {
2027 $LabelAlign = TEXT_ALIGN_MIDDLERIGHT;
2028 $YLabelOffset = 5;
2029 }
2030 if (!$RemoveXAxis) {
2031 if ($Floating) {
2032 $FloatingOffset = $YMargin;
2033 $this->drawLine($this->GraphAreaX1 + $Parameters["Margin"], $AxisPos["T"], $this->GraphAreaX2 - $Parameters["Margin"], $AxisPos["T"], ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2034 } else {
2035 $FloatingOffset = 0;
2036 $this->drawLine($this->GraphAreaX1, $AxisPos["T"], $this->GraphAreaX2, $AxisPos["T"], ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2037 }
2038 if ($DrawArrows) {
2039 $this->drawArrow($this->GraphAreaX2 - $Parameters["Margin"], $AxisPos["T"], $this->GraphAreaX2 + $ArrowSize * 2, $AxisPos["T"], ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize]);
2040 }
2041 }
2042 $Width = $this->GraphAreaX2 - $this->GraphAreaX1 - $Parameters["Margin"] * 2;
2043 if ($Parameters["Rows"] == 0) {
2044 $Step = $Width;
2045 } else {
2046 $Step = $Width / $Parameters["Rows"];
2047 }
2048 $MinTop = $AxisPos["T"];
2049 for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
2050 $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step * $i;
2051 $YPos = $AxisPos["T"];
2052 if ($Abscissa != null) {
2053 $Value = "";
2054 if (isset($Data["Series"][$Abscissa]["Data"][$i])) {
2055 $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i], $Data["XAxisDisplay"], $Data["XAxisFormat"], $Data["XAxisUnit"]);
2056 }
2057 } else {
2058 $Value = $i;
2059 if (isset($Parameters["ScaleMin"]) && isset($Parameters["RowHeight"])) {
2060 $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, $Data["XAxisDisplay"], $Data["XAxisFormat"], $Data["XAxisUnit"]);
2061 }
2062 }
2063 $ID++;
2064 $Skipped = \true;
2065 if ($this->isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip) && !$RemoveXAxis) {
2066 $Bounds = $this->drawText($XPos, $YPos - $OuterTickWidth - $YLabelOffset, $Value, ["Angle" => $LabelRotation, "Align" => $LabelAlign]);
2067 $TxtBox = $YPos - $OuterTickWidth - 2 - ($Bounds[0]["Y"] - $Bounds[2]["Y"]);
2068 $MinTop = min($MinTop, $TxtBox);
2069 $LastValue = $Value;
2070 $Skipped = \false;
2071 }
2072 if ($RemoveXAxis) {
2073 $Skipped = \false;
2074 }
2075 if ($Skipped) {
2076 if ($DrawXLines && !$RemoveSkippedAxis) {
2077 $this->drawLine($XPos, $this->GraphAreaY1 + $FloatingOffset, $XPos, $this->GraphAreaY2 - $FloatingOffset, $SkippedAxisColor);
2078 }
2079 if (($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis && !$RemoveSkippedAxis) {
2080 $this->drawLine($XPos, $YPos + $SkippedInnerTickWidth, $XPos, $YPos - $SkippedOuterTickWidth, $SkippedTickColor);
2081 }
2082 } else {
2083 if ($DrawXLines) {
2084 $this->drawLine($XPos, $this->GraphAreaY1 + $FloatingOffset, $XPos, $this->GraphAreaY2 - $FloatingOffset, ["R" => $GridR, "G" => $GridG, "B" => $GridB, "Alpha" => $GridAlpha, "Ticks" => $GridTicks]);
2085 }
2086 if (($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis) {
2087 $this->drawLine($XPos, $YPos + $InnerTickWidth, $XPos, $YPos - $OuterTickWidth, ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]);
2088 }
2089 }
2090 }
2091 if (isset($Parameters["Name"]) && !$RemoveXAxis) {
2092 $YPos = $MinTop - 2;
2093 $XPos = $this->GraphAreaX1 + ($this->GraphAreaX2 - $this->GraphAreaX1) / 2;
2094 $Bounds = $this->drawText($XPos, $YPos, $Parameters["Name"], ["Align" => TEXT_ALIGN_BOTTOMMIDDLE]);
2095 $MinTop = $Bounds[2]["Y"];
2096 $this->DataSet->Data["GraphArea"]["Y1"] = $MinTop;
2097 }
2098 $AxisPos["T"] = $MinTop - $ScaleSpacing;
2099 }
2100 } elseif ($Pos == SCALE_POS_TOPBOTTOM) {
2101 if ($Parameters["Position"] == AXIS_POSITION_LEFT) {
2102 if ($LabelRotation == 0) {
2103 $LabelAlign = TEXT_ALIGN_MIDDLERIGHT;
2104 $XLabelOffset = -2;
2105 }
2106 if ($LabelRotation > 0 && $LabelRotation < 190) {
2107 $LabelAlign = TEXT_ALIGN_MIDDLERIGHT;
2108 $XLabelOffset = -6;
2109 }
2110 if ($LabelRotation == 180) {
2111 $LabelAlign = TEXT_ALIGN_MIDDLELEFT;
2112 $XLabelOffset = -2;
2113 }
2114 if ($LabelRotation > 180 && $LabelRotation < 360) {
2115 $LabelAlign = TEXT_ALIGN_MIDDLELEFT;
2116 $XLabelOffset = -5;
2117 }
2118 if (!$RemoveXAxis) {
2119 if ($Floating) {
2120 $FloatingOffset = $YMargin;
2121 $this->drawLine($AxisPos["L"], $this->GraphAreaY1 + $Parameters["Margin"], $AxisPos["L"], $this->GraphAreaY2 - $Parameters["Margin"], ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2122 } else {
2123 $FloatingOffset = 0;
2124 $this->drawLine($AxisPos["L"], $this->GraphAreaY1, $AxisPos["L"], $this->GraphAreaY2, ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2125 }
2126 if ($DrawArrows) {
2127 $this->drawArrow($AxisPos["L"], $this->GraphAreaY2 - $Parameters["Margin"], $AxisPos["L"], $this->GraphAreaY2 + $ArrowSize * 2, ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize]);
2128 }
2129 }
2130 $Height = $this->GraphAreaY2 - $this->GraphAreaY1 - $Parameters["Margin"] * 2;
2131 if ($Parameters["Rows"] == 0) {
2132 $Step = $Height;
2133 } else {
2134 $Step = $Height / $Parameters["Rows"];
2135 }
2136 $MinLeft = $AxisPos["L"];
2137 for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
2138 $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step * $i;
2139 $XPos = $AxisPos["L"];
2140 if ($Abscissa != null) {
2141 $Value = "";
2142 if (isset($Data["Series"][$Abscissa]["Data"][$i])) {
2143 $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i], $Data["XAxisDisplay"], $Data["XAxisFormat"], $Data["XAxisUnit"]);
2144 }
2145 } else {
2146 $Value = $i;
2147 if (isset($Parameters["ScaleMin"]) && isset($Parameters["RowHeight"])) {
2148 $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, $Data["XAxisDisplay"], $Data["XAxisFormat"], $Data["XAxisUnit"]);
2149 }
2150 }
2151 $ID++;
2152 $Skipped = \true;
2153 if ($this->isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip) && !$RemoveXAxis) {
2154 $Bounds = $this->drawText($XPos - $OuterTickWidth + $XLabelOffset, $YPos, $Value, ["Angle" => $LabelRotation, "Align" => $LabelAlign]);
2155 $TxtBox = $XPos - $OuterTickWidth - 2 - ($Bounds[1]["X"] - $Bounds[0]["X"]);
2156 $MinLeft = min($MinLeft, $TxtBox);
2157 $LastValue = $Value;
2158 $Skipped = \false;
2159 }
2160 if ($RemoveXAxis) {
2161 $Skipped = \false;
2162 }
2163 if ($Skipped) {
2164 if ($DrawXLines && !$RemoveSkippedAxis) {
2165 $this->drawLine($this->GraphAreaX1 + $FloatingOffset, $YPos, $this->GraphAreaX2 - $FloatingOffset, $YPos, $SkippedAxisColor);
2166 }
2167 if (($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis && !$RemoveSkippedAxis) {
2168 $this->drawLine($XPos - $SkippedOuterTickWidth, $YPos, $XPos + $SkippedInnerTickWidth, $YPos, $SkippedTickColor);
2169 }
2170 } else {
2171 if ($DrawXLines && ($YPos != $this->GraphAreaY1 && $YPos != $this->GraphAreaY2)) {
2172 $this->drawLine($this->GraphAreaX1 + $FloatingOffset, $YPos, $this->GraphAreaX2 - $FloatingOffset, $YPos, ["R" => $GridR, "G" => $GridG, "B" => $GridB, "Alpha" => $GridAlpha, "Ticks" => $GridTicks]);
2173 }
2174 if (($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis) {
2175 $this->drawLine($XPos - $OuterTickWidth, $YPos, $XPos + $InnerTickWidth, $YPos, ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]);
2176 }
2177 }
2178 }
2179 if (isset($Parameters["Name"]) && !$RemoveXAxis) {
2180 $XPos = $MinLeft - 2;
2181 $YPos = $this->GraphAreaY1 + ($this->GraphAreaY2 - $this->GraphAreaY1) / 2;
2182 $Bounds = $this->drawText($XPos, $YPos, $Parameters["Name"], ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 90]);
2183 $MinLeft = $Bounds[0]["X"];
2184 $this->DataSet->Data["GraphArea"]["X1"] = $MinLeft;
2185 }
2186 $AxisPos["L"] = $MinLeft - $ScaleSpacing;
2187 } elseif ($Parameters["Position"] == AXIS_POSITION_RIGHT) {
2188 if ($LabelRotation == 0) {
2189 $LabelAlign = TEXT_ALIGN_MIDDLELEFT;
2190 $XLabelOffset = 2;
2191 }
2192 if ($LabelRotation > 0 && $LabelRotation < 190) {
2193 $LabelAlign = TEXT_ALIGN_MIDDLELEFT;
2194 $XLabelOffset = 6;
2195 }
2196 if ($LabelRotation == 180) {
2197 $LabelAlign = TEXT_ALIGN_MIDDLERIGHT;
2198 $XLabelOffset = 5;
2199 }
2200 if ($LabelRotation > 180 && $LabelRotation < 360) {
2201 $LabelAlign = TEXT_ALIGN_MIDDLERIGHT;
2202 $XLabelOffset = 7;
2203 }
2204 if (!$RemoveXAxis) {
2205 if ($Floating) {
2206 $FloatingOffset = $YMargin;
2207 $this->drawLine($AxisPos["R"], $this->GraphAreaY1 + $Parameters["Margin"], $AxisPos["R"], $this->GraphAreaY2 - $Parameters["Margin"], ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2208 } else {
2209 $FloatingOffset = 0;
2210 $this->drawLine($AxisPos["R"], $this->GraphAreaY1, $AxisPos["R"], $this->GraphAreaY2, ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2211 }
2212 if ($DrawArrows) {
2213 $this->drawArrow($AxisPos["R"], $this->GraphAreaY2 - $Parameters["Margin"], $AxisPos["R"], $this->GraphAreaY2 + $ArrowSize * 2, ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize]);
2214 }
2215 }
2216 $Height = $this->GraphAreaY2 - $this->GraphAreaY1 - $Parameters["Margin"] * 2;
2217 if ($Parameters["Rows"] == 0) {
2218 $Step = $Height;
2219 } else {
2220 $Step = $Height / $Parameters["Rows"];
2221 }
2222 $MaxRight = $AxisPos["R"];
2223 for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
2224 $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step * $i;
2225 $XPos = $AxisPos["R"];
2226 if ($Abscissa != null) {
2227 $Value = "";
2228 if (isset($Data["Series"][$Abscissa]["Data"][$i])) {
2229 $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i], $Data["XAxisDisplay"], $Data["XAxisFormat"], $Data["XAxisUnit"]);
2230 }
2231 } else {
2232 $Value = $i;
2233 if (isset($Parameters["ScaleMin"]) && isset($Parameters["RowHeight"])) {
2234 $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, $Data["XAxisDisplay"], $Data["XAxisFormat"], $Data["XAxisUnit"]);
2235 }
2236 }
2237 $ID++;
2238 $Skipped = \true;
2239 if ($this->isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip) && !$RemoveXAxis) {
2240 $Bounds = $this->drawText($XPos + $OuterTickWidth + $XLabelOffset, $YPos, $Value, ["Angle" => $LabelRotation, "Align" => $LabelAlign]);
2241 $TxtBox = $XPos + $OuterTickWidth + 2 + ($Bounds[1]["X"] - $Bounds[0]["X"]);
2242 $MaxRight = max($MaxRight, $TxtBox);
2243 $LastValue = $Value;
2244 $Skipped = \false;
2245 }
2246 if ($RemoveXAxis) {
2247 $Skipped = \false;
2248 }
2249 if ($Skipped) {
2250 if ($DrawXLines && !$RemoveSkippedAxis) {
2251 $this->drawLine($this->GraphAreaX1 + $FloatingOffset, $YPos, $this->GraphAreaX2 - $FloatingOffset, $YPos, $SkippedAxisColor);
2252 }
2253 if (($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis && !$RemoveSkippedAxis) {
2254 $this->drawLine($XPos + $SkippedOuterTickWidth, $YPos, $XPos - $SkippedInnerTickWidth, $YPos, $SkippedTickColor);
2255 }
2256 } else {
2257 if ($DrawXLines) {
2258 $this->drawLine($this->GraphAreaX1 + $FloatingOffset, $YPos, $this->GraphAreaX2 - $FloatingOffset, $YPos, ["R" => $GridR, "G" => $GridG, "B" => $GridB, "Alpha" => $GridAlpha, "Ticks" => $GridTicks]);
2259 }
2260 if (($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis) {
2261 $this->drawLine($XPos + $OuterTickWidth, $YPos, $XPos - $InnerTickWidth, $YPos, ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]);
2262 }
2263 }
2264 }
2265 if (isset($Parameters["Name"]) && !$RemoveXAxis) {
2266 $XPos = $MaxRight + 4;
2267 $YPos = $this->GraphAreaY1 + ($this->GraphAreaY2 - $this->GraphAreaY1) / 2;
2268 $Bounds = $this->drawText($XPos, $YPos, $Parameters["Name"], ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 270]);
2269 $MaxRight = $Bounds[1]["X"];
2270 $this->DataSet->Data["GraphArea"]["X2"] = $MaxRight + $this->FontSize;
2271 }
2272 $AxisPos["R"] = $MaxRight + $ScaleSpacing;
2273 }
2274 }
2275 }
2276 if ($Parameters["Identity"] == AXIS_Y && !$RemoveYAxis) {
2277 if ($Pos == SCALE_POS_LEFTRIGHT) {
2278 if ($Parameters["Position"] == AXIS_POSITION_LEFT) {
2279 if ($Floating) {
2280 $FloatingOffset = $XMargin;
2281 $this->drawLine($AxisPos["L"], $this->GraphAreaY1 + $Parameters["Margin"], $AxisPos["L"], $this->GraphAreaY2 - $Parameters["Margin"], ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2282 } else {
2283 $FloatingOffset = 0;
2284 $this->drawLine($AxisPos["L"], $this->GraphAreaY1, $AxisPos["L"], $this->GraphAreaY2, ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2285 }
2286 if ($DrawArrows) {
2287 $this->drawArrow($AxisPos["L"], $this->GraphAreaY1 + $Parameters["Margin"], $AxisPos["L"], $this->GraphAreaY1 - $ArrowSize * 2, ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize]);
2288 }
2289 $Height = $this->GraphAreaY2 - $this->GraphAreaY1 - $Parameters["Margin"] * 2;
2290 $Step = $Height / $Parameters["Rows"];
2291 $SubTicksSize = $Step / 2;
2292 $MinLeft = $AxisPos["L"];
2293 $LastY = null;
2294 for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
2295 $YPos = $this->GraphAreaY2 - $Parameters["Margin"] - $Step * $i;
2296 $XPos = $AxisPos["L"];
2297 $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, $Parameters["Display"], $Parameters["Format"], $Parameters["Unit"]);
2298 if ($i % 2 == 1) {
2299 $BGColor = ["R" => $BackgroundR1, "G" => $BackgroundG1, "B" => $BackgroundB1, "Alpha" => $BackgroundAlpha1];
2300 } else {
2301 $BGColor = ["R" => $BackgroundR2, "G" => $BackgroundG2, "B" => $BackgroundB2, "Alpha" => $BackgroundAlpha2];
2302 }
2303 if ($LastY != null && $CycleBackground && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines))) {
2304 $this->drawFilledRectangle($this->GraphAreaX1 + $FloatingOffset, $LastY, $this->GraphAreaX2 - $FloatingOffset, $YPos, $BGColor);
2305 }
2306 if ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) {
2307 $this->drawLine($this->GraphAreaX1 + $FloatingOffset, $YPos, $this->GraphAreaX2 - $FloatingOffset, $YPos, ["R" => $GridR, "G" => $GridG, "B" => $GridB, "Alpha" => $GridAlpha, "Ticks" => $GridTicks]);
2308 }
2309 if ($DrawSubTicks && $i != $Parameters["Rows"]) {
2310 $this->drawLine($XPos - $OuterSubTickWidth, $YPos - $SubTicksSize, $XPos + $InnerSubTickWidth, $YPos - $SubTicksSize, ["R" => $SubTickR, "G" => $SubTickG, "B" => $SubTickB, "Alpha" => $SubTickAlpha]);
2311 }
2312 if (!$RemoveYAxiValues) {
2313 $this->drawLine($XPos - $OuterTickWidth, $YPos, $XPos + $InnerTickWidth, $YPos, ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]);
2314 $Bounds = $this->drawText($XPos - $OuterTickWidth - 2, $YPos, $Value, ["Align" => TEXT_ALIGN_MIDDLERIGHT]);
2315 $TxtLeft = $XPos - $OuterTickWidth - 2 - ($Bounds[1]["X"] - $Bounds[0]["X"]);
2316 $MinLeft = min($MinLeft, $TxtLeft);
2317 }
2318 $LastY = $YPos;
2319 }
2320 if (isset($Parameters["Name"])) {
2321 $XPos = $MinLeft - 2;
2322 $YPos = $this->GraphAreaY1 + ($this->GraphAreaY2 - $this->GraphAreaY1) / 2;
2323 $Bounds = $this->drawText($XPos, $YPos, $Parameters["Name"], ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 90]);
2324 $MinLeft = $Bounds[2]["X"];
2325 $this->DataSet->Data["GraphArea"]["X1"] = $MinLeft;
2326 }
2327 $AxisPos["L"] = $MinLeft - $ScaleSpacing;
2328 } elseif ($Parameters["Position"] == AXIS_POSITION_RIGHT) {
2329 if ($Floating) {
2330 $FloatingOffset = $XMargin;
2331 $this->drawLine($AxisPos["R"], $this->GraphAreaY1 + $Parameters["Margin"], $AxisPos["R"], $this->GraphAreaY2 - $Parameters["Margin"], ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2332 } else {
2333 $FloatingOffset = 0;
2334 $this->drawLine($AxisPos["R"], $this->GraphAreaY1, $AxisPos["R"], $this->GraphAreaY2, ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2335 }
2336 if ($DrawArrows) {
2337 $this->drawArrow($AxisPos["R"], $this->GraphAreaY1 + $Parameters["Margin"], $AxisPos["R"], $this->GraphAreaY1 - $ArrowSize * 2, ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize]);
2338 }
2339 $Height = $this->GraphAreaY2 - $this->GraphAreaY1 - $Parameters["Margin"] * 2;
2340 $Step = $Height / $Parameters["Rows"];
2341 $SubTicksSize = $Step / 2;
2342 $MaxLeft = $AxisPos["R"];
2343 $LastY = null;
2344 for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
2345 $YPos = $this->GraphAreaY2 - $Parameters["Margin"] - $Step * $i;
2346 $XPos = $AxisPos["R"];
2347 $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, $Parameters["Display"], $Parameters["Format"], $Parameters["Unit"]);
2348 if ($i % 2 == 1) {
2349 $BGColor = ["R" => $BackgroundR1, "G" => $BackgroundG1, "B" => $BackgroundB1, "Alpha" => $BackgroundAlpha1];
2350 } else {
2351 $BGColor = ["R" => $BackgroundR2, "G" => $BackgroundG2, "B" => $BackgroundB2, "Alpha" => $BackgroundAlpha2];
2352 }
2353 if ($LastY != null && $CycleBackground && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines))) {
2354 $this->drawFilledRectangle($this->GraphAreaX1 + $FloatingOffset, $LastY, $this->GraphAreaX2 - $FloatingOffset, $YPos, $BGColor);
2355 }
2356 if ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) {
2357 $this->drawLine($this->GraphAreaX1 + $FloatingOffset, $YPos, $this->GraphAreaX2 - $FloatingOffset, $YPos, ["R" => $GridR, "G" => $GridG, "B" => $GridB, "Alpha" => $GridAlpha, "Ticks" => $GridTicks]);
2358 }
2359 if ($DrawSubTicks && $i != $Parameters["Rows"]) {
2360 $this->drawLine($XPos - $OuterSubTickWidth, $YPos - $SubTicksSize, $XPos + $InnerSubTickWidth, $YPos - $SubTicksSize, ["R" => $SubTickR, "G" => $SubTickG, "B" => $SubTickB, "Alpha" => $SubTickAlpha]);
2361 }
2362 $this->drawLine($XPos - $InnerTickWidth, $YPos, $XPos + $OuterTickWidth, $YPos, ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]);
2363 $Bounds = $this->drawText($XPos + $OuterTickWidth + 2, $YPos, $Value, ["Align" => TEXT_ALIGN_MIDDLELEFT]);
2364 $TxtLeft = $XPos + $OuterTickWidth + 2 + ($Bounds[1]["X"] - $Bounds[0]["X"]);
2365 $MaxLeft = max($MaxLeft, $TxtLeft);
2366 $LastY = $YPos;
2367 }
2368 if (isset($Parameters["Name"])) {
2369 $XPos = $MaxLeft + 6;
2370 $YPos = $this->GraphAreaY1 + ($this->GraphAreaY2 - $this->GraphAreaY1) / 2;
2371 $Bounds = $this->drawText($XPos, $YPos, $Parameters["Name"], ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 270]);
2372 $MaxLeft = $Bounds[2]["X"];
2373 $this->DataSet->Data["GraphArea"]["X2"] = $MaxLeft + $this->FontSize;
2374 }
2375 $AxisPos["R"] = $MaxLeft + $ScaleSpacing;
2376 }
2377 } elseif ($Pos == SCALE_POS_TOPBOTTOM) {
2378 if ($Parameters["Position"] == AXIS_POSITION_TOP) {
2379 if ($Floating) {
2380 $FloatingOffset = $XMargin;
2381 $this->drawLine($this->GraphAreaX1 + $Parameters["Margin"], $AxisPos["T"], $this->GraphAreaX2 - $Parameters["Margin"], $AxisPos["T"], ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2382 } else {
2383 $FloatingOffset = 0;
2384 $this->drawLine($this->GraphAreaX1, $AxisPos["T"], $this->GraphAreaX2, $AxisPos["T"], ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2385 }
2386 if ($DrawArrows) {
2387 $this->drawArrow($this->GraphAreaX2 - $Parameters["Margin"], $AxisPos["T"], $this->GraphAreaX2 + $ArrowSize * 2, $AxisPos["T"], ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize]);
2388 }
2389 $Width = $this->GraphAreaX2 - $this->GraphAreaX1 - $Parameters["Margin"] * 2;
2390 $Step = $Width / $Parameters["Rows"];
2391 $SubTicksSize = $Step / 2;
2392 $MinTop = $AxisPos["T"];
2393 $LastX = null;
2394 for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
2395 $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step * $i;
2396 $YPos = $AxisPos["T"];
2397 $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, $Parameters["Display"], $Parameters["Format"], $Parameters["Unit"]);
2398 if ($i % 2 == 1) {
2399 $BGColor = ["R" => $BackgroundR1, "G" => $BackgroundG1, "B" => $BackgroundB1, "Alpha" => $BackgroundAlpha1];
2400 } else {
2401 $BGColor = ["R" => $BackgroundR2, "G" => $BackgroundG2, "B" => $BackgroundB2, "Alpha" => $BackgroundAlpha2];
2402 }
2403 if ($LastX != null && $CycleBackground && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines))) {
2404 $this->drawFilledRectangle($LastX, $this->GraphAreaY1 + $FloatingOffset, $XPos, $this->GraphAreaY2 - $FloatingOffset, $BGColor);
2405 }
2406 if ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) {
2407 $this->drawLine($XPos, $this->GraphAreaY1 + $FloatingOffset, $XPos, $this->GraphAreaY2 - $FloatingOffset, ["R" => $GridR, "G" => $GridG, "B" => $GridB, "Alpha" => $GridAlpha, "Ticks" => $GridTicks]);
2408 }
2409 if ($DrawSubTicks && $i != $Parameters["Rows"]) {
2410 $this->drawLine($XPos + $SubTicksSize, $YPos - $OuterSubTickWidth, $XPos + $SubTicksSize, $YPos + $InnerSubTickWidth, ["R" => $SubTickR, "G" => $SubTickG, "B" => $SubTickB, "Alpha" => $SubTickAlpha]);
2411 }
2412 $this->drawLine($XPos, $YPos - $OuterTickWidth, $XPos, $YPos + $InnerTickWidth, ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]);
2413 $Bounds = $this->drawText($XPos, $YPos - $OuterTickWidth - 2, $Value, ["Align" => TEXT_ALIGN_BOTTOMMIDDLE]);
2414 $TxtHeight = $YPos - $OuterTickWidth - 2 - ($Bounds[1]["Y"] - $Bounds[2]["Y"]);
2415 $MinTop = min($MinTop, $TxtHeight);
2416 $LastX = $XPos;
2417 }
2418 if (isset($Parameters["Name"])) {
2419 $YPos = $MinTop - 2;
2420 $XPos = $this->GraphAreaX1 + ($this->GraphAreaX2 - $this->GraphAreaX1) / 2;
2421 $Bounds = $this->drawText($XPos, $YPos, $Parameters["Name"], ["Align" => TEXT_ALIGN_BOTTOMMIDDLE]);
2422 $MinTop = $Bounds[2]["Y"];
2423 $this->DataSet->Data["GraphArea"]["Y1"] = $MinTop;
2424 }
2425 $AxisPos["T"] = $MinTop - $ScaleSpacing;
2426 } elseif ($Parameters["Position"] == AXIS_POSITION_BOTTOM) {
2427 if ($Floating) {
2428 $FloatingOffset = $XMargin;
2429 $this->drawLine($this->GraphAreaX1 + $Parameters["Margin"], $AxisPos["B"], $this->GraphAreaX2 - $Parameters["Margin"], $AxisPos["B"], ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2430 } else {
2431 $FloatingOffset = 0;
2432 $this->drawLine($this->GraphAreaX1, $AxisPos["B"], $this->GraphAreaX2, $AxisPos["B"], ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]);
2433 }
2434 if ($DrawArrows) {
2435 $this->drawArrow($this->GraphAreaX2 - $Parameters["Margin"], $AxisPos["B"], $this->GraphAreaX2 + $ArrowSize * 2, $AxisPos["B"], ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize]);
2436 }
2437 $Width = $this->GraphAreaX2 - $this->GraphAreaX1 - $Parameters["Margin"] * 2;
2438 $Step = $Width / $Parameters["Rows"];
2439 $SubTicksSize = $Step / 2;
2440 $MaxBottom = $AxisPos["B"];
2441 $LastX = null;
2442 for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
2443 $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step * $i;
2444 $YPos = $AxisPos["B"];
2445 $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i, $Parameters["Display"], $Parameters["Format"], $Parameters["Unit"]);
2446 if ($i % 2 == 1) {
2447 $BGColor = ["R" => $BackgroundR1, "G" => $BackgroundG1, "B" => $BackgroundB1, "Alpha" => $BackgroundAlpha1];
2448 } else {
2449 $BGColor = ["R" => $BackgroundR2, "G" => $BackgroundG2, "B" => $BackgroundB2, "Alpha" => $BackgroundAlpha2];
2450 }
2451 if ($LastX != null && $CycleBackground && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines))) {
2452 $this->drawFilledRectangle($LastX, $this->GraphAreaY1 + $FloatingOffset, $XPos, $this->GraphAreaY2 - $FloatingOffset, $BGColor);
2453 }
2454 if ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) {
2455 $this->drawLine($XPos, $this->GraphAreaY1 + $FloatingOffset, $XPos, $this->GraphAreaY2 - $FloatingOffset, ["R" => $GridR, "G" => $GridG, "B" => $GridB, "Alpha" => $GridAlpha, "Ticks" => $GridTicks]);
2456 }
2457 if ($DrawSubTicks && $i != $Parameters["Rows"]) {
2458 $this->drawLine($XPos + $SubTicksSize, $YPos - $OuterSubTickWidth, $XPos + $SubTicksSize, $YPos + $InnerSubTickWidth, ["R" => $SubTickR, "G" => $SubTickG, "B" => $SubTickB, "Alpha" => $SubTickAlpha]);
2459 }
2460 $this->drawLine($XPos, $YPos - $OuterTickWidth, $XPos, $YPos + $InnerTickWidth, ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]);
2461 $Bounds = $this->drawText($XPos, $YPos + $OuterTickWidth + 2, $Value, ["Align" => TEXT_ALIGN_TOPMIDDLE]);
2462 $TxtHeight = $YPos + $OuterTickWidth + 2 + ($Bounds[1]["Y"] - $Bounds[2]["Y"]);
2463 $MaxBottom = max($MaxBottom, $TxtHeight);
2464 $LastX = $XPos;
2465 }
2466 if (isset($Parameters["Name"])) {
2467 $YPos = $MaxBottom + 2;
2468 $XPos = $this->GraphAreaX1 + ($this->GraphAreaX2 - $this->GraphAreaX1) / 2;
2469 $Bounds = $this->drawText($XPos, $YPos, $Parameters["Name"], ["Align" => TEXT_ALIGN_TOPMIDDLE]);
2470 $MaxBottom = $Bounds[0]["Y"];
2471 $this->DataSet->Data["GraphArea"]["Y2"] = $MaxBottom + $this->FontSize;
2472 }
2473 $AxisPos["B"] = $MaxBottom + $ScaleSpacing;
2474 }
2475 }
2476 }
2477 }
2478 }
2479 /**
2480 * Draw an X threshold
2481 * @param mixed $Value
2482 * @param boolean $Format
2483 * @return array|null|integer
2484 */
2485 public function drawXThreshold($Value, array $Format = [])
2486 {
2487 $R = isset($Format["R"]) ? $Format["R"] : 255;
2488 $G = isset($Format["G"]) ? $Format["G"] : 0;
2489 $B = isset($Format["B"]) ? $Format["B"] : 0;
2490 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50;
2491 $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null;
2492 $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 6;
2493 $Wide = isset($Format["Wide"]) ? $Format["Wide"] : \false;
2494 $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5;
2495 $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : \false;
2496 $Caption = isset($Format["Caption"]) ? $Format["Caption"] : null;
2497 $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP;
2498 $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 5;
2499 $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255;
2500 $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255;
2501 $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255;
2502 $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100;
2503 $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : \true;
2504 $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : \false;
2505 $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 3;
2506 $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : \true;
2507 $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3;
2508 $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0;
2509 $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0;
2510 $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0;
2511 $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 30;
2512 $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : "";
2513 $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255;
2514 $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255;
2515 $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255;
2516 $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100;
2517 $ValueIsLabel = isset($Format["ValueIsLabel"]) ? $Format["ValueIsLabel"] : \false;
2518 $Data = $this->DataSet->getData();
2519 $AbscissaMargin = $this->getAbscissaMargin($Data);
2520 $XScale = $this->scaleGetXSettings();
2521 if (is_array($Value)) {
2522 foreach ($Value as $Key => $ID) {
2523 $this->drawXThreshold($ID, $Format);
2524 }
2525 return 0;
2526 }
2527 if ($ValueIsLabel) {
2528 $Format["ValueIsLabel"] = \false;
2529 foreach ($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $SerieValue) {
2530 if ($SerieValue == $Value) {
2531 $this->drawXThreshold($Key, $Format);
2532 }
2533 }
2534 return 0;
2535 }
2536 $CaptionSettings = ["DrawBox" => $DrawBox, "DrawBoxBorder" => $DrawBoxBorder, "BorderOffset" => $BorderOffset, "BoxRounded" => $BoxRounded, "RoundedRadius" => $RoundedRadius, "BoxR" => $BoxR, "BoxG" => $BoxG, "BoxB" => $BoxB, "BoxAlpha" => $BoxAlpha, "BoxSurrounding" => $BoxSurrounding, "BoxBorderR" => $BoxBorderR, "BoxBorderG" => $BoxBorderG, "BoxBorderB" => $BoxBorderB, "BoxBorderAlpha" => $BoxBorderAlpha, "R" => $CaptionR, "G" => $CaptionG, "B" => $CaptionB, "Alpha" => $CaptionAlpha];
2537 if ($Caption == null) {
2538 $Caption = $Value;
2539 if (isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Value])) {
2540 $Caption = $Data["Series"][$Data["Abscissa"]]["Data"][$Value];
2541 }
2542 }
2543 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
2544 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XScale[0] * 2) / $XScale[1];
2545 $XPos = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value;
2546 $YPos1 = $this->GraphAreaY1 + $Data["YMargin"];
2547 $YPos2 = $this->GraphAreaY2 - $Data["YMargin"];
2548 if ($XPos >= $this->GraphAreaX1 + $AbscissaMargin && $XPos <= $this->GraphAreaX2 - $AbscissaMargin) {
2549 $this->drawLine($XPos, $YPos1, $XPos, $YPos2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight]);
2550 if ($Wide) {
2551 $this->drawLine($XPos - 1, $YPos1, $XPos - 1, $YPos2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / $WideFactor, "Ticks" => $Ticks]);
2552 $this->drawLine($XPos + 1, $YPos1, $XPos + 1, $YPos2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / $WideFactor, "Ticks" => $Ticks]);
2553 }
2554 if ($WriteCaption) {
2555 if ($CaptionAlign == CAPTION_LEFT_TOP) {
2556 $Y = $YPos1 + $CaptionOffset;
2557 $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE;
2558 } else {
2559 $Y = $YPos2 - $CaptionOffset;
2560 $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE;
2561 }
2562 $this->drawText($XPos, $Y, $Caption, $CaptionSettings);
2563 }
2564 return ["X" => $XPos];
2565 }
2566 } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) {
2567 $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XScale[0] * 2) / $XScale[1];
2568 $XPos = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value;
2569 $YPos1 = $this->GraphAreaX1 + $Data["YMargin"];
2570 $YPos2 = $this->GraphAreaX2 - $Data["YMargin"];
2571 if ($XPos >= $this->GraphAreaY1 + $AbscissaMargin && $XPos <= $this->GraphAreaY2 - $AbscissaMargin) {
2572 $this->drawLine($YPos1, $XPos, $YPos2, $XPos, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight]);
2573 if ($Wide) {
2574 $this->drawLine($YPos1, $XPos - 1, $YPos2, $XPos - 1, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / $WideFactor, "Ticks" => $Ticks]);
2575 $this->drawLine($YPos1, $XPos + 1, $YPos2, $XPos + 1, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / $WideFactor, "Ticks" => $Ticks]);
2576 }
2577 if ($WriteCaption) {
2578 if ($CaptionAlign == CAPTION_LEFT_TOP) {
2579 $Y = $YPos1 + $CaptionOffset;
2580 $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT;
2581 } else {
2582 $Y = $YPos2 - $CaptionOffset;
2583 $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT;
2584 }
2585 $this->drawText($Y, $XPos, $Caption, $CaptionSettings);
2586 }
2587 return ["X" => $XPos];
2588 }
2589 }
2590 }
2591 /**
2592 * Draw an X threshold area
2593 * @param mixed $Value1
2594 * @param mixed $Value2
2595 * @param array $Format
2596 * @return array|null
2597 */
2598 public function drawXThresholdArea($Value1, $Value2, array $Format = [])
2599 {
2600 $R = isset($Format["R"]) ? $Format["R"] : 255;
2601 $G = isset($Format["G"]) ? $Format["G"] : 0;
2602 $B = isset($Format["B"]) ? $Format["B"] : 0;
2603 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20;
2604 $Border = isset($Format["Border"]) ? $Format["Border"] : \true;
2605 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
2606 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
2607 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
2608 $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20;
2609 $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2;
2610 $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : null;
2611 $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO;
2612 $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255;
2613 $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255;
2614 $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255;
2615 $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100;
2616 $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : \true;
2617 $RestoreShadow = $this->Shadow;
2618 if ($DisableShadowOnArea && $this->Shadow) {
2619 $this->Shadow = \false;
2620 }
2621 if ($BorderAlpha > 100) {
2622 $BorderAlpha = 100;
2623 }
2624 $Data = $this->DataSet->getData();
2625 $XScale = $this->scaleGetXSettings();
2626 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
2627 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XScale[0] * 2) / $XScale[1];
2628 $XPos1 = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value1;
2629 $XPos2 = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value2;
2630 $YPos1 = $this->GraphAreaY1 + $Data["YMargin"];
2631 $YPos2 = $this->GraphAreaY2 - $Data["YMargin"];
2632 if ($XPos1 < $this->GraphAreaX1 + $XScale[0]) {
2633 $XPos1 = $this->GraphAreaX1 + $XScale[0];
2634 }
2635 if ($XPos1 > $this->GraphAreaX2 - $XScale[0]) {
2636 $XPos1 = $this->GraphAreaX2 - $XScale[0];
2637 }
2638 if ($XPos2 < $this->GraphAreaX1 + $XScale[0]) {
2639 $XPos2 = $this->GraphAreaX1 + $XScale[0];
2640 }
2641 if ($XPos2 > $this->GraphAreaX2 - $XScale[0]) {
2642 $XPos2 = $this->GraphAreaX2 - $XScale[0];
2643 }
2644 $this->drawFilledRectangle($XPos1, $YPos1, $XPos2, $YPos2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
2645 if ($Border) {
2646 $this->drawLine($XPos1, $YPos1, $XPos1, $YPos2, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha, "Ticks" => $BorderTicks]);
2647 $this->drawLine($XPos2, $YPos1, $XPos2, $YPos2, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha, "Ticks" => $BorderTicks]);
2648 }
2649 if ($AreaName != null) {
2650 $XPos = ($XPos2 - $XPos1) / 2 + $XPos1;
2651 $YPos = ($YPos2 - $YPos1) / 2 + $YPos1;
2652 if ($NameAngle == ZONE_NAME_ANGLE_AUTO) {
2653 $TxtPos = $this->getTextBox($XPos, $YPos, $this->FontName, $this->FontSize, 0, $AreaName);
2654 $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"];
2655 $NameAngle = 90;
2656 if (abs($XPos2 - $XPos1) > $TxtWidth) {
2657 $NameAngle = 0;
2658 }
2659 }
2660 $this->Shadow = $RestoreShadow;
2661 $this->drawText($XPos, $YPos, $AreaName, ["R" => $NameR, "G" => $NameG, "B" => $NameB, "Alpha" => $NameAlpha, "Angle" => $NameAngle, "Align" => TEXT_ALIGN_MIDDLEMIDDLE]);
2662 if ($DisableShadowOnArea) {
2663 $this->Shadow = \false;
2664 }
2665 }
2666 $this->Shadow = $RestoreShadow;
2667 return ["X1" => $XPos1, "X2" => $XPos2];
2668 } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) {
2669 $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XScale[0] * 2) / $XScale[1];
2670 $XPos1 = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value1;
2671 $XPos2 = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value2;
2672 $YPos1 = $this->GraphAreaX1 + $Data["YMargin"];
2673 $YPos2 = $this->GraphAreaX2 - $Data["YMargin"];
2674 if ($XPos1 < $this->GraphAreaY1 + $XScale[0]) {
2675 $XPos1 = $this->GraphAreaY1 + $XScale[0];
2676 }
2677 if ($XPos1 > $this->GraphAreaY2 - $XScale[0]) {
2678 $XPos1 = $this->GraphAreaY2 - $XScale[0];
2679 }
2680 if ($XPos2 < $this->GraphAreaY1 + $XScale[0]) {
2681 $XPos2 = $this->GraphAreaY1 + $XScale[0];
2682 }
2683 if ($XPos2 > $this->GraphAreaY2 - $XScale[0]) {
2684 $XPos2 = $this->GraphAreaY2 - $XScale[0];
2685 }
2686 $this->drawFilledRectangle($YPos1, $XPos1, $YPos2, $XPos2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
2687 if ($Border) {
2688 $this->drawLine($YPos1, $XPos1, $YPos2, $XPos1, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha, "Ticks" => $BorderTicks]);
2689 $this->drawLine($YPos1, $XPos2, $YPos2, $XPos2, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha, "Ticks" => $BorderTicks]);
2690 }
2691 if ($AreaName != null) {
2692 $XPos = ($XPos2 - $XPos1) / 2 + $XPos1;
2693 $YPos = ($YPos2 - $YPos1) / 2 + $YPos1;
2694 $this->Shadow = $RestoreShadow;
2695 $this->drawText($YPos, $XPos, $AreaName, ["R" => $NameR, "G" => $NameG, "B" => $NameB, "Alpha" => $NameAlpha, "Angle" => 0, "Align" => TEXT_ALIGN_MIDDLEMIDDLE]);
2696 if ($DisableShadowOnArea) {
2697 $this->Shadow = \false;
2698 }
2699 }
2700 $this->Shadow = $RestoreShadow;
2701 return ["X1" => $XPos1, "X2" => $XPos2];
2702 }
2703 }
2704 /**
2705 * Draw an Y threshold with the computed scale
2706 * @param mixed $Value
2707 * @param array $Format
2708 * @return array|int
2709 */
2710 public function drawThreshold($Value, array $Format = [])
2711 {
2712 $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0;
2713 $R = isset($Format["R"]) ? $Format["R"] : 255;
2714 $G = isset($Format["G"]) ? $Format["G"] : 0;
2715 $B = isset($Format["B"]) ? $Format["B"] : 0;
2716 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50;
2717 $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null;
2718 $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 6;
2719 $Wide = isset($Format["Wide"]) ? $Format["Wide"] : \false;
2720 $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5;
2721 $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : \false;
2722 $Caption = isset($Format["Caption"]) ? $Format["Caption"] : null;
2723 $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP;
2724 $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 10;
2725 $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255;
2726 $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255;
2727 $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255;
2728 $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100;
2729 $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : \true;
2730 $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : \false;
2731 $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5;
2732 $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : \true;
2733 $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3;
2734 $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0;
2735 $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0;
2736 $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0;
2737 $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20;
2738 $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : "";
2739 $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255;
2740 $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255;
2741 $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255;
2742 $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100;
2743 $NoMargin = isset($Format["NoMargin"]) ? $Format["NoMargin"] : \false;
2744 if (is_array($Value)) {
2745 foreach ($Value as $Key => $ID) {
2746 $this->drawThreshold($ID, $Format);
2747 }
2748 return 0;
2749 }
2750 $CaptionSettings = ["DrawBox" => $DrawBox, "DrawBoxBorder" => $DrawBoxBorder, "BorderOffset" => $BorderOffset, "BoxRounded" => $BoxRounded, "RoundedRadius" => $RoundedRadius, "BoxR" => $BoxR, "BoxG" => $BoxG, "BoxB" => $BoxB, "BoxAlpha" => $BoxAlpha, "BoxSurrounding" => $BoxSurrounding, "BoxBorderR" => $BoxBorderR, "BoxBorderG" => $BoxBorderG, "BoxBorderB" => $BoxBorderB, "BoxBorderAlpha" => $BoxBorderAlpha, "R" => $CaptionR, "G" => $CaptionG, "B" => $CaptionB, "Alpha" => $CaptionAlpha];
2751 $Data = $this->DataSet->getData();
2752 $AbscissaMargin = $this->getAbscissaMargin($Data);
2753 if ($NoMargin) {
2754 $AbscissaMargin = 0;
2755 }
2756 if (!isset($Data["Axis"][$AxisID])) {
2757 return -1;
2758 }
2759 if ($Caption == null) {
2760 $Caption = $Value;
2761 }
2762 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
2763 $YPos = $this->scaleComputeY($Value, ["AxisID" => $AxisID]);
2764 if ($YPos >= $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"] && $YPos <= $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]) {
2765 $X1 = $this->GraphAreaX1 + $AbscissaMargin;
2766 $X2 = $this->GraphAreaX2 - $AbscissaMargin;
2767 $this->drawLine($X1, $YPos, $X2, $YPos, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight]);
2768 if ($Wide) {
2769 $this->drawLine($X1, $YPos - 1, $X2, $YPos - 1, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / $WideFactor, "Ticks" => $Ticks]);
2770 $this->drawLine($X1, $YPos + 1, $X2, $YPos + 1, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / $WideFactor, "Ticks" => $Ticks]);
2771 }
2772 if ($WriteCaption) {
2773 if ($CaptionAlign == CAPTION_LEFT_TOP) {
2774 $X = $X1 + $CaptionOffset;
2775 $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT;
2776 } else {
2777 $X = $X2 - $CaptionOffset;
2778 $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT;
2779 }
2780 $this->drawText($X, $YPos, $Caption, $CaptionSettings);
2781 }
2782 }
2783 return ["Y" => $YPos];
2784 }
2785 if ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) {
2786 $XPos = $this->scaleComputeY($Value, ["AxisID" => $AxisID]);
2787 if ($XPos >= $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"] && $XPos <= $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]) {
2788 $Y1 = $this->GraphAreaY1 + $AbscissaMargin;
2789 $Y2 = $this->GraphAreaY2 - $AbscissaMargin;
2790 $this->drawLine($XPos, $Y1, $XPos, $Y2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight]);
2791 if ($Wide) {
2792 $this->drawLine($XPos - 1, $Y1, $XPos - 1, $Y2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / $WideFactor, "Ticks" => $Ticks]);
2793 $this->drawLine($XPos + 1, $Y1, $XPos + 1, $Y2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / $WideFactor, "Ticks" => $Ticks]);
2794 }
2795 if ($WriteCaption) {
2796 if ($CaptionAlign == CAPTION_LEFT_TOP) {
2797 $Y = $Y1 + $CaptionOffset;
2798 $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE;
2799 } else {
2800 $Y = $Y2 - $CaptionOffset;
2801 $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE;
2802 }
2803 $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE;
2804 $this->drawText($XPos, $Y, $Caption, $CaptionSettings);
2805 }
2806 }
2807 return ["Y" => $XPos];
2808 }
2809 }
2810 /**
2811 * Draw a threshold with the computed scale
2812 * @param mixed $Value1
2813 * @param mixed $Value2
2814 * @param array $Format
2815 * @return array|int|null
2816 */
2817 public function drawThresholdArea($Value1, $Value2, array $Format = [])
2818 {
2819 $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0;
2820 $R = isset($Format["R"]) ? $Format["R"] : 255;
2821 $G = isset($Format["G"]) ? $Format["G"] : 0;
2822 $B = isset($Format["B"]) ? $Format["B"] : 0;
2823 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20;
2824 $Border = isset($Format["Border"]) ? $Format["Border"] : \true;
2825 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
2826 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
2827 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
2828 $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20;
2829 $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2;
2830 $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : null;
2831 $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO;
2832 $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255;
2833 $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255;
2834 $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255;
2835 $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100;
2836 $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : \true;
2837 $NoMargin = isset($Format["NoMargin"]) ? $Format["NoMargin"] : \false;
2838 if ($Value1 > $Value2) {
2839 list($Value1, $Value2) = [$Value2, $Value1];
2840 }
2841 $RestoreShadow = $this->Shadow;
2842 if ($DisableShadowOnArea && $this->Shadow) {
2843 $this->Shadow = \false;
2844 }
2845 if ($BorderAlpha > 100) {
2846 $BorderAlpha = 100;
2847 }
2848 $Data = $this->DataSet->getData();
2849 $AbscissaMargin = $this->getAbscissaMargin($Data);
2850 if ($NoMargin) {
2851 $AbscissaMargin = 0;
2852 }
2853 if (!isset($Data["Axis"][$AxisID])) {
2854 return -1;
2855 }
2856 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
2857 $XPos1 = $this->GraphAreaX1 + $AbscissaMargin;
2858 $XPos2 = $this->GraphAreaX2 - $AbscissaMargin;
2859 $YPos1 = $this->scaleComputeY($Value1, ["AxisID" => $AxisID]);
2860 $YPos2 = $this->scaleComputeY($Value2, ["AxisID" => $AxisID]);
2861 if ($YPos1 < $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]) {
2862 $YPos1 = $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"];
2863 }
2864 if ($YPos1 > $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]) {
2865 $YPos1 = $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"];
2866 }
2867 if ($YPos2 < $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]) {
2868 $YPos2 = $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"];
2869 }
2870 if ($YPos2 > $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]) {
2871 $YPos2 = $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"];
2872 }
2873 $this->drawFilledRectangle($XPos1, $YPos1, $XPos2, $YPos2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
2874 if ($Border) {
2875 $this->drawLine($XPos1, $YPos1, $XPos2, $YPos1, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha, "Ticks" => $BorderTicks]);
2876 $this->drawLine($XPos1, $YPos2, $XPos2, $YPos2, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha, "Ticks" => $BorderTicks]);
2877 }
2878 if ($AreaName != null) {
2879 $XPos = ($XPos2 - $XPos1) / 2 + $XPos1;
2880 $YPos = ($YPos2 - $YPos1) / 2 + $YPos1;
2881 $this->Shadow = $RestoreShadow;
2882 $this->drawText($XPos, $YPos, $AreaName, ["R" => $NameR, "G" => $NameG, "B" => $NameB, "Alpha" => $NameAlpha, "Angle" => 0, "Align" => TEXT_ALIGN_MIDDLEMIDDLE]);
2883 if ($DisableShadowOnArea) {
2884 $this->Shadow = \false;
2885 }
2886 }
2887 $this->Shadow = $RestoreShadow;
2888 return ["Y1" => $YPos1, "Y2" => $YPos2];
2889 } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) {
2890 $YPos1 = $this->GraphAreaY1 + $AbscissaMargin;
2891 $YPos2 = $this->GraphAreaY2 - $AbscissaMargin;
2892 $XPos1 = $this->scaleComputeY($Value1, ["AxisID" => $AxisID]);
2893 $XPos2 = $this->scaleComputeY($Value2, ["AxisID" => $AxisID]);
2894 if ($XPos1 < $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]) {
2895 $XPos1 = $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"];
2896 }
2897 if ($XPos1 > $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]) {
2898 $XPos1 = $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"];
2899 }
2900 if ($XPos2 < $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]) {
2901 $XPos2 = $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"];
2902 }
2903 if ($XPos2 > $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]) {
2904 $XPos2 = $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"];
2905 }
2906 $this->drawFilledRectangle($XPos1, $YPos1, $XPos2, $YPos2, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
2907 if ($Border) {
2908 $this->drawLine($XPos1, $YPos1, $XPos1, $YPos2, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha, "Ticks" => $BorderTicks]);
2909 $this->drawLine($XPos2, $YPos1, $XPos2, $YPos2, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha, "Ticks" => $BorderTicks]);
2910 }
2911 if ($AreaName != null) {
2912 $XPos = ($YPos2 - $YPos1) / 2 + $YPos1;
2913 $YPos = ($XPos2 - $XPos1) / 2 + $XPos1;
2914 if ($NameAngle == ZONE_NAME_ANGLE_AUTO) {
2915 $TxtPos = $this->getTextBox($XPos, $YPos, $this->FontName, $this->FontSize, 0, $AreaName);
2916 $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"];
2917 $NameAngle = 90;
2918 if (abs($XPos2 - $XPos1) > $TxtWidth) {
2919 $NameAngle = 0;
2920 }
2921 }
2922 $this->Shadow = $RestoreShadow;
2923 $this->drawText($YPos, $XPos, $AreaName, ["R" => $NameR, "G" => $NameG, "B" => $NameB, "Alpha" => $NameAlpha, "Angle" => $NameAngle, "Align" => TEXT_ALIGN_MIDDLEMIDDLE]);
2924 if ($DisableShadowOnArea) {
2925 $this->Shadow = \false;
2926 }
2927 }
2928 $this->Shadow = $RestoreShadow;
2929 return ["Y1" => $XPos1, "Y2" => $XPos2];
2930 }
2931 }
2932 /**
2933 * Draw a plot chart
2934 * @param array $Format
2935 */
2936 public function drawPlotChart(array $Format = [])
2937 {
2938 $PlotSize = isset($Format["PlotSize"]) ? $Format["PlotSize"] : null;
2939 $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : \false;
2940 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 50;
2941 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 50;
2942 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 50;
2943 $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30;
2944 $BorderSize = isset($Format["BorderSize"]) ? $Format["BorderSize"] : 2;
2945 $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
2946 $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : \false;
2947 $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 4;
2948 $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
2949 $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
2950 $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
2951 $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
2952 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : \false;
2953 $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
2954 $Data = $this->DataSet->getData();
2955 list($XMargin, $XDivs) = $this->scaleGetXSettings();
2956 foreach ($Data["Series"] as $SerieName => $Serie) {
2957 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
2958 if (isset($Serie["Weight"])) {
2959 $SerieWeight = $Serie["Weight"] + 2;
2960 } else {
2961 $SerieWeight = 2;
2962 }
2963 if ($PlotSize != null) {
2964 $SerieWeight = $PlotSize;
2965 }
2966 $R = $Serie["Color"]["R"];
2967 $G = $Serie["Color"]["G"];
2968 $B = $Serie["Color"]["B"];
2969 $Alpha = (int) $Serie["Color"]["Alpha"];
2970 $Ticks = $Serie["Ticks"];
2971 if ($Surrounding != null) {
2972 $BorderR = $R + $Surrounding;
2973 $BorderG = $G + $Surrounding;
2974 $BorderB = $B + $Surrounding;
2975 }
2976 if (isset($Serie["Picture"])) {
2977 $Picture = $Serie["Picture"];
2978 list($PicWidth, $PicHeight, $PicType) = $this->getPicInfo($Picture);
2979 } else {
2980 $Picture = null;
2981 $PicOffset = 0;
2982 }
2983 if ($DisplayColor == DISPLAY_AUTO) {
2984 $DisplayR = $R;
2985 $DisplayG = $G;
2986 $DisplayB = $B;
2987 }
2988 $AxisID = $Serie["Axis"];
2989 $Shape = $Serie["Shape"];
2990 $Mode = $Data["Axis"][$AxisID]["Display"];
2991 $Format = $Data["Axis"][$AxisID]["Format"];
2992 $Unit = $Data["Axis"][$AxisID]["Unit"];
2993 if (isset($Serie["Description"])) {
2994 $SerieDescription = $Serie["Description"];
2995 } else {
2996 $SerieDescription = $SerieName;
2997 }
2998 $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
2999 $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
3000 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
3001 if ($XDivs == 0) {
3002 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
3003 } else {
3004 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
3005 }
3006 if ($Picture != null) {
3007 $PicOffset = $PicHeight / 2;
3008 $SerieWeight = 0;
3009 }
3010 $X = $this->GraphAreaX1 + $XMargin;
3011 if (!is_array($PosArray)) {
3012 $Value = $PosArray;
3013 $PosArray = [];
3014 $PosArray[0] = $Value;
3015 }
3016 foreach ($PosArray as $Key => $Y) {
3017 if ($DisplayValues) {
3018 $this->drawText($X, $Y - $DisplayOffset - $SerieWeight - $BorderSize - $PicOffset, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]);
3019 }
3020 if ($Y != VOID) {
3021 if ($RecordImageMap) {
3022 $this->addToImageMap("CIRCLE", floor($X) . "," . floor($Y) . "," . $SerieWeight, $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3023 }
3024 if ($Picture != null) {
3025 $this->drawFromPicture($PicType, $Picture, $X - $PicWidth / 2, $Y - $PicHeight / 2);
3026 } else {
3027 $this->drawShape($X, $Y, $Shape, $SerieWeight, $PlotBorder, $BorderSize, $R, $G, $B, $Alpha, $BorderR, $BorderG, $BorderB, $BorderAlpha);
3028 }
3029 }
3030 $X = $X + $XStep;
3031 }
3032 } else {
3033 if ($XDivs == 0) {
3034 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
3035 } else {
3036 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
3037 }
3038 if ($Picture != null) {
3039 $PicOffset = $PicWidth / 2;
3040 $SerieWeight = 0;
3041 }
3042 $Y = $this->GraphAreaY1 + $XMargin;
3043 if (!is_array($PosArray)) {
3044 $Value = $PosArray;
3045 $PosArray = [];
3046 $PosArray[0] = $Value;
3047 }
3048 foreach ($PosArray as $Key => $X) {
3049 if ($DisplayValues) {
3050 $this->drawText($X + $DisplayOffset + $SerieWeight + $BorderSize + $PicOffset, $Y, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["Angle" => 270, "R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]);
3051 }
3052 if ($X != VOID) {
3053 if ($RecordImageMap) {
3054 $this->addToImageMap("CIRCLE", floor($X) . "," . floor($Y) . "," . $SerieWeight, $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3055 }
3056 if ($Picture != null) {
3057 $this->drawFromPicture($PicType, $Picture, $X - $PicWidth / 2, $Y - $PicHeight / 2);
3058 } else {
3059 $this->drawShape($X, $Y, $Shape, $SerieWeight, $PlotBorder, $BorderSize, $R, $G, $B, $Alpha, $BorderR, $BorderG, $BorderB, $BorderAlpha);
3060 }
3061 }
3062 $Y = $Y + $YStep;
3063 }
3064 }
3065 }
3066 }
3067 }
3068 /**
3069 * Draw a spline chart
3070 * @param array $Format
3071 */
3072 public function drawSplineChart(array $Format = [])
3073 {
3074 $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : \true;
3075 $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4;
3076 $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : null;
3077 // 234
3078 $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : null;
3079 // 55
3080 $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : null;
3081 // 26
3082 $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : \false;
3083 $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
3084 $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
3085 $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
3086 $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
3087 $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
3088 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : \false;
3089 $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5;
3090 $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
3091 $Data = $this->DataSet->getData();
3092 list($XMargin, $XDivs) = $this->scaleGetXSettings();
3093 foreach ($Data["Series"] as $SerieName => $Serie) {
3094 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
3095 $R = $Serie["Color"]["R"];
3096 $G = $Serie["Color"]["G"];
3097 $B = $Serie["Color"]["B"];
3098 $Alpha = $Serie["Color"]["Alpha"];
3099 $Ticks = $Serie["Ticks"];
3100 $Weight = $Serie["Weight"];
3101 if ($BreakR == null) {
3102 $BreakSettings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $VoidTicks];
3103 } else {
3104 $BreakSettings = ["R" => $BreakR, "G" => $BreakG, "B" => $BreakB, "Alpha" => $Alpha, "Ticks" => $VoidTicks, "Weight" => $Weight];
3105 }
3106 if ($DisplayColor == DISPLAY_AUTO) {
3107 $DisplayR = $R;
3108 $DisplayG = $G;
3109 $DisplayB = $B;
3110 }
3111 $AxisID = $Serie["Axis"];
3112 $Mode = $Data["Axis"][$AxisID]["Display"];
3113 $Format = $Data["Axis"][$AxisID]["Format"];
3114 $Unit = $Data["Axis"][$AxisID]["Unit"];
3115 if (isset($Serie["Description"])) {
3116 $SerieDescription = $Serie["Description"];
3117 } else {
3118 $SerieDescription = $SerieName;
3119 }
3120 $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
3121 $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
3122 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
3123 if ($XDivs == 0) {
3124 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
3125 } else {
3126 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
3127 }
3128 $X = $this->GraphAreaX1 + $XMargin;
3129 $WayPoints = [];
3130 $Force = $XStep / 5;
3131 if (!is_array($PosArray)) {
3132 $Value = $PosArray;
3133 $PosArray = [];
3134 $PosArray[0] = $Value;
3135 }
3136 $LastGoodY = null;
3137 $LastGoodX = null;
3138 $LastX = 1;
3139 $LastY = 1;
3140 foreach ($PosArray as $Key => $Y) {
3141 if ($DisplayValues) {
3142 $this->drawText($X, $Y - $DisplayOffset, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]);
3143 }
3144 if ($RecordImageMap && $Y != VOID) {
3145 $this->addToImageMap("CIRCLE", floor($X) . "," . floor($Y) . "," . $ImageMapPlotSize, $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3146 }
3147 if ($Y == VOID && $LastY != null) {
3148 $this->drawSpline($WayPoints, ["Force" => $Force, "R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight]);
3149 $WayPoints = [];
3150 }
3151 if ($Y != VOID && $LastY == null && $LastGoodY != null && !$BreakVoid) {
3152 $this->drawLine($LastGoodX, $LastGoodY, $X, $Y, $BreakSettings);
3153 }
3154 if ($Y != VOID) {
3155 $WayPoints[] = [$X, $Y];
3156 }
3157 if ($Y != VOID) {
3158 $LastGoodY = $Y;
3159 $LastGoodX = $X;
3160 }
3161 if ($Y == VOID) {
3162 $Y = null;
3163 }
3164 $LastX = $X;
3165 $LastY = $Y;
3166 $X = $X + $XStep;
3167 }
3168 $this->drawSpline($WayPoints, ["Force" => $Force, "R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight]);
3169 } else {
3170 if ($XDivs == 0) {
3171 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
3172 } else {
3173 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
3174 }
3175 $Y = $this->GraphAreaY1 + $XMargin;
3176 $WayPoints = [];
3177 $Force = $YStep / 5;
3178 if (!is_array($PosArray)) {
3179 $Value = $PosArray;
3180 $PosArray = [];
3181 $PosArray[0] = $Value;
3182 }
3183 $LastGoodY = null;
3184 $LastGoodX = null;
3185 $LastX = 1;
3186 $LastY = 1;
3187 foreach ($PosArray as $Key => $X) {
3188 if ($DisplayValues) {
3189 $this->drawText($X + $DisplayOffset, $Y, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["Angle" => 270, "R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]);
3190 }
3191 if ($RecordImageMap && $X != VOID) {
3192 $this->addToImageMap("CIRCLE", floor($X) . "," . floor($Y) . "," . $ImageMapPlotSize, $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3193 }
3194 if ($X == VOID && $LastX != null) {
3195 $this->drawSpline($WayPoints, ["Force" => $Force, "R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight]);
3196 $WayPoints = [];
3197 }
3198 if ($X != VOID && $LastX == null && $LastGoodX != null && !$BreakVoid) {
3199 $this->drawLine($LastGoodX, $LastGoodY, $X, $Y, $BreakSettings);
3200 }
3201 if ($X != VOID) {
3202 $WayPoints[] = [$X, $Y];
3203 }
3204 if ($X != VOID) {
3205 $LastGoodX = $X;
3206 $LastGoodY = $Y;
3207 }
3208 if ($X == VOID) {
3209 $X = null;
3210 }
3211 $LastX = $X;
3212 $LastY = $Y;
3213 $Y = $Y + $YStep;
3214 }
3215 $this->drawSpline($WayPoints, ["Force" => $Force, "R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight]);
3216 }
3217 }
3218 }
3219 }
3220 /**
3221 * Draw a filled spline chart
3222 * @param array $Format
3223 */
3224 public function drawFilledSplineChart(array $Format = [])
3225 {
3226 $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : \false;
3227 $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
3228 $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
3229 $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
3230 $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
3231 $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
3232 $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : \true;
3233 $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : null;
3234 $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
3235 $Data = $this->DataSet->getData();
3236 list($XMargin, $XDivs) = $this->scaleGetXSettings();
3237 foreach ($Data["Series"] as $SerieName => $Serie) {
3238 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
3239 $R = $Serie["Color"]["R"];
3240 $G = $Serie["Color"]["G"];
3241 $B = $Serie["Color"]["B"];
3242 $Alpha = $Serie["Color"]["Alpha"];
3243 $Ticks = $Serie["Ticks"];
3244 if ($DisplayColor == DISPLAY_AUTO) {
3245 $DisplayR = $R;
3246 $DisplayG = $G;
3247 $DisplayB = $B;
3248 }
3249 $AxisID = $Serie["Axis"];
3250 $Mode = $Data["Axis"][$AxisID]["Display"];
3251 $Format = $Data["Axis"][$AxisID]["Format"];
3252 $Unit = $Data["Axis"][$AxisID]["Unit"];
3253 $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
3254 if ($AroundZero) {
3255 $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]);
3256 }
3257 if ($Threshold != null) {
3258 foreach ($Threshold as $Key => $Params) {
3259 $Threshold[$Key]["MinX"] = $this->scaleComputeY($Params["Min"], ["AxisID" => $Serie["Axis"]]);
3260 $Threshold[$Key]["MaxX"] = $this->scaleComputeY($Params["Max"], ["AxisID" => $Serie["Axis"]]);
3261 }
3262 }
3263 $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
3264 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
3265 if ($XDivs == 0) {
3266 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
3267 } else {
3268 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
3269 }
3270 $X = $this->GraphAreaX1 + $XMargin;
3271 $WayPoints = [];
3272 $Force = $XStep / 5;
3273 if (!$AroundZero) {
3274 $YZero = $this->GraphAreaY2 - 1;
3275 }
3276 if ($YZero > $this->GraphAreaY2 - 1) {
3277 $YZero = $this->GraphAreaY2 - 1;
3278 }
3279 if ($YZero < $this->GraphAreaY1 + 1) {
3280 $YZero = $this->GraphAreaY1 + 1;
3281 }
3282 if (!is_array($PosArray)) {
3283 $Value = $PosArray;
3284 $PosArray = [];
3285 $PosArray[0] = $Value;
3286 }
3287 foreach ($PosArray as $Key => $Y) {
3288 if ($DisplayValues) {
3289 $this->drawText($X, $Y - $DisplayOffset, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]);
3290 }
3291 if ($Y == VOID) {
3292 $Area = $this->drawSpline($WayPoints, ["Force" => $Force, "PathOnly" => \true]);
3293 if (count($Area)) {
3294 foreach ($Area as $key => $Points) {
3295 $Corners = [];
3296 $Corners[] = $Area[$key][0]["X"];
3297 $Corners[] = $YZero;
3298 foreach ($Points as $subKey => $Point) {
3299 if ($subKey == count($Points) - 1) {
3300 $Corners[] = $Point["X"] - 1;
3301 } else {
3302 $Corners[] = $Point["X"];
3303 }
3304 $Corners[] = $Point["Y"] + 1;
3305 }
3306 $Corners[] = $Points[$subKey]["X"] - 1;
3307 $Corners[] = $YZero;
3308 $this->drawPolygonChart($Corners, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / 2, "NoBorder" => \true, "Threshold" => $Threshold]);
3309 }
3310 $this->drawSpline($WayPoints, ["Force" => $Force, "R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
3311 }
3312 $WayPoints = [];
3313 } else {
3314 $WayPoints[] = [$X, $Y - 0.5];
3315 /* -.5 for AA visual fix */
3316 }
3317 $X = $X + $XStep;
3318 }
3319 $Area = $this->drawSpline($WayPoints, ["Force" => $Force, "PathOnly" => \true]);
3320 if (count($Area)) {
3321 foreach ($Area as $key => $Points) {
3322 $Corners = [];
3323 $Corners[] = $Area[$key][0]["X"];
3324 $Corners[] = $YZero;
3325 foreach ($Points as $subKey => $Point) {
3326 if ($subKey == count($Points) - 1) {
3327 $Corners[] = $Point["X"] - 1;
3328 } else {
3329 $Corners[] = $Point["X"];
3330 }
3331 $Corners[] = $Point["Y"] + 1;
3332 }
3333 $Corners[] = $Points[$subKey]["X"] - 1;
3334 $Corners[] = $YZero;
3335 $this->drawPolygonChart($Corners, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / 2, "NoBorder" => \true, "Threshold" => $Threshold]);
3336 }
3337 $this->drawSpline($WayPoints, ["Force" => $Force, "R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
3338 }
3339 } else {
3340 if ($XDivs == 0) {
3341 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
3342 } else {
3343 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
3344 }
3345 $Y = $this->GraphAreaY1 + $XMargin;
3346 $WayPoints = [];
3347 $Force = $YStep / 5;
3348 if (!$AroundZero) {
3349 $YZero = $this->GraphAreaX1 + 1;
3350 }
3351 if ($YZero > $this->GraphAreaX2 - 1) {
3352 $YZero = $this->GraphAreaX2 - 1;
3353 }
3354 if ($YZero < $this->GraphAreaX1 + 1) {
3355 $YZero = $this->GraphAreaX1 + 1;
3356 }
3357 if (!is_array($PosArray)) {
3358 $Value = $PosArray;
3359 $PosArray = [];
3360 $PosArray[0] = $Value;
3361 }
3362 foreach ($PosArray as $Key => $X) {
3363 if ($DisplayValues) {
3364 $this->drawText($X + $DisplayOffset, $Y, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["Angle" => 270, "R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]);
3365 }
3366 if ($X == VOID) {
3367 $Area = $this->drawSpline($WayPoints, ["Force" => $Force, "PathOnly" => \true]);
3368 if (count($Area)) {
3369 foreach ($Area as $key => $Points) {
3370 $Corners = [];
3371 $Corners[] = $YZero;
3372 $Corners[] = $Area[$key][0]["Y"];
3373 foreach ($Points as $subKey => $Point) {
3374 if ($subKey == count($Points) - 1) {
3375 $Corners[] = $Point["X"] - 1;
3376 } else {
3377 $Corners[] = $Point["X"];
3378 }
3379 $Corners[] = $Point["Y"];
3380 }
3381 $Corners[] = $YZero;
3382 $Corners[] = $Points[$subKey]["Y"] - 1;
3383 $this->drawPolygonChart($Corners, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / 2, "NoBorder" => \true, "Threshold" => $Threshold]);
3384 }
3385 $this->drawSpline($WayPoints, ["Force" => $Force, "R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
3386 }
3387 $WayPoints = [];
3388 } else {
3389 $WayPoints[] = [$X, $Y];
3390 }
3391 $Y = $Y + $YStep;
3392 }
3393 $Area = $this->drawSpline($WayPoints, ["Force" => $Force, "PathOnly" => \true]);
3394 if (count($Area)) {
3395 foreach ($Area as $key => $Points) {
3396 $Corners = [];
3397 $Corners[] = $YZero;
3398 $Corners[] = $Area[$key][0]["Y"];
3399 foreach ($Points as $subKey => $Point) {
3400 if ($subKey == count($Points) - 1) {
3401 $Corners[] = $Point["X"] - 1;
3402 } else {
3403 $Corners[] = $Point["X"];
3404 }
3405 $Corners[] = $Point["Y"];
3406 }
3407 $Corners[] = $YZero;
3408 $Corners[] = $Points[$subKey]["Y"] - 1;
3409 $this->drawPolygonChart($Corners, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha / 2, "NoBorder" => \true, "Threshold" => $Threshold]);
3410 }
3411 $this->drawSpline($WayPoints, ["Force" => $Force, "R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]);
3412 }
3413 }
3414 }
3415 }
3416 }
3417 /**
3418 * Draw a line chart
3419 * @param array $Format
3420 */
3421 public function drawLineChart(array $Format = [])
3422 {
3423 $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : \true;
3424 $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4;
3425 $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : null;
3426 $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : null;
3427 $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : null;
3428 $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : \false;
3429 $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
3430 $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
3431 $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
3432 $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
3433 $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
3434 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : \false;
3435 $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5;
3436 $ForceColor = isset($Format["ForceColor"]) ? $Format["ForceColor"] : \false;
3437 $ForceR = isset($Format["ForceR"]) ? $Format["ForceR"] : 0;
3438 $ForceG = isset($Format["ForceG"]) ? $Format["ForceG"] : 0;
3439 $ForceB = isset($Format["ForceB"]) ? $Format["ForceB"] : 0;
3440 $ForceAlpha = isset($Format["ForceAlpha"]) ? $Format["ForceAlpha"] : 100;
3441 $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
3442 $Data = $this->DataSet->getData();
3443 list($XMargin, $XDivs) = $this->scaleGetXSettings();
3444 foreach ($Data["Series"] as $SerieName => $Serie) {
3445 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
3446 $R = $Serie["Color"]["R"];
3447 $G = $Serie["Color"]["G"];
3448 $B = $Serie["Color"]["B"];
3449 $Alpha = $Serie["Color"]["Alpha"];
3450 $Ticks = $Serie["Ticks"];
3451 $Weight = $Serie["Weight"];
3452 if ($ForceColor) {
3453 $R = $ForceR;
3454 $G = $ForceG;
3455 $B = $ForceB;
3456 $Alpha = $ForceAlpha;
3457 }
3458 if ($BreakR == null) {
3459 $BreakSettings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $VoidTicks, "Weight" => $Weight];
3460 } else {
3461 $BreakSettings = ["R" => $BreakR, "G" => $BreakG, "B" => $BreakB, "Alpha" => $Alpha, "Ticks" => $VoidTicks, "Weight" => $Weight];
3462 }
3463 if ($DisplayColor == DISPLAY_AUTO) {
3464 $DisplayR = $R;
3465 $DisplayG = $G;
3466 $DisplayB = $B;
3467 }
3468 $AxisID = $Serie["Axis"];
3469 $Mode = $Data["Axis"][$AxisID]["Display"];
3470 $Format = $Data["Axis"][$AxisID]["Format"];
3471 $Unit = $Data["Axis"][$AxisID]["Unit"];
3472 if (isset($Serie["Description"])) {
3473 $SerieDescription = $Serie["Description"];
3474 } else {
3475 $SerieDescription = $SerieName;
3476 }
3477 $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
3478 $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
3479 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
3480 if ($XDivs == 0) {
3481 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
3482 } else {
3483 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
3484 }
3485 $X = $this->GraphAreaX1 + $XMargin;
3486 $LastX = null;
3487 $LastY = null;
3488 if (!is_array($PosArray)) {
3489 $Value = $PosArray;
3490 $PosArray = [];
3491 $PosArray[0] = $Value;
3492 }
3493 $LastGoodY = null;
3494 $LastGoodX = null;
3495 foreach ($PosArray as $Key => $Y) {
3496 if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
3497 if ($Serie["Data"][$Key] > 0) {
3498 $Align = TEXT_ALIGN_BOTTOMMIDDLE;
3499 $Offset = $DisplayOffset;
3500 } else {
3501 $Align = TEXT_ALIGN_TOPMIDDLE;
3502 $Offset = -$DisplayOffset;
3503 }
3504 $this->drawText($X, $Y - $Offset - $Weight, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => $Align]);
3505 }
3506 if ($RecordImageMap && $Y != VOID) {
3507 $this->addToImageMap("CIRCLE", floor($X) . "," . floor($Y) . "," . $ImageMapPlotSize, $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3508 }
3509 if ($Y != VOID && $LastX != null && $LastY != null) {
3510 $this->drawLine($LastX, $LastY, $X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight]);
3511 }
3512 if ($Y != VOID && $LastY == null && $LastGoodY != null && !$BreakVoid) {
3513 $this->drawLine($LastGoodX, $LastGoodY, $X, $Y, $BreakSettings);
3514 $LastGoodY = null;
3515 }
3516 if ($Y != VOID) {
3517 $LastGoodY = $Y;
3518 $LastGoodX = $X;
3519 }
3520 if ($Y == VOID) {
3521 $Y = null;
3522 }
3523 $LastX = $X;
3524 $LastY = $Y;
3525 $X = $X + $XStep;
3526 }
3527 } else {
3528 if ($XDivs == 0) {
3529 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
3530 } else {
3531 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
3532 }
3533 $Y = $this->GraphAreaY1 + $XMargin;
3534 $LastX = null;
3535 $LastY = null;
3536 if (!is_array($PosArray)) {
3537 $Value = $PosArray;
3538 $PosArray = [];
3539 $PosArray[0] = $Value;
3540 }
3541 $LastGoodY = null;
3542 $LastGoodX = null;
3543 foreach ($PosArray as $Key => $X) {
3544 if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
3545 $this->drawText($X + $DisplayOffset + $Weight, $Y, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["Angle" => 270, "R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => TEXT_ALIGN_BOTTOMMIDDLE]);
3546 }
3547 if ($RecordImageMap && $X != VOID) {
3548 $this->addToImageMap("CIRCLE", floor($X) . "," . floor($Y) . "," . $ImageMapPlotSize, $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3549 }
3550 if ($X != VOID && $LastX != null && $LastY != null) {
3551 $this->drawLine($LastX, $LastY, $X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight]);
3552 }
3553 if ($X != VOID && $LastX == null && $LastGoodY != null && !$BreakVoid) {
3554 $this->drawLine($LastGoodX, $LastGoodY, $X, $Y, $BreakSettings);
3555 $LastGoodY = null;
3556 }
3557 if ($X != VOID) {
3558 $LastGoodY = $Y;
3559 $LastGoodX = $X;
3560 }
3561 if ($X == VOID) {
3562 $X = null;
3563 }
3564 $LastX = $X;
3565 $LastY = $Y;
3566 $Y = $Y + $YStep;
3567 }
3568 }
3569 }
3570 }
3571 }
3572 /**
3573 * Draw a zone chart
3574 *
3575 * @param string $SerieA
3576 * @param string $SerieB
3577 * @param array $Format
3578 * @return null|integer
3579 */
3580 public function drawZoneChart($SerieA, $SerieB, array $Format = [])
3581 {
3582 $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0;
3583 $LineR = isset($Format["LineR"]) ? $Format["LineR"] : 150;
3584 $LineG = isset($Format["LineG"]) ? $Format["LineG"] : 150;
3585 $LineB = isset($Format["LineB"]) ? $Format["LineB"] : 150;
3586 $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 50;
3587 $LineTicks = isset($Format["LineTicks"]) ? $Format["LineTicks"] : 1;
3588 $AreaR = isset($Format["AreaR"]) ? $Format["AreaR"] : 150;
3589 $AreaG = isset($Format["AreaG"]) ? $Format["AreaG"] : 150;
3590 $AreaB = isset($Format["AreaB"]) ? $Format["AreaB"] : 150;
3591 $AreaAlpha = isset($Format["AreaAlpha"]) ? $Format["AreaAlpha"] : 5;
3592 $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
3593 $Data = $this->DataSet->getData();
3594 if (!isset($Data["Series"][$SerieA]["Data"]) || !isset($Data["Series"][$SerieB]["Data"])) {
3595 return 0;
3596 }
3597 $SerieAData = $Data["Series"][$SerieA]["Data"];
3598 $SerieBData = $Data["Series"][$SerieB]["Data"];
3599 list($XMargin, $XDivs) = $this->scaleGetXSettings();
3600 $Mode = $Data["Axis"][$AxisID]["Display"];
3601 $Format = $Data["Axis"][$AxisID]["Format"];
3602 $PosArrayA = $this->scaleComputeY($SerieAData, ["AxisID" => $AxisID]);
3603 $PosArrayB = $this->scaleComputeY($SerieBData, ["AxisID" => $AxisID]);
3604 if (count($PosArrayA) != count($PosArrayB)) {
3605 return 0;
3606 }
3607 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
3608 if ($XDivs == 0) {
3609 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
3610 } else {
3611 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
3612 }
3613 $X = $this->GraphAreaX1 + $XMargin;
3614 $LastX = null;
3615 $LastY = null;
3616 $LastY1 = null;
3617 $LastY2 = null;
3618 $BoundsA = [];
3619 $BoundsB = [];
3620 foreach ($PosArrayA as $Key => $Y1) {
3621 $Y2 = $PosArrayB[$Key];
3622 $BoundsA[] = $X;
3623 $BoundsA[] = $Y1;
3624 $BoundsB[] = $X;
3625 $BoundsB[] = $Y2;
3626 $LastX = $X;
3627 $LastY1 = $Y1;
3628 $LastY2 = $Y2;
3629 $X = $X + $XStep;
3630 }
3631 $Bounds = array_merge($BoundsA, $this->reversePlots($BoundsB));
3632 $this->drawPolygonChart($Bounds, ["R" => $AreaR, "G" => $AreaG, "B" => $AreaB, "Alpha" => $AreaAlpha]);
3633 for ($i = 0; $i <= count($BoundsA) - 4; $i = $i + 2) {
3634 $this->drawLine($BoundsA[$i], $BoundsA[$i + 1], $BoundsA[$i + 2], $BoundsA[$i + 3], ["R" => $LineR, "G" => $LineG, "B" => $LineB, "Alpha" => $LineAlpha, "Ticks" => $LineTicks]);
3635 $this->drawLine($BoundsB[$i], $BoundsB[$i + 1], $BoundsB[$i + 2], $BoundsB[$i + 3], ["R" => $LineR, "G" => $LineG, "B" => $LineB, "Alpha" => $LineAlpha, "Ticks" => $LineTicks]);
3636 }
3637 } else {
3638 if ($XDivs == 0) {
3639 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
3640 } else {
3641 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
3642 }
3643 $Y = $this->GraphAreaY1 + $XMargin;
3644 $LastX = null;
3645 $LastY = null;
3646 $LastX1 = null;
3647 $LastX2 = null;
3648 $BoundsA = [];
3649 $BoundsB = [];
3650 foreach ($PosArrayA as $Key => $X1) {
3651 $X2 = $PosArrayB[$Key];
3652 $BoundsA[] = $X1;
3653 $BoundsA[] = $Y;
3654 $BoundsB[] = $X2;
3655 $BoundsB[] = $Y;
3656 $LastY = $Y;
3657 $LastX1 = $X1;
3658 $LastX2 = $X2;
3659 $Y = $Y + $YStep;
3660 }
3661 $Bounds = array_merge($BoundsA, $this->reversePlots($BoundsB));
3662 $this->drawPolygonChart($Bounds, ["R" => $AreaR, "G" => $AreaG, "B" => $AreaB, "Alpha" => $AreaAlpha]);
3663 for ($i = 0; $i <= count($BoundsA) - 4; $i = $i + 2) {
3664 $this->drawLine($BoundsA[$i], $BoundsA[$i + 1], $BoundsA[$i + 2], $BoundsA[$i + 3], ["R" => $LineR, "G" => $LineG, "B" => $LineB, "Alpha" => $LineAlpha, "Ticks" => $LineTicks]);
3665 $this->drawLine($BoundsB[$i], $BoundsB[$i + 1], $BoundsB[$i + 2], $BoundsB[$i + 3], ["R" => $LineR, "G" => $LineG, "B" => $LineB, "Alpha" => $LineAlpha, "Ticks" => $LineTicks]);
3666 }
3667 }
3668 }
3669 /**
3670 * Draw a step chart
3671 * @param array $Format
3672 */
3673 public function drawStepChart(array $Format = [])
3674 {
3675 $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : \false;
3676 $ReCenter = isset($Format["ReCenter"]) ? $Format["ReCenter"] : \true;
3677 $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4;
3678 $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : null;
3679 $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : null;
3680 $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : null;
3681 $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : \false;
3682 $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
3683 $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
3684 $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
3685 $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
3686 $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
3687 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : \false;
3688 $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5;
3689 $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
3690 $Data = $this->DataSet->getData();
3691 list($XMargin, $XDivs) = $this->scaleGetXSettings();
3692 foreach ($Data["Series"] as $SerieName => $Serie) {
3693 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
3694 $R = $Serie["Color"]["R"];
3695 $G = $Serie["Color"]["G"];
3696 $B = $Serie["Color"]["B"];
3697 $Alpha = $Serie["Color"]["Alpha"];
3698 $Ticks = $Serie["Ticks"];
3699 $Weight = $Serie["Weight"];
3700 if (isset($Serie["Description"])) {
3701 $SerieDescription = $Serie["Description"];
3702 } else {
3703 $SerieDescription = $SerieName;
3704 }
3705 if ($BreakR == null) {
3706 $BreakSettings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $VoidTicks, "Weight" => $Weight];
3707 } else {
3708 $BreakSettings = ["R" => $BreakR, "G" => $BreakG, "B" => $BreakB, "Alpha" => $Alpha, "Ticks" => $VoidTicks, "Weight" => $Weight];
3709 }
3710 if ($DisplayColor == DISPLAY_AUTO) {
3711 $DisplayR = $R;
3712 $DisplayG = $G;
3713 $DisplayB = $B;
3714 }
3715 $AxisID = $Serie["Axis"];
3716 $Mode = $Data["Axis"][$AxisID]["Display"];
3717 $Format = $Data["Axis"][$AxisID]["Format"];
3718 $Unit = $Data["Axis"][$AxisID]["Unit"];
3719 $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight];
3720 $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
3721 $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
3722 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
3723 if ($XDivs == 0) {
3724 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
3725 } else {
3726 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
3727 }
3728 $X = $this->GraphAreaX1 + $XMargin;
3729 $LastX = null;
3730 $LastY = null;
3731 if (!is_array($PosArray)) {
3732 $Value = $PosArray;
3733 $PosArray = [];
3734 $PosArray[0] = $Value;
3735 }
3736 $LastGoodY = null;
3737 $LastGoodX = null;
3738 $Init = \false;
3739 foreach ($PosArray as $Key => $Y) {
3740 if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
3741 if ($Y <= $LastY) {
3742 $Align = TEXT_ALIGN_BOTTOMMIDDLE;
3743 $Offset = $DisplayOffset;
3744 } else {
3745 $Align = TEXT_ALIGN_TOPMIDDLE;
3746 $Offset = -$DisplayOffset;
3747 }
3748 $this->drawText($X, $Y - $Offset - $Weight, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => $Align]);
3749 }
3750 if ($Y != VOID && $LastX != null && $LastY != null) {
3751 $this->drawLine($LastX, $LastY, $X, $LastY, $Color);
3752 $this->drawLine($X, $LastY, $X, $Y, $Color);
3753 if ($ReCenter && $X + $XStep < $this->GraphAreaX2 - $XMargin) {
3754 $this->drawLine($X, $Y, $X + $XStep, $Y, $Color);
3755 if ($RecordImageMap) {
3756 $this->addToImageMap("RECT", sprintf('%s,%s,%s,%s', floor($X - $ImageMapPlotSize), floor($Y - $ImageMapPlotSize), floor($X + $XStep + $ImageMapPlotSize), floor($Y + $ImageMapPlotSize)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3757 }
3758 } else {
3759 if ($RecordImageMap) {
3760 $this->addToImageMap("RECT", sprintf('%s,%s,%s,%s', floor($LastX - $ImageMapPlotSize), floor($LastY - $ImageMapPlotSize), floor($X + $ImageMapPlotSize), floor($LastY + $ImageMapPlotSize)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3761 }
3762 }
3763 }
3764 if ($Y != VOID && $LastY == null && $LastGoodY != null && !$BreakVoid) {
3765 if ($ReCenter) {
3766 $this->drawLine($LastGoodX + $XStep, $LastGoodY, $X, $LastGoodY, $BreakSettings);
3767 if ($RecordImageMap) {
3768 $this->addToImageMap("RECT", sprintf('%s,%s,%s,%s', floor($LastGoodX + $XStep - $ImageMapPlotSize), floor($LastGoodY - $ImageMapPlotSize), floor($X + $ImageMapPlotSize), floor($LastGoodY + $ImageMapPlotSize)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3769 }
3770 } else {
3771 $this->drawLine($LastGoodX, $LastGoodY, $X, $LastGoodY, $BreakSettings);
3772 if ($RecordImageMap) {
3773 $this->addToImageMap("RECT", sprintf('%s,%s,%s,%s', floor($LastGoodX - $ImageMapPlotSize), floor($LastGoodY - $ImageMapPlotSize), floor($X + $ImageMapPlotSize), floor($LastGoodY + $ImageMapPlotSize)), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3774 }
3775 }
3776 $this->drawLine($X, $LastGoodY, $X, $Y, $BreakSettings);
3777 $LastGoodY = null;
3778 } elseif (!$BreakVoid && $LastGoodY == null && $Y != VOID) {
3779 $this->drawLine($this->GraphAreaX1 + $XMargin, $Y, $X, $Y, $BreakSettings);
3780 if ($RecordImageMap) {
3781 $this->addToImageMap("RECT", sprintf('%s,%s,%s,%s', floor($this->GraphAreaX1 + $XMargin - $ImageMapPlotSize), floor($Y - $ImageMapPlotSize), floor($X + $ImageMapPlotSize), floor($Y + $ImageMapPlotSize)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3782 }
3783 }
3784 if ($Y != VOID) {
3785 $LastGoodY = $Y;
3786 $LastGoodX = $X;
3787 }
3788 if ($Y == VOID) {
3789 $Y = null;
3790 }
3791 if (!$Init && $ReCenter) {
3792 $X = $X - $XStep / 2;
3793 $Init = \true;
3794 }
3795 $LastX = $X;
3796 $LastY = $Y;
3797 if ($LastX < $this->GraphAreaX1 + $XMargin) {
3798 $LastX = $this->GraphAreaX1 + $XMargin;
3799 }
3800 $X = $X + $XStep;
3801 }
3802 if ($ReCenter) {
3803 $this->drawLine($LastX, $LastY, $this->GraphAreaX2 - $XMargin, $LastY, $Color);
3804 if ($RecordImageMap) {
3805 $this->addToImageMap("RECT", sprintf('%s,%s,%s,%s', floor($LastX - $ImageMapPlotSize), floor($LastY - $ImageMapPlotSize), floor($this->GraphAreaX2 - $XMargin + $ImageMapPlotSize), floor($LastY + $ImageMapPlotSize)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3806 }
3807 }
3808 } else {
3809 if ($XDivs == 0) {
3810 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
3811 } else {
3812 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
3813 }
3814 $Y = $this->GraphAreaY1 + $XMargin;
3815 $LastX = null;
3816 $LastY = null;
3817 if (!is_array($PosArray)) {
3818 $Value = $PosArray;
3819 $PosArray = [];
3820 $PosArray[0] = $Value;
3821 }
3822 $LastGoodY = null;
3823 $LastGoodX = null;
3824 $Init = \false;
3825 foreach ($PosArray as $Key => $X) {
3826 if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
3827 if ($X >= $LastX) {
3828 $Align = TEXT_ALIGN_MIDDLELEFT;
3829 $Offset = $DisplayOffset;
3830 } else {
3831 $Align = TEXT_ALIGN_MIDDLERIGHT;
3832 $Offset = -$DisplayOffset;
3833 }
3834 $this->drawText($X + $Offset + $Weight, $Y, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => $Align]);
3835 }
3836 if ($X != VOID && $LastX != null && $LastY != null) {
3837 $this->drawLine($LastX, $LastY, $LastX, $Y, $Color);
3838 $this->drawLine($LastX, $Y, $X, $Y, $Color);
3839 if ($RecordImageMap) {
3840 $this->addToImageMap("RECT", sprintf('%s,%s,%s,%s', floor($LastX - $ImageMapPlotSize), floor($LastY - $ImageMapPlotSize), floor($LastX + $XStep + $ImageMapPlotSize), floor($Y + $ImageMapPlotSize)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3841 }
3842 }
3843 if ($X != VOID && $LastX == null && $LastGoodY != null && !$BreakVoid) {
3844 $this->drawLine($LastGoodX, $LastGoodY, $LastGoodX, $LastGoodY + $YStep, $Color);
3845 if ($RecordImageMap) {
3846 $this->addToImageMap("RECT", sprintf('%s,%s,%s,%s', floor($LastGoodX - $ImageMapPlotSize), floor($LastGoodY - $ImageMapPlotSize), floor($LastGoodX + $ImageMapPlotSize), floor($LastGoodY + $YStep + $ImageMapPlotSize)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3847 }
3848 $this->drawLine($LastGoodX, $LastGoodY + $YStep, $LastGoodX, $Y, $BreakSettings);
3849 if ($RecordImageMap) {
3850 $this->addToImageMap("RECT", sprintf('%s,%s,%s,%s', floor($LastGoodX - $ImageMapPlotSize), floor($LastGoodY + $YStep - $ImageMapPlotSize), floor($LastGoodX + $ImageMapPlotSize), floor($YStep + $ImageMapPlotSize)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3851 }
3852 $this->drawLine($LastGoodX, $Y, $X, $Y, $BreakSettings);
3853 $LastGoodY = null;
3854 } elseif ($X != VOID && $LastGoodY == null && !$BreakVoid) {
3855 $this->drawLine($X, $this->GraphAreaY1 + $XMargin, $X, $Y, $BreakSettings);
3856 if ($RecordImageMap) {
3857 $this->addToImageMap("RECT", sprintf('%s,%s,%s,%s', floor($X - $ImageMapPlotSize), floor($this->GraphAreaY1 + $XMargin - $ImageMapPlotSize), floor($X + $ImageMapPlotSize), floor($Y + $ImageMapPlotSize)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3858 }
3859 }
3860 if ($X != VOID) {
3861 $LastGoodY = $Y;
3862 $LastGoodX = $X;
3863 }
3864 if ($X == VOID) {
3865 $X = null;
3866 }
3867 if (!$Init && $ReCenter) {
3868 $Y = $Y - $YStep / 2;
3869 $Init = \true;
3870 }
3871 $LastX = $X;
3872 $LastY = $Y;
3873 if ($LastY < $this->GraphAreaY1 + $XMargin) {
3874 $LastY = $this->GraphAreaY1 + $XMargin;
3875 }
3876 $Y = $Y + $YStep;
3877 }
3878 if ($ReCenter) {
3879 $this->drawLine($LastX, $LastY, $LastX, $this->GraphAreaY2 - $XMargin, $Color);
3880 if ($RecordImageMap) {
3881 $this->addToImageMap("RECT", sprintf('%s,%s,%s,%s', floor($LastX - $ImageMapPlotSize), floor($LastY - $ImageMapPlotSize), floor($LastX + $ImageMapPlotSize), floor($this->GraphAreaY2 - $XMargin + $ImageMapPlotSize)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
3882 }
3883 }
3884 }
3885 }
3886 }
3887 }
3888 /**
3889 * Draw a step chart
3890 * @param array $Format
3891 */
3892 public function drawFilledStepChart(array $Format = [])
3893 {
3894 $ReCenter = isset($Format["ReCenter"]) ? $Format["ReCenter"] : \true;
3895 $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : \false;
3896 $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
3897 $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
3898 $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : null;
3899 $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
3900 $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
3901 $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
3902 $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : \true;
3903 $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
3904 $Data = $this->DataSet->getData();
3905 list($XMargin, $XDivs) = $this->scaleGetXSettings();
3906 foreach ($Data["Series"] as $SerieName => $Serie) {
3907 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
3908 $R = $Serie["Color"]["R"];
3909 $G = $Serie["Color"]["G"];
3910 $B = $Serie["Color"]["B"];
3911 $Alpha = $Serie["Color"]["Alpha"];
3912 if ($DisplayColor == DISPLAY_AUTO) {
3913 $DisplayR = $R;
3914 $DisplayG = $G;
3915 $DisplayB = $B;
3916 }
3917 $AxisID = $Serie["Axis"];
3918 $Format = $Data["Axis"][$AxisID]["Format"];
3919 $Color = ["R" => $R, "G" => $G, "B" => $B];
3920 if ($ForceTransparency != null) {
3921 $Color["Alpha"] = $ForceTransparency;
3922 } else {
3923 $Color["Alpha"] = $Alpha;
3924 }
3925 $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
3926 $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]);
3927 $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
3928 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
3929 if ($YZero > $this->GraphAreaY2 - 1) {
3930 $YZero = $this->GraphAreaY2 - 1;
3931 }
3932 if ($YZero < $this->GraphAreaY1 + 1) {
3933 $YZero = $this->GraphAreaY1 + 1;
3934 }
3935 if ($XDivs == 0) {
3936 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
3937 } else {
3938 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
3939 }
3940 $X = $this->GraphAreaX1 + $XMargin;
3941 $LastX = null;
3942 $LastY = null;
3943 if (!$AroundZero) {
3944 $YZero = $this->GraphAreaY2 - 1;
3945 }
3946 if (!is_array($PosArray)) {
3947 $Value = $PosArray;
3948 $PosArray = [];
3949 $PosArray[0] = $Value;
3950 }
3951 $LastGoodY = null;
3952 $LastGoodX = null;
3953 $Points = [];
3954 $Init = \false;
3955 foreach ($PosArray as $Key => $Y) {
3956 if ($Y == VOID && $LastX != null && $LastY != null && count($Points)) {
3957 $Points[] = $LastX;
3958 $Points[] = $LastY;
3959 $Points[] = $X;
3960 $Points[] = $LastY;
3961 $Points[] = $X;
3962 $Points[] = $YZero;
3963 $this->drawPolygon($Points, $Color);
3964 $Points = [];
3965 }
3966 if ($Y != VOID && $LastX != null && $LastY != null) {
3967 if (count($Points)) {
3968 $Points[] = $LastX;
3969 $Points[] = $YZero;
3970 }
3971 $Points[] = $LastX;
3972 $Points[] = $LastY;
3973 $Points[] = $X;
3974 $Points[] = $LastY;
3975 $Points[] = $X;
3976 $Points[] = $Y;
3977 }
3978 if ($Y != VOID) {
3979 $LastGoodY = $Y;
3980 $LastGoodX = $X;
3981 }
3982 if ($Y == VOID) {
3983 $Y = null;
3984 }
3985 if (!$Init && $ReCenter) {
3986 $X = $X - $XStep / 2;
3987 $Init = \true;
3988 }
3989 $LastX = $X;
3990 $LastY = $Y;
3991 if ($LastX < $this->GraphAreaX1 + $XMargin) {
3992 $LastX = $this->GraphAreaX1 + $XMargin;
3993 }
3994 $X = $X + $XStep;
3995 }
3996 if ($ReCenter) {
3997 $Points[] = $LastX + $XStep / 2;
3998 $Points[] = $LastY;
3999 $Points[] = $LastX + $XStep / 2;
4000 $Points[] = $YZero;
4001 } else {
4002 $Points[] = $LastX;
4003 $Points[] = $YZero;
4004 }
4005 $this->drawPolygon($Points, $Color);
4006 } else {
4007 if ($YZero < $this->GraphAreaX1 + 1) {
4008 $YZero = $this->GraphAreaX1 + 1;
4009 }
4010 if ($YZero > $this->GraphAreaX2 - 1) {
4011 $YZero = $this->GraphAreaX2 - 1;
4012 }
4013 if ($XDivs == 0) {
4014 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
4015 } else {
4016 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
4017 }
4018 $Y = $this->GraphAreaY1 + $XMargin;
4019 $LastX = null;
4020 $LastY = null;
4021 if (!is_array($PosArray)) {
4022 $Value = $PosArray;
4023 $PosArray = [];
4024 $PosArray[0] = $Value;
4025 }
4026 $LastGoodY = null;
4027 $LastGoodX = null;
4028 $Points = [];
4029 foreach ($PosArray as $Key => $X) {
4030 if ($X == VOID && $LastX != null && $LastY != null && count($Points)) {
4031 $Points[] = $LastX;
4032 $Points[] = $LastY;
4033 $Points[] = $LastX;
4034 $Points[] = $Y;
4035 $Points[] = $YZero;
4036 $Points[] = $Y;
4037 $this->drawPolygon($Points, $Color);
4038 $Points = [];
4039 }
4040 if ($X != VOID && $LastX != null && $LastY != null) {
4041 if (count($Points)) {
4042 $Points[] = $YZero;
4043 $Points[] = $LastY;
4044 }
4045 $Points[] = $LastX;
4046 $Points[] = $LastY;
4047 $Points[] = $LastX;
4048 $Points[] = $Y;
4049 $Points[] = $X;
4050 $Points[] = $Y;
4051 }
4052 if ($X != VOID) {
4053 $LastGoodY = $Y;
4054 $LastGoodX = $X;
4055 }
4056 if ($X == VOID) {
4057 $X = null;
4058 }
4059 if ($LastX == null && $ReCenter) {
4060 $Y = $Y - $YStep / 2;
4061 }
4062 $LastX = $X;
4063 $LastY = $Y;
4064 if ($LastY < $this->GraphAreaY1 + $XMargin) {
4065 $LastY = $this->GraphAreaY1 + $XMargin;
4066 }
4067 $Y = $Y + $YStep;
4068 }
4069 if ($ReCenter) {
4070 $Points[] = $LastX;
4071 $Points[] = $LastY + $YStep / 2;
4072 $Points[] = $YZero;
4073 $Points[] = $LastY + $YStep / 2;
4074 } else {
4075 $Points[] = $YZero;
4076 $Points[] = $LastY;
4077 }
4078 $this->drawPolygon($Points, $Color);
4079 }
4080 }
4081 }
4082 }
4083 /**
4084 * Draw an area chart
4085 * @param array $Format
4086 */
4087 public function drawAreaChart(array $Format = [])
4088 {
4089 $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : \false;
4090 $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
4091 $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
4092 $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
4093 $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
4094 $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
4095 $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : 25;
4096 $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : \true;
4097 $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : null;
4098 $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
4099 $Data = $this->DataSet->getData();
4100 list($XMargin, $XDivs) = $this->scaleGetXSettings();
4101 foreach ($Data["Series"] as $SerieName => $Serie) {
4102 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
4103 $R = $Serie["Color"]["R"];
4104 $G = $Serie["Color"]["G"];
4105 $B = $Serie["Color"]["B"];
4106 $Alpha = $Serie["Color"]["Alpha"];
4107 $Ticks = $Serie["Ticks"];
4108 if ($DisplayColor == DISPLAY_AUTO) {
4109 $DisplayR = $R;
4110 $DisplayG = $G;
4111 $DisplayB = $B;
4112 }
4113 $AxisID = $Serie["Axis"];
4114 $Mode = $Data["Axis"][$AxisID]["Display"];
4115 $Format = $Data["Axis"][$AxisID]["Format"];
4116 $Unit = $Data["Axis"][$AxisID]["Unit"];
4117 $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
4118 $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]);
4119 if ($Threshold != null) {
4120 foreach ($Threshold as $Key => $Params) {
4121 $Threshold[$Key]["MinX"] = $this->scaleComputeY($Params["Min"], ["AxisID" => $Serie["Axis"]]);
4122 $Threshold[$Key]["MaxX"] = $this->scaleComputeY($Params["Max"], ["AxisID" => $Serie["Axis"]]);
4123 }
4124 }
4125 $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
4126 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
4127 if ($YZero > $this->GraphAreaY2 - 1) {
4128 $YZero = $this->GraphAreaY2 - 1;
4129 }
4130 $Areas = [];
4131 $AreaID = 0;
4132 $Areas[$AreaID][] = $this->GraphAreaX1 + $XMargin;
4133 if ($AroundZero) {
4134 $Areas[$AreaID][] = $YZero;
4135 } else {
4136 $Areas[$AreaID][] = $this->GraphAreaY2 - 1;
4137 }
4138 if ($XDivs == 0) {
4139 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
4140 } else {
4141 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
4142 }
4143 $X = $this->GraphAreaX1 + $XMargin;
4144 $LastX = null;
4145 $LastY = null;
4146 if (!is_array($PosArray)) {
4147 $Value = $PosArray;
4148 $PosArray = [];
4149 $PosArray[0] = $Value;
4150 }
4151 foreach ($PosArray as $Key => $Y) {
4152 if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
4153 if ($Serie["Data"][$Key] > 0) {
4154 $Align = TEXT_ALIGN_BOTTOMMIDDLE;
4155 $Offset = $DisplayOffset;
4156 } else {
4157 $Align = TEXT_ALIGN_TOPMIDDLE;
4158 $Offset = -$DisplayOffset;
4159 }
4160 $this->drawText($X, $Y - $Offset, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => $Align]);
4161 }
4162 if ($Y == VOID && isset($Areas[$AreaID])) {
4163 if ($LastX == null) {
4164 $Areas[$AreaID][] = $X;
4165 } else {
4166 $Areas[$AreaID][] = $LastX;
4167 }
4168 if ($AroundZero) {
4169 $Areas[$AreaID][] = $YZero;
4170 } else {
4171 $Areas[$AreaID][] = $this->GraphAreaY2 - 1;
4172 }
4173 $AreaID++;
4174 } elseif ($Y != VOID) {
4175 if (!isset($Areas[$AreaID])) {
4176 $Areas[$AreaID][] = $X;
4177 if ($AroundZero) {
4178 $Areas[$AreaID][] = $YZero;
4179 } else {
4180 $Areas[$AreaID][] = $this->GraphAreaY2 - 1;
4181 }
4182 }
4183 $Areas[$AreaID][] = $X;
4184 $Areas[$AreaID][] = $Y;
4185 }
4186 $LastX = $X;
4187 $X = $X + $XStep;
4188 }
4189 $Areas[$AreaID][] = $LastX;
4190 if ($AroundZero) {
4191 $Areas[$AreaID][] = $YZero;
4192 } else {
4193 $Areas[$AreaID][] = $this->GraphAreaY2 - 1;
4194 }
4195 /* Handle shadows in the areas */
4196 if ($this->Shadow) {
4197 $ShadowArea = [];
4198 foreach ($Areas as $Key => $Points) {
4199 $ShadowArea[$Key] = [];
4200 foreach ($Points as $Key2 => $Value) {
4201 if ($Key2 % 2 == 0) {
4202 $ShadowArea[$Key][] = $Value + $this->ShadowX;
4203 } else {
4204 $ShadowArea[$Key][] = $Value + $this->ShadowY;
4205 }
4206 }
4207 }
4208 foreach ($ShadowArea as $Key => $Points) {
4209 $this->drawPolygonChart($Points, ["R" => $this->ShadowR, "G" => $this->ShadowG, "B" => $this->ShadowB, "Alpha" => $this->Shadowa]);
4210 }
4211 }
4212 $Alpha = $ForceTransparency != null ? $ForceTransparency : $Alpha;
4213 $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Threshold" => $Threshold];
4214 foreach ($Areas as $Key => $Points) {
4215 $this->drawPolygonChart($Points, $Color);
4216 }
4217 } else {
4218 if ($YZero < $this->GraphAreaX1 + 1) {
4219 $YZero = $this->GraphAreaX1 + 1;
4220 }
4221 if ($YZero > $this->GraphAreaX2 - 1) {
4222 $YZero = $this->GraphAreaX2 - 1;
4223 }
4224 $Areas = [];
4225 $AreaID = 0;
4226 if ($AroundZero) {
4227 $Areas[$AreaID][] = $YZero;
4228 } else {
4229 $Areas[$AreaID][] = $this->GraphAreaX1 + 1;
4230 }
4231 $Areas[$AreaID][] = $this->GraphAreaY1 + $XMargin;
4232 if ($XDivs == 0) {
4233 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
4234 } else {
4235 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
4236 }
4237 $Y = $this->GraphAreaY1 + $XMargin;
4238 $LastX = null;
4239 $LastY = null;
4240 if (!is_array($PosArray)) {
4241 $Value = $PosArray;
4242 $PosArray = [];
4243 $PosArray[0] = $Value;
4244 }
4245 foreach ($PosArray as $Key => $X) {
4246 if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
4247 if ($Serie["Data"][$Key] > 0) {
4248 $Align = TEXT_ALIGN_BOTTOMMIDDLE;
4249 $Offset = $DisplayOffset;
4250 } else {
4251 $Align = TEXT_ALIGN_TOPMIDDLE;
4252 $Offset = -$DisplayOffset;
4253 }
4254 $this->drawText($X + $Offset, $Y, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["Angle" => 270, "R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => $Align]);
4255 }
4256 if ($X == VOID && isset($Areas[$AreaID])) {
4257 if ($AroundZero) {
4258 $Areas[$AreaID][] = $YZero;
4259 } else {
4260 $Areas[$AreaID][] = $this->GraphAreaX1 + 1;
4261 }
4262 if ($LastY == null) {
4263 $Areas[$AreaID][] = $Y;
4264 } else {
4265 $Areas[$AreaID][] = $LastY;
4266 }
4267 $AreaID++;
4268 } elseif ($X != VOID) {
4269 if (!isset($Areas[$AreaID])) {
4270 if ($AroundZero) {
4271 $Areas[$AreaID][] = $YZero;
4272 } else {
4273 $Areas[$AreaID][] = $this->GraphAreaX1 + 1;
4274 }
4275 $Areas[$AreaID][] = $Y;
4276 }
4277 $Areas[$AreaID][] = $X;
4278 $Areas[$AreaID][] = $Y;
4279 }
4280 $LastX = $X;
4281 $LastY = $Y;
4282 $Y = $Y + $YStep;
4283 }
4284 if ($AroundZero) {
4285 $Areas[$AreaID][] = $YZero;
4286 } else {
4287 $Areas[$AreaID][] = $this->GraphAreaX1 + 1;
4288 }
4289 $Areas[$AreaID][] = $LastY;
4290 /* Handle shadows in the areas */
4291 if ($this->Shadow) {
4292 $ShadowArea = [];
4293 foreach ($Areas as $Key => $Points) {
4294 $ShadowArea[$Key] = [];
4295 foreach ($Points as $Key2 => $Value) {
4296 if ($Key2 % 2 == 0) {
4297 $ShadowArea[$Key][] = $Value + $this->ShadowX;
4298 } else {
4299 $ShadowArea[$Key][] = $Value + $this->ShadowY;
4300 }
4301 }
4302 }
4303 foreach ($ShadowArea as $Key => $Points) {
4304 $this->drawPolygonChart($Points, ["R" => $this->ShadowR, "G" => $this->ShadowG, "B" => $this->ShadowB, "Alpha" => $this->Shadowa]);
4305 }
4306 }
4307 $Alpha = $ForceTransparency != null ? $ForceTransparency : $Alpha;
4308 $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Threshold" => $Threshold];
4309 foreach ($Areas as $Key => $Points) {
4310 $this->drawPolygonChart($Points, $Color);
4311 }
4312 }
4313 }
4314 }
4315 }
4316 /**
4317 * Draw a bar chart
4318 * @param array $Format
4319 */
4320 public function drawBarChart(array $Format = [])
4321 {
4322 $Floating0Serie = isset($Format["Floating0Serie"]) ? $Format["Floating0Serie"] : null;
4323 $Floating0Value = isset($Format["Floating0Value"]) ? $Format["Floating0Value"] : null;
4324 $Draw0Line = isset($Format["Draw0Line"]) ? $Format["Draw0Line"] : \false;
4325 $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : \false;
4326 $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
4327 $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
4328 $DisplayFont = isset($Format["DisplayFont"]) ? $Format["DisplayFont"] : $this->FontName;
4329 $DisplaySize = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontSize;
4330 $DisplayPos = isset($Format["DisplayPos"]) ? $Format["DisplayPos"] : LABEL_POS_OUTSIDE;
4331 $DisplayShadow = isset($Format["DisplayShadow"]) ? $Format["DisplayShadow"] : \true;
4332 $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
4333 $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
4334 $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
4335 $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : \true;
4336 $Interleave = isset($Format["Interleave"]) ? $Format["Interleave"] : 0.5;
4337 $Rounded = isset($Format["Rounded"]) ? $Format["Rounded"] : \false;
4338 $RoundRadius = isset($Format["RoundRadius"]) ? $Format["RoundRadius"] : 4;
4339 $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
4340 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
4341 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
4342 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
4343 $Gradient = isset($Format["Gradient"]) ? $Format["Gradient"] : \false;
4344 $GradientMode = isset($Format["GradientMode"]) ? $Format["GradientMode"] : GRADIENT_SIMPLE;
4345 $GradientAlpha = isset($Format["GradientAlpha"]) ? $Format["GradientAlpha"] : 20;
4346 $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255;
4347 $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255;
4348 $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255;
4349 $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 0;
4350 $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 0;
4351 $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 0;
4352 $TxtMargin = isset($Format["TxtMargin"]) ? $Format["TxtMargin"] : 6;
4353 $OverrideColors = isset($Format["OverrideColors"]) ? $Format["OverrideColors"] : null;
4354 $OverrideSurrounding = isset($Format["OverrideSurrounding"]) ? $Format["OverrideSurrounding"] : 30;
4355 $InnerSurrounding = isset($Format["InnerSurrounding"]) ? $Format["InnerSurrounding"] : null;
4356 $InnerBorderR = isset($Format["InnerBorderR"]) ? $Format["InnerBorderR"] : -1;
4357 $InnerBorderG = isset($Format["InnerBorderG"]) ? $Format["InnerBorderG"] : -1;
4358 $InnerBorderB = isset($Format["InnerBorderB"]) ? $Format["InnerBorderB"] : -1;
4359 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : \false;
4360 $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
4361 $Data = $this->DataSet->getData();
4362 list($XMargin, $XDivs) = $this->scaleGetXSettings();
4363 if ($OverrideColors != null) {
4364 $OverrideColors = $this->validatePalette($OverrideColors, $OverrideSurrounding);
4365 $this->DataSet->saveExtendedData("Palette", $OverrideColors);
4366 }
4367 $RestoreShadow = $this->Shadow;
4368 $SeriesCount = $this->countDrawableSeries();
4369 $CurrentSerie = 0;
4370 foreach ($Data["Series"] as $SerieName => $Serie) {
4371 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
4372 $R = $Serie["Color"]["R"];
4373 $G = $Serie["Color"]["G"];
4374 $B = $Serie["Color"]["B"];
4375 $Alpha = $Serie["Color"]["Alpha"];
4376 $Ticks = $Serie["Ticks"];
4377 if ($DisplayColor == DISPLAY_AUTO) {
4378 $DisplayR = $R;
4379 $DisplayG = $G;
4380 $DisplayB = $B;
4381 }
4382 if ($Surrounding != null) {
4383 $BorderR = $R + $Surrounding;
4384 $BorderG = $G + $Surrounding;
4385 $BorderB = $B + $Surrounding;
4386 }
4387 if ($InnerSurrounding != null) {
4388 $InnerBorderR = $R + $InnerSurrounding;
4389 $InnerBorderG = $G + $InnerSurrounding;
4390 $InnerBorderB = $B + $InnerSurrounding;
4391 }
4392 if ($InnerBorderR == -1) {
4393 $InnerColor = null;
4394 } else {
4395 $InnerColor = ["R" => $InnerBorderR, "G" => $InnerBorderG, "B" => $InnerBorderB];
4396 }
4397 $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "BorderR" => $BorderR, "BorderG" => $BorderG, "BorderB" => $BorderB];
4398 $AxisID = $Serie["Axis"];
4399 $Mode = $Data["Axis"][$AxisID]["Display"];
4400 $Format = $Data["Axis"][$AxisID]["Format"];
4401 $Unit = $Data["Axis"][$AxisID]["Unit"];
4402 if (isset($Serie["Description"])) {
4403 $SerieDescription = $Serie["Description"];
4404 } else {
4405 $SerieDescription = $SerieName;
4406 }
4407 $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
4408 if ($Floating0Value != null) {
4409 $YZero = $this->scaleComputeY($Floating0Value, ["AxisID" => $Serie["Axis"]]);
4410 } else {
4411 $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]);
4412 }
4413 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
4414 if ($YZero > $this->GraphAreaY2 - 1) {
4415 $YZero = $this->GraphAreaY2 - 1;
4416 }
4417 if ($YZero < $this->GraphAreaY1 + 1) {
4418 $YZero = $this->GraphAreaY1 + 1;
4419 }
4420 if ($XDivs == 0) {
4421 $XStep = 0;
4422 } else {
4423 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
4424 }
4425 $X = $this->GraphAreaX1 + $XMargin;
4426 if ($AroundZero) {
4427 $Y1 = $YZero;
4428 } else {
4429 $Y1 = $this->GraphAreaY2 - 1;
4430 }
4431 if ($XDivs == 0) {
4432 $XSize = ($this->GraphAreaX2 - $this->GraphAreaX1) / ($SeriesCount + $Interleave);
4433 } else {
4434 $XSize = $XStep / ($SeriesCount + $Interleave);
4435 }
4436 $XOffset = -($XSize * $SeriesCount) / 2 + $CurrentSerie * $XSize;
4437 if ($X + $XOffset <= $this->GraphAreaX1) {
4438 $XOffset = $this->GraphAreaX1 - $X + 1;
4439 }
4440 $this->DataSet->Data["Series"][$SerieName]["XOffset"] = $XOffset + $XSize / 2;
4441 if ($Rounded || $BorderR != -1) {
4442 $XSpace = 1;
4443 } else {
4444 $XSpace = 0;
4445 }
4446 if (!is_array($PosArray)) {
4447 $Value = $PosArray;
4448 $PosArray = [];
4449 $PosArray[0] = $Value;
4450 }
4451 $ID = 0;
4452 foreach ($PosArray as $Key => $Y2) {
4453 if ($Floating0Serie != null) {
4454 if (isset($Data["Series"][$Floating0Serie]["Data"][$Key])) {
4455 $Value = $Data["Series"][$Floating0Serie]["Data"][$Key];
4456 } else {
4457 $Value = 0;
4458 }
4459 $YZero = $this->scaleComputeY($Value, ["AxisID" => $Serie["Axis"]]);
4460 if ($YZero > $this->GraphAreaY2 - 1) {
4461 $YZero = $this->GraphAreaY2 - 1;
4462 }
4463 if ($YZero < $this->GraphAreaY1 + 1) {
4464 $YZero = $this->GraphAreaY1 + 1;
4465 }
4466 if ($AroundZero) {
4467 $Y1 = $YZero;
4468 } else {
4469 $Y1 = $this->GraphAreaY2 - 1;
4470 }
4471 }
4472 if ($OverrideColors != null) {
4473 if (isset($OverrideColors[$ID])) {
4474 $Color = ["R" => $OverrideColors[$ID]["R"], "G" => $OverrideColors[$ID]["G"], "B" => $OverrideColors[$ID]["B"], "Alpha" => $OverrideColors[$ID]["Alpha"], "BorderR" => $OverrideColors[$ID]["BorderR"], "BorderG" => $OverrideColors[$ID]["BorderG"], "BorderB" => $OverrideColors[$ID]["BorderB"]];
4475 } else {
4476 $Color = $this->getRandomColor();
4477 }
4478 }
4479 if ($Y2 != VOID) {
4480 $BarHeight = $Y1 - $Y2;
4481 if ($Serie["Data"][$Key] == 0) {
4482 $this->drawLine($X + $XOffset + $XSpace, $Y1, $X + $XOffset + $XSize - $XSpace, $Y1, $Color);
4483 if ($RecordImageMap) {
4484 $this->addToImageMap("RECT", sprintf("%s,%s,%s,%s", floor($X + $XOffset + $XSpace), floor($Y1 - 1), floor($X + $XOffset + $XSize - $XSpace), floor($Y1 + 1)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
4485 }
4486 } else {
4487 if ($RecordImageMap) {
4488 $this->addToImageMap("RECT", sprintf("%s,%s,%s,%s", floor($X + $XOffset + $XSpace), floor($Y1), floor($X + $XOffset + $XSize - $XSpace), floor($Y2)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
4489 }
4490 if ($Rounded) {
4491 $this->drawRoundedFilledRectangle($X + $XOffset + $XSpace, $Y1, $X + $XOffset + $XSize - $XSpace, $Y2, $RoundRadius, $Color);
4492 } else {
4493 $this->drawFilledRectangle($X + $XOffset + $XSpace, $Y1, $X + $XOffset + $XSize - $XSpace, $Y2, $Color);
4494 if ($InnerColor != null) {
4495 $this->drawRectangle($X + $XOffset + $XSpace + 1, min($Y1, $Y2) + 1, $X + $XOffset + $XSize - $XSpace - 1, max($Y1, $Y2) - 1, $InnerColor);
4496 }
4497 if ($Gradient) {
4498 $this->Shadow = \false;
4499 if ($GradientMode == GRADIENT_SIMPLE) {
4500 if ($Serie["Data"][$Key] >= 0) {
4501 $GradienColor = ["StartR" => $GradientStartR, "StartG" => $GradientStartG, "StartB" => $GradientStartB, "EndR" => $GradientEndR, "EndG" => $GradientEndG, "EndB" => $GradientEndB, "Alpha" => $GradientAlpha];
4502 } else {
4503 $GradienColor = ["StartR" => $GradientEndR, "StartG" => $GradientEndG, "StartB" => $GradientEndB, "EndR" => $GradientStartR, "EndG" => $GradientStartG, "EndB" => $GradientStartB, "Alpha" => $GradientAlpha];
4504 }
4505 $this->drawGradientArea($X + $XOffset + $XSpace, $Y1, $X + $XOffset + $XSize - $XSpace, $Y2, DIRECTION_VERTICAL, $GradienColor);
4506 } elseif ($GradientMode == GRADIENT_EFFECT_CAN) {
4507 $GradienColor1 = ["StartR" => $GradientEndR, "StartG" => $GradientEndG, "StartB" => $GradientEndB, "EndR" => $GradientStartR, "EndG" => $GradientStartG, "EndB" => $GradientStartB, "Alpha" => $GradientAlpha];
4508 $GradienColor2 = ["StartR" => $GradientStartR, "StartG" => $GradientStartG, "StartB" => $GradientStartB, "EndR" => $GradientEndR, "EndG" => $GradientEndG, "EndB" => $GradientEndB, "Alpha" => $GradientAlpha];
4509 $XSpan = floor($XSize / 3);
4510 $this->drawGradientArea($X + $XOffset + $XSpace, $Y1, $X + $XOffset + $XSpan - $XSpace, $Y2, DIRECTION_HORIZONTAL, $GradienColor1);
4511 $this->drawGradientArea($X + $XOffset + $XSpan + $XSpace, $Y1, $X + $XOffset + $XSize - $XSpace, $Y2, DIRECTION_HORIZONTAL, $GradienColor2);
4512 }
4513 $this->Shadow = $RestoreShadow;
4514 }
4515 }
4516 if ($Draw0Line) {
4517 $Line0Color = ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20];
4518 if (abs($Y1 - $Y2) > 3) {
4519 $Line0Width = 3;
4520 } else {
4521 $Line0Width = 1;
4522 }
4523 if ($Y1 - $Y2 < 0) {
4524 $Line0Width = -$Line0Width;
4525 }
4526 $this->drawFilledRectangle($X + $XOffset + $XSpace, floor($Y1), $X + $XOffset + $XSize - $XSpace, floor($Y1) - $Line0Width, $Line0Color);
4527 $this->drawLine($X + $XOffset + $XSpace, floor($Y1), $X + $XOffset + $XSize - $XSpace, floor($Y1), $Line0Color);
4528 }
4529 }
4530 if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
4531 if ($DisplayShadow) {
4532 $this->Shadow = \true;
4533 }
4534 $Caption = $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit);
4535 $TxtPos = $this->getTextBox(0, 0, $DisplayFont, $DisplaySize, 90, $Caption);
4536 $TxtHeight = $TxtPos[0]["Y"] - $TxtPos[1]["Y"] + $TxtMargin;
4537 if ($DisplayPos == LABEL_POS_INSIDE && abs($TxtHeight) < abs($BarHeight)) {
4538 $CenterX = ($X + $XOffset + $XSize - $XSpace - ($X + $XOffset + $XSpace)) / 2 + $X + $XOffset + $XSpace;
4539 $CenterY = ($Y2 - $Y1) / 2 + $Y1;
4540 $this->drawText($CenterX, $CenterY, $Caption, ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => TEXT_ALIGN_MIDDLEMIDDLE, "FontSize" => $DisplaySize, "Angle" => 90]);
4541 } else {
4542 if ($Serie["Data"][$Key] >= 0) {
4543 $Align = TEXT_ALIGN_BOTTOMMIDDLE;
4544 $Offset = $DisplayOffset;
4545 } else {
4546 $Align = TEXT_ALIGN_TOPMIDDLE;
4547 $Offset = -$DisplayOffset;
4548 }
4549 $this->drawText($X + $XOffset + $XSize / 2, $Y2 - $Offset, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => $Align, "FontSize" => $DisplaySize]);
4550 }
4551 $this->Shadow = $RestoreShadow;
4552 }
4553 }
4554 $X = $X + $XStep;
4555 $ID++;
4556 }
4557 } else {
4558 if ($YZero < $this->GraphAreaX1 + 1) {
4559 $YZero = $this->GraphAreaX1 + 1;
4560 }
4561 if ($YZero > $this->GraphAreaX2 - 1) {
4562 $YZero = $this->GraphAreaX2 - 1;
4563 }
4564 if ($XDivs == 0) {
4565 $YStep = 0;
4566 } else {
4567 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
4568 }
4569 $Y = $this->GraphAreaY1 + $XMargin;
4570 if ($AroundZero) {
4571 $X1 = $YZero;
4572 } else {
4573 $X1 = $this->GraphAreaX1 + 1;
4574 }
4575 if ($XDivs == 0) {
4576 $YSize = ($this->GraphAreaY2 - $this->GraphAreaY1) / ($SeriesCount + $Interleave);
4577 } else {
4578 $YSize = $YStep / ($SeriesCount + $Interleave);
4579 }
4580 $YOffset = -($YSize * $SeriesCount) / 2 + $CurrentSerie * $YSize;
4581 if ($Y + $YOffset <= $this->GraphAreaY1) {
4582 $YOffset = $this->GraphAreaY1 - $Y + 1;
4583 }
4584 $this->DataSet->Data["Series"][$SerieName]["XOffset"] = $YOffset + $YSize / 2;
4585 if ($Rounded || $BorderR != -1) {
4586 $YSpace = 1;
4587 } else {
4588 $YSpace = 0;
4589 }
4590 if (!is_array($PosArray)) {
4591 $Value = $PosArray;
4592 $PosArray = [];
4593 $PosArray[0] = $Value;
4594 }
4595 $ID = 0;
4596 foreach ($PosArray as $Key => $X2) {
4597 if ($Floating0Serie != null) {
4598 if (isset($Data["Series"][$Floating0Serie]["Data"][$Key])) {
4599 $Value = $Data["Series"][$Floating0Serie]["Data"][$Key];
4600 } else {
4601 $Value = 0;
4602 }
4603 $YZero = $this->scaleComputeY($Value, ["AxisID" => $Serie["Axis"]]);
4604 if ($YZero < $this->GraphAreaX1 + 1) {
4605 $YZero = $this->GraphAreaX1 + 1;
4606 }
4607 if ($YZero > $this->GraphAreaX2 - 1) {
4608 $YZero = $this->GraphAreaX2 - 1;
4609 }
4610 if ($AroundZero) {
4611 $X1 = $YZero;
4612 } else {
4613 $X1 = $this->GraphAreaX1 + 1;
4614 }
4615 }
4616 if ($OverrideColors != null) {
4617 if (isset($OverrideColors[$ID])) {
4618 $Color = ["R" => $OverrideColors[$ID]["R"], "G" => $OverrideColors[$ID]["G"], "B" => $OverrideColors[$ID]["B"], "Alpha" => $OverrideColors[$ID]["Alpha"], "BorderR" => $OverrideColors[$ID]["BorderR"], "BorderG" => $OverrideColors[$ID]["BorderG"], "BorderB" => $OverrideColors[$ID]["BorderB"]];
4619 } else {
4620 $Color = $this->getRandomColor();
4621 }
4622 }
4623 if ($X2 != VOID) {
4624 $BarWidth = $X2 - $X1;
4625 if ($Serie["Data"][$Key] == 0) {
4626 $this->drawLine($X1, $Y + $YOffset + $YSpace, $X1, $Y + $YOffset + $YSize - $YSpace, $Color);
4627 if ($RecordImageMap) {
4628 $this->addToImageMap("RECT", sprintf("%s,%s,%s,%s", floor($X1 - 1), floor($Y + $YOffset + $YSpace), floor($X1 + 1), floor($Y + $YOffset + $YSize - $YSpace)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
4629 }
4630 } else {
4631 if ($RecordImageMap) {
4632 $this->addToImageMap("RECT", sprintf("%s,%s,%s,%s", floor($X1), floor($Y + $YOffset + $YSpace), floor($X2), floor($Y + $YOffset + $YSize - $YSpace)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
4633 }
4634 if ($Rounded) {
4635 $this->drawRoundedFilledRectangle($X1 + 1, $Y + $YOffset + $YSpace, $X2, $Y + $YOffset + $YSize - $YSpace, $RoundRadius, $Color);
4636 } else {
4637 $this->drawFilledRectangle($X1, $Y + $YOffset + $YSpace, $X2, $Y + $YOffset + $YSize - $YSpace, $Color);
4638 if ($InnerColor != null) {
4639 $this->drawRectangle(min($X1, $X2) + 1, $Y + $YOffset + $YSpace + 1, max($X1, $X2) - 1, $Y + $YOffset + $YSize - $YSpace - 1, $InnerColor);
4640 }
4641 if ($Gradient) {
4642 $this->Shadow = \false;
4643 if ($GradientMode == GRADIENT_SIMPLE) {
4644 if ($Serie["Data"][$Key] >= 0) {
4645 $GradienColor = ["StartR" => $GradientStartR, "StartG" => $GradientStartG, "StartB" => $GradientStartB, "EndR" => $GradientEndR, "EndG" => $GradientEndG, "EndB" => $GradientEndB, "Alpha" => $GradientAlpha];
4646 } else {
4647 $GradienColor = ["StartR" => $GradientEndR, "StartG" => $GradientEndG, "StartB" => $GradientEndB, "EndR" => $GradientStartR, "EndG" => $GradientStartG, "EndB" => $GradientStartB, "Alpha" => $GradientAlpha];
4648 }
4649 $this->drawGradientArea($X1, $Y + $YOffset + $YSpace, $X2, $Y + $YOffset + $YSize - $YSpace, DIRECTION_HORIZONTAL, $GradienColor);
4650 } elseif ($GradientMode == GRADIENT_EFFECT_CAN) {
4651 $GradienColor1 = ["StartR" => $GradientEndR, "StartG" => $GradientEndG, "StartB" => $GradientEndB, "EndR" => $GradientStartR, "EndG" => $GradientStartG, "EndB" => $GradientStartB, "Alpha" => $GradientAlpha];
4652 $GradienColor2 = ["StartR" => $GradientStartR, "StartG" => $GradientStartG, "StartB" => $GradientStartB, "EndR" => $GradientEndR, "EndG" => $GradientEndG, "EndB" => $GradientEndB, "Alpha" => $GradientAlpha];
4653 $YSpan = floor($YSize / 3);
4654 $this->drawGradientArea($X1, $Y + $YOffset + $YSpace, $X2, $Y + $YOffset + $YSpan - $YSpace, DIRECTION_VERTICAL, $GradienColor1);
4655 $this->drawGradientArea($X1, $Y + $YOffset + $YSpan, $X2, $Y + $YOffset + $YSize - $YSpace, DIRECTION_VERTICAL, $GradienColor2);
4656 }
4657 $this->Shadow = $RestoreShadow;
4658 }
4659 }
4660 if ($Draw0Line) {
4661 $Line0Color = ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20];
4662 if (abs($X1 - $X2) > 3) {
4663 $Line0Width = 3;
4664 } else {
4665 $Line0Width = 1;
4666 }
4667 if ($X2 - $X1 < 0) {
4668 $Line0Width = -$Line0Width;
4669 }
4670 $this->drawFilledRectangle(floor($X1), $Y + $YOffset + $YSpace, floor($X1) + $Line0Width, $Y + $YOffset + $YSize - $YSpace, $Line0Color);
4671 $this->drawLine(floor($X1), $Y + $YOffset + $YSpace, floor($X1), $Y + $YOffset + $YSize - $YSpace, $Line0Color);
4672 }
4673 }
4674 if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
4675 if ($DisplayShadow) {
4676 $this->Shadow = \true;
4677 }
4678 $Caption = $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit);
4679 $TxtPos = $this->getTextBox(0, 0, $DisplayFont, $DisplaySize, 0, $Caption);
4680 $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"] + $TxtMargin;
4681 if ($DisplayPos == LABEL_POS_INSIDE && abs($TxtWidth) < abs($BarWidth)) {
4682 $CenterX = ($X2 - $X1) / 2 + $X1;
4683 $CenterY = ($Y + $YOffset + $YSize - $YSpace - ($Y + $YOffset + $YSpace)) / 2 + ($Y + $YOffset + $YSpace);
4684 $this->drawText($CenterX, $CenterY, $Caption, ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => TEXT_ALIGN_MIDDLEMIDDLE, "FontSize" => $DisplaySize]);
4685 } else {
4686 if ($Serie["Data"][$Key] >= 0) {
4687 $Align = TEXT_ALIGN_MIDDLELEFT;
4688 $Offset = $DisplayOffset;
4689 } else {
4690 $Align = TEXT_ALIGN_MIDDLERIGHT;
4691 $Offset = -$DisplayOffset;
4692 }
4693 $this->drawText($X2 + $Offset, $Y + $YOffset + $YSize / 2, $Caption, ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => $Align, "FontSize" => $DisplaySize]);
4694 }
4695 $this->Shadow = $RestoreShadow;
4696 }
4697 }
4698 $Y = $Y + $YStep;
4699 $ID++;
4700 }
4701 }
4702 $CurrentSerie++;
4703 }
4704 }
4705 }
4706 /**
4707 * Draw a bar chart
4708 * @param array $Format
4709 */
4710 public function drawStackedBarChart(array $Format = [])
4711 {
4712 $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : \false;
4713 $DisplayOrientation = isset($Format["DisplayOrientation"]) ? $Format["DisplayOrientation"] : ORIENTATION_AUTO;
4714 $DisplayRound = isset($Format["DisplayRound"]) ? $Format["DisplayRound"] : 0;
4715 $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
4716 $DisplayFont = isset($Format["DisplayFont"]) ? $Format["DisplayFont"] : $this->FontName;
4717 $DisplaySize = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontSize;
4718 $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
4719 $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
4720 $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
4721 $Interleave = isset($Format["Interleave"]) ? $Format["Interleave"] : 0.5;
4722 $Rounded = isset($Format["Rounded"]) ? $Format["Rounded"] : \false;
4723 $RoundRadius = isset($Format["RoundRadius"]) ? $Format["RoundRadius"] : 4;
4724 $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
4725 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
4726 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
4727 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
4728 $Gradient = isset($Format["Gradient"]) ? $Format["Gradient"] : \false;
4729 $GradientMode = isset($Format["GradientMode"]) ? $Format["GradientMode"] : GRADIENT_SIMPLE;
4730 $GradientAlpha = isset($Format["GradientAlpha"]) ? $Format["GradientAlpha"] : 20;
4731 $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255;
4732 $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255;
4733 $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255;
4734 $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 0;
4735 $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 0;
4736 $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 0;
4737 $InnerSurrounding = isset($Format["InnerSurrounding"]) ? $Format["InnerSurrounding"] : null;
4738 $InnerBorderR = isset($Format["InnerBorderR"]) ? $Format["InnerBorderR"] : -1;
4739 $InnerBorderG = isset($Format["InnerBorderG"]) ? $Format["InnerBorderG"] : -1;
4740 $InnerBorderB = isset($Format["InnerBorderB"]) ? $Format["InnerBorderB"] : -1;
4741 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : \false;
4742 $FontFactor = isset($Format["FontFactor"]) ? $Format["FontFactor"] : 8;
4743 $this->LastChartLayout = CHART_LAST_LAYOUT_STACKED;
4744 $Data = $this->DataSet->getData();
4745 list($XMargin, $XDivs) = $this->scaleGetXSettings();
4746 $RestoreShadow = $this->Shadow;
4747 $LastX = [];
4748 $LastY = [];
4749 foreach ($Data["Series"] as $SerieName => $Serie) {
4750 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
4751 $R = $Serie["Color"]["R"];
4752 $G = $Serie["Color"]["G"];
4753 $B = $Serie["Color"]["B"];
4754 $Alpha = $Serie["Color"]["Alpha"];
4755 $Ticks = $Serie["Ticks"];
4756 if ($DisplayColor == DISPLAY_AUTO) {
4757 $DisplayR = 255;
4758 $DisplayG = 255;
4759 $DisplayB = 255;
4760 }
4761 if ($Surrounding != null) {
4762 $BorderR = $R + $Surrounding;
4763 $BorderG = $G + $Surrounding;
4764 $BorderB = $B + $Surrounding;
4765 }
4766 if ($InnerSurrounding != null) {
4767 $InnerBorderR = $R + $InnerSurrounding;
4768 $InnerBorderG = $G + $InnerSurrounding;
4769 $InnerBorderB = $B + $InnerSurrounding;
4770 }
4771 if ($InnerBorderR == -1) {
4772 $InnerColor = null;
4773 } else {
4774 $InnerColor = ["R" => $InnerBorderR, "G" => $InnerBorderG, "B" => $InnerBorderB];
4775 }
4776 $AxisID = $Serie["Axis"];
4777 $Mode = $Data["Axis"][$AxisID]["Display"];
4778 $Format = $Data["Axis"][$AxisID]["Format"];
4779 $Unit = $Data["Axis"][$AxisID]["Unit"];
4780 if (isset($Serie["Description"])) {
4781 $SerieDescription = $Serie["Description"];
4782 } else {
4783 $SerieDescription = $SerieName;
4784 }
4785 $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]], \true);
4786 $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]);
4787 $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
4788 $Color = ["TransCorner" => \true, "R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "BorderR" => $BorderR, "BorderG" => $BorderG, "BorderB" => $BorderB];
4789 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
4790 if ($YZero > $this->GraphAreaY2 - 1) {
4791 $YZero = $this->GraphAreaY2 - 1;
4792 }
4793 if ($YZero > $this->GraphAreaY2 - 1) {
4794 $YZero = $this->GraphAreaY2 - 1;
4795 }
4796 if ($XDivs == 0) {
4797 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
4798 } else {
4799 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
4800 }
4801 $X = $this->GraphAreaX1 + $XMargin;
4802 $XSize = $XStep / (1 + $Interleave);
4803 $XOffset = -($XSize / 2);
4804 if (!is_array($PosArray)) {
4805 $Value = $PosArray;
4806 $PosArray = [];
4807 $PosArray[0] = $Value;
4808 }
4809 foreach ($PosArray as $Key => $Height) {
4810 if ($Height != VOID && $Serie["Data"][$Key] != 0) {
4811 if ($Serie["Data"][$Key] > 0) {
4812 $Pos = "+";
4813 } else {
4814 $Pos = "-";
4815 }
4816 if (!isset($LastY[$Key])) {
4817 $LastY[$Key] = [];
4818 }
4819 if (!isset($LastY[$Key][$Pos])) {
4820 $LastY[$Key][$Pos] = $YZero;
4821 }
4822 $Y1 = $LastY[$Key][$Pos];
4823 $Y2 = $Y1 - $Height;
4824 if (($Rounded || $BorderR != -1) && ($Pos == "+" && $Y1 != $YZero)) {
4825 $YSpaceUp = 1;
4826 } else {
4827 $YSpaceUp = 0;
4828 }
4829 if (($Rounded || $BorderR != -1) && ($Pos == "-" && $Y1 != $YZero)) {
4830 $YSpaceDown = 1;
4831 } else {
4832 $YSpaceDown = 0;
4833 }
4834 if ($RecordImageMap) {
4835 $this->addToImageMap("RECT", sprintf("%s,%s,%s,%s", floor($X + $XOffset), floor($Y1 - $YSpaceUp + $YSpaceDown), floor($X + $XOffset + $XSize), floor($Y2)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
4836 }
4837 if ($Rounded) {
4838 $this->drawRoundedFilledRectangle($X + $XOffset, $Y1 - $YSpaceUp + $YSpaceDown, $X + $XOffset + $XSize, $Y2, $RoundRadius, $Color);
4839 } else {
4840 $this->drawFilledRectangle($X + $XOffset, $Y1 - $YSpaceUp + $YSpaceDown, $X + $XOffset + $XSize, $Y2, $Color);
4841 if ($InnerColor != null) {
4842 $RestoreShadow = $this->Shadow;
4843 $this->Shadow = \false;
4844 $this->drawRectangle(min($X + $XOffset + 1, $X + $XOffset + $XSize), min($Y1 - $YSpaceUp + $YSpaceDown, $Y2) + 1, max($X + $XOffset + 1, $X + $XOffset + $XSize) - 1, max($Y1 - $YSpaceUp + $YSpaceDown, $Y2) - 1, $InnerColor);
4845 $this->Shadow = $RestoreShadow;
4846 }
4847 if ($Gradient) {
4848 $this->Shadow = \false;
4849 if ($GradientMode == GRADIENT_SIMPLE) {
4850 $GradientColor = ["StartR" => $GradientStartR, "StartG" => $GradientStartG, "StartB" => $GradientStartB, "EndR" => $GradientEndR, "EndG" => $GradientEndG, "EndB" => $GradientEndB, "Alpha" => $GradientAlpha];
4851 $this->drawGradientArea($X + $XOffset, $Y1 - 1 - $YSpaceUp + $YSpaceDown, $X + $XOffset + $XSize, $Y2 + 1, DIRECTION_VERTICAL, $GradientColor);
4852 } elseif ($GradientMode == GRADIENT_EFFECT_CAN) {
4853 $GradientColor1 = ["StartR" => $GradientEndR, "StartG" => $GradientEndG, "StartB" => $GradientEndB, "EndR" => $GradientStartR, "EndG" => $GradientStartG, "EndB" => $GradientStartB, "Alpha" => $GradientAlpha];
4854 $GradientColor2 = ["StartR" => $GradientStartR, "StartG" => $GradientStartG, "StartB" => $GradientStartB, "EndR" => $GradientEndR, "EndG" => $GradientEndG, "EndB" => $GradientEndB, "Alpha" => $GradientAlpha];
4855 $XSpan = floor($XSize / 3);
4856 $this->drawGradientArea($X + $XOffset - 0.5, $Y1 - 0.5 - $YSpaceUp + $YSpaceDown, $X + $XOffset + $XSpan, $Y2 + 0.5, DIRECTION_HORIZONTAL, $GradientColor1);
4857 $this->drawGradientArea($X + $XSpan + $XOffset - 0.5, $Y1 - 0.5 - $YSpaceUp + $YSpaceDown, $X + $XOffset + $XSize, $Y2 + 0.5, DIRECTION_HORIZONTAL, $GradientColor2);
4858 }
4859 $this->Shadow = $RestoreShadow;
4860 }
4861 }
4862 if ($DisplayValues) {
4863 $BarHeight = abs($Y2 - $Y1) - 2;
4864 $BarWidth = $XSize + $XOffset / 2 - $FontFactor;
4865 $Caption = $this->scaleFormat(round($Serie["Data"][$Key], $DisplayRound), $Mode, $Format, $Unit);
4866 $TxtPos = $this->getTextBox(0, 0, $DisplayFont, $DisplaySize, 0, $Caption);
4867 $TxtHeight = abs($TxtPos[2]["Y"] - $TxtPos[0]["Y"]);
4868 $TxtWidth = abs($TxtPos[1]["X"] - $TxtPos[0]["X"]);
4869 $XCenter = ($X + $XOffset + $XSize - ($X + $XOffset)) / 2 + $X + $XOffset;
4870 $YCenter = ($Y2 - ($Y1 - $YSpaceUp + $YSpaceDown)) / 2 + $Y1 - $YSpaceUp + $YSpaceDown;
4871 $Done = \false;
4872 if ($DisplayOrientation == ORIENTATION_HORIZONTAL || $DisplayOrientation == ORIENTATION_AUTO) {
4873 if ($TxtHeight < $BarHeight && $TxtWidth < $BarWidth) {
4874 $this->drawText($XCenter, $YCenter, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => TEXT_ALIGN_MIDDLEMIDDLE, "FontSize" => $DisplaySize, "FontName" => $DisplayFont]);
4875 $Done = \true;
4876 }
4877 }
4878 if ($DisplayOrientation == ORIENTATION_VERTICAL || $DisplayOrientation == ORIENTATION_AUTO && !$Done) {
4879 if ($TxtHeight < $BarWidth && $TxtWidth < $BarHeight) {
4880 $this->drawText($XCenter, $YCenter, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Angle" => 90, "Align" => TEXT_ALIGN_MIDDLEMIDDLE, "FontSize" => $DisplaySize, "FontName" => $DisplayFont]);
4881 }
4882 }
4883 }
4884 $LastY[$Key][$Pos] = $Y2;
4885 }
4886 $X = $X + $XStep;
4887 }
4888 } else {
4889 if ($YZero < $this->GraphAreaX1 + 1) {
4890 $YZero = $this->GraphAreaX1 + 1;
4891 }
4892 if ($YZero > $this->GraphAreaX2 - 1) {
4893 $YZero = $this->GraphAreaX2 - 1;
4894 }
4895 if ($XDivs == 0) {
4896 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
4897 } else {
4898 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
4899 }
4900 $Y = $this->GraphAreaY1 + $XMargin;
4901 $YSize = $YStep / (1 + $Interleave);
4902 $YOffset = -($YSize / 2);
4903 if (!is_array($PosArray)) {
4904 $Value = $PosArray;
4905 $PosArray = [];
4906 $PosArray[0] = $Value;
4907 }
4908 foreach ($PosArray as $Key => $Width) {
4909 if ($Width != VOID && $Serie["Data"][$Key] != 0) {
4910 if ($Serie["Data"][$Key] > 0) {
4911 $Pos = "+";
4912 } else {
4913 $Pos = "-";
4914 }
4915 if (!isset($LastX[$Key])) {
4916 $LastX[$Key] = [];
4917 }
4918 if (!isset($LastX[$Key][$Pos])) {
4919 $LastX[$Key][$Pos] = $YZero;
4920 }
4921 $X1 = $LastX[$Key][$Pos];
4922 $X2 = $X1 + $Width;
4923 if (($Rounded || $BorderR != -1) && ($Pos == "+" && $X1 != $YZero)) {
4924 $XSpaceLeft = 2;
4925 } else {
4926 $XSpaceLeft = 0;
4927 }
4928 if (($Rounded || $BorderR != -1) && ($Pos == "-" && $X1 != $YZero)) {
4929 $XSpaceRight = 2;
4930 } else {
4931 $XSpaceRight = 0;
4932 }
4933 if ($RecordImageMap) {
4934 $this->addToImageMap("RECT", sprintf("%s,%s,%s,%s", floor($X1 + $XSpaceLeft), floor($Y + $YOffset), floor($X2 - $XSpaceRight), floor($Y + $YOffset + $YSize)), $this->toHTMLColor($R, $G, $B), $SerieDescription, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit));
4935 }
4936 if ($Rounded) {
4937 $this->drawRoundedFilledRectangle($X1 + $XSpaceLeft, $Y + $YOffset, $X2 - $XSpaceRight, $Y + $YOffset + $YSize, $RoundRadius, $Color);
4938 } else {
4939 $this->drawFilledRectangle($X1 + $XSpaceLeft, $Y + $YOffset, $X2 - $XSpaceRight, $Y + $YOffset + $YSize, $Color);
4940 if ($InnerColor != null) {
4941 $RestoreShadow = $this->Shadow;
4942 $this->Shadow = \false;
4943 $this->drawRectangle(min($X1 + $XSpaceLeft, $X2 - $XSpaceRight) + 1, min($Y + $YOffset, $Y + $YOffset + $YSize) + 1, max($X1 + $XSpaceLeft, $X2 - $XSpaceRight) - 1, max($Y + $YOffset, $Y + $YOffset + $YSize) - 1, $InnerColor);
4944 $this->Shadow = $RestoreShadow;
4945 }
4946 if ($Gradient) {
4947 $this->Shadow = \false;
4948 if ($GradientMode == GRADIENT_SIMPLE) {
4949 $GradientColor = ["StartR" => $GradientStartR, "StartG" => $GradientStartG, "StartB" => $GradientStartB, "EndR" => $GradientEndR, "EndG" => $GradientEndG, "EndB" => $GradientEndB, "Alpha" => $GradientAlpha];
4950 $this->drawGradientArea($X1 + $XSpaceLeft, $Y + $YOffset, $X2 - $XSpaceRight, $Y + $YOffset + $YSize, DIRECTION_HORIZONTAL, $GradientColor);
4951 } elseif ($GradientMode == GRADIENT_EFFECT_CAN) {
4952 $GradientColor1 = ["StartR" => $GradientEndR, "StartG" => $GradientEndG, "StartB" => $GradientEndB, "EndR" => $GradientStartR, "EndG" => $GradientStartG, "EndB" => $GradientStartB, "Alpha" => $GradientAlpha];
4953 $GradientColor2 = ["StartR" => $GradientStartR, "StartG" => $GradientStartG, "StartB" => $GradientStartB, "EndR" => $GradientEndR, "EndG" => $GradientEndG, "EndB" => $GradientEndB, "Alpha" => $GradientAlpha];
4954 $YSpan = floor($YSize / 3);
4955 $this->drawGradientArea($X1 + $XSpaceLeft, $Y + $YOffset, $X2 - $XSpaceRight, $Y + $YOffset + $YSpan, DIRECTION_VERTICAL, $GradientColor1);
4956 $this->drawGradientArea($X1 + $XSpaceLeft, $Y + $YOffset + $YSpan, $X2 - $XSpaceRight, $Y + $YOffset + $YSize, DIRECTION_VERTICAL, $GradientColor2);
4957 }
4958 $this->Shadow = $RestoreShadow;
4959 }
4960 }
4961 if ($DisplayValues) {
4962 $BarWidth = abs($X2 - $X1) - $FontFactor;
4963 $BarHeight = $YSize + $YOffset / 2 - $FontFactor / 2;
4964 $Caption = $this->scaleFormat(round($Serie["Data"][$Key], $DisplayRound), $Mode, $Format, $Unit);
4965 $TxtPos = $this->getTextBox(0, 0, $DisplayFont, $DisplaySize, 0, $Caption);
4966 $TxtHeight = abs($TxtPos[2]["Y"] - $TxtPos[0]["Y"]);
4967 $TxtWidth = abs($TxtPos[1]["X"] - $TxtPos[0]["X"]);
4968 $XCenter = ($X2 - $X1) / 2 + $X1;
4969 $YCenter = ($Y + $YOffset + $YSize - ($Y + $YOffset)) / 2 + $Y + $YOffset;
4970 $Done = \false;
4971 if ($DisplayOrientation == ORIENTATION_HORIZONTAL || $DisplayOrientation == ORIENTATION_AUTO) {
4972 if ($TxtHeight < $BarHeight && $TxtWidth < $BarWidth) {
4973 $this->drawText($XCenter, $YCenter, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => TEXT_ALIGN_MIDDLEMIDDLE, "FontSize" => $DisplaySize, "FontName" => $DisplayFont]);
4974 $Done = \true;
4975 }
4976 }
4977 if ($DisplayOrientation == ORIENTATION_VERTICAL || $DisplayOrientation == ORIENTATION_AUTO && !$Done) {
4978 if ($TxtHeight < $BarWidth && $TxtWidth < $BarHeight) {
4979 $this->drawText($XCenter, $YCenter, $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit), ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Angle" => 90, "Align" => TEXT_ALIGN_MIDDLEMIDDLE, "FontSize" => $DisplaySize, "FontName" => $DisplayFont]);
4980 }
4981 }
4982 }
4983 $LastX[$Key][$Pos] = $X2;
4984 }
4985 $Y = $Y + $YStep;
4986 }
4987 }
4988 }
4989 }
4990 }
4991 /**
4992 * Draw a stacked area chart
4993 * @param array $Format
4994 */
4995 public function drawStackedAreaChart(array $Format = [])
4996 {
4997 $DrawLine = isset($Format["DrawLine"]) ? $Format["DrawLine"] : \false;
4998 $LineSurrounding = isset($Format["LineSurrounding"]) ? $Format["LineSurrounding"] : null;
4999 $LineR = isset($Format["LineR"]) ? $Format["LineR"] : VOID;
5000 $LineG = isset($Format["LineG"]) ? $Format["LineG"] : VOID;
5001 $LineB = isset($Format["LineB"]) ? $Format["LineB"] : VOID;
5002 $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 100;
5003 $DrawPlot = isset($Format["DrawPlot"]) ? $Format["DrawPlot"] : \false;
5004 $PlotRadius = isset($Format["PlotRadius"]) ? $Format["PlotRadius"] : 2;
5005 $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : 1;
5006 $PlotBorderSurrounding = isset($Format["PlotBorderSurrounding"]) ? $Format["PlotBorderSurrounding"] : null;
5007 $PlotBorderR = isset($Format["PlotBorderR"]) ? $Format["PlotBorderR"] : 0;
5008 $PlotBorderG = isset($Format["PlotBorderG"]) ? $Format["PlotBorderG"] : 0;
5009 $PlotBorderB = isset($Format["PlotBorderB"]) ? $Format["PlotBorderB"] : 0;
5010 $PlotBorderAlpha = isset($Format["PlotBorderAlpha"]) ? $Format["PlotBorderAlpha"] : 50;
5011 $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : null;
5012 $this->LastChartLayout = CHART_LAST_LAYOUT_STACKED;
5013 $Data = $this->DataSet->getData();
5014 list($XMargin, $XDivs) = $this->scaleGetXSettings();
5015 $RestoreShadow = $this->Shadow;
5016 $this->Shadow = \false;
5017 /* Build the offset data series */
5018 $OverallOffset = [];
5019 $SerieOrder = [];
5020 foreach ($Data["Series"] as $SerieName => $Serie) {
5021 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
5022 $SerieOrder[] = $SerieName;
5023 foreach ($Serie["Data"] as $Key => $Value) {
5024 if ($Value == VOID) {
5025 $Value = 0;
5026 }
5027 if ($Value >= 0) {
5028 $Sign = "+";
5029 } else {
5030 $Sign = "-";
5031 }
5032 if (!isset($OverallOffset[$Key]) || !isset($OverallOffset[$Key][$Sign])) {
5033 $OverallOffset[$Key][$Sign] = 0;
5034 }
5035 if ($Sign == "+") {
5036 $Data["Series"][$SerieName]["Data"][$Key] = $Value + $OverallOffset[$Key][$Sign];
5037 } else {
5038 $Data["Series"][$SerieName]["Data"][$Key] = $Value - $OverallOffset[$Key][$Sign];
5039 }
5040 $OverallOffset[$Key][$Sign] = $OverallOffset[$Key][$Sign] + abs($Value);
5041 }
5042 }
5043 }
5044 $SerieOrder = array_reverse($SerieOrder);
5045 foreach ($SerieOrder as $Key => $SerieName) {
5046 $Serie = $Data["Series"][$SerieName];
5047 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
5048 $R = $Serie["Color"]["R"];
5049 $G = $Serie["Color"]["G"];
5050 $B = $Serie["Color"]["B"];
5051 $Alpha = $Serie["Color"]["Alpha"];
5052 $Ticks = $Serie["Ticks"];
5053 if ($ForceTransparency != null) {
5054 $Alpha = $ForceTransparency;
5055 }
5056 $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha];
5057 if ($LineSurrounding != null) {
5058 $LineColor = ["R" => $R + $LineSurrounding, "G" => $G + $LineSurrounding, "B" => $B + $LineSurrounding, "Alpha" => $Alpha];
5059 } elseif ($LineR != VOID) {
5060 $LineColor = ["R" => $LineR, "G" => $LineG, "B" => $LineB, "Alpha" => $LineAlpha];
5061 } else {
5062 $LineColor = $Color;
5063 }
5064 if ($PlotBorderSurrounding != null) {
5065 $PlotBorderColor = ["R" => $R + $PlotBorderSurrounding, "G" => $G + $PlotBorderSurrounding, "B" => $B + $PlotBorderSurrounding, "Alpha" => $PlotBorderAlpha];
5066 } else {
5067 $PlotBorderColor = ["R" => $PlotBorderR, "G" => $PlotBorderG, "B" => $PlotBorderB, "Alpha" => $PlotBorderAlpha];
5068 }
5069 $AxisID = $Serie["Axis"];
5070 $Format = $Data["Axis"][$AxisID]["Format"];
5071 $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]], \true);
5072 $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]);
5073 $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
5074 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
5075 if ($YZero < $this->GraphAreaY1 + 1) {
5076 $YZero = $this->GraphAreaY1 + 1;
5077 }
5078 if ($YZero > $this->GraphAreaY2 - 1) {
5079 $YZero = $this->GraphAreaY2 - 1;
5080 }
5081 if ($XDivs == 0) {
5082 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
5083 } else {
5084 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
5085 }
5086 $X = $this->GraphAreaX1 + $XMargin;
5087 if (!is_array($PosArray)) {
5088 $Value = $PosArray;
5089 $PosArray = [];
5090 $PosArray[0] = $Value;
5091 }
5092 $Plots = [];
5093 $Plots[] = $X;
5094 $Plots[] = $YZero;
5095 foreach ($PosArray as $Key => $Height) {
5096 if ($Height != VOID) {
5097 $Plots[] = $X;
5098 $Plots[] = $YZero - $Height;
5099 }
5100 $X = $X + $XStep;
5101 }
5102 $Plots[] = $X - $XStep;
5103 $Plots[] = $YZero;
5104 $this->drawPolygon($Plots, $Color);
5105 $this->Shadow = $RestoreShadow;
5106 if ($DrawLine) {
5107 for ($i = 2; $i <= count($Plots) - 6; $i = $i + 2) {
5108 $this->drawLine($Plots[$i], $Plots[$i + 1], $Plots[$i + 2], $Plots[$i + 3], $LineColor);
5109 }
5110 }
5111 if ($DrawPlot) {
5112 for ($i = 2; $i <= count($Plots) - 4; $i = $i + 2) {
5113 if ($PlotBorder != 0) {
5114 $this->drawFilledCircle($Plots[$i], $Plots[$i + 1], $PlotRadius + $PlotBorder, $PlotBorderColor);
5115 }
5116 $this->drawFilledCircle($Plots[$i], $Plots[$i + 1], $PlotRadius, $Color);
5117 }
5118 }
5119 $this->Shadow = \false;
5120 } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) {
5121 if ($YZero < $this->GraphAreaX1 + 1) {
5122 $YZero = $this->GraphAreaX1 + 1;
5123 }
5124 if ($YZero > $this->GraphAreaX2 - 1) {
5125 $YZero = $this->GraphAreaX2 - 1;
5126 }
5127 if ($XDivs == 0) {
5128 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
5129 } else {
5130 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
5131 }
5132 $Y = $this->GraphAreaY1 + $XMargin;
5133 if (!is_array($PosArray)) {
5134 $Value = $PosArray;
5135 $PosArray = [];
5136 $PosArray[0] = $Value;
5137 }
5138 $Plots = [];
5139 $Plots[] = $YZero;
5140 $Plots[] = $Y;
5141 foreach ($PosArray as $Key => $Height) {
5142 if ($Height != VOID) {
5143 $Plots[] = $YZero + $Height;
5144 $Plots[] = $Y;
5145 }
5146 $Y = $Y + $YStep;
5147 }
5148 $Plots[] = $YZero;
5149 $Plots[] = $Y - $YStep;
5150 $this->drawPolygon($Plots, $Color);
5151 $this->Shadow = $RestoreShadow;
5152 if ($DrawLine) {
5153 for ($i = 2; $i <= count($Plots) - 6; $i = $i + 2) {
5154 $this->drawLine($Plots[$i], $Plots[$i + 1], $Plots[$i + 2], $Plots[$i + 3], $LineColor);
5155 }
5156 }
5157 if ($DrawPlot) {
5158 for ($i = 2; $i <= count($Plots) - 4; $i = $i + 2) {
5159 if ($PlotBorder != 0) {
5160 $this->drawFilledCircle($Plots[$i], $Plots[$i + 1], $PlotRadius + $PlotBorder, $PlotBorderColor);
5161 }
5162 $this->drawFilledCircle($Plots[$i], $Plots[$i + 1], $PlotRadius, $Color);
5163 }
5164 }
5165 $this->Shadow = \false;
5166 }
5167 }
5168 }
5169 $this->Shadow = $RestoreShadow;
5170 }
5171 /**
5172 * Draw the derivative chart associated to the data series
5173 * @param array $Format
5174 */
5175 public function drawDerivative(array $Format = [])
5176 {
5177 $Offset = isset($Format["Offset"]) ? $Format["Offset"] : 10;
5178 $SerieSpacing = isset($Format["SerieSpacing"]) ? $Format["SerieSpacing"] : 3;
5179 $DerivativeHeight = isset($Format["DerivativeHeight"]) ? $Format["DerivativeHeight"] : 4;
5180 $ShadedSlopeBox = isset($Format["ShadedSlopeBox"]) ? $Format["ShadedSlopeBox"] : \false;
5181 $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : \true;
5182 $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255;
5183 $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255;
5184 $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255;
5185 $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 20;
5186 $DrawBorder = isset($Format["DrawBorder"]) ? $Format["DrawBorder"] : \true;
5187 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0;
5188 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0;
5189 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0;
5190 $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 100;
5191 $Caption = isset($Format["Caption"]) ? $Format["Caption"] : \true;
5192 $CaptionHeight = isset($Format["CaptionHeight"]) ? $Format["CaptionHeight"] : 10;
5193 $CaptionWidth = isset($Format["CaptionWidth"]) ? $Format["CaptionWidth"] : 20;
5194 $CaptionMargin = isset($Format["CaptionMargin"]) ? $Format["CaptionMargin"] : 4;
5195 $CaptionLine = isset($Format["CaptionLine"]) ? $Format["CaptionLine"] : \false;
5196 $CaptionBox = isset($Format["CaptionBox"]) ? $Format["CaptionBox"] : \false;
5197 $CaptionBorderR = isset($Format["CaptionBorderR"]) ? $Format["CaptionBorderR"] : 0;
5198 $CaptionBorderG = isset($Format["CaptionBorderG"]) ? $Format["CaptionBorderG"] : 0;
5199 $CaptionBorderB = isset($Format["CaptionBorderB"]) ? $Format["CaptionBorderB"] : 0;
5200 $CaptionFillR = isset($Format["CaptionFillR"]) ? $Format["CaptionFillR"] : 255;
5201 $CaptionFillG = isset($Format["CaptionFillG"]) ? $Format["CaptionFillG"] : 255;
5202 $CaptionFillB = isset($Format["CaptionFillB"]) ? $Format["CaptionFillB"] : 255;
5203 $CaptionFillAlpha = isset($Format["CaptionFillAlpha"]) ? $Format["CaptionFillAlpha"] : 80;
5204 $PositiveSlopeStartR = isset($Format["PositiveSlopeStartR"]) ? $Format["PositiveSlopeStartR"] : 184;
5205 $PositiveSlopeStartG = isset($Format["PositiveSlopeStartG"]) ? $Format["PositiveSlopeStartG"] : 234;
5206 $PositiveSlopeStartB = isset($Format["PositiveSlopeStartB"]) ? $Format["PositiveSlopeStartB"] : 88;
5207 $PositiveSlopeEndR = isset($Format["PositiveSlopeStartR"]) ? $Format["PositiveSlopeStartR"] : 239;
5208 $PositiveSlopeEndG = isset($Format["PositiveSlopeStartG"]) ? $Format["PositiveSlopeStartG"] : 31;
5209 $PositiveSlopeEndB = isset($Format["PositiveSlopeStartB"]) ? $Format["PositiveSlopeStartB"] : 36;
5210 $NegativeSlopeStartR = isset($Format["NegativeSlopeStartR"]) ? $Format["NegativeSlopeStartR"] : 184;
5211 $NegativeSlopeStartG = isset($Format["NegativeSlopeStartG"]) ? $Format["NegativeSlopeStartG"] : 234;
5212 $NegativeSlopeStartB = isset($Format["NegativeSlopeStartB"]) ? $Format["NegativeSlopeStartB"] : 88;
5213 $NegativeSlopeEndR = isset($Format["NegativeSlopeStartR"]) ? $Format["NegativeSlopeStartR"] : 67;
5214 $NegativeSlopeEndG = isset($Format["NegativeSlopeStartG"]) ? $Format["NegativeSlopeStartG"] : 124;
5215 $NegativeSlopeEndB = isset($Format["NegativeSlopeStartB"]) ? $Format["NegativeSlopeStartB"] : 227;
5216 $Data = $this->DataSet->getData();
5217 list($XMargin, $XDivs) = $this->scaleGetXSettings();
5218 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
5219 $YPos = $this->DataSet->Data["GraphArea"]["Y2"] + $Offset;
5220 } else {
5221 $XPos = $this->DataSet->Data["GraphArea"]["X2"] + $Offset;
5222 }
5223 foreach ($Data["Series"] as $SerieName => $Serie) {
5224 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
5225 $R = $Serie["Color"]["R"];
5226 $G = $Serie["Color"]["G"];
5227 $B = $Serie["Color"]["B"];
5228 $Alpha = $Serie["Color"]["Alpha"];
5229 $Ticks = $Serie["Ticks"];
5230 $Weight = $Serie["Weight"];
5231 $AxisID = $Serie["Axis"];
5232 $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
5233 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
5234 if ($Caption) {
5235 if ($CaptionLine) {
5236 $StartX = floor($this->GraphAreaX1 - $CaptionWidth + $XMargin - $CaptionMargin);
5237 $EndX = floor($this->GraphAreaX1 - $CaptionMargin + $XMargin);
5238 $CaptionSettings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight];
5239 if ($CaptionBox) {
5240 $this->drawFilledRectangle($StartX, $YPos, $EndX, $YPos + $CaptionHeight, ["R" => $CaptionFillR, "G" => $CaptionFillG, "B" => $CaptionFillB, "BorderR" => $CaptionBorderR, "BorderG" => $CaptionBorderG, "BorderB" => $CaptionBorderB, "Alpha" => $CaptionFillAlpha]);
5241 }
5242 $this->drawLine($StartX + 2, $YPos + $CaptionHeight / 2, $EndX - 2, $YPos + $CaptionHeight / 2, $CaptionSettings);
5243 } else {
5244 $this->drawFilledRectangle($this->GraphAreaX1 - $CaptionWidth + $XMargin - $CaptionMargin, $YPos, $this->GraphAreaX1 - $CaptionMargin + $XMargin, $YPos + $CaptionHeight, ["R" => $R, "G" => $G, "B" => $B, "BorderR" => $CaptionBorderR, "BorderG" => $CaptionBorderG, "BorderB" => $CaptionBorderB]);
5245 }
5246 }
5247 if ($XDivs == 0) {
5248 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
5249 } else {
5250 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
5251 }
5252 $X = $this->GraphAreaX1 + $XMargin;
5253 $TopY = $YPos + $CaptionHeight / 2 - $DerivativeHeight / 2;
5254 $BottomY = $YPos + $CaptionHeight / 2 + $DerivativeHeight / 2;
5255 $StartX = floor($this->GraphAreaX1 + $XMargin);
5256 $EndX = floor($this->GraphAreaX2 - $XMargin);
5257 if ($DrawBackground) {
5258 $this->drawFilledRectangle($StartX - 1, $TopY - 1, $EndX + 1, $BottomY + 1, ["R" => $BackgroundR, "G" => $BackgroundG, "B" => $BackgroundB, "Alpha" => $BackgroundAlpha]);
5259 }
5260 if ($DrawBorder) {
5261 $this->drawRectangle($StartX - 1, $TopY - 1, $EndX + 1, $BottomY + 1, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha]);
5262 }
5263 if (!is_array($PosArray)) {
5264 $Value = $PosArray;
5265 $PosArray = [];
5266 $PosArray[0] = $Value;
5267 }
5268 $RestoreShadow = $this->Shadow;
5269 $this->Shadow = \false;
5270 /* Determine the Max slope index */
5271 $LastX = null;
5272 $LastY = null;
5273 $MinSlope = 0;
5274 $MaxSlope = 1;
5275 foreach ($PosArray as $Key => $Y) {
5276 if ($Y != VOID && $LastX != null) {
5277 $Slope = $LastY - $Y;
5278 if ($Slope > $MaxSlope) {
5279 $MaxSlope = $Slope;
5280 }
5281 if ($Slope < $MinSlope) {
5282 $MinSlope = $Slope;
5283 }
5284 }
5285 if ($Y == VOID) {
5286 $LastX = null;
5287 $LastY = null;
5288 } else {
5289 $LastX = $X;
5290 $LastY = $Y;
5291 }
5292 }
5293 $LastX = null;
5294 $LastY = null;
5295 $LastColor = null;
5296 foreach ($PosArray as $Key => $Y) {
5297 if ($Y != VOID && $LastY != null) {
5298 $Slope = $LastY - $Y;
5299 if ($Slope >= 0) {
5300 $SlopeIndex = 100 / $MaxSlope * $Slope;
5301 $R = ($PositiveSlopeEndR - $PositiveSlopeStartR) / 100 * $SlopeIndex + $PositiveSlopeStartR;
5302 $G = ($PositiveSlopeEndG - $PositiveSlopeStartG) / 100 * $SlopeIndex + $PositiveSlopeStartG;
5303 $B = ($PositiveSlopeEndB - $PositiveSlopeStartB) / 100 * $SlopeIndex + $PositiveSlopeStartB;
5304 } elseif ($Slope < 0) {
5305 $SlopeIndex = 100 / abs($MinSlope) * abs($Slope);
5306 $R = ($NegativeSlopeEndR - $NegativeSlopeStartR) / 100 * $SlopeIndex + $NegativeSlopeStartR;
5307 $G = ($NegativeSlopeEndG - $NegativeSlopeStartG) / 100 * $SlopeIndex + $NegativeSlopeStartG;
5308 $B = ($NegativeSlopeEndB - $NegativeSlopeStartB) / 100 * $SlopeIndex + $NegativeSlopeStartB;
5309 }
5310 $Color = ["R" => $R, "G" => $G, "B" => $B];
5311 if ($ShadedSlopeBox && $LastColor != null) {
5312 // && $Slope != 0
5313 $GradientSettings = ["StartR" => $LastColor["R"], "StartG" => $LastColor["G"], "StartB" => $LastColor["B"], "EndR" => $R, "EndG" => $G, "EndB" => $B];
5314 $this->drawGradientArea($LastX, $TopY, $X, $BottomY, DIRECTION_HORIZONTAL, $GradientSettings);
5315 } elseif (!$ShadedSlopeBox || $LastColor == null) {
5316 // || $Slope == 0
5317 $this->drawFilledRectangle(floor($LastX), $TopY, floor($X), $BottomY, $Color);
5318 }
5319 $LastColor = $Color;
5320 }
5321 if ($Y == VOID) {
5322 $LastY = null;
5323 } else {
5324 $LastX = $X;
5325 $LastY = $Y;
5326 }
5327 $X = $X + $XStep;
5328 }
5329 $YPos = $YPos + $CaptionHeight + $SerieSpacing;
5330 } else {
5331 if ($Caption) {
5332 $StartY = floor($this->GraphAreaY1 - $CaptionWidth + $XMargin - $CaptionMargin);
5333 $EndY = floor($this->GraphAreaY1 - $CaptionMargin + $XMargin);
5334 if ($CaptionLine) {
5335 $CaptionSettings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks, "Weight" => $Weight];
5336 if ($CaptionBox) {
5337 $this->drawFilledRectangle($XPos, $StartY, $XPos + $CaptionHeight, $EndY, ["R" => $CaptionFillR, "G" => $CaptionFillG, "B" => $CaptionFillB, "BorderR" => $CaptionBorderR, "BorderG" => $CaptionBorderG, "BorderB" => $CaptionBorderB, "Alpha" => $CaptionFillAlpha]);
5338 }
5339 $this->drawLine($XPos + $CaptionHeight / 2, $StartY + 2, $XPos + $CaptionHeight / 2, $EndY - 2, $CaptionSettings);
5340 } else {
5341 $this->drawFilledRectangle($XPos, $StartY, $XPos + $CaptionHeight, $EndY, ["R" => $R, "G" => $G, "B" => $B, "BorderR" => $CaptionBorderR, "BorderG" => $CaptionBorderG, "BorderB" => $CaptionBorderB]);
5342 }
5343 }
5344 if ($XDivs == 0) {
5345 $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
5346 } else {
5347 $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
5348 }
5349 $Y = $this->GraphAreaY1 + $XMargin;
5350 $TopX = $XPos + $CaptionHeight / 2 - $DerivativeHeight / 2;
5351 $BottomX = $XPos + $CaptionHeight / 2 + $DerivativeHeight / 2;
5352 $StartY = floor($this->GraphAreaY1 + $XMargin);
5353 $EndY = floor($this->GraphAreaY2 - $XMargin);
5354 if ($DrawBackground) {
5355 $this->drawFilledRectangle($TopX - 1, $StartY - 1, $BottomX + 1, $EndY + 1, ["R" => $BackgroundR, "G" => $BackgroundG, "B" => $BackgroundB, "Alpha" => $BackgroundAlpha]);
5356 }
5357 if ($DrawBorder) {
5358 $this->drawRectangle($TopX - 1, $StartY - 1, $BottomX + 1, $EndY + 1, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha]);
5359 }
5360 if (!is_array($PosArray)) {
5361 $Value = $PosArray;
5362 $PosArray = [];
5363 $PosArray[0] = $Value;
5364 }
5365 $RestoreShadow = $this->Shadow;
5366 $this->Shadow = \false;
5367 /* Determine the Max slope index */
5368 $LastX = null;
5369 $LastY = null;
5370 $MinSlope = 0;
5371 $MaxSlope = 1;
5372 foreach ($PosArray as $Key => $X) {
5373 if ($X != VOID && $LastX != null) {
5374 $Slope = $X - $LastX;
5375 if ($Slope > $MaxSlope) {
5376 $MaxSlope = $Slope;
5377 }
5378 if ($Slope < $MinSlope) {
5379 $MinSlope = $Slope;
5380 }
5381 }
5382 if ($X == VOID) {
5383 $LastX = null;
5384 } else {
5385 $LastX = $X;
5386 }
5387 }
5388 $LastX = null;
5389 $LastY = null;
5390 $LastColor = null;
5391 foreach ($PosArray as $Key => $X) {
5392 if ($X != VOID && $LastX != null) {
5393 $Slope = $X - $LastX;
5394 if ($Slope >= 0) {
5395 $SlopeIndex = 100 / $MaxSlope * $Slope;
5396 $R = ($PositiveSlopeEndR - $PositiveSlopeStartR) / 100 * $SlopeIndex + $PositiveSlopeStartR;
5397 $G = ($PositiveSlopeEndG - $PositiveSlopeStartG) / 100 * $SlopeIndex + $PositiveSlopeStartG;
5398 $B = ($PositiveSlopeEndB - $PositiveSlopeStartB) / 100 * $SlopeIndex + $PositiveSlopeStartB;
5399 } elseif ($Slope < 0) {
5400 $SlopeIndex = 100 / abs($MinSlope) * abs($Slope);
5401 $R = ($NegativeSlopeEndR - $NegativeSlopeStartR) / 100 * $SlopeIndex + $NegativeSlopeStartR;
5402 $G = ($NegativeSlopeEndG - $NegativeSlopeStartG) / 100 * $SlopeIndex + $NegativeSlopeStartG;
5403 $B = ($NegativeSlopeEndB - $NegativeSlopeStartB) / 100 * $SlopeIndex + $NegativeSlopeStartB;
5404 }
5405 $Color = ["R" => $R, "G" => $G, "B" => $B];
5406 if ($ShadedSlopeBox && $LastColor != null) {
5407 $GradientSettings = ["StartR" => $LastColor["R"], "StartG" => $LastColor["G"], "StartB" => $LastColor["B"], "EndR" => $R, "EndG" => $G, "EndB" => $B];
5408 $this->drawGradientArea($TopX, $LastY, $BottomX, $Y, DIRECTION_VERTICAL, $GradientSettings);
5409 } elseif (!$ShadedSlopeBox || $LastColor == null) {
5410 $this->drawFilledRectangle($TopX, floor($LastY), $BottomX, floor($Y), $Color);
5411 }
5412 $LastColor = $Color;
5413 }
5414 if ($X == VOID) {
5415 $LastX = null;
5416 } else {
5417 $LastX = $X;
5418 $LastY = $Y;
5419 }
5420 $Y = $Y + $XStep;
5421 }
5422 $XPos = $XPos + $CaptionHeight + $SerieSpacing;
5423 }
5424 $this->Shadow = $RestoreShadow;
5425 }
5426 }
5427 }
5428 /**
5429 * Draw the line of best fit
5430 * @param array $Format
5431 */
5432 public function drawBestFit(array $Format = [])
5433 {
5434 $OverrideTicks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
5435 $OverrideR = isset($Format["R"]) ? $Format["R"] : VOID;
5436 $OverrideG = isset($Format["G"]) ? $Format["G"] : VOID;
5437 $OverrideB = isset($Format["B"]) ? $Format["B"] : VOID;
5438 $OverrideAlpha = isset($Format["Alpha"]) ? $Format["Alpha"] : VOID;
5439 $Data = $this->DataSet->getData();
5440 list($XMargin, $XDivs) = $this->scaleGetXSettings();
5441 foreach ($Data["Series"] as $SerieName => $Serie) {
5442 if ($Serie["isDrawable"] == \true && $SerieName != $Data["Abscissa"]) {
5443 if ($OverrideR != VOID && $OverrideG != VOID && $OverrideB != VOID) {
5444 $R = $OverrideR;
5445 $G = $OverrideG;
5446 $B = $OverrideB;
5447 } else {
5448 $R = $Serie["Color"]["R"];
5449 $G = $Serie["Color"]["G"];
5450 $B = $Serie["Color"]["B"];
5451 }
5452 if ($OverrideTicks == null) {
5453 $Ticks = $Serie["Ticks"];
5454 } else {
5455 $Ticks = $OverrideTicks;
5456 }
5457 if ($OverrideAlpha == VOID) {
5458 $Alpha = $Serie["Color"]["Alpha"];
5459 } else {
5460 $Alpha = $OverrideAlpha;
5461 }
5462 $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks];
5463 $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
5464 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
5465 if ($XDivs == 0) {
5466 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
5467 } else {
5468 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
5469 }
5470 $X = $this->GraphAreaX1 + $XMargin;
5471 if (!is_array($PosArray)) {
5472 $Value = $PosArray;
5473 $PosArray = [];
5474 $PosArray[0] = $Value;
5475 }
5476 $Sxy = 0;
5477 $Sx = 0;
5478 $Sy = 0;
5479 $Sxx = 0;
5480 foreach ($PosArray as $Key => $Y) {
5481 if ($Y != VOID) {
5482 $Sxy = $Sxy + $X * $Y;
5483 $Sx = $Sx + $X;
5484 $Sy = $Sy + $Y;
5485 $Sxx = $Sxx + $X * $X;
5486 }
5487 $X = $X + $XStep;
5488 }
5489 $n = count($this->DataSet->stripVOID($PosArray));
5490 //$n = count($PosArray);
5491 $M = ($n * $Sxy - $Sx * $Sy) / ($n * $Sxx - $Sx * $Sx);
5492 $B = ($Sy - $M * $Sx) / $n;
5493 $X1 = $this->GraphAreaX1 + $XMargin;
5494 $Y1 = $M * $X1 + $B;
5495 $X2 = $this->GraphAreaX2 - $XMargin;
5496 $Y2 = $M * $X2 + $B;
5497 if ($Y1 < $this->GraphAreaY1) {
5498 $X1 = $X1 + ($this->GraphAreaY1 - $Y1);
5499 $Y1 = $this->GraphAreaY1;
5500 }
5501 if ($Y1 > $this->GraphAreaY2) {
5502 $X1 = $X1 + ($Y1 - $this->GraphAreaY2);
5503 $Y1 = $this->GraphAreaY2;
5504 }
5505 if ($Y2 < $this->GraphAreaY1) {
5506 $X2 = $X2 - ($this->GraphAreaY1 - $Y2);
5507 $Y2 = $this->GraphAreaY1;
5508 }
5509 if ($Y2 > $this->GraphAreaY2) {
5510 $X2 = $X2 - ($Y2 - $this->GraphAreaY2);
5511 $Y2 = $this->GraphAreaY2;
5512 }
5513 $this->drawLine($X1, $Y1, $X2, $Y2, $Color);
5514 } else {
5515 if ($XDivs == 0) {
5516 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
5517 } else {
5518 $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
5519 }
5520 $Y = $this->GraphAreaY1 + $XMargin;
5521 if (!is_array($PosArray)) {
5522 $Value = $PosArray;
5523 $PosArray = [];
5524 $PosArray[0] = $Value;
5525 }
5526 $Sxy = 0;
5527 $Sx = 0;
5528 $Sy = 0;
5529 $Sxx = 0;
5530 foreach ($PosArray as $Key => $X) {
5531 if ($X != VOID) {
5532 $Sxy = $Sxy + $X * $Y;
5533 $Sx = $Sx + $Y;
5534 $Sy = $Sy + $X;
5535 $Sxx = $Sxx + $Y * $Y;
5536 }
5537 $Y = $Y + $YStep;
5538 }
5539 $n = count($this->DataSet->stripVOID($PosArray));
5540 //$n = count($PosArray);
5541 $M = ($n * $Sxy - $Sx * $Sy) / ($n * $Sxx - $Sx * $Sx);
5542 $B = ($Sy - $M * $Sx) / $n;
5543 $Y1 = $this->GraphAreaY1 + $XMargin;
5544 $X1 = $M * $Y1 + $B;
5545 $Y2 = $this->GraphAreaY2 - $XMargin;
5546 $X2 = $M * $Y2 + $B;
5547 if ($X1 < $this->GraphAreaX1) {
5548 $Y1 = $Y1 + ($this->GraphAreaX1 - $X1);
5549 $X1 = $this->GraphAreaX1;
5550 }
5551 if ($X1 > $this->GraphAreaX2) {
5552 $Y1 = $Y1 + ($X1 - $this->GraphAreaX2);
5553 $X1 = $this->GraphAreaX2;
5554 }
5555 if ($X2 < $this->GraphAreaX1) {
5556 $Y2 = $Y2 - ($this->GraphAreaY1 - $X2);
5557 $X2 = $this->GraphAreaX1;
5558 }
5559 if ($X2 > $this->GraphAreaX2) {
5560 $Y2 = $Y2 - ($X2 - $this->GraphAreaX2);
5561 $X2 = $this->GraphAreaX2;
5562 }
5563 $this->drawLine($X1, $Y1, $X2, $Y2, $Color);
5564 }
5565 }
5566 }
5567 }
5568 /**
5569 * Draw a label box
5570 * @param int $X
5571 * @param int $Y
5572 * @param string $Title
5573 * @param array $Captions
5574 * @param array $Format
5575 */
5576 public function drawLabelBox($X, $Y, $Title, array $Captions, array $Format = [])
5577 {
5578 $NoTitle = isset($Format["NoTitle"]) ? $Format["NoTitle"] : null;
5579 $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 50;
5580 $DrawSerieColor = isset($Format["DrawSerieColor"]) ? $Format["DrawSerieColor"] : \true;
5581 $SerieBoxSize = isset($Format["SerieBoxSize"]) ? $Format["SerieBoxSize"] : 6;
5582 $SerieBoxSpacing = isset($Format["SerieBoxSpacing"]) ? $Format["SerieBoxSpacing"] : 4;
5583 $VerticalMargin = isset($Format["VerticalMargin"]) ? $Format["VerticalMargin"] : 10;
5584 $HorizontalMargin = isset($Format["HorizontalMargin"]) ? $Format["HorizontalMargin"] : 8;
5585 $R = isset($Format["R"]) ? $Format["R"] : $this->FontColorR;
5586 $G = isset($Format["G"]) ? $Format["G"] : $this->FontColorG;
5587 $B = isset($Format["B"]) ? $Format["B"] : $this->FontColorB;
5588 $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName;
5589 $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
5590 $TitleMode = isset($Format["TitleMode"]) ? $Format["TitleMode"] : LABEL_TITLE_NOBACKGROUND;
5591 $TitleR = isset($Format["TitleR"]) ? $Format["TitleR"] : $R;
5592 $TitleG = isset($Format["TitleG"]) ? $Format["TitleG"] : $G;
5593 $TitleB = isset($Format["TitleB"]) ? $Format["TitleB"] : $B;
5594 $TitleBackgroundR = isset($Format["TitleBackgroundR"]) ? $Format["TitleBackgroundR"] : 0;
5595 $TitleBackgroundG = isset($Format["TitleBackgroundG"]) ? $Format["TitleBackgroundG"] : 0;
5596 $TitleBackgroundB = isset($Format["TitleBackgroundB"]) ? $Format["TitleBackgroundB"] : 0;
5597 $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255;
5598 $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255;
5599 $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255;
5600 $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 220;
5601 $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 220;
5602 $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 220;
5603 $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 100;
5604 if (!$DrawSerieColor) {
5605 $SerieBoxSize = 0;
5606 $SerieBoxSpacing = 0;
5607 }
5608 $TxtPos = $this->getTextBox($X, $Y, $FontName, $FontSize, 0, $Title);
5609 $TitleWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"] + $VerticalMargin * 2;
5610 $TitleHeight = $TxtPos[0]["Y"] - $TxtPos[2]["Y"];
5611 if ($NoTitle) {
5612 $TitleWidth = 0;
5613 $TitleHeight = 0;
5614 }
5615 $CaptionWidth = 0;
5616 $CaptionHeight = -$HorizontalMargin;
5617 foreach ($Captions as $Key => $Caption) {
5618 $TxtPos = $this->getTextBox($X, $Y, $FontName, $FontSize, 0, $Caption["Caption"]);
5619 $CaptionWidth = max($CaptionWidth, $TxtPos[1]["X"] - $TxtPos[0]["X"] + $VerticalMargin * 2);
5620 $CaptionHeight = $CaptionHeight + max($TxtPos[0]["Y"] - $TxtPos[2]["Y"], $SerieBoxSize + 2) + $HorizontalMargin;
5621 }
5622 if ($CaptionHeight <= 5) {
5623 $CaptionHeight = $CaptionHeight + $HorizontalMargin / 2;
5624 }
5625 if ($DrawSerieColor) {
5626 $CaptionWidth = $CaptionWidth + $SerieBoxSize + $SerieBoxSpacing;
5627 }
5628 $BoxWidth = max($BoxWidth, $TitleWidth, $CaptionWidth);
5629 $XMin = $X - 5 - floor(($BoxWidth - 10) / 2);
5630 $XMax = $X + 5 + floor(($BoxWidth - 10) / 2);
5631 $RestoreShadow = $this->Shadow;
5632 if ($this->Shadow == \true) {
5633 $this->Shadow = \false;
5634 $Poly = [];
5635 $Poly[] = $X + $this->ShadowX;
5636 $Poly[] = $Y + $this->ShadowX;
5637 $Poly[] = $X + 5 + $this->ShadowX;
5638 $Poly[] = $Y - 5 + $this->ShadowX;
5639 $Poly[] = $XMax + $this->ShadowX;
5640 $Poly[] = $Y - 5 + $this->ShadowX;
5641 if ($NoTitle) {
5642 $Poly[] = $XMax + $this->ShadowX;
5643 $Poly[] = $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2 + $this->ShadowX;
5644 $Poly[] = $XMin + $this->ShadowX;
5645 $Poly[] = $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2 + $this->ShadowX;
5646 } else {
5647 $Poly[] = $XMax + $this->ShadowX;
5648 $Poly[] = $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3 + $this->ShadowX;
5649 $Poly[] = $XMin + $this->ShadowX;
5650 $Poly[] = $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3 + $this->ShadowX;
5651 }
5652 $Poly[] = $XMin + $this->ShadowX;
5653 $Poly[] = $Y - 5 + $this->ShadowX;
5654 $Poly[] = $X - 5 + $this->ShadowX;
5655 $Poly[] = $Y - 5 + $this->ShadowX;
5656 $this->drawPolygon($Poly, ["R" => $this->ShadowR, "G" => $this->ShadowG, "B" => $this->ShadowB, "Alpha" => $this->Shadowa]);
5657 }
5658 /* Draw the background */
5659 $GradientSettings = ["StartR" => $GradientStartR, "StartG" => $GradientStartG, "StartB" => $GradientStartB, "EndR" => $GradientEndR, "EndG" => $GradientEndG, "EndB" => $GradientEndB, "Alpha" => $BoxAlpha];
5660 if ($NoTitle) {
5661 $this->drawGradientArea($XMin, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, $XMax, $Y - 6, DIRECTION_VERTICAL, $GradientSettings);
5662 } else {
5663 $this->drawGradientArea($XMin, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, $XMax, $Y - 6, DIRECTION_VERTICAL, $GradientSettings);
5664 }
5665 $Poly = [];
5666 $Poly[] = $X;
5667 $Poly[] = $Y;
5668 $Poly[] = $X - 5;
5669 $Poly[] = $Y - 5;
5670 $Poly[] = $X + 5;
5671 $Poly[] = $Y - 5;
5672 $this->drawPolygon($Poly, ["R" => $GradientEndR, "G" => $GradientEndG, "B" => $GradientEndB, "Alpha" => $BoxAlpha, "NoBorder" => \true]);
5673 /* Outer border */
5674 $OuterBorderColor = $this->allocateColor($this->Picture, 100, 100, 100, $BoxAlpha);
5675 imageline($this->Picture, $XMin, $Y - 5, $X - 5, $Y - 5, $OuterBorderColor);
5676 imageline($this->Picture, $X, $Y, $X - 5, $Y - 5, $OuterBorderColor);
5677 imageline($this->Picture, $X, $Y, $X + 5, $Y - 5, $OuterBorderColor);
5678 imageline($this->Picture, $X + 5, $Y - 5, $XMax, $Y - 5, $OuterBorderColor);
5679 if ($NoTitle) {
5680 imageline($this->Picture, $XMin, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, $XMin, $Y - 5, $OuterBorderColor);
5681 imageline($this->Picture, $XMax, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, $XMax, $Y - 5, $OuterBorderColor);
5682 imageline($this->Picture, $XMin, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, $XMax, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, $OuterBorderColor);
5683 } else {
5684 imageline($this->Picture, $XMin, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, $XMin, $Y - 5, $OuterBorderColor);
5685 imageline($this->Picture, $XMax, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, $XMax, $Y - 5, $OuterBorderColor);
5686 imageline($this->Picture, $XMin, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, $XMax, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, $OuterBorderColor);
5687 }
5688 /* Inner border */
5689 $InnerBorderColor = $this->allocateColor($this->Picture, 255, 255, 255, $BoxAlpha);
5690 imageline($this->Picture, $XMin + 1, $Y - 6, $X - 5, $Y - 6, $InnerBorderColor);
5691 imageline($this->Picture, $X, $Y - 1, $X - 5, $Y - 6, $InnerBorderColor);
5692 imageline($this->Picture, $X, $Y - 1, $X + 5, $Y - 6, $InnerBorderColor);
5693 imageline($this->Picture, $X + 5, $Y - 6, $XMax - 1, $Y - 6, $InnerBorderColor);
5694 if ($NoTitle) {
5695 imageline($this->Picture, $XMin + 1, $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, $XMin + 1, $Y - 6, $InnerBorderColor);
5696 imageline($this->Picture, $XMax - 1, $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, $XMax - 1, $Y - 6, $InnerBorderColor);
5697 imageline($this->Picture, $XMin + 1, $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, $XMax - 1, $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2, $InnerBorderColor);
5698 } else {
5699 imageline($this->Picture, $XMin + 1, $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, $XMin + 1, $Y - 6, $InnerBorderColor);
5700 imageline($this->Picture, $XMax - 1, $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, $XMax - 1, $Y - 6, $InnerBorderColor);
5701 imageline($this->Picture, $XMin + 1, $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, $XMax - 1, $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, $InnerBorderColor);
5702 }
5703 /* Draw the separator line */
5704 if ($TitleMode == LABEL_TITLE_NOBACKGROUND && !$NoTitle) {
5705 $YPos = $Y - 7 - $CaptionHeight - $HorizontalMargin - $HorizontalMargin / 2;
5706 $XMargin = $VerticalMargin / 2;
5707 $this->drawLine($XMin + $XMargin, $YPos + 1, $XMax - $XMargin, $YPos + 1, ["R" => $GradientEndR, "G" => $GradientEndG, "B" => $GradientEndB, "Alpha" => $BoxAlpha]);
5708 $this->drawLine($XMin + $XMargin, $YPos, $XMax - $XMargin, $YPos, ["R" => $GradientStartR, "G" => $GradientStartG, "B" => $GradientStartB, "Alpha" => $BoxAlpha]);
5709 } elseif ($TitleMode == LABEL_TITLE_BACKGROUND) {
5710 $this->drawFilledRectangle($XMin, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3, $XMax, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin + $HorizontalMargin / 2, ["R" => $TitleBackgroundR, "G" => $TitleBackgroundG, "B" => $TitleBackgroundB, "Alpha" => $BoxAlpha]);
5711 imageline($this->Picture, $XMin + 1, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin + $HorizontalMargin / 2 + 1, $XMax - 1, $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin + $HorizontalMargin / 2 + 1, $InnerBorderColor);
5712 }
5713 /* Write the description */
5714 if (!$NoTitle) {
5715 $this->drawText($XMin + $VerticalMargin, $Y - 7 - $CaptionHeight - $HorizontalMargin * 2, $Title, ["Align" => TEXT_ALIGN_BOTTOMLEFT, "R" => $TitleR, "G" => $TitleG, "B" => $TitleB]);
5716 }
5717 /* Write the value */
5718 $YPos = $Y - 5 - $HorizontalMargin;
5719 $XPos = $XMin + $VerticalMargin + $SerieBoxSize + $SerieBoxSpacing;
5720 foreach ($Captions as $Key => $Caption) {
5721 $CaptionTxt = $Caption["Caption"];
5722 $TxtPos = $this->getTextBox($XPos, $YPos, $FontName, $FontSize, 0, $CaptionTxt);
5723 $CaptionHeight = $TxtPos[0]["Y"] - $TxtPos[2]["Y"];
5724 /* Write the serie color if needed */
5725 if ($DrawSerieColor) {
5726 $BoxSettings = ["R" => $Caption["Format"]["R"], "G" => $Caption["Format"]["G"], "B" => $Caption["Format"]["B"], "Alpha" => $Caption["Format"]["Alpha"], "BorderR" => 0, "BorderG" => 0, "BorderB" => 0];
5727 $this->drawFilledRectangle($XMin + $VerticalMargin, $YPos - $SerieBoxSize, $XMin + $VerticalMargin + $SerieBoxSize, $YPos, $BoxSettings);
5728 }
5729 $this->drawText($XPos, $YPos, $CaptionTxt, ["Align" => TEXT_ALIGN_BOTTOMLEFT]);
5730 $YPos = $YPos - $CaptionHeight - $HorizontalMargin;
5731 }
5732 $this->Shadow = $RestoreShadow;
5733 }
5734 /**
5735 * Draw a basic shape
5736 * @param int $X
5737 * @param int $Y
5738 * @param int $Shape
5739 * @param int $PlotSize
5740 * @param int $PlotBorder
5741 * @param int $BorderSize
5742 * @param int $R
5743 * @param int $G
5744 * @param int $B
5745 * @param int|float $Alpha
5746 * @param int $BorderR
5747 * @param int $BorderG
5748 * @param int $BorderB
5749 * @param int|float $BorderAlpha
5750 */
5751 public function drawShape($X, $Y, $Shape, $PlotSize, $PlotBorder, $BorderSize, $R, $G, $B, $Alpha, $BorderR, $BorderG, $BorderB, $BorderAlpha)
5752 {
5753 if ($Shape == SERIE_SHAPE_FILLEDCIRCLE) {
5754 if ($PlotBorder) {
5755 $this->drawFilledCircle($X, $Y, $PlotSize + $BorderSize, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha]);
5756 }
5757 $this->drawFilledCircle($X, $Y, $PlotSize, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
5758 } elseif ($Shape == SERIE_SHAPE_FILLEDSQUARE) {
5759 if ($PlotBorder) {
5760 $this->drawFilledRectangle($X - $PlotSize - $BorderSize, $Y - $PlotSize - $BorderSize, $X + $PlotSize + $BorderSize, $Y + $PlotSize + $BorderSize, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha]);
5761 }
5762 $this->drawFilledRectangle($X - $PlotSize, $Y - $PlotSize, $X + $PlotSize, $Y + $PlotSize, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
5763 } elseif ($Shape == SERIE_SHAPE_FILLEDTRIANGLE) {
5764 if ($PlotBorder) {
5765 $Pos = [];
5766 $Pos[] = $X;
5767 $Pos[] = $Y - $PlotSize - $BorderSize;
5768 $Pos[] = $X - $PlotSize - $BorderSize;
5769 $Pos[] = $Y + $PlotSize + $BorderSize;
5770 $Pos[] = $X + $PlotSize + $BorderSize;
5771 $Pos[] = $Y + $PlotSize + $BorderSize;
5772 $this->drawPolygon($Pos, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha]);
5773 }
5774 $Pos = [];
5775 $Pos[] = $X;
5776 $Pos[] = $Y - $PlotSize;
5777 $Pos[] = $X - $PlotSize;
5778 $Pos[] = $Y + $PlotSize;
5779 $Pos[] = $X + $PlotSize;
5780 $Pos[] = $Y + $PlotSize;
5781 $this->drawPolygon($Pos, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
5782 } elseif ($Shape == SERIE_SHAPE_TRIANGLE) {
5783 $this->drawLine($X, $Y - $PlotSize, $X - $PlotSize, $Y + $PlotSize, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
5784 $this->drawLine($X - $PlotSize, $Y + $PlotSize, $X + $PlotSize, $Y + $PlotSize, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
5785 $this->drawLine($X + $PlotSize, $Y + $PlotSize, $X, $Y - $PlotSize, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
5786 } elseif ($Shape == SERIE_SHAPE_SQUARE) {
5787 $this->drawRectangle($X - $PlotSize, $Y - $PlotSize, $X + $PlotSize, $Y + $PlotSize, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
5788 } elseif ($Shape == SERIE_SHAPE_CIRCLE) {
5789 $this->drawCircle($X, $Y, $PlotSize, $PlotSize, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
5790 } elseif ($Shape == SERIE_SHAPE_DIAMOND) {
5791 $Pos = [];
5792 $Pos[] = $X - $PlotSize;
5793 $Pos[] = $Y;
5794 $Pos[] = $X;
5795 $Pos[] = $Y - $PlotSize;
5796 $Pos[] = $X + $PlotSize;
5797 $Pos[] = $Y;
5798 $Pos[] = $X;
5799 $Pos[] = $Y + $PlotSize;
5800 $this->drawPolygon($Pos, ["NoFill" => \true, "BorderR" => $R, "BorderG" => $G, "BorderB" => $B, "BorderAlpha" => $Alpha]);
5801 } elseif ($Shape == SERIE_SHAPE_FILLEDDIAMOND) {
5802 if ($PlotBorder) {
5803 $Pos = [];
5804 $Pos[] = $X - $PlotSize - $BorderSize;
5805 $Pos[] = $Y;
5806 $Pos[] = $X;
5807 $Pos[] = $Y - $PlotSize - $BorderSize;
5808 $Pos[] = $X + $PlotSize + $BorderSize;
5809 $Pos[] = $Y;
5810 $Pos[] = $X;
5811 $Pos[] = $Y + $PlotSize + $BorderSize;
5812 $this->drawPolygon($Pos, ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha]);
5813 }
5814 $Pos = [];
5815 $Pos[] = $X - $PlotSize;
5816 $Pos[] = $Y;
5817 $Pos[] = $X;
5818 $Pos[] = $Y - $PlotSize;
5819 $Pos[] = $X + $PlotSize;
5820 $Pos[] = $Y;
5821 $Pos[] = $X;
5822 $Pos[] = $Y + $PlotSize;
5823 $this->drawPolygon($Pos, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
5824 }
5825 }
5826 /**
5827 *
5828 * @param array $Points
5829 * @param array $Format
5830 * @return null|integer
5831 */
5832 public function drawPolygonChart(array $Points, array $Format = [])
5833 {
5834 $R = isset($Format["R"]) ? $Format["R"] : 0;
5835 $G = isset($Format["G"]) ? $Format["G"] : 0;
5836 $B = isset($Format["B"]) ? $Format["B"] : 0;
5837 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
5838 $NoFill = isset($Format["NoFill"]) ? $Format["NoFill"] : \false;
5839 $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : \false;
5840 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
5841 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
5842 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
5843 $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha / 2;
5844 $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
5845 $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : null;
5846 if ($Surrounding != null) {
5847 $BorderR = $R + $Surrounding;
5848 $BorderG = $G + $Surrounding;
5849 $BorderB = $B + $Surrounding;
5850 }
5851 $RestoreShadow = $this->Shadow;
5852 $this->Shadow = \false;
5853 $AllIntegers = \true;
5854 for ($i = 0; $i <= count($Points) - 2; $i = $i + 2) {
5855 if ($this->getFirstDecimal($Points[$i + 1]) != 0) {
5856 $AllIntegers = \false;
5857 }
5858 }
5859 /* Convert polygon to segments */
5860 $Segments = [];
5861 for ($i = 2; $i <= count($Points) - 2; $i = $i + 2) {
5862 $Segments[] = ["X1" => $Points[$i - 2], "Y1" => $Points[$i - 1], "X2" => $Points[$i], "Y2" => $Points[$i + 1]];
5863 }
5864 $Segments[] = ["X1" => $Points[$i - 2], "Y1" => $Points[$i - 1], "X2" => $Points[0], "Y2" => $Points[1]];
5865 /* Simplify straight lines */
5866 $Result = [];
5867 $inHorizon = \false;
5868 $LastX = VOID;
5869 foreach ($Segments as $Key => $Pos) {
5870 if ($Pos["Y1"] != $Pos["Y2"]) {
5871 if ($inHorizon) {
5872 $inHorizon = \false;
5873 $Result[] = ["X1" => $LastX, "Y1" => $Pos["Y1"], "X2" => $Pos["X1"], "Y2" => $Pos["Y1"]];
5874 }
5875 $Result[] = ["X1" => $Pos["X1"], "Y1" => $Pos["Y1"], "X2" => $Pos["X2"], "Y2" => $Pos["Y2"]];
5876 } else {
5877 if (!$inHorizon) {
5878 $inHorizon = \true;
5879 $LastX = $Pos["X1"];
5880 }
5881 }
5882 }
5883 $Segments = $Result;
5884 /* Do we have something to draw */
5885 if (!count($Segments)) {
5886 return 0;
5887 }
5888 /* For segments debugging purpose */
5889 //foreach($Segments as $Key => $Pos)
5890 // echo $Pos["X1"].",".$Pos["Y1"].",".$Pos["X2"].",".$Pos["Y2"]."\r\n";
5891 /* Find out the min & max Y boundaries */
5892 $MinY = OUT_OF_SIGHT;
5893 $MaxY = OUT_OF_SIGHT;
5894 foreach ($Segments as $Key => $Coords) {
5895 if ($MinY == OUT_OF_SIGHT || $MinY > min($Coords["Y1"], $Coords["Y2"])) {
5896 $MinY = min($Coords["Y1"], $Coords["Y2"]);
5897 }
5898 if ($MaxY == OUT_OF_SIGHT || $MaxY < max($Coords["Y1"], $Coords["Y2"])) {
5899 $MaxY = max($Coords["Y1"], $Coords["Y2"]);
5900 }
5901 }
5902 if ($AllIntegers) {
5903 $YStep = 1;
5904 } else {
5905 $YStep = 0.5;
5906 }
5907 $MinY = floor($MinY);
5908 $MaxY = floor($MaxY);
5909 /* Scan each Y lines */
5910 $DefaultColor = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
5911 $DebugLine = 0;
5912 $DebugColor = $this->allocateColor($this->Picture, 255, 0, 0, 100);
5913 $MinY = floor($MinY);
5914 $MaxY = floor($MaxY);
5915 $YStep = 1;
5916 if (!$NoFill) {
5917 //if ($DebugLine ) { $MinY = $DebugLine; $MaxY = $DebugLine; }
5918 for ($Y = $MinY; $Y <= $MaxY; $Y = $Y + $YStep) {
5919 $Intersections = [];
5920 $LastSlope = null;
5921 $RestoreLast = "-";
5922 foreach ($Segments as $Key => $Coords) {
5923 $X1 = $Coords["X1"];
5924 $X2 = $Coords["X2"];
5925 $Y1 = $Coords["Y1"];
5926 $Y2 = $Coords["Y2"];
5927 if (min($Y1, $Y2) <= $Y && max($Y1, $Y2) >= $Y) {
5928 if ($Y1 == $Y2) {
5929 $X = $X1;
5930 } else {
5931 $X = $X1 + (($Y - $Y1) * $X2 - ($Y - $Y1) * $X1) / ($Y2 - $Y1);
5932 }
5933 $X = floor($X);
5934 if ($X2 == $X1) {
5935 $Slope = "!";
5936 } else {
5937 $SlopeC = ($Y2 - $Y1) / ($X2 - $X1);
5938 if ($SlopeC == 0) {
5939 $Slope = "=";
5940 } elseif ($SlopeC > 0) {
5941 $Slope = "+";
5942 } elseif ($SlopeC < 0) {
5943 $Slope = "-";
5944 }
5945 }
5946 if (!is_array($Intersections)) {
5947 $Intersections[] = $X;
5948 } elseif (!in_array($X, $Intersections)) {
5949 $Intersections[] = $X;
5950 } elseif (in_array($X, $Intersections)) {
5951 if ($Y == $DebugLine) {
5952 echo $Slope . "/" . $LastSlope . "(" . $X . ") ";
5953 }
5954 if ($Slope == "=" && $LastSlope == "-") {
5955 $Intersections[] = $X;
5956 }
5957 if ($Slope != $LastSlope && $LastSlope != "!" && $LastSlope != "=") {
5958 $Intersections[] = $X;
5959 }
5960 if ($Slope != $LastSlope && $LastSlope == "!" && $Slope == "+") {
5961 $Intersections[] = $X;
5962 }
5963 }
5964 if (is_array($Intersections) && in_array($X, $Intersections) && $LastSlope == "=" && $Slope == "-") {
5965 $Intersections[] = $X;
5966 }
5967 $LastSlope = $Slope;
5968 }
5969 }
5970 if ($RestoreLast != "-") {
5971 $Intersections[] = $RestoreLast;
5972 echo "@" . $Y . "\r\n";
5973 }
5974 if (is_array($Intersections)) {
5975 sort($Intersections);
5976 if ($Y == $DebugLine) {
5977 print_r($Intersections);
5978 }
5979 /* Remove null plots */
5980 $Result = [];
5981 for ($i = 0; $i <= count($Intersections) - 1; $i = $i + 2) {
5982 if (isset($Intersections[$i + 1])) {
5983 if ($Intersections[$i] != $Intersections[$i + 1]) {
5984 $Result[] = $Intersections[$i];
5985 $Result[] = $Intersections[$i + 1];
5986 }
5987 }
5988 }
5989 if (is_array($Result)) {
5990 $Intersections = $Result;
5991 $LastX = OUT_OF_SIGHT;
5992 foreach ($Intersections as $Key => $X) {
5993 if ($LastX == OUT_OF_SIGHT) {
5994 $LastX = $X;
5995 } elseif ($LastX != OUT_OF_SIGHT) {
5996 if ($this->getFirstDecimal($LastX) > 1) {
5997 $LastX++;
5998 }
5999 $Color = $DefaultColor;
6000 if ($Threshold != null) {
6001 foreach ($Threshold as $Key => $Parameters) {
6002 if ($Y <= $Parameters["MinX"] && $Y >= $Parameters["MaxX"]) {
6003 if (isset($Parameters["R"])) {
6004 $R = $Parameters["R"];
6005 } else {
6006 $R = 0;
6007 }
6008 if (isset($Parameters["G"])) {
6009 $G = $Parameters["G"];
6010 } else {
6011 $G = 0;
6012 }
6013 if (isset($Parameters["B"])) {
6014 $B = $Parameters["B"];
6015 } else {
6016 $B = 0;
6017 }
6018 if (isset($Parameters["Alpha"])) {
6019 $Alpha = $Parameters["Alpha"];
6020 } else {
6021 $Alpha = 100;
6022 }
6023 $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
6024 }
6025 }
6026 }
6027 imageline($this->Picture, $LastX, $Y, $X, $Y, $Color);
6028 if ($Y == $DebugLine) {
6029 imageline($this->Picture, $LastX, $Y, $X, $Y, $DebugColor);
6030 }
6031 $LastX = OUT_OF_SIGHT;
6032 }
6033 }
6034 }
6035 }
6036 }
6037 }
6038 /* Draw the polygon border, if required */
6039 if (!$NoBorder) {
6040 foreach ($Segments as $Key => $Coords) {
6041 $this->drawLine($Coords["X1"], $Coords["Y1"], $Coords["X2"], $Coords["Y2"], ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha, "Threshold" => $Threshold]);
6042 }
6043 }
6044 $this->Shadow = $RestoreShadow;
6045 }
6046 }
6047