1<?php
2/*
3** Zabbix
4** Copyright (C) 2001-2021 Zabbix SIA
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19**/
20
21
22class CPieGraphDraw extends CGraphDraw {
23
24	public function __construct($type = GRAPH_TYPE_PIE) {
25		parent::__construct($type);
26		$this->background = false;
27		$this->sum = false;
28		$this->exploderad = 1;
29		$this->exploderad3d = 3;
30		$this->graphheight3d = 12;
31		$this->shiftlegendright = 17 * 7 + 7 + 10; // count of static chars * px/char + for color rectangle + space
32	}
33
34	/********************************************************************************************************/
35	/* PRE CONFIG: ADD / SET / APPLY
36	/********************************************************************************************************/
37	public function addItem($itemid, $calc_fnc = CALC_FNC_AVG, $color = null, $type = null) {
38		$items = CMacrosResolverHelper::resolveItemNames([get_item_by_itemid($itemid)]);
39
40		$this->items[$this->num] = reset($items);
41
42		$host = get_host_by_hostid($this->items[$this->num]['hostid']);
43
44		$this->items[$this->num]['host'] = $host['host'];
45		$this->items[$this->num]['hostname'] = $host['name'];
46		$this->items[$this->num]['color'] = is_null($color) ? 'Dark Green' : $color;
47		$this->items[$this->num]['calc_fnc'] = is_null($calc_fnc) ? CALC_FNC_AVG : $calc_fnc;
48		$this->items[$this->num]['calc_type'] = is_null($type) ? GRAPH_ITEM_SIMPLE : $type;
49
50		$this->num++;
51	}
52
53	public function set3DAngle($angle = 70) {
54		if (is_numeric($angle) && $angle < 85 && $angle > 10) {
55			$this->angle3d = (int) $angle;
56		}
57		else {
58			$this->angle3d = 70;
59		}
60	}
61
62	public function switchPie3D($type = false) {
63		if ($type) {
64			$this->type = $type;
65		}
66		else {
67			switch ($this->type) {
68				case GRAPH_TYPE_EXPLODED:
69					$this->type = GRAPH_TYPE_3D_EXPLODED;
70					break;
71				case GRAPH_TYPE_3D_EXPLODED:
72					$this->type = GRAPH_TYPE_EXPLODED;
73					break;
74				case GRAPH_TYPE_3D:
75					$this->type = GRAPH_TYPE_PIE;
76					break;
77				case GRAPH_TYPE_PIE:
78					$this->type = GRAPH_TYPE_3D;
79					break;
80				default:
81					$this->type = GRAPH_TYPE_PIE;
82			}
83		}
84		return $this->type;
85	}
86
87	public function switchPieExploded($type) {
88		if ($type) {
89			$this->type = $type;
90		}
91		else {
92			switch ($this->type) {
93				case GRAPH_TYPE_EXPLODED:
94					$this->type = GRAPH_TYPE_PIE;
95					break;
96				case GRAPH_TYPE_3D_EXPLODED:
97					$this->type = GRAPH_TYPE_3D;
98					break;
99				case GRAPH_TYPE_3D:
100					$this->type = GRAPH_TYPE_3D_EXPLODED;
101					break;
102				case GRAPH_TYPE_PIE:
103					$this->type = GRAPH_TYPE_EXPLODED;
104					break;
105				default:
106					$this->type = GRAPH_TYPE_PIE;
107			}
108		}
109		return $this->type;
110	}
111
112	protected function calc3dheight($height) {
113		$this->graphheight3d = (int) ($height / 20);
114	}
115
116	protected function calcExplodedCenter($anglestart, $angleend, $x, $y, $count) {
117		$count *= $this->exploderad;
118		$anglemid = (int) (($anglestart + $angleend) / 2);
119
120		$y+= round($count * sin(deg2rad($anglemid)));
121		$x+= round($count * cos(deg2rad($anglemid)));
122
123		return [$x, $y];
124	}
125
126	protected function calcExplodedRadius($sizeX, $sizeY, $count) {
127		$count *= $this->exploderad * 2;
128		$sizeX -= $count;
129		$sizeY -= $count;
130		return [$sizeX, $sizeY];
131	}
132
133	protected function calc3DAngle($sizeX, $sizeY) {
134		$sizeY *= GRAPH_3D_ANGLE / 90;
135		return [$sizeX, round($sizeY)];
136	}
137
138	protected function selectData() {
139		$this->data = [];
140		$now = time(null);
141
142		if (isset($this->stime)) {
143			$this->from_time = $this->stime;
144			$this->to_time = $this->stime + $this->period;
145		}
146		else {
147			$this->to_time = $now - SEC_PER_HOUR * $this->from;
148			$this->from_time = $this->to_time - $this->period;
149		}
150
151		$strvaluelength = 0; // we need to know how long in px will be our legend
152
153		// fetch values for items with the "last" function
154		$lastValueItems = [];
155		foreach ($this->items as $item) {
156			if ($item['calc_fnc'] == CALC_FNC_LST) {
157				$lastValueItems[] = $item;
158			}
159		}
160		if ($lastValueItems) {
161			$history = Manager::History()->getLast($lastValueItems);
162		}
163
164		$config = select_config();
165
166		for ($i = 0; $i < $this->num; $i++) {
167			$item = get_item_by_itemid($this->items[$i]['itemid']);
168			$type = $this->items[$i]['calc_type'];
169			$from_time = $this->from_time;
170			$to_time = $this->to_time;
171
172			$sql_arr = [];
173
174			// override item history setting with housekeeping settings
175			if ($config['hk_history_global']) {
176				$item['history'] = $config['hk_history'];
177			}
178
179			$trendsEnabled = $config['hk_trends_global'] ? ($config['hk_trends'] > 0) : ($item['trends'] > 0);
180
181			if (!$trendsEnabled || (($item['history'] * SEC_PER_DAY) > (time() - ($from_time + $this->period / 2)))) {
182				$this->dataFrom = 'history';
183
184				array_push($sql_arr,
185					'SELECT h.itemid,'.
186						'AVG(h.value) AS avg,MIN(h.value) AS min,'.
187						'MAX(h.value) AS max,MAX(h.clock) AS clock'.
188					' FROM history h'.
189					' WHERE h.itemid='.zbx_dbstr($this->items[$i]['itemid']).
190						' AND h.clock>='.zbx_dbstr($from_time).
191						' AND h.clock<='.zbx_dbstr($to_time).
192					' GROUP BY h.itemid'
193					,
194					'SELECT hu.itemid,'.
195						'AVG(hu.value) AS avg,MIN(hu.value) AS min,'.
196						'MAX(hu.value) AS max,MAX(hu.clock) AS clock'.
197					' FROM history_uint hu'.
198					' WHERE hu.itemid='.zbx_dbstr($this->items[$i]['itemid']).
199						' AND hu.clock>='.zbx_dbstr($from_time).
200						' AND hu.clock<='.zbx_dbstr($to_time).
201					' GROUP BY hu.itemid'
202				);
203			}
204			else {
205				$this->dataFrom = 'trends';
206
207				array_push($sql_arr,
208					'SELECT t.itemid,'.
209						'AVG(t.value_avg) AS avg,MIN(t.value_min) AS min,'.
210						'MAX(t.value_max) AS max,MAX(t.clock) AS clock'.
211					' FROM trends t'.
212					' WHERE t.itemid='.zbx_dbstr($this->items[$i]['itemid']).
213						' AND t.clock>='.zbx_dbstr($from_time).
214						' AND t.clock<='.zbx_dbstr($to_time).
215					' GROUP BY t.itemid'
216					,
217					'SELECT t.itemid,'.
218						'AVG(t.value_avg) AS avg,MIN(t.value_min) AS min,'.
219						'MAX(t.value_max) AS max,MAX(t.clock) AS clock'.
220					' FROM trends_uint t'.
221					' WHERE t.itemid='.zbx_dbstr($this->items[$i]['itemid']).
222						' AND t.clock>='.zbx_dbstr($from_time).
223						' AND t.clock<='.zbx_dbstr($to_time).
224					' GROUP BY t.itemid'
225				);
226			}
227
228			$this->data[$this->items[$i]['itemid']][$type]['last'] = isset($history[$item['itemid']])
229				? $history[$item['itemid']][0]['value'] : null;
230			$this->data[$this->items[$i]['itemid']][$type]['shift_min'] = 0;
231			$this->data[$this->items[$i]['itemid']][$type]['shift_max'] = 0;
232			$this->data[$this->items[$i]['itemid']][$type]['shift_avg'] = 0;
233
234			foreach ($sql_arr as $sql) {
235				$result = DBselect($sql);
236				while ($row = DBfetch($result)) {
237					$this->data[$this->items[$i]['itemid']][$type]['min'] = $row['min'];
238					$this->data[$this->items[$i]['itemid']][$type]['max'] = $row['max'];
239					$this->data[$this->items[$i]['itemid']][$type]['avg'] = $row['avg'];
240					$this->data[$this->items[$i]['itemid']][$type]['clock'] = $row['clock'];
241				}
242				unset($row);
243			}
244
245			switch ($this->items[$i]['calc_fnc']) {
246				case CALC_FNC_MIN:
247					$fncName = 'min';
248					break;
249				case CALC_FNC_MAX:
250					$fncName = 'max';
251					break;
252				case CALC_FNC_LST:
253					$fncName = 'last';
254					break;
255				case CALC_FNC_AVG:
256				default:
257					$fncName = 'avg';
258			}
259
260			$item_value = empty($this->data[$this->items[$i]['itemid']][$type][$fncName])
261				? 0
262				: abs($this->data[$this->items[$i]['itemid']][$type][$fncName]);
263
264			if ($type == GRAPH_ITEM_SUM) {
265				$this->background = $i;
266				$graph_sum = $item_value;
267			}
268
269			$this->sum += $item_value;
270
271			$convertedUnit = strlen(convert_units([
272				'value' => $item_value,
273				'units' => $this->items[$i]['units']
274			]));
275			$strvaluelength = max($strvaluelength, $convertedUnit);
276		}
277
278		if (isset($graph_sum)) {
279			$this->sum = $graph_sum;
280		}
281		$this->shiftlegendright += $strvaluelength * 7;
282	}
283
284	protected function drawLegend() {
285		$shiftY = $this->shiftY + $this->shiftYLegend;
286		$fontSize = 8;
287
288		// check if host name will be displayed
289		$displayHostName = (count(array_unique(zbx_objectValues($this->items, 'hostname'))) > 1);
290
291		// calculate function name X shift
292		$functionNameXShift = 0;
293
294		foreach ($this->items as $item) {
295			$name = $displayHostName ? $item['hostname'].': '.$item['name_expanded'] : $item['name_expanded'];
296			$dims = imageTextSize($fontSize, 0, $name);
297
298			if ($dims['width'] > $functionNameXShift) {
299				$functionNameXShift = $dims['width'];
300			}
301		}
302
303		// display items
304		$i = 0;
305
306		foreach ($this->items as $item) {
307			$color = $this->getColor($item['color'], 0);
308
309			// function name
310			switch ($item['calc_fnc']) {
311				case CALC_FNC_MIN:
312					$fncName = 'min';
313					$fncRealName = _('min');
314					break;
315				case CALC_FNC_MAX:
316					$fncName = 'max';
317					$fncRealName = _('max');
318					break;
319				case CALC_FNC_LST:
320					$fncName = 'last';
321					$fncRealName = _('last');
322					break;
323				case CALC_FNC_AVG:
324				default:
325					$fncName = 'avg';
326					$fncRealName = _('avg');
327			}
328
329			if (isset($this->data[$item['itemid']][$item['calc_type']])
330					&& isset($this->data[$item['itemid']][$item['calc_type']][$fncName])) {
331				$dataValue = $this->data[$item['itemid']][$item['calc_type']][$fncName];
332				$proc = ($this->sum == 0) ? 0 : ($dataValue * 100) / $this->sum;
333
334				$strValue = sprintf(_('Value').': %s ('.(round($proc) != round($proc, 2) ? '%0.2f' : '%0.0f').'%%)',
335					convert_units([
336						'value' => $dataValue,
337						'units' => $this->items[$i]['units']
338					]),
339					$proc
340				);
341
342				$str = '['.$fncRealName.']';
343			}
344			else {
345				$strValue = _('Value: no data');
346
347				$str = '['._('no data').']';
348			}
349
350			// item name
351			imageText(
352				$this->im,
353				$fontSize,
354				0,
355				$this->shiftXleft + 15,
356				$this->sizeY + $shiftY + 14 * $i + 5,
357				$this->getColor($this->graphtheme['textcolor'], 0),
358				$displayHostName ? $item['hostname'].': '.$item['name_expanded'] : $item['name_expanded']
359			);
360
361			// function name
362			imageText(
363				$this->im,
364				$fontSize,
365				0,
366				$this->shiftXleft + $functionNameXShift + 30,
367				$this->sizeY + $shiftY + 14 * $i + 5,
368				$this->getColor($this->graphtheme['textcolor'], 0),
369				$str
370			);
371
372			// left square fill
373			imagefilledrectangle(
374				$this->im,
375				$this->shiftXleft,
376				$this->sizeY + $shiftY + 14 * $i - 5,
377				$this->shiftXleft + 10,
378				$this->sizeY + $shiftY + 5 + 14 * $i,
379				$color
380			);
381
382			// left square frame
383			imagerectangle(
384				$this->im,
385				$this->shiftXleft,
386				$this->sizeY + $shiftY + 14 * $i - 5,
387				$this->shiftXleft + 10,
388				$this->sizeY + $shiftY + 5 + 14 * $i,
389				$this->getColor('Black No Alpha')
390			);
391
392			$shiftX = $this->fullSizeX - $this->shiftlegendright - $this->shiftXright + 25;
393
394			// right square fill
395			imagefilledrectangle(
396				$this->im,
397				$shiftX - 10,
398				$this->shiftY + 10 + 14 * $i,
399				$shiftX,
400				$this->shiftY + 10 + 10 + 14 * $i,
401				$color
402			);
403
404			// right square frame
405			imagerectangle(
406				$this->im,
407				$shiftX - 10,
408				$this->shiftY + 10 + 14 * $i,
409				$shiftX,
410				$this->shiftY + 10 + 10 + 14 * $i,
411				$this->GetColor('Black No Alpha')
412			);
413
414			// item value
415			imagetext(
416				$this->im,
417				$fontSize,
418				0,
419				$shiftX + 5,
420				$this->shiftY + 10 + 14 * $i + 10,
421				$this->getColor($this->graphtheme['textcolor'], 0),
422				$strValue
423			);
424
425			$i++;
426		}
427
428		if ($this->sizeY < 120) {
429			return;
430		}
431	}
432
433	protected function drawElementPie($values) {
434		$sum = $this->sum;
435
436		if ($this->background !== false) {
437			$least = 0;
438			foreach ($values as $item => $value) {
439				if ($item != $this->background) {
440					$least += $value;
441				}
442			}
443			$values[$this->background] -= $least;
444		}
445
446		if ($sum <= 0) {
447			$values = [0 => 1];
448			$sum = 1;
449			$isEmptyData = true;
450		}
451		else {
452			$isEmptyData = false;
453		}
454
455		$sizeX = $this->sizeX;
456		$sizeY = $this->sizeY;
457
458		if ($this->type == GRAPH_TYPE_EXPLODED) {
459			list($sizeX, $sizeY) = $this->calcExplodedRadius($sizeX, $sizeY, count($values));
460		}
461		else {
462			$sizeX = (int) $sizeX * 0.95;
463			$sizeY = (int) $sizeY * 0.95;
464		}
465
466		$xc = $x = (int) $this->sizeX / 2 + $this->shiftXleft;
467		$yc = $y = (int) $this->sizeY / 2 + $this->shiftY;
468
469		$anglestart = 0;
470		$angleend = 0;
471
472		foreach ($values as $item => $value) {
473			if ($value == 0) {
474				continue;
475			}
476
477			$angleend += (int) (360 * $value / $sum) + 1;
478			$angleend = ($angleend > 360) ? 360 : $angleend;
479
480			if (($angleend - $anglestart) < 1) {
481				continue;
482			}
483
484			if ($this->type == GRAPH_TYPE_EXPLODED) {
485				list($x, $y) = $this->calcExplodedCenter($anglestart, $angleend, $xc, $yc, count($values));
486			}
487
488			imagefilledarc(
489				$this->im,
490				$x,
491				$y,
492				$sizeX,
493				$sizeY,
494				$anglestart,
495				$angleend,
496				$this->getColor((!$isEmptyData ? $this->items[$item]['color'] : 'FFFFFF'), 0),
497				IMG_ARC_PIE
498			);
499			imagefilledarc(
500				$this->im,
501				$x,
502				$y,
503				$sizeX,
504				$sizeY,
505				$anglestart,
506				$angleend,
507				$this->getColor('Black'),
508				IMG_ARC_PIE | IMG_ARC_EDGED | IMG_ARC_NOFILL
509			);
510			$anglestart = $angleend;
511		}
512	}
513
514	protected function drawElementPie3D($values) {
515		$sum = $this->sum;
516
517		if ($this->background !== false) {
518			$least = 0;
519			foreach ($values as $item => $value) {
520				if ($item != $this->background) {
521					$least += $value;
522				}
523			}
524			$values[$this->background] -= $least;
525		}
526
527		if ($sum <= 0) {
528			$values = [0 => 1];
529			$sum = 1;
530			$isEmptyData = true;
531		}
532		else {
533			$isEmptyData = false;
534		}
535
536		$sizeX = $this->sizeX;
537		$sizeY = $this->sizeY;
538
539		$this->exploderad = $this->exploderad3d;
540
541		if ($this->type == GRAPH_TYPE_3D_EXPLODED) {
542			list($sizeX, $sizeY) = $this->calcExplodedRadius($sizeX, $sizeY, count($values));
543		}
544
545		list($sizeX, $sizeY) = $this->calc3DAngle($sizeX, $sizeY);
546
547		$xc = $x = (int) $this->sizeX / 2 + $this->shiftXleft;
548		$yc = $y = (int) $this->sizeY / 2 + $this->shiftY;
549
550		// bottom angle line
551		$anglestart = 0;
552		$angleend = 0;
553
554		foreach ($values as $item => $value) {
555			if ($value == 0) {
556				continue;
557			}
558
559			$angleend += (int) (360 * $value / $sum) + 1;
560			$angleend = ($angleend > 360) ? 360 : $angleend;
561
562			if (($angleend - $anglestart) < 1) {
563				continue;
564			}
565
566			if ($this->type == GRAPH_TYPE_3D_EXPLODED) {
567				list($x, $y) = $this->calcExplodedCenter($anglestart, $angleend, $xc, $yc, count($values));
568			}
569
570			imagefilledarc(
571				$this->im,
572				$x,
573				$y + $this->graphheight3d + 1,
574				$sizeX,
575				$sizeY,
576				$anglestart,
577				$angleend,
578				$this->getShadow((!$isEmptyData ? $this->items[$item]['color'] : 'FFFFFF'), 0),
579				IMG_ARC_PIE
580			);
581			imagefilledarc(
582				$this->im,
583				$x,
584				$y + $this->graphheight3d + 1,
585				$sizeX,
586				$sizeY,
587				$anglestart,
588				$angleend,
589				$this->getColor('Black'),
590				IMG_ARC_PIE | IMG_ARC_EDGED | IMG_ARC_NOFILL
591			);
592			$anglestart = $angleend;
593		}
594
595		// 3d effect
596		for ($i = $this->graphheight3d; $i > 0; $i--) {
597			$anglestart = 0;
598			$angleend = 0;
599
600			foreach ($values as $item => $value) {
601				if ($value == 0) {
602					continue;
603				}
604
605				$angleend += (int) (360 * $value / $sum) + 1;
606				$angleend = ($angleend > 360) ? 360 : $angleend;
607
608				if (($angleend - $anglestart) < 1) {
609					continue;
610				}
611				elseif ($this->sum == 0) {
612					continue;
613				}
614
615				if ($this->type == GRAPH_TYPE_3D_EXPLODED) {
616					list($x, $y) = $this->calcExplodedCenter($anglestart, $angleend, $xc, $yc, count($values));
617				}
618
619				imagefilledarc(
620					$this->im,
621					$x,
622					$y + $i,
623					$sizeX,
624					$sizeY,
625					$anglestart,
626					$angleend,
627					$this->getShadow((!$isEmptyData ? $this->items[$item]['color'] : 'FFFFFF'), 0),
628					IMG_ARC_PIE
629				);
630				$anglestart = $angleend;
631			}
632		}
633
634		$anglestart = 0;
635		$angleend = 0;
636
637		foreach ($values as $item => $value) {
638			if ($value == 0) {
639				continue;
640			}
641
642			$angleend += (int) (360 * $value / $sum) + 1;
643			$angleend = ($angleend > 360) ? 360 : $angleend;
644
645			if (($angleend - $anglestart) < 1) {
646				continue;
647			}
648
649			if ($this->type == GRAPH_TYPE_3D_EXPLODED) {
650				list($x, $y) = $this->calcExplodedCenter($anglestart, $angleend, $xc, $yc, count($values));
651			}
652
653			imagefilledarc(
654				$this->im,
655				$x,
656				$y,
657				$sizeX,
658				$sizeY,
659				$anglestart,
660				$angleend,
661				$this->getColor((!$isEmptyData ? $this->items[$item]['color'] : 'FFFFFF'), 0),
662				IMG_ARC_PIE
663			);
664			imagefilledarc(
665				$this->im,
666				$x,
667				$y,
668				$sizeX,
669				$sizeY,
670				$anglestart,
671				$angleend,
672				$this->getColor('Black'),
673				IMG_ARC_PIE | IMG_ARC_EDGED | IMG_ARC_NOFILL
674			);
675			$anglestart = $angleend;
676		}
677	}
678
679	public function draw() {
680		$start_time = microtime(true);
681		set_image_header();
682
683		$this->selectData();
684
685		$this->shiftY = 30;
686		$this->shiftYLegend = 20;
687		$this->shiftXleft = 10;
688		$this->shiftXright = 0;
689		$this->fullSizeX = $this->sizeX;
690		$this->fullSizeY = $this->sizeY;
691
692		if ($this->sizeX < 300 || $this->sizeY < 200) {
693			$this->showLegend(0);
694		}
695
696		if ($this->drawLegend == 1) {
697			$this->sizeX -= $this->shiftXleft + $this->shiftXright + $this->shiftlegendright;
698			$this->sizeY -= $this->shiftY + $this->shiftYLegend + 12 * $this->num + 8;
699		}
700		else {
701			$this->sizeX -= $this->shiftXleft * 2;
702			$this->sizeY -= $this->shiftY * 2;
703		}
704
705		$this->sizeX = min($this->sizeX, $this->sizeY);
706		$this->sizeY = min($this->sizeX, $this->sizeY);
707
708		$this->calc3dheight($this->sizeY);
709
710		$this->exploderad = (int) $this->sizeX / 100;
711		$this->exploderad3d = (int) $this->sizeX / 60;
712
713		if (function_exists('ImageColorExactAlpha') && function_exists('ImageCreateTrueColor') && @imagecreatetruecolor(1, 1)) {
714			$this->im = imagecreatetruecolor($this->fullSizeX, $this->fullSizeY);
715		}
716		else {
717			$this->im = imagecreate($this->fullSizeX, $this->fullSizeY);
718		}
719		$this->initColors();
720		$this->drawRectangle();
721		$this->drawHeader();
722
723		// for each metric
724		$values = [];
725		for ($i = 0; $i < $this->num; $i++) {
726			$type = $this->items[$i]['calc_type'];
727
728			$data = &$this->data[$this->items[$i]['itemid']][$type];
729
730			if (!isset($data)) {
731				continue;
732			}
733
734			switch ($this->items[$i]['calc_fnc']) {
735				case CALC_FNC_MIN:
736					$fncName = 'min';
737					break;
738				case CALC_FNC_MAX:
739					$fncName = 'max';
740					break;
741				case CALC_FNC_LST:
742					$fncName = 'last';
743					break;
744				case CALC_FNC_AVG:
745				default:
746					$fncName = 'avg';
747			}
748
749			$values[$i] = empty($this->data[$this->items[$i]['itemid']][$type][$fncName])
750				? 0
751				: abs($this->data[$this->items[$i]['itemid']][$type][$fncName]);
752		}
753
754		switch ($this->type) {
755			case GRAPH_TYPE_EXPLODED:
756				$this->drawElementPie($values);
757				break;
758			case GRAPH_TYPE_3D:
759				$this->drawElementPie3D($values);
760				break;
761			case GRAPH_TYPE_3D_EXPLODED:
762				$this->drawElementPie3D($values);
763				break;
764			default:
765				$this->drawElementPie($values);
766		}
767
768		$this->drawLogo();
769		if ($this->drawLegend == 1) {
770			$this->drawLegend();
771		}
772
773		$str = sprintf('%0.2f', microtime(true) - $start_time);
774		$str = _s('Data from %1$s. Generated in %2$s sec.', $this->dataFrom, $str);
775		$strSize = imageTextSize(6, 0, $str);
776		imageText(
777			$this->im,
778			6,
779			0,
780			$this->fullSizeX - $strSize['width'] - 5,
781			$this->fullSizeY - 5,
782			$this->getColor('Gray'),
783			$str
784		);
785
786		unset($this->items, $this->data);
787
788		imageOut($this->im);
789	}
790}
791