Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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