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