diff -ru zabbix-2.0.9/api/classes/CGraphGeneral.php zabbix-test/api/classes/CGraphGeneral.php --- zabbix-2.0.9/api/classes/CGraphGeneral.php 2013-12-05 12:22:43.000000000 -0500 +++ zabbix-test/api/classes/CGraphGeneral.php 2013-12-05 13:31:25.000000000 -0500 @@ -86,7 +86,8 @@ } // Y axis MIN value < Y axis MAX value - if (($graph['graphtype'] == GRAPH_TYPE_NORMAL || $graph['graphtype'] == GRAPH_TYPE_STACKED) + if (($graph['graphtype'] == GRAPH_TYPE_NORMAL || $graph['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC + || $graph['graphtype'] == GRAPH_TYPE_STACKED || $graph['graphtype'] == GRAPH_TYPE_STACKED_LOGARITHMIC) && $graph['ymin_type'] == GRAPH_YAXIS_TYPE_FIXED && $graph['ymax_type'] == GRAPH_YAXIS_TYPE_FIXED && $graph['yaxismin'] >= $graph['yaxismax']) { diff -ru zabbix-2.0.9/chart3.php zabbix-test/chart3.php --- zabbix-2.0.9/chart3.php 2013-12-05 12:22:43.000000000 -0500 +++ zabbix-test/chart3.php 2013-12-05 12:50:55.000000000 -0500 @@ -43,7 +43,7 @@ 'legend' => array(T_ZBX_INT, O_OPT, null, IN('0,1'), null), 'showworkperiod' => array(T_ZBX_INT, O_OPT, null, IN('0,1'), null), 'showtriggers' => array(T_ZBX_INT, O_OPT, null, IN('0,1'), null), - 'graphtype' => array(T_ZBX_INT, O_OPT, null, IN('0,1'), null), + 'graphtype' => array(T_ZBX_INT, O_OPT, null, IN('0,1,10,11'), null), 'yaxismin' => array(T_ZBX_DBL, O_OPT, null, null, null), 'yaxismax' => array(T_ZBX_DBL, O_OPT, null, null, null), 'percent_left' => array(T_ZBX_DBL, O_OPT, null, BETWEEN(0, 100), null), diff -ru zabbix-2.0.9/graphs.php zabbix-test/graphs.php --- zabbix-2.0.9/graphs.php 2013-12-05 12:22:43.000000000 -0500 +++ zabbix-test/graphs.php 2013-12-05 12:50:55.000000000 -0500 @@ -43,7 +43,7 @@ 'height' => array(T_ZBX_INT, O_OPT, null, BETWEEN(20, 65535), 'isset({save})', _('Height').' (min:20, max:65535)'), 'ymin_type' => array(T_ZBX_INT, O_OPT, null, IN('0,1,2'), null), 'ymax_type' => array(T_ZBX_INT, O_OPT, null, IN('0,1,2'), null), - 'graphtype' => array(T_ZBX_INT, O_OPT, null, IN('0,1,2,3'), 'isset({save})'), + 'graphtype' => array(T_ZBX_INT, O_OPT, null, IN('0,1,2,3,10,11'), 'isset({save})'), 'yaxismin' => array(T_ZBX_DBL, O_OPT, null, null, 'isset({save})&&(({graphtype}==0)||({graphtype}==1))'), 'yaxismax' => array(T_ZBX_DBL, O_OPT, null, null, 'isset({save})&&(({graphtype}==0)||({graphtype}==1))'), 'show_3d' => array(T_ZBX_INT, O_OPT, P_NZERO, IN('0,1'), null), diff -ru zabbix-2.0.9/include/classes/class.cchart.php zabbix-test/include/classes/class.cchart.php --- zabbix-2.0.9/include/classes/class.cchart.php 2013-12-05 12:22:43.000000000 -0500 +++ zabbix-test/include/classes/class.cchart.php 2013-12-05 13:39:46.000000000 -0500 @@ -18,6 +18,7 @@ ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +define(LOGARITHMIC_MIN, 0.0001); class CChart extends CGraphDraw { @@ -48,15 +49,25 @@ $this->m_showWorkPeriod = 1; $this->m_showTriggers = 1; $this->zero = array(); - $this->graphOrientation = array( - GRAPH_YAXIS_SIDE_LEFT => '', - GRAPH_YAXIS_SIDE_RIGHT => '' + $this->haveNegativeValues = array( + GRAPH_YAXIS_SIDE_LEFT => false, + GRAPH_YAXIS_SIDE_RIGHT => false ); $this->grid = array(); // vertical & horizontal grids params $this->gridLinesCount = array(); // How many grids to draw $this->gridStep = array(); // grid step $this->gridPixels = 25; // optimal grid size $this->gridPixelsVert = 40; + + $this->isLogarithmic = false; + if ($this->type == GRAPH_TYPE_NORMAL_LOGARITHMIC) { + $this->type = GRAPH_TYPE_NORMAL; + $this->isLogarithmic = true; + } + if ($this->type == GRAPH_TYPE_STACKED_LOGARITHMIC) { + $this->type = GRAPH_TYPE_STACKED; + $this->isLogarithmic = true; + } } /********************************************************************************************************/ @@ -131,16 +142,6 @@ $this->num++; } - public function setGraphOrientation($value, $axisside) { - if ($value < 0) { - $this->graphOrientation[$axisside] = '-'; - } - elseif (zbx_empty($this->graphOrientation[$axisside]) && $value > 0) { - $this->graphOrientation[$axisside] = '+'; - } - return $this->graphOrientation[$axisside]; - } - public function setYMinAxisType($yaxistype) { $this->ymin_type = $yaxistype; } @@ -305,9 +306,10 @@ $curr_data['shift_max'][$idx] = 0; $curr_data['shift_avg'][$idx] = 0; } - $loc_min = is_array($curr_data['min']) ? min($curr_data['min']) : null; - $this->setGraphOrientation($loc_min, $this->items[$i]['axisside']); + if ($loc_min < 0) { + $this->haveNegativeValues[$this->items[$i]['axisside']] = true; + } unset($row); } $curr_data['avg_orig'] = is_array($curr_data['avg']) ? zbx_avg($curr_data['avg']) : null; @@ -379,47 +381,46 @@ } // end of missed points calculation } + } - // calculte shift for stacked graphs + /********************************************************************************************************/ + // CALCULATIONS + /********************************************************************************************************/ + protected function calcStackedShifts() { if ($this->type == GRAPH_TYPE_STACKED) { - for ($i = 1; $i < $this->num; $i++) { + $prev_datas = array(); + for ($i = 0; $i < $this->num; $i++) { + //find cur_data and prev_data + if (isset($curr_data)) { + $prev_datas[$curr_side] = &$curr_data; + } $curr_data = &$this->data[$this->items[$i]['itemid']][$this->items[$i]['calc_type']]; - - if (!isset($curr_data)) { + $curr_side = $this->items[$i]['axisside']; + $prev_data = &$prev_datas[$curr_side]; + if (!isset($curr_data) || !isset($prev_data)) { continue; } - for ($j = $i - 1; $j >= 0; $j--) { - if ($this->items[$j]['axisside'] != $this->items[$i]['axisside']) { - continue; - } - - $prev_data = &$this->data[$this->items[$j]['itemid']][$this->items[$j]['calc_type']]; - - if (!isset($prev_data)) { - continue; - } - - for ($ci = 0; $ci < $this->sizeX; $ci++) { - foreach (array('min', 'max', 'avg') as $var_name) { - $shift_var_name = 'shift_'.$var_name; - $curr_shift = &$curr_data[$shift_var_name]; - $curr_var = &$curr_data[$var_name]; - $prev_shift = &$prev_data[$shift_var_name]; - $prev_var = &$prev_data[$var_name]; - $curr_shift[$ci] = $prev_var[$ci] + $prev_shift[$ci]; - } - } - break; + for ($ci = 0; $ci < $this->sizeX; $ci++) { + // min + $curr_shift = &$curr_data['shift_min']; + $prev_shift = &$prev_data['shift_min']; + $curr_shift[$ci] = $prev_data['min'][$ci] + $prev_shift[$ci]; + + // max + $curr_shift = &$curr_data['shift_max']; + $prev_shift = &$prev_data['shift_max']; + $curr_shift[$ci] = $prev_data['max'][$ci] + $prev_shift[$ci]; + + // avg + $curr_shift = &$curr_data['shift_avg']; + $prev_shift = &$prev_data['shift_avg']; + $curr_shift[$ci] = $prev_data['avg'][$ci] + $prev_shift[$ci]; } } } - // end calculation of stacked graphs } - /********************************************************************************************************/ - // CALCULATIONS - /********************************************************************************************************/ protected function calcTriggers() { $this->triggers = array(); if ($this->m_showTriggers != 1) { @@ -1768,7 +1769,7 @@ $fnc_name = _('avg'); } - $data = &$this->data[$this->items[$i]['itemid']][$this->items[$i]['calc_type']]; + $curr_data = &$this->data[$this->items[$i]['itemid']][$this->items[$i]['calc_type']]; // draw color square if (function_exists('imagecolorexactalpha') && function_exists('imagecreatetruecolor') && @imagecreatetruecolor(1, 1)) { @@ -1790,7 +1791,7 @@ } // draw legend of an item with data - if (isset($data) && isset($data['min'])) { + if (isset($curr_data) && isset($curr_data['min'])) { if ($this->items[$i]['axisside'] == GRAPH_YAXIS_SIDE_LEFT) { $units['left'] = $this->items[$i]['units']; } @@ -1806,15 +1807,15 @@ 'align' => 2 )); $legend->addCell($rowNum, array( - 'text' => convert_units(min($data['min']), $this->items[$i]['units'], ITEM_CONVERT_NO_UNITS), + 'text' => convert_units(min($curr_data['min']), $this->items[$i]['units'], ITEM_CONVERT_NO_UNITS), 'align' => 2 )); $legend->addCell($rowNum, array( - 'text' => convert_units($data['avg_orig'], $this->items[$i]['units'], ITEM_CONVERT_NO_UNITS), + 'text' => convert_units($curr_data['avg_orig'], $this->items[$i]['units'], ITEM_CONVERT_NO_UNITS), 'align' => 2 )); $legend->addCell($rowNum, array( - 'text' => convert_units(max($data['max']), $this->items[$i]['units'], ITEM_CONVERT_NO_UNITS), + 'text' => convert_units(max($curr_data['max']), $this->items[$i]['units'], ITEM_CONVERT_NO_UNITS), 'align' => 2 )); } @@ -2185,62 +2186,549 @@ } } - public function draw() { - $start_time = microtime(true); - - set_image_header(); - - $this->selectData(); + protected function calcLogZero() { + $sides = array(GRAPH_YAXIS_SIDE_LEFT, GRAPH_YAXIS_SIDE_RIGHT); - $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = $this->calculateMinY(GRAPH_YAXIS_SIDE_LEFT); - $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = $this->calculateMinY(GRAPH_YAXIS_SIDE_RIGHT); - $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT] = $this->calculateMaxY(GRAPH_YAXIS_SIDE_LEFT); - $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT] = $this->calculateMaxY(GRAPH_YAXIS_SIDE_RIGHT); + foreach ($sides as $num => $side) { + $this->unit2px[$side] = (log10($this->m_maxY[$side]) - log10($this->m_minY[$side])) / $this->sizeY; + if ($this->unit2px[$side] == 0) { + $this->unit2px[$side] = 1; + } - if ($this->m_minY[GRAPH_YAXIS_SIDE_LEFT] == $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT]) { - if ($this->graphOrientation[GRAPH_YAXIS_SIDE_LEFT] == '-') { - $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT] = 0; + $this->zero[$side] = $this->sizeY + $this->shiftY; + $this->oxy[$side] = log10($this->m_minY[$side]); + } + } + + protected function calcLogMinMaxInterval() { + $sides = array(GRAPH_YAXIS_SIDE_LEFT, GRAPH_YAXIS_SIDE_RIGHT); + foreach ($sides as $side) { + //floor(log10($this->minY[$side])); + $bottom = LOGARITHMIC_MIN * 10; + while ($this->m_minY[$side] > $bottom) { + $bottom *= 10; } - elseif ($this->m_minY[GRAPH_YAXIS_SIDE_LEFT] == 0) { - $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT] = 1; + $bottom /= 10; + $this->m_minY[$side] = $bottom; + + //ceil(log10($this->maxY[$side])); + $top = $bottom; + while ($this->m_maxY[$side] > $top) { + $top *= 10; } - else { - $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = 0; + + if ($top / $bottom + 0.0001 < 100) { + $top = $bottom * 100; } + $this->m_maxY[$side] = $top; + + $this->gridLinesCount[$side] = round(log10($top) - log10($bottom)); + $this->gridStepX[$side] = floor($this->sizeY / $this->gridLinesCount[$side]); + } + } + + private function drawLogVerticalGrid() { + $mainSide = ($this->yaxisleft) ? GRAPH_YAXIS_SIDE_LEFT : GRAPH_YAXIS_SIDE_RIGHT; + for ($i = 1; $i < $this->gridLinesCount[$mainSide]; $i++) { + $y = $this->shiftY + $i * $this->gridStepX[$mainSide]; + + dashedLine( + $this->im, + $this->shiftXleft, + $y, + $this->sizeX + $this->shiftXleft, + $y, + $this->getColor($this->graphtheme['maingridcolor'], 0) + ); + } + } + + private function drawLogLeftSide() { + if ($this->yaxisleft == 0 || $this->skipLeftScale == 1) { + return; } - elseif ($this->m_minY[GRAPH_YAXIS_SIDE_LEFT] > $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT]) { - if ($this->graphOrientation[GRAPH_YAXIS_SIDE_LEFT] == '-') { - $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = 0.2 * $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT]; + + $minY = $this->m_minY[GRAPH_YAXIS_SIDE_LEFT]; + $maxY = $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT]; + + $units = null; + $unitsLong = null; + for ($item = 0; $item < $this->num; $item++) { + if ($this->items[$item]['axisside'] == GRAPH_YAXIS_SIDE_LEFT) { + if (is_null($units)) { + $units = $this->items[$item]['units']; + } + elseif ($this->items[$item]['units'] != $units) { + $units = false; + } } - else { - $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = 0; + } + + if (is_null($units) || $units === false) { + $units = ''; + } + else { + for ($item = 0; $item < $this->num; $item++) { + if ($this->items[$item]['axisside'] == GRAPH_YAXIS_SIDE_LEFT && !empty($this->items[$item]['unitsLong'])) { + $unitsLong = $this->items[$item]['unitsLong']; + break; + } } } - if ($this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] == $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]) { - if ($this->graphOrientation[GRAPH_YAXIS_SIDE_RIGHT] == '-') { - $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT] = 0; + if (!empty($unitsLong)) { + $dims = imageTextSize(9, 90, $unitsLong); + + $tmpY = $this->sizeY / 2 + $this->shiftY+$dims['height'] / 2; + if ($tmpY < $dims['height']) { + $tmpY = $dims['height'] + 6; } - elseif ($this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] == 0) { - $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT] = 1; + + imageText( + $this->im, + 9, + 90, + $dims['width'] + 8, + $tmpY, + $this->getColor($this->graphtheme['textcolor'], 0), + $unitsLong + ); + } + + $hstr_count = $this->gridLinesCount[GRAPH_YAXIS_SIDE_LEFT]; + $val = $this->m_minY[GRAPH_YAXIS_SIDE_LEFT]; + for ($i = 0; $i <= $hstr_count; $i++) { + // using bc library, incase of large numbers + $str = convert_units($val, $units, ITEM_CONVERT_NO_UNITS); + $dims = imageTextSize(8, 0, $str); + $posX = $this->shiftXleft - $dims['width'] - 9; + $posY = $this->getYStepMarkerPosY(GRAPH_YAXIS_SIDE_LEFT, $i); + + // only draw the marker if it doesn't overlay the previous one + imageText( + $this->im, + 8, + 0, + $posX, + $posY, + $this->getColor($this->graphtheme['textcolor'], 0), + $str + ); + + $val = bcmul($val, 10); + } + } + + private function drawLogRightSide() { + if ($this->yaxisright == 0 || $this->skipRightScale == 1) { + return; + } + + $minY = $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT]; + $maxY = $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]; + + $units = null; + $unitsLong = null; + for ($item = 0; $item < $this->num; $item++) { + if ($this->items[$item]['axisside'] == GRAPH_YAXIS_SIDE_RIGHT) { + if (is_null($units)) { + $units = $this->items[$item]['units']; + } + elseif ($this->items[$item]['units'] != $units) { + $units = false; + } } - else { - $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = 0; + } + + if (is_null($units) || $units === false) { + $units = ''; + } + else { + for ($item = 0; $item < $this->num; $item++) { + if ($this->items[$item]['axisside'] == GRAPH_YAXIS_SIDE_RIGHT && !empty($this->items[$item]['unitsLong'])) { + $unitsLong = $this->items[$item]['unitsLong']; + break; + } } } - elseif ($this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] > $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]) { - if ($this->graphOrientation[GRAPH_YAXIS_SIDE_RIGHT] == '-') { - $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = 0.2 * $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]; + + if (!empty($unitsLong)) { + $dims = imageTextSize(9, 90, $unitsLong); + + $tmpY = $this->sizeY / 2 + $this->shiftY + $dims['height'] / 2; + if ($tmpY < $dims['height']) { + $tmpY = $dims['height'] + 6; } - else { - $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = 0; + + imageText( + $this->im, + 9, + 90, + $this->fullSizeX - $dims['width'], + $tmpY, + $this->getColor($this->graphtheme['textcolor'], 0), + $unitsLong + ); + } + + $hstr_count = $this->gridLinesCount[GRAPH_YAXIS_SIDE_RIGHT]; + $val = $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT]; + for ($i = 0; $i <= $hstr_count; $i++) { + // using bc library, incase of large numbers + $str = convert_units($val, $units, ITEM_CONVERT_NO_UNITS); + $dims = imageTextSize(8, 0, $str); + $posX = $this->sizeX + $this->shiftXleft + 12; + $posY = $this->getYStepMarkerPosY(GRAPH_YAXIS_SIDE_RIGHT, $i); + + // only draw the marker if it doesn't overlay the previous one + imageText( + $this->im, + 8, + 0, + $posX, + $posY, + $this->getColor($this->graphtheme['textcolor'], 0), + $str + ); + + $val = bcmul($val, 10); + } + } + + protected function drawLogPercentile() { + if ($this->type != GRAPH_TYPE_NORMAL) { + return ; + } + + foreach ($this->percentile as $side => $percentile) { + if ($percentile['percent'] > 0 && $percentile['value']) { + if ($side == 'left') { + $axisside = GRAPH_YAXIS_SIDE_LEFT; + $color = $this->graphtheme['leftpercentilecolor']; + } + else { + $axisside = GRAPH_YAXIS_SIDE_RIGHT; + $color = $this->graphtheme['rightpercentilecolor']; + } + + $y = $this->zero[$axisside] - (log10($percentile['value']) - $this->oxy[$axisside]) / $this->unit2px[$axisside]; + imageline( + $this->im, + $this->shiftXleft, + $y, + $this->sizeX + $this->shiftXleft, + $y, + $this->getColor($color) + ); } } + } + + protected function limitToBoundsLog(&$value, $min, $max) { + if ($value > $max) + $value = $max; + if ($value < $min) + $value = $min; + } + + protected function drawLogElement(&$data, $from, $to, $minX, $maxX, $minY, $maxY, $drawtype, $max_color, $avg_color, $min_color, $minmax_color, $calc_fnc, $axisside) { + if (!isset($data['max'][$from]) || !isset($data['max'][$to])) { + return; + } + + $oxy = $this->oxy[$axisside]; + $zero = $this->zero[$axisside]; + $unit2px = $this->unit2px[$axisside]; + + $shift_min_from = $shift_min_to = 0; + $shift_max_from = $shift_max_to = 0; + $shift_avg_from = $shift_avg_to = 0; + + if (isset($data['shift_min'][$from])) { + $shift_min_from = log10($data['shift_min'][$from]); + } + if (isset($data['shift_min'][$to])) { + $shift_min_to = log10($data['shift_min'][$to]); + } + + if (isset($data['shift_max'][$from])) { + $shift_max_from = log10($data['shift_max'][$from]); + } + if (isset($data['shift_max'][$to])) { + $shift_max_to = log10($data['shift_max'][$to]); + } + + if (isset($data['shift_avg'][$from])) { + $shift_avg_from = log10($data['shift_avg'][$from]); + } + if (isset($data['shift_avg'][$to])) { + $shift_avg_to = log10($data['shift_avg'][$to]); + } + + $min_from = log10($data['min'][$from] + $data['shift_min'][$from]); + $min_to = log10($data['min'][$to] + $data['shift_min'][$to]); + + $max_from = log10($data['max'][$from] + $data['shift_max'][$from]); + $max_to = log10($data['max'][$to] + $data['shift_max'][$to]); + + $avg_from = log10($data['avg'][$from] + $data['shift_avg'][$from]); + $avg_to = log10($data['avg'][$to] + $data['shift_avg'][$to]); + + $x1 = $from + $this->shiftXleft - 1; + $x2 = $to + $this->shiftXleft; + + $y1min = $zero - ($min_from - $oxy) / $unit2px; + $y2min = $zero - ($min_to - $oxy) / $unit2px; + + $y1max = $zero - ($max_from - $oxy) / $unit2px; + $y2max = $zero - ($max_to - $oxy) / $unit2px; + + $y1avg = $zero - ($avg_from - $oxy) / $unit2px; + $y2avg = $zero - ($avg_to - $oxy) / $unit2px; + + switch ($calc_fnc) { + case CALC_FNC_MAX: + $y1 = $y1max; + $y2 = $y2max; + $shift_from = $shift_max_from; + $shift_to = $shift_max_to; + break; + case CALC_FNC_MIN: + $y1 = $y1min; + $y2 = $y2min; + $shift_from = $shift_min_from; + $shift_to = $shift_min_to; + break; + case CALC_FNC_ALL: + // max + $y1x = (($y1max > ($this->sizeY + $this->shiftY)) || $y1max < $this->shiftY); + $y2x = (($y2max > ($this->sizeY + $this->shiftY)) || $y2max < $this->shiftY); + + if ($y1x) { + $y1max = ($y1max > ($this->sizeY + $this->shiftY)) ? $this->sizeY + $this->shiftY : $this->shiftY; + } + if ($y2x) { + $y2max = ($y2max > ($this->sizeY + $this->shiftY)) ? $this->sizeY + $this->shiftY : $this->shiftY; + } + + // min + $y1n = (($y1min > ($this->sizeY + $this->shiftY)) || $y1min < $this->shiftY); + $y2n = (($y2min > ($this->sizeY + $this->shiftY)) || $y2min < $this->shiftY); + + if ($y1n) { + $y1min = ($y1min > ($this->sizeY + $this->shiftY)) ? $this->sizeY + $this->shiftY : $this->shiftY; + } + if ($y2n) { + $y2min = ($y2min > ($this->sizeY + $this->shiftY)) ? $this->sizeY + $this->shiftY : $this->shiftY; + } + + $a[0] = $x1; + $a[1] = $y1max; + $a[2] = $x1; + $a[3] = $y1min; + $a[4] = $x2; + $a[5] = $y2min; + $a[6] = $x2; + $a[7] = $y2max; + + // don't use break, avg must be drawed in this statement + case CALC_FNC_AVG: + + // don't use break, avg must be drawed in this statement + default: + $y1 = $y1avg; + $y2 = $y2avg; + $shift_from = $shift_avg_from; + $shift_to = $shift_avg_to; + } + + $y1_shift = $zero - ($shift_from - $oxy) / $unit2px; + $y2_shift = $zero - ($shift_to - $oxy) / $unit2px; + + // так как координаты графика вверх ногами, минимум и максимум перекручены + $graphMax = $zero - (log10($this->m_minY[$axisside]) - $oxy) / $unit2px; + $graphMin = $zero - (log10($this->m_maxY[$axisside]) - $oxy) / $unit2px; + + $this->limitToBoundsLog($y1, $graphMin, $graphMax); + $this->limitToBoundsLog($y2, $graphMin, $graphMax); + $this->limitToBoundsLog($y1_shift, $graphMin, $graphMax); + $this->limitToBoundsLog($y2_shift, $graphMin, $graphMax); + + // draw main line + switch ($drawtype) { + case GRAPH_ITEM_DRAWTYPE_BOLD_LINE: + if ($calc_fnc == CALC_FNC_ALL) { + imagefilledpolygon($this->im, $a, 4, $minmax_color); + if (!$y1x || !$y2x) { + imageline($this->im, $x1 + 1, $y1max, $x2 + 1, $y2max, $max_color); + imageline($this->im, $x1, $y1max, $x2, $y2max, $max_color); + } + + if (!$y1n || !$y2n) { + imageline($this->im, $x1 - 1, $y1min, $x2 - 1, $y2min, $min_color); + imageline($this->im, $x1, $y1min, $x2, $y2min, $min_color); + } + } + + imageline($this->im, $x1, $y1 + 1, $x2, $y2 + 1, $avg_color); + imageline($this->im, $x1, $y1, $x2, $y2, $avg_color); + break; + case GRAPH_ITEM_DRAWTYPE_LINE: + if ($calc_fnc == CALC_FNC_ALL) { + imagefilledpolygon($this->im, $a, 4, $minmax_color); + if (!$y1x || !$y2x) { + imageline($this->im, $x1, $y1max, $x2, $y2max, $max_color); + } + if (!$y1n || !$y2n) { + imageline($this->im, $x1, $y1min, $x2, $y2min, $min_color); + } + } + + imageline($this->im, $x1, $y1, $x2, $y2, $avg_color); + break; + case GRAPH_ITEM_DRAWTYPE_FILLED_REGION: + $a[0] = $x1; + $a[1] = $y1; + $a[2] = $x1; + $a[3] = $y1_shift; + $a[4] = $x2; + $a[5] = $y2_shift; + $a[6] = $x2; + $a[7] = $y2; + + imagefilledpolygon($this->im, $a, 4, $avg_color); + break; + case GRAPH_ITEM_DRAWTYPE_DOT: + imagefilledrectangle($this->im, $x1 - 1, $y1 - 1, $x1, $y1, $avg_color); + break; + case GRAPH_ITEM_DRAWTYPE_BOLD_DOT: + imagefilledrectangle($this->im, $x2 - 1, $y2 - 1, $x2 + 1, $y2 + 1, $avg_color); + break; + case GRAPH_ITEM_DRAWTYPE_DASHED_LINE: + if (function_exists('imagesetstyle')) { + // use imagesetstyle+imageline instead of bugged imagedashedline + $style = array($avg_color, $avg_color, IMG_COLOR_TRANSPARENT, IMG_COLOR_TRANSPARENT); + imagesetstyle($this->im, $style); + imageline($this->im, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED); + } + else { + imagedashedline($this->im, $x1, $y1, $x2, $y2, $avg_color); + } + break; + case GRAPH_ITEM_DRAWTYPE_GRADIENT_LINE: + imageLine($this->im, $x1, $y1, $x2, $y2, $avg_color); // draw the initial line + imageLine($this->im, $x1, $y1 - 1, $x2, $y2 - 1, $avg_color); + + $bitmask = 255; + $blue = $avg_color & $bitmask; + + // $blue_diff = 255 - $blue; + $bitmask = $bitmask << 8; + $green = ($avg_color & $bitmask) >> 8; + + // $green_diff = 255 - $green; + $bitmask = $bitmask << 8; + $red = ($avg_color & $bitmask) >> 16; + // $red_diff = 255 - $red; + + // note: though gradients on the chart looks ok, the formula used is completely incorrect + // if you plan to fix something here, it would be better to start from scratch + $maxAlpha = 110; + $startAlpha = 50; + $alphaRatio = $maxAlpha / ($this->sizeY - $startAlpha); + + $diffX = $x1 - $x2; + for ($i = 0; $i <= $diffX; $i++) { + $Yincr = ($diffX > 0) ? (abs($y2 - $y1) / $diffX) : 0; + + $gy = ($y1 > $y2) ? ($y2 + $Yincr * $i) : ($y2 - $Yincr * $i); + $steps = $this->sizeY + $this->shiftY - $gy + 1; + + for ($j = 0; $j < $steps; $j++) { + if (($gy + $j) < ($this->shiftY + $startAlpha)) { + $alpha = 0; + } + else { + $alpha = 127 - abs(127 - ($alphaRatio * ($gy + $j - $this->shiftY - $startAlpha))); + } + + $color = imagecolorexactalpha($this->im, $red, $green, $blue, $alpha); + imagesetpixel($this->im, $x2 + $i, $gy + $j, $color); + } + } + break; + } + } + + public function draw() { + $start_time = microtime(true); + + set_image_header(); + + $this->selectData(); + $this->calcStackedShifts(); + + $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = $this->calculateMinY(GRAPH_YAXIS_SIDE_LEFT); + $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = $this->calculateMinY(GRAPH_YAXIS_SIDE_RIGHT); + $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT] = $this->calculateMaxY(GRAPH_YAXIS_SIDE_LEFT); + $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT] = $this->calculateMaxY(GRAPH_YAXIS_SIDE_RIGHT); + if ($this->isLogarithmic) { + $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = min($this->m_minY[GRAPH_YAXIS_SIDE_LEFT], $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT]); + $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT] = $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT] = max($this->m_maxY[GRAPH_YAXIS_SIDE_LEFT], $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]); + } + + if (!$this->isLogarithmic) { + if ($this->m_minY[GRAPH_YAXIS_SIDE_LEFT] == $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT]) { + if ($this->haveNegativeValues[GRAPH_YAXIS_SIDE_LEFT] == '-') { + $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT] = 0; + } + elseif ($this->m_minY[GRAPH_YAXIS_SIDE_LEFT] == 0) { + $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT] = 1; + } + else { + $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = 0; + } + } + elseif ($this->m_minY[GRAPH_YAXIS_SIDE_LEFT] > $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT]) { + if ($this->haveNegativeValues[GRAPH_YAXIS_SIDE_LEFT] == '-') { + $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = 0.2 * $this->m_maxY[GRAPH_YAXIS_SIDE_LEFT]; + } + else { + $this->m_minY[GRAPH_YAXIS_SIDE_LEFT] = 0; + } + } - $this->calcMinMaxInterval(); + if ($this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] == $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]) { + if ($this->haveNegativeValues[GRAPH_YAXIS_SIDE_RIGHT] == '-') { + $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT] = 0; + } + elseif ($this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] == 0) { + $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT] = 1; + } + else { + $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = 0; + } + } + elseif ($this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] > $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]) { + if ($this->haveNegativeValues[GRAPH_YAXIS_SIDE_RIGHT] == '-') { + $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = 0.2 * $this->m_maxY[GRAPH_YAXIS_SIDE_RIGHT]; + } + else { + $this->m_minY[GRAPH_YAXIS_SIDE_RIGHT] = 0; + } + } + + $this->calcMinMaxInterval(); + } else { + $this->calcLogMinMaxInterval(); + } $this->updateShifts(); $this->calcTriggers(); - $this->calcZero(); + if (!$this->isLogarithmic) { + $this->calcZero(); + } else { + $this->calcLogZero(); + } $this->calcPercentile(); $this->fullSizeX = $this->sizeX + $this->shiftXleft + $this->shiftXright + 1; @@ -2271,7 +2759,11 @@ $this->drawHeader(); $this->drawWorkPeriod(); $this->drawTimeGrid(); - $this->drawVerticalGrid(); + if (!$this->isLogarithmic) { + $this->drawVerticalGrid(); + } else { + $this->drawLogVerticalGrid(); + } $this->drawXYAxisScale($this->graphtheme['gridbordercolor']); $maxX = $this->sizeX; @@ -2339,34 +2831,61 @@ } if ($draw) { - $this->drawElement( - $data, - $i, - $j, - 0, - $this->sizeX, - $minY, - $maxY, - $valueDrawType, - $max_color, - $avg_color, - $min_color, - $minmax_color, - $calc_fnc, - $this->items[$item]['axisside'] - ); + if (!$this->isLogarithmic) { + $this->drawElement( + $data, + $i, + $j, + 0, + $this->sizeX, + $minY, + $maxY, + $valueDrawType, + $max_color, + $avg_color, + $min_color, + $minmax_color, + $calc_fnc, + $this->items[$item]['axisside'] + ); + } else { + $this->drawLogElement( + $data, + $i, + $j, + 0, + $this->sizeX, + $minY, + $maxY, + $valueDrawType, + $max_color, + $avg_color, + $min_color, + $minmax_color, + $calc_fnc, + $this->items[$item]['axisside'] + ); + } } $j = $i; } } - $this->drawLeftSide(); - $this->drawRightSide(); + if (!$this->isLogarithmic) { + $this->drawLeftSide(); + $this->drawRightSide(); + } else { + $this->drawLogLeftSide(); + $this->drawLogRightSide(); + } if ($this->drawLegend) { $this->drawTriggers(); - $this->drawPercentile(); + if (!$this->isLogarithmic) + $this->drawPercentile(); + else + $this->drawLogPercentile(); $this->drawLegend(); } diff -ru zabbix-2.0.9/include/defines.inc.php zabbix-test/include/defines.inc.php --- zabbix-2.0.9/include/defines.inc.php 2013-12-05 12:22:43.000000000 -0500 +++ zabbix-test/include/defines.inc.php 2013-12-05 12:50:55.000000000 -0500 @@ -684,6 +684,8 @@ define('GRAPH_TYPE_COLUMN', 7); define('GRAPH_TYPE_BAR_STACKED', 8); define('GRAPH_TYPE_COLUMN_STACKED', 9); +define('GRAPH_TYPE_NORMAL_LOGARITHMIC', 10); +define('GRAPH_TYPE_STACKED_LOGARITHMIC', 11); define('GRAPH_3D_ANGLE', 70); diff -ru zabbix-2.0.9/include/graphs.inc.php zabbix-test/include/graphs.inc.php --- zabbix-2.0.9/include/graphs.inc.php 2013-12-05 12:22:43.000000000 -0500 +++ zabbix-test/include/graphs.inc.php 2013-12-05 12:50:55.000000000 -0500 @@ -24,7 +24,9 @@ GRAPH_TYPE_NORMAL => _('Normal'), GRAPH_TYPE_STACKED => _('Stacked'), GRAPH_TYPE_PIE => _('Pie'), - GRAPH_TYPE_EXPLODED => _('Exploded') + GRAPH_TYPE_EXPLODED => _('Exploded'), + GRAPH_TYPE_NORMAL_LOGARITHMIC => _('Logarithmic'), + GRAPH_TYPE_STACKED_LOGARITHMIC => _('Stacked logarithmic') ); if (is_null($type)) { diff -ru zabbix-2.0.9/include/views/configuration.graph.edit.php zabbix-test/include/views/configuration.graph.edit.php --- zabbix-2.0.9/include/views/configuration.graph.edit.php 2013-12-05 12:22:43.000000000 -0500 +++ zabbix-test/include/views/configuration.graph.edit.php 2013-12-05 12:54:13.000000000 -0500 @@ -65,12 +65,15 @@ $itemsTable->setHeader(array( new CCol(SPACE, null, null, 15), new CCol(SPACE, null, null, 15), - new CCol(_('Name'), null, null, ($this->data['graphtype'] != GRAPH_TYPE_NORMAL) ? 360 : 280), + new CCol(_('Name'), null, null, + ($this->data['graphtype'] != GRAPH_TYPE_NORMAL && $this->data['graphtype'] != GRAPH_TYPE_NORMAL_LOGARITHMIC) ? 360 : 280), ($this->data['graphtype'] == GRAPH_TYPE_PIE || $this->data['graphtype'] == GRAPH_TYPE_EXPLODED) ? new CCol(_('Type'), null, null, 80) : null, new CCol(_('Function'), null, null, 80), - ($this->data['graphtype'] == GRAPH_TYPE_NORMAL) ? new CCol(_('Draw style'), 'nowrap', null, 80) : null, - ($this->data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_STACKED) + ($this->data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC) + ? new CCol(_('Draw style'), 'nowrap', null, 80) : null, + ($this->data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC + || $this->data['graphtype'] == GRAPH_TYPE_STACKED || $this->data['graphtype'] == GRAPH_TYPE_STACKED_LOGARITHMIC) ? new CCol(_('Y axis side'), 'nowrap', null, 80) : null, new CCol(_('Colour'), null, null, 100), new CCol(_('Action'), null, null, 50) @@ -126,11 +129,12 @@ $graphFormList->addRow(_('Show legend'), new CCheckBox('show_legend', $this->data['show_legend'], null, 1)); // append graph types to form list -if ($this->data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_STACKED) { + if ($this->data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC + || $this->data['graphtype'] == GRAPH_TYPE_STACKED || $this->data['graphtype'] == GRAPH_TYPE_STACKED_LOGARITHMIC) { $graphFormList->addRow(_('Show working time'), new CCheckBox('show_work_period', $this->data['show_work_period'], null, 1)); $graphFormList->addRow(_('Show triggers'), new CCheckBox('show_triggers', $this->data['show_triggers'], null, 1)); - if ($this->data['graphtype'] == GRAPH_TYPE_NORMAL) { + if ($this->data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC) { if (is_numeric($this->data['percent_left'])) { $this->data['percent_left'] = sprintf('%2.2f', $this->data['percent_left']); } @@ -260,6 +264,7 @@ '&srcfld2=name", 0, 0, "zbx_popup_item");', 'formlist' ); + $graphFormList->addRow(_('Y axis MAX value'), $yaxisMaxData); } } else { diff -ru zabbix-2.0.9/include/views/js/configuration.graph.edit.js.php zabbix-test/include/views/js/configuration.graph.edit.js.php --- zabbix-2.0.9/include/views/js/configuration.graph.edit.js.php 2013-12-05 12:22:43.000000000 -0500 +++ zabbix-test/include/views/js/configuration.graph.edit.js.php 2013-12-05 12:50:55.000000000 -0500 @@ -41,7 +41,7 @@ - data['graphtype'] == GRAPH_TYPE_NORMAL): ?> + data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC): ?> @@ -52,7 +52,7 @@ - data['graphtype'] == GRAPH_TYPE_NORMAL): ?> + data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC): ?> @@ -290,7 +291,7 @@ src += '&graph3d=' + (jQuery('#show_3d').is(':checked') ? 1 : 0); - data['graphtype'] == GRAPH_TYPE_NORMAL): ?> + data['graphtype'] == GRAPH_TYPE_NORMAL || $this->data['graphtype'] == GRAPH_TYPE_NORMAL_LOGARITHMIC): ?> src += '&percent_left=' + jQuery('#percent_left').val() + '&percent_right=' + jQuery('#percent_right').val();