. */ require_once(dirname(__FILE__).'/ConversionHelpers.php'); require_once(dirname(__FILE__).'/ShadowProperties.php'); require_once(dirname(__FILE__).'/Color.php'); require_once(dirname(__FILE__).'/Palette.php'); require_once(dirname(__FILE__).'/ICanvas.php'); require_once(dirname(__FILE__).'/GridStyle.php'); require_once(dirname(__FILE__).'/ScaleStyle.php'); /* Declare some script wide constants */ define ( "SCALE_NORMAL", 1 ); define ( "SCALE_ADDALL", 2 ); define ( "SCALE_START0", 3 ); define ( "SCALE_ADDALLSTART0", 4 ); define ( "TARGET_GRAPHAREA", 1 ); define ( "TARGET_BACKGROUND", 2 ); define ( "ALIGN_TOP_LEFT", 1 ); define ( "ALIGN_TOP_CENTER", 2 ); define ( "ALIGN_TOP_RIGHT", 3 ); define ( "ALIGN_LEFT", 4 ); define ( "ALIGN_CENTER", 5 ); define ( "ALIGN_RIGHT", 6 ); define ( "ALIGN_BOTTOM_LEFT", 7 ); define ( "ALIGN_BOTTOM_CENTER", 8 ); define ( "ALIGN_BOTTOM_RIGHT", 9 ); /** * pChart class definition */ class pChart { protected $palette; /* Some static vars used in the class */ protected $XSize = NULL; protected $YSize = NULL; protected $Picture = NULL; protected $ImageMap = NULL; /* Error management */ protected $ErrorReporting = FALSE; protected $ErrorInterface = "CLI"; protected $Errors = NULL; protected $ErrorFontName = "Fonts/pf_arma_five.ttf"; protected $ErrorFontSize = 6; /* vars related to the graphing area */ protected $GArea_X1 = NULL; protected $GArea_Y1 = NULL; protected $GArea_X2 = NULL; protected $GArea_Y2 = NULL; protected $GAreaXOffset = NULL; protected $VMax = NULL; protected $VMin = NULL; protected $VXMax = NULL; protected $VXMin = NULL; protected $Divisions = NULL; protected $XDivisions = NULL; protected $DivisionHeight = NULL; protected $XDivisionHeight = NULL; protected $DivisionCount = NULL; protected $XDivisionCount = NULL; protected $DivisionRatio = NULL; protected $XDivisionRatio = NULL; protected $DivisionWidth = NULL; protected $DataCount = NULL; /* Text format related vars */ protected $FontName = NULL; protected $FontSize = NULL; protected $DateFormat = "d/m/Y"; /* Lines format related vars */ protected $LineWidth = 1; protected $LineDotSize = 0; /* Shadow settings */ private $shadowProperties; /* Image Map settings */ protected $BuildMap = FALSE; protected $MapFunction = NULL; protected $tmpFolder = "tmp/"; protected $MapID = NULL; /** * @brief An abstract ICanvas onto which we draw the chart * * @todo This probably shouldn't be protected, I'm still working * on how the modules are going to break down between the various * chart types. */ protected $canvas = null; /** * This function create the background picture */ function __construct($XSize, $YSize, ICanvas $canvas) { $this->palette = Palette::defaultPalette(); $this->XSize = $XSize; $this->YSize = $YSize; $this->setFontProperties ( "tahoma.ttf", 8 ); $this->shadowProperties = ShadowProperties::FromDefaults(); $this->canvas = $canvas; } /** * Set if warnings should be reported */ function reportWarnings($Interface = "CLI") { $this->ErrorReporting = TRUE; $this->ErrorInterface = $Interface; } /** * Set the font properties */ function setFontProperties($FontName, $FontSize) { $this->FontName = $FontName; $this->FontSize = $FontSize; } public function setPalette(Palette $newPalette) { $this->palette = $newPalette; } /** * Set the shadow properties */ function setShadowProperties($XDistance = 1, $YDistance = 1, Color $color = null, $Alpha = 50, $Blur = 0) { if ($color == null) { $color = new Color(60, 60, 60); } $this->shadowProperties = ShadowProperties::FromSettings($XDistance, $YDistance, $color, $Alpha, $Blur); } /** * Remove shadow option */ function clearShadow() { $this->shadowProperties = ShadowProperties::FromDefaults(); } /** * Load Color Palette from file */ function loadColorPalette($FileName, $Delimiter = ",") { $handle = @fopen ( $FileName, "r" ); if ($handle == null) { throw new Exception("Failed to open file in loadColorPalette"); } $ColorID = 0; if ($handle) { while ( ! feof ( $handle ) ) { $buffer = fgets ( $handle, 4096 ); $buffer = str_replace ( chr ( 10 ), "", $buffer ); $buffer = str_replace ( chr ( 13 ), "", $buffer ); $Values = explode ( $Delimiter, $buffer ); if (count ( $Values ) == 3) { $this->palette->colors[$ColorID] = new Color($Values[0], $Values[1], $Values[2]); $ColorID ++; } } } } /** * Set line style */ function setLineStyle($Width = 1, $DotSize = 0) { $this->LineWidth = $Width; $this->LineDotSize = $DotSize; } /** * Set the graph area location */ function setGraphArea($X1, $Y1, $X2, $Y2) { $this->GArea_X1 = $X1; $this->GArea_Y1 = $Y1; $this->GArea_X2 = $X2; $this->GArea_Y2 = $Y2; } /** * Prepare the graph area */ private function drawGraphArea(BackgroundStyle $style) { $this->canvas->drawFilledRectangle(new Point($this->GArea_X1, $this->GArea_Y1), new Point($this->GArea_X2, $this->GArea_Y2), $style->getBackgroundColor(), $this->shadowProperties, FALSE ); $this->canvas->drawRectangle(new Point($this->GArea_X1, $this->GArea_Y1), new Point($this->GArea_X2, $this->GArea_Y2), $style->getBackgroundColor()->addRGBIncrement(-40), $style->getBorderWidth(), $style->getBorderDotSize(), $this->shadowProperties); if ($style->useStripe()) { $color2 = $style->getBackgroundColor()->addRGBIncrement(-15); $SkewWidth = $this->GArea_Y2 - $this->GArea_Y1 - 1; for($i = $this->GArea_X1 - $SkewWidth; $i <= $this->GArea_X2; $i = $i + 4) { $X1 = $i; $Y1 = $this->GArea_Y2; $X2 = $i + $SkewWidth; $Y2 = $this->GArea_Y1; if ($X1 < $this->GArea_X1) { $X1 = $this->GArea_X1; $Y1 = $this->GArea_Y1 + $X2 - $this->GArea_X1 + 1; } if ($X2 >= $this->GArea_X2) { $Y2 = $this->GArea_Y1 + $X2 - $this->GArea_X2 + 1; $X2 = $this->GArea_X2 - 1; } $this->canvas->drawLine(new Point($X1, $Y1), new Point($X2, $Y2 + 1), $color2, 1, 0, ShadowProperties::NoShadow()); } } } public function drawGraphBackground(BackgroundStyle $style) { $this->drawGraphArea($style); $this->drawGraphAreaGradient($style); } /** * Allow you to clear the scale : used if drawing multiple charts */ function clearScale() { $this->VMin = NULL; $this->VMax = NULL; $this->VXMin = NULL; $this->VXMax = NULL; $this->Divisions = NULL; $this->XDivisions = NULL; } /** * Allow you to fix the scale, use this to bypass the automatic scaling */ function setFixedScale($VMin, $VMax, $Divisions = 5, $VXMin = 0, $VXMax = 0, $XDivisions = 5) { $this->VMin = $VMin; $this->VMax = $VMax; $this->Divisions = $Divisions; if (! $VXMin == 0) { $this->VXMin = $VXMin; $this->VXMax = $VXMax; $this->XDivisions = $XDivisions; } } /** * Wrapper to the drawScale() function allowing a second scale to * be drawn */ function drawRightScale(pData $data, ScaleStyle $style, $Angle = 0, $Decimals = 1, $WithMargin = FALSE, $SkipLabels = 1) { $this->drawScale($data, $style, $Angle, $Decimals, $WithMargin, $SkipLabels, TRUE ); } /** * Compute and draw the scale */ function drawScale(pData $Data, ScaleStyle $style, $Angle = 0, $Decimals = 1, $WithMargin = FALSE, $SkipLabels = 1, $RightScale = FALSE) { /* Validate the Data and DataDescription array */ $this->validateData ( "drawScale", $Data->getData() ); $this->canvas->drawLine(new Point($this->GArea_X1, $this->GArea_Y1), new Point($this->GArea_X1, $this->GArea_Y2), $style->getColor(), $style->getLineWidth(), $style->getLineDotSize(), $this->shadowProperties); $this->canvas->drawLine(new Point($this->GArea_X1, $this->GArea_Y2), new Point($this->GArea_X2, $this->GArea_Y2), $style->getColor(), $style->getLineWidth(), $style->getLineDotSize(), $this->shadowProperties); if ($this->VMin == NULL && $this->VMax == NULL) { $Divisions = $this->calculateDivisions($Data, $style); } else $Divisions = $this->Divisions; $this->DivisionCount = $Divisions; $DataRange = $this->VMax - $this->VMin; if ($DataRange == 0) { $DataRange = .1; } $this->DivisionHeight = ($this->GArea_Y2 - $this->GArea_Y1) / $Divisions; $this->DivisionRatio = ($this->GArea_Y2 - $this->GArea_Y1) / $DataRange; $this->GAreaXOffset = 0; if (count ( $Data->getData() ) > 1) { if ($WithMargin == FALSE) $this->DivisionWidth = ($this->GArea_X2 - $this->GArea_X1) / (count ( $Data->getData() ) - 1); else { $this->DivisionWidth = ($this->GArea_X2 - $this->GArea_X1) / (count ( $Data->getData() )); $this->GAreaXOffset = $this->DivisionWidth / 2; } } else { $this->DivisionWidth = $this->GArea_X2 - $this->GArea_X1; $this->GAreaXOffset = $this->DivisionWidth / 2; } $this->DataCount = count ( $Data->getData() ); if ($style->getDrawTicks() == FALSE) return (0); $YPos = $this->GArea_Y2; $XMin = NULL; for($i = 1; $i <= $Divisions + 1; $i ++) { if ($RightScale) $this->canvas->drawLine(new Point($this->GArea_X2, $YPos), new Point($this->GArea_X2 + 5, $YPos), $style->getColor(), $style->getLineWidth(), $style->getLineDotSize(), $this->shadowProperties); else $this->canvas->drawLine(new Point($this->GArea_X1, $YPos), new Point($this->GArea_X1 - 5, $YPos), $style->getColor(), $style->getLineWidth(), $style->getLineDotSize(), $this->shadowProperties); $Value = $this->VMin + ($i - 1) * (($this->VMax - $this->VMin) / $Divisions); $Value = round ( $Value * pow ( 10, $Decimals ) ) / pow ( 10, $Decimals ); $Value = $this->convertValueForDisplay($Value, $Data->getDataDescription()->getYFormat(), $Data->getDataDescription()->getYUnit()); $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); $TextWidth = $Position [2] - $Position [0]; if ($RightScale) { $this->canvas->drawText($this->FontSize, 0, new Point($this->GArea_X2 + 10, $YPos + ($this->FontSize / 2)), $style->getColor(), $this->FontName, $Value, ShadowProperties::NoShadow()); if ($XMin < $this->GArea_X2 + 15 + $TextWidth || $XMin == NULL) { $XMin = $this->GArea_X2 + 15 + $TextWidth; } } else { $this->canvas->drawText($this->FontSize, 0, new Point($this->GArea_X1 - 10 - $TextWidth, $YPos + ($this->FontSize / 2)), $style->getColor(), $this->FontName, $Value, ShadowProperties::NoShadow()); if ($XMin > $this->GArea_X1 - 10 - $TextWidth || $XMin == NULL) { $XMin = $this->GArea_X1 - 10 - $TextWidth; } } $YPos = $YPos - $this->DivisionHeight; } /* Write the Y Axis caption if set */ if ($Data->getDataDescription()->getYAxisName() != '') { $Position = imageftbbox ( $this->FontSize, 90, $this->FontName, $Data->getDataDescription()->getYAxisName() ); $TextHeight = abs ( $Position [1] ) + abs ( $Position [3] ); $TextTop = (($this->GArea_Y2 - $this->GArea_Y1) / 2) + $this->GArea_Y1 + ($TextHeight / 2); if ($RightScale) { $this->canvas->drawText($this->FontSize, 90, new Point($XMin + $this->FontSize, $TextTop), $style->getColor(), $this->FontName, $Data->getDataDescription()->getYAxisName(), ShadowProperties::NoShadow()); } else { $this->canvas->drawText($this->FontSize, 90, new Point($XMin - $this->FontSize, $TextTop), $style->getColor(), $this->FontName, $Data->getDataDescription()->getYAxisName(), ShadowProperties::NoShadow()); } } /* Horizontal Axis */ $XPos = $this->GArea_X1 + $this->GAreaXOffset; $ID = 1; $YMax = NULL; foreach ( $Data->getData() as $Values ) { if ($ID % $SkipLabels == 0) { $this->canvas->drawLine(new Point(floor($XPos), $this->GArea_Y2), new Point(floor($XPos), $this->GArea_Y2 + 5), $style->getColor(), $style->getLineWidth(), $style->getLineDotSize(), $this->shadowProperties); $Value = $Values[$Data->getDataDescription()->getPosition()]; $Value = $this->convertValueForDisplay($Value, $Data->getDataDescription()->getXFormat(), $Data->getDataDescription()->getXUnit()); $Position = imageftbbox ( $this->FontSize, $Angle, $this->FontName, $Value ); $TextWidth = abs ( $Position [2] ) + abs ( $Position [0] ); $TextHeight = abs ( $Position [1] ) + abs ( $Position [3] ); if ($Angle == 0) { $YPos = $this->GArea_Y2 + 18; $this->canvas->drawText($this->FontSize, $Angle, new Point(floor ( $XPos ) - floor ( $TextWidth / 2 ), $YPos), $style->getColor(), $this->FontName, $Value, ShadowProperties::NoShadow()); } else { $YPos = $this->GArea_Y2 + 10 + $TextHeight; if ($Angle <= 90) { $this->canvas->drawText($this->FontSize, $Angle, new Point(floor($XPos) - $TextWidth + 5, $YPos), $style->getColor(), $this->FontName, $Value, ShadowProperties::NoShadow()); } else { $this->canvas->drawText($this->FontSize, $Angle, new Point(floor ( $XPos ) + $TextWidth + 5, $YPos), $style->getColor(), $this->FontName, $Value, ShadowProperties::NoShadow()); } } if ($YMax < $YPos || $YMax == NULL) { $YMax = $YPos; } } $XPos = $XPos + $this->DivisionWidth; $ID ++; } /* Write the X Axis caption if set */ if ($Data->getDataDescription()->getXAxisName() != '') { $Position = imageftbbox ( $this->FontSize, 90, $this->FontName, $Data->getDataDescription()->getXAxisName()); $TextWidth = abs ( $Position [2] ) + abs ( $Position [0] ); $TextLeft = (($this->GArea_X2 - $this->GArea_X1) / 2) + $this->GArea_X1 + ($TextWidth / 2); $this->canvas->drawText($this->FontSize, 0, new Point($TextLeft, $YMax + $this->FontSize + 5), $style->getColor(), $this->FontName, $Data->getDataDescription()->getXAxisName(), ShadowProperties::NoShadow()); } } /** * Calculate the number of divisions that the Y axis will be * divided into. This is a function of the range of Y values the * data covers, as well as the scale style. Divisions should have * some minimum size in screen coordinates in order that the * divisions are clearly visible, so this is also a function of * the graph size in screen coordinates. * * This method returns the number of divisions, but it also has * side-effects on some class data members. This needs to be * refactored to make it clearer what is and isn't affected. */ private function calculateDivisions(pData $Data, ScaleStyle $style) { if (isset ( $Data->getDataDescription()->values[0] )) { /* Pointless temporary is necessary because you can't * directly apply an array index to the return value * of a function in PHP */ $dataArray = $Data->getData(); $this->VMin = $dataArray[0] [$Data->getDataDescription()->values[0]]; $this->VMax = $dataArray[0] [$Data->getDataDescription()->values[0]]; } else { $this->VMin = 2147483647; $this->VMax = - 2147483647; } /* Compute Min and Max values */ if ($style->getScaleMode() == SCALE_NORMAL || $style->getScaleMode() == SCALE_START0) { if ($style->getScaleMode() == SCALE_START0) { $this->VMin = 0; } foreach ( $Data->getData() as $Values ) { foreach ( $Data->getDataDescription()->values as $ColName ) { if (isset ( $Values[$ColName] )) { $Value = $Values[$ColName]; if (is_numeric ( $Value )) { if ($this->VMax < $Value) { $this->VMax = $Value; } if ($this->VMin > $Value) { $this->VMin = $Value; } } } } } } elseif ($style->getScaleMode() == SCALE_ADDALL || $style->getScaleMode() == SCALE_ADDALLSTART0 ) /* Experimental */ { if ($style->getScaleMode() == SCALE_ADDALLSTART0) { $this->VMin = 0; } foreach ( $Data->getData() as $Values ) { $Sum = 0; foreach ( $Data->getDataDescription()->values as $ColName ) { $dataArray = $Data->getData(); if (isset ( $Values[$ColName] )) { $Value = $Values[$ColName]; if (is_numeric ( $Value )) $Sum += $Value; } } if ($this->VMax < $Sum) { $this->VMax = $Sum; } if ($this->VMin > $Sum) { $this->VMin = $Sum; } } } $this->VMax = ceil($this->VMax); /* If all values are the same */ if ($this->VMax == $this->VMin) { if ($this->VMax >= 0) { $this->VMax ++; } else { $this->VMin --; } } $DataRange = $this->VMax - $this->VMin; if ($DataRange == 0) { $DataRange = .1; } $this->calculateScales($Scale, $Divisions); if (! isset ( $Divisions )) $Divisions = 2; if ($Scale == 1 && $Divisions % 2 == 1) $Divisions --; return $Divisions; } /** * Compute and draw the scale for X/Y charts */ function drawXYScale(pData $Data, ScaleStyle $style, $YSerieName, $XSerieName, $Angle = 0, $Decimals = 1) { /* Validate the Data and DataDescription array */ $this->validateData ( "drawScale", $Data->getData()); $this->canvas->drawLine(new Point($this->GArea_X1, $this->GArea_Y1), new Point($this->GArea_X1, $this->GArea_Y2), $style->getColor(), $style->getLineWidth(), $style->getLineDotSize(), $this->shadowProperties); $this->canvas->drawLine(new Point($this->GArea_X1, $this->GArea_Y2), new Point($this->GArea_X2, $this->GArea_Y2), $style->getColor(), $style->getLineWidth(), $style->getLineDotSize(), $this->shadowProperties); /* Process Y scale */ if ($this->VMin == NULL && $this->VMax == NULL) { $this->VMin = $Data->getSeriesMin($YSerieName); $this->VMax = $Data->getSeriesMax($YSerieName); /** @todo The use of ceil() here is questionable if all * the values are much less than 1, AIUI */ $this->VMax = ceil($this->VMax); $DataRange = $this->VMax - $this->VMin; if ($DataRange == 0) { $DataRange = .1; } self::computeAutomaticScaling($this->GArea_Y1, $this->GArea_Y2, $this->VMin, $this->VMax, $Divisions); } else $Divisions = $this->Divisions; $this->DivisionCount = $Divisions; $DataRange = $this->VMax - $this->VMin; if ($DataRange == 0) { $DataRange = .1; } $this->DivisionHeight = ($this->GArea_Y2 - $this->GArea_Y1) / $Divisions; $this->DivisionRatio = ($this->GArea_Y2 - $this->GArea_Y1) / $DataRange; $YPos = $this->GArea_Y2; $XMin = NULL; for($i = 1; $i <= $Divisions + 1; $i ++) { $this->canvas->drawLine(new Point($this->GArea_X1, $YPos), new Point($this->GArea_X1 - 5, $YPos), $style->getColor(), $style->getLineWidth(), $style->getLineDotSize(), $this->shadowProperties); $Value = $this->VMin + ($i - 1) * (($this->VMax - $this->VMin) / $Divisions); $Value = round ( $Value * pow ( 10, $Decimals ) ) / pow ( 10, $Decimals ); $Value = $this->convertValueForDisplay($Value, $Data->getDataDescription()->getYFormat(), $Data->getDataDescription()->getYUnit()); $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); $TextWidth = $Position [2] - $Position [0]; $this->canvas->drawText($this->FontSize, 0, new Point($this->GArea_X1 - 10 - $TextWidth, $YPos + ($this->FontSize / 2)), $style->getColor(), $this->FontName, $Value, $this->shadowProperties); if ($XMin > $this->GArea_X1 - 10 - $TextWidth || $XMin == NULL) { $XMin = $this->GArea_X1 - 10 - $TextWidth; } $YPos = $YPos - $this->DivisionHeight; } /* Process X scale */ if ($this->VXMin == NULL && $this->VXMax == NULL) { $this->VXMax = $Data->getSeriesMax($XSerieName); $this->VXMin = $Data->getSeriesMin($XSerieName); $this->VXMax = ceil($this->VXMax); $DataRange = $this->VMax - $this->VMin; if ($DataRange == 0) { $DataRange = .1; } /* Compute automatic scaling */ self::computeAutomaticScaling($this->GArea_X1, $this->GArea_X2, $this->VXMin, $this->VXMax, $XDivisions); } else $XDivisions = $this->XDivisions; $this->XDivisionCount = $Divisions; $this->DataCount = $Divisions + 2; $XDataRange = $this->VXMax - $this->VXMin; if ($XDataRange == 0) { $XDataRange = .1; } $this->DivisionWidth = ($this->GArea_X2 - $this->GArea_X1) / $XDivisions; $this->XDivisionRatio = ($this->GArea_X2 - $this->GArea_X1) / $XDataRange; $XPos = $this->GArea_X1; $YMax = NULL; for($i = 1; $i <= $XDivisions + 1; $i ++) { $this->canvas->drawLine(new Point($XPos, $this->GArea_Y2), new Point($XPos, $this->GArea_Y2 + 5), $style->getColor(), $style->getLineWidth(), $style->getLineDotSize(), $this->shadowProperties); $Value = $this->VXMin + ($i - 1) * (($this->VXMax - $this->VXMin) / $XDivisions); $Value = round ( $Value * pow ( 10, $Decimals ) ) / pow ( 10, $Decimals ); $Value = $this->convertValueForDisplay($Value, $Data->getDataDescription()->getYFormat(), $Data->getDataDescription()->getYUnit()); $Position = imageftbbox ( $this->FontSize, $Angle, $this->FontName, $Value ); $TextWidth = abs ( $Position [2] ) + abs ( $Position [0] ); $TextHeight = abs ( $Position [1] ) + abs ( $Position [3] ); if ($Angle == 0) { $YPos = $this->GArea_Y2 + 18; $this->canvas->drawText($this->FontSize, $Angle, new Point(floor ( $XPos ) - floor ( $TextWidth / 2 ), $YPos), $style->getColor(), $this->FontName, $Value, $this->shadowProperties); } else { $YPos = $this->GArea_Y2 + 10 + $TextHeight; if ($Angle <= 90) { $this->canvas->drawText($this->FontSize, $Angle, new Point(floor ( $XPos ) - $TextWidth + 5, $YPos), $style->getColor(), $this->FontName, $Value, $this->shadowProperties); } else { $this->canvas->drawText($this->FontSize, $Angle, new Point(floor ( $XPos ) + $TextWidth + 5, $YPos), $style->getColor(), $this->FontName, $Value, $this->shadowProperties); } } if ($YMax < $YPos || $YMax == NULL) { $YMax = $YPos; } $XPos = $XPos + $this->DivisionWidth; } /* Write the Y Axis caption if set */ if ($Data->getDataDescription()->getYAxisName() != '') { $Position = imageftbbox ( $this->FontSize, 90, $this->FontName, $Data->getDataDescription()->getYAxisName()); $TextHeight = abs ( $Position [1] ) + abs ( $Position [3] ); $TextTop = (($this->GArea_Y2 - $this->GArea_Y1) / 2) + $this->GArea_Y1 + ($TextHeight / 2); $this->canvas->drawText($this->FontSize, 90, new Point($XMin - $this->FontSize, $TextTop), $style->getColor(), $this->FontName, $Data->getDataDescription()->getYAxisName(), $this->shadowProperties); } /* Write the X Axis caption if set */ $this->writeScaleXAxisCaption($Data, $style, $YMax); } private function drawGridMosaic(GridStyle $style, $divisionCount, $divisionHeight) { $LayerHeight = $this->GArea_Y2 - $this->GArea_Y1; $YPos = $LayerHeight - 1; //$this->GArea_Y2-1; $LastY = $YPos; for($i = 0; $i < $divisionCount; $i ++) { $LastY = $YPos; $YPos = $YPos - $divisionHeight; if ($YPos <= 0) { $YPos = 1; } if ($i % 2 == 0) { $this->canvas->drawFilledRectangle(new Point($this->GArea_X1 + 1, $this->GArea_Y1 + $YPos), new Point($this->GArea_X2 - 1, $this->GArea_Y1 + $LastY), new Color(250, 250, 250), ShadowProperties::NoShadow(), false, $style->getAlpha()); } } } /** * Write the X Axis caption on the scale, if set */ private function writeScaleXAxisCaption(pData $data, ScaleStyle $style, $YMax) { if ($data->getDataDescription()->getXAxisName() != '') { $Position = imageftbbox ( $this->FontSize, 90, $this->FontName, $data->getDataDescription()->getXAxisName()); $TextWidth = abs ( $Position [2] ) + abs ( $Position [0] ); $TextLeft = (($this->GArea_X2 - $this->GArea_X1) / 2) + $this->GArea_X1 + ($TextWidth / 2); $this->canvas->drawText($this->FontSize, 0, new Point($TextLeft, $YMax + $this->FontSize + 5), $style->getColor(), $this->FontName, $data->getDataDescription()->getXAxisName(), $this->shadowProperties); } } /** * Compute and draw the scale */ function drawGrid(GridStyle $style) { /* Draw mosaic */ if ($style->getMosaic()) { $this->drawGridMosaic($style, $this->DivisionCount, $this->DivisionHeight); } /* Horizontal lines */ $YPos = $this->GArea_Y2 - $this->DivisionHeight; for($i = 1; $i <= $this->DivisionCount; $i ++) { if ($YPos > $this->GArea_Y1 && $YPos < $this->GArea_Y2) $this->canvas->drawDottedLine(new Point($this->GArea_X1, $YPos), new Point($this->GArea_X2, $YPos), $style->getLineWidth(), $this->LineWidth, $style->getColor(), ShadowProperties::NoShadow()); /** @todo There's some inconsistency here. The parameter * $lineWidth appears to be used to control the dot size, * not the line width? This is the same way it's always * been done, although now it's more obvious that there's * a problem. */ $YPos = $YPos - $this->DivisionHeight; } /* Vertical lines */ if ($this->GAreaXOffset == 0) { $XPos = $this->GArea_X1 + $this->DivisionWidth + $this->GAreaXOffset; $ColCount = $this->DataCount - 2; } else { $XPos = $this->GArea_X1 + $this->GAreaXOffset; $ColCount = floor ( ($this->GArea_X2 - $this->GArea_X1) / $this->DivisionWidth ); } for($i = 1; $i <= $ColCount; $i ++) { if ($XPos > $this->GArea_X1 && $XPos < $this->GArea_X2) $this->canvas->drawDottedLine(new Point(floor($XPos), $this->GArea_Y1), new Point(floor($XPos), $this->GArea_Y2), $style->getLineWidth(), $this->LineWidth, $style->getcolor(), $this->shadowProperties); $XPos = $XPos + $this->DivisionWidth; } } /** * retrieve the legends size */ public function getLegendBoxSize($DataDescription) { if (! isset ( $DataDescription->description)) return (- 1); /* <-10->[8]<-4->Text<-10-> */ $MaxWidth = 0; $MaxHeight = 8; foreach ( $DataDescription->description as $Value ) { $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); $TextWidth = $Position [2] - $Position [0]; $TextHeight = $Position [1] - $Position [7]; if ($TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; } $MaxHeight = $MaxHeight + $TextHeight + 4; } $MaxHeight = $MaxHeight - 3; $MaxWidth = $MaxWidth + 32; return (array ($MaxWidth, $MaxHeight )); } /** * Draw the data legends */ public function drawLegend($XPos, $YPos, $DataDescription, Color $color, Color $color2 = null, Color $color3 = null, $Border = TRUE) { if ($color2 == null) { $color2 = $color->addRGBIncrement(-30); } if ($color3 == null) { $color3 = new Color(0, 0, 0); } /* Validate the Data and DataDescription array */ $this->validateDataDescription("drawLegend", $DataDescription); if (! isset ( $DataDescription->description)) return (- 1); /* <-10->[8]<-4->Text<-10-> */ $MaxWidth = 0; $MaxHeight = 8; foreach ( $DataDescription->description as $Key => $Value ) { $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); $TextWidth = $Position [2] - $Position [0]; $TextHeight = $Position [1] - $Position [7]; if ($TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; } $MaxHeight = $MaxHeight + $TextHeight + 4; } $MaxHeight = $MaxHeight - 5; $MaxWidth = $MaxWidth + 32; if ($Border) { $this->canvas->drawFilledRoundedRectangle(new Point($XPos + 1, $YPos + 1), new Point($XPos + $MaxWidth + 1, $YPos + $MaxHeight + 1), 5, $color2, $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $this->canvas->drawFilledRoundedRectangle(new Point($XPos, $YPos), new Point($XPos + $MaxWidth, $YPos + $MaxHeight), 5, $color, $this->LineWidth, $this->LineDotSize, $this->shadowProperties); } $YOffset = 4 + $this->FontSize; $ID = 0; foreach ( $DataDescription->description as $Key => $Value ) { $this->canvas->drawFilledRoundedRectangle(new Point($XPos + 10, $YPos + $YOffset - 4), new Point($XPos + 14, $YPos + $YOffset - 4), 2, $this->palette->colors[$ID], $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $this->canvas->drawText($this->FontSize, 0, new Point($XPos + 22, $YPos + $YOffset), $color3, $this->FontName, $Value, $this->shadowProperties); $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); $TextHeight = $Position [1] - $Position [7]; $YOffset = $YOffset + $TextHeight + 4; $ID ++; } } /** * Draw the graph title * * @todo Should we pass in a ShadowProperties object here? Or is * this a public function? */ public function drawTitle($XPos, $YPos, $Value, Color $color, $XPos2 = -1, $YPos2 = -1, ShadowProperties $shadowProperties = null) { if ($shadowProperties == null) { $shadowProperties = ShadowProperties::NoShadow(); } if ($XPos2 != - 1) { $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); $TextWidth = $Position [2] - $Position [0]; $XPos = floor ( ($XPos2 - $XPos - $TextWidth) / 2 ) + $XPos; } if ($YPos2 != - 1) { $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Value ); $TextHeight = $Position [5] - $Position [3]; $YPos = floor ( ($YPos2 - $YPos - $TextHeight) / 2 ) + $YPos; } $this->canvas->drawText($this->FontSize, 0, new Point($XPos, $YPos), $color, $this->FontName, $Value, $shadowProperties); } /** * Draw a text box with text align & alpha properties * * @param $point1 Minimum corner of the box * @param $point2 Maximum corner of the box * * @todo This should probably be a method on the ICanvas * interface, since it doesn't have anything specifically to do * with graphs */ public function drawTextBox(Point $point1, Point $point2, $Text, $Angle = 0, Color $color = null, $Align = ALIGN_LEFT, ShadowProperties $shadowProperties = null, Color $backgroundColor = null, $Alpha = 100) { if ($color == null) { $color = new Color(255, 255, 255); } if ($shadowProperties == null) { $shadowProperties = ShadowProperties::NoShadow(); } $Position = imageftbbox ( $this->FontSize, $Angle, $this->FontName, $Text ); $TextWidth = $Position [2] - $Position [0]; $TextHeight = $Position [5] - $Position [3]; $AreaWidth = $point2->getX() - $point1->getX(); $AreaHeight = $point2->getY() - $point1->getY(); if ($backgroundColor != null) $this->canvas->drawFilledRectangle($point1, $point2, $backgroundColor, $shadowProperties, FALSE, $Alpha ); if ($Align == ALIGN_TOP_LEFT) { $newPosition = $point1->addIncrement(1, $this->FontSize + 1); } if ($Align == ALIGN_TOP_CENTER) { $newPosition = $point1->addIncrement(($AreaWidth / 2) - ($TextWidth / 2), $this->FontSize + 1); } if ($Align == ALIGN_TOP_RIGHT) { $newPosition = new Point($point2->getX() - $TextWidth - 1, $point1->getY() + $this->FontSize + 1); } if ($Align == ALIGN_LEFT) { $newPosition = $point1->addIncrement(1, ($AreaHeight / 2) - ($TextHeight / 2)); } if ($Align == ALIGN_CENTER) { $newPosition = $point1->addIncrement(($AreaWidth / 2) - ($TextWidth / 2), ($AreaHeight / 2) - ($TextHeight / 2)); } if ($Align == ALIGN_RIGHT) { $newPosition = new Point($point2->getX() - $TextWidth - 1, $point1->getY() + ($AreaHeight / 2) - ($TextHeight / 2)); } if ($Align == ALIGN_BOTTOM_LEFT) { $newPosition = new Point($point1->getX() + 1, $point2->getY() - 1); } if ($Align == ALIGN_BOTTOM_CENTER) { $newPosition = new Point($point1->getX() + ($AreaWidth / 2) - ($TextWidth / 2), $point2->getY() - 1); } if ($Align == ALIGN_BOTTOM_RIGHT) { $newPosition = $point2->addIncrement(- $TextWidth - 1, -1); } $this->canvas->drawText($this->FontSize, $Angle, $newPosition, $color, $this->FontName, $Text, $shadowProperties); } /** * Compute and draw the scale * * @todo What is the method name a typo for? Threshold? */ function drawTreshold($Value, Color $color, $ShowLabel = FALSE, $ShowOnRight = FALSE, $TickWidth = 4, $FreeText = NULL) { $Y = $this->GArea_Y2 - ($Value - $this->VMin) * $this->DivisionRatio; if ($Y <= $this->GArea_Y1 || $Y >= $this->GArea_Y2) return (- 1); if ($TickWidth == 0) $this->canvas->drawLine(new Point($this->GArea_X1, $Y), new Point($this->GArea_X2, $Y), $color, $this->LineWidth, $this->LineDotSize, $this->shadowProperties); else $this->canvas->drawDottedLine(new Point($this->GArea_X1, $Y), new Point($this->GArea_X2, $Y), $TickWidth, $this->LineWidth, $color, $this->shadowProperties); if ($ShowLabel) { if ($FreeText == NULL) { $Label = $Value; } else { $Label = $FreeText; } if ($ShowOnRight) { $position = new Point($this->GArea_X2 + 2, $Y + ($this->FontSize/ 2)); } else { $position = new Point($this->GArea_X1 + 2, $Y - ($this->FontSize / 2)); } $this->canvas->drawText($this->FontSize, 0, $position, $color, $this->FontName, $Label, ShadowProperties::NoShadow()); } } /** * This function put a label on a specific point */ function setLabel($Data, $DataDescription, $SerieName, $ValueName, $Caption, Color $color = null) { if ($color == null) { $color = new Color(210, 210, 210); } /* Validate the Data and DataDescription array */ $this->validateDataDescription ( "setLabel", $DataDescription ); $this->validateData ( "setLabel", $Data ); $ShadowFactor = 100; $Cp = 0; $Found = FALSE; foreach ( $Data as $Value ) { if ($Value[$DataDescription->getPosition()] == $ValueName) { $NumericalValue = $Value[$SerieName]; $Found = TRUE; } if (! $Found) $Cp ++; } $XPos = $this->GArea_X1 + $this->GAreaXOffset + ($this->DivisionWidth * $Cp) + 2; $YPos = $this->GArea_Y2 - ($NumericalValue - $this->VMin) * $this->DivisionRatio; $Position = imageftbbox ( $this->FontSize, 0, $this->FontName, $Caption ); $TextHeight = $Position [3] - $Position [5]; $TextWidth = $Position [2] - $Position [0] + 2; $TextOffset = floor ( $TextHeight / 2 ); // Shadow $Poly = array ($XPos + 1, $YPos + 1, $XPos + 9, $YPos - $TextOffset, $XPos + 8, $YPos + $TextOffset + 2 ); $this->canvas->drawFilledPolygon($Poly, 3, $color->addRGBIncrement(-$ShadowFactor)); $this->canvas->drawLine(new Point($XPos, $YPos + 1), new Point($XPos + 9, $YPos - $TextOffset - .2), $color->addRGBIncrement(-$ShadowFactor), $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $this->canvas->drawLine(new Point($XPos, $YPos + 1), new Point($XPos + 9, $YPos + $TextOffset + 2.2), $color->addRGBIncrement(-$ShadowFactor), $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $this->canvas->drawFilledRectangle(new Point($XPos + 9, $YPos - $TextOffset - .2), new Point($XPos + 13 + $TextWidth, $YPos + $TextOffset + 2.2), $color->addRGBIncrement(-$ShadowFactor), $this->shadowProperties); // Label background $Poly = array ($XPos, $YPos, $XPos + 8, $YPos - $TextOffset - 1, $XPos + 8, $YPos + $TextOffset + 1 ); $this->canvas->drawFilledPolygon($Poly, 3, $color); /** @todo We draw exactly the same line twice, with the same settings. * Surely this is pointless? */ $this->canvas->drawLine(new Point($XPos - 1, $YPos), new Point($XPos + 8, $YPos - $TextOffset - 1.2), $color, $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $this->canvas->drawLine(new Point($XPos - 1, $YPos), new Point($XPos + 8, $YPos + $TextOffset + 1.2), $color, $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $this->canvas->drawFilledRectangle(new Point($XPos + 8, $YPos - $TextOffset - 1.2), new Point($XPos + 12 + $TextWidth, $YPos + $TextOffset + 1.2), $color, $this->shadowProperties); $this->canvas->drawText($this->FontSize, 0, new Point($XPos + 10, $YPos + $TextOffset), new Color(0, 0, 0), $this->FontName, $Caption, ShadowProperties::NoShadow()); } /** * This function draw a plot graph */ function drawPlotGraph($Data, $DataDescription, $BigRadius = 5, $SmallRadius = 2, Color $color2 = null, $Shadow = FALSE) { /* Validate the Data and DataDescription array */ $this->validateDataDescription ( "drawPlotGraph", $DataDescription ); $this->validateData ( "drawPlotGraph", $Data ); $GraphID = 0; $colorO = $color2; foreach ( $DataDescription->values as $ColName ) { $ColorID = $DataDescription->getColumnIndex($ColName); $color = $this->palette->colors[$ColorID]; $color2 = $colorO; if (isset ( $DataDescription->seriesSymbols[$ColName] )) { $Infos = getimagesize ( $DataDescription->seriesSymbols[$ColName] ); $ImageWidth = $Infos [0]; $ImageHeight = $Infos [1]; $Symbol = imagecreatefromgif ( $DataDescription->seriesSymbols[$ColName] ); } $XPos = $this->GArea_X1 + $this->GAreaXOffset; $Hsize = round ( $BigRadius / 2 ); $color3 = null; foreach ( $Data as $Values ) { $Value = $Values[$ColName]; $YPos = $this->GArea_Y2 - (($Value - $this->VMin) * $this->DivisionRatio); /* Save point into the image map if option activated */ if ($this->BuildMap) $this->addToImageMap ( $XPos - $Hsize, $YPos - $Hsize, $XPos + 1 + $Hsize, $YPos + $Hsize + 1, $DataDescription->description[$ColName], $Values[$ColName] . $DataDescription->getYUnit(), "Plot" ); if (is_numeric ( $Value )) { if (! isset ( $DataDescription->seriesSymbols[$ColName] )) { if ($Shadow) { if ($color3 != null) { $this->canvas->drawFilledCircle(new Point($XPos + 2, $YPos + 2), $BigRadius, $color3, $this->shadowProperties); } else { $color3 = $this->palette->colors[$ColorID]->addRGBIncrement(-20); $this->canvas->drawFilledCircle(new Point($XPos + 2, $YPos + 2), $BigRadius, $color3, $this->shadowProperties); } } $this->canvas->drawFilledCircle(new Point($XPos + 1, $YPos + 1), $BigRadius, $color, $this->shadowProperties); if ($SmallRadius != 0) { if ($color2 != null) { $this->canvas->drawFilledCircle(new Point($XPos + 1, $YPos + 1), $SmallRadius, $color2, $this->shadowProperties); } else { $color2 = $this->palette->colors[$ColorID]->addRGBIncrement(-15); $this->canvas->drawFilledCircle(new Point($XPos + 1, $YPos + 1), $SmallRadius, $color2, $this->shadowProperties); } } } else { imagecopymerge ( $this->canvas->getPicture(), $Symbol, $XPos + 1 - $ImageWidth / 2, $YPos + 1 - $ImageHeight / 2, 0, 0, $ImageWidth, $ImageHeight, 100 ); } } $XPos = $XPos + $this->DivisionWidth; } $GraphID ++; } } /** * Linearly Scale a given value * * using it's own minima/maxima and the desired output minima/maxima */ function linearScale($value, $istart, $istop, $ostart, $ostop){ $div = ($istop - $istart); if($div == 0.0) $div = 1; return $ostart + ($ostop - $ostart) * (($value - $istart) / $div); } /** * @brief This function draw a plot graph in an X/Y space */ function drawXYPlotGraph(pData $DataSet, $YSerieName, $XSerieName, $PaletteID = 0, $BigRadius = 5, $SmallRadius = 2, Color $color2 = null, $Shadow = TRUE, $SizeSerieName = '') { $color = $this->palette->colors[$PaletteID]; $color3 = null; $Data = $DataSet->getData(); foreach ( $Data as $Values ) { if (isset($Values[$YSerieName]) && isset ($Values[$XSerieName])) { $X = $Values[$XSerieName]; $Y = $Values[$YSerieName]; $Y = $this->GArea_Y2 - (($Y - $this->VMin) * $this->DivisionRatio); $X = $this->GArea_X1 + (($X - $this->VXMin) * $this->XDivisionRatio); if(isset($Values[$SizeSerieName])){ $br = $this->linearScale( $Values[$SizeSerieName], $DataSet->getSeriesMin($SizeSerieName), $DataSet->getSeriesMax($SizeSerieName), $SmallRadius, $BigRadius ); $sr = $br; }else{ $br = $BigRadius; $sr = $SmallRadius; } if ($Shadow) { if ($color3 != null) { $this->canvas->drawFilledCircle(new Point($X + 2, $Y + 2), $br, $color3, $this->shadowProperties); } else { $color3 = $this->palette->colors[$PaletteID]->addRGBIncrement(-20); $this->canvas->drawFilledCircle(new Point($X + 2, $Y + 2), $br, $color3, $this->shadowProperties); } } $this->canvas->drawFilledCircle(new Point($X + 1, $Y + 1), $br, $color, $this->shadowProperties); if ($color2 != null) { $this->canvas->drawFilledCircle(new Point($X + 1, $Y + 1), $sr, $color2, $this->shadowProperties); } else { $color2 = $this->palette->colors[$PaletteID]->addRGBIncrement(20); $this->canvas->drawFilledCircle(new Point($X + 1, $Y + 1), $sr, $color2, $this->shadowProperties); } } } } /** * This function draw an area between two series */ function drawArea($Data, $Serie1, $Serie2, Color $color, $Alpha = 50) { /* Validate the Data and DataDescription array */ $this->validateData ( "drawArea", $Data ); $LayerHeight = $this->GArea_Y2 - $this->GArea_Y1; $XPos = $this->GAreaXOffset; $LastXPos = - 1; foreach ( $Data as $Values ) { $Value1 = $Values[$Serie1]; $Value2 = $Values[$Serie2]; $YPos1 = $LayerHeight - (($Value1 - $this->VMin) * $this->DivisionRatio); $YPos2 = $LayerHeight - (($Value2 - $this->VMin) * $this->DivisionRatio); if ($LastXPos != - 1) { $Points = array(); $Points [] = $LastXPos + $this->GArea_X1; $Points [] = $LastYPos1 + $this->GArea_Y1; $Points [] = $LastXPos + $this->GArea_X1; $Points [] = $LastYPos2 + $this->GArea_Y1; $Points [] = $XPos + $this->GArea_X1; $Points [] = $YPos2 + $this->GArea_Y1; $Points [] = $XPos + $this->GArea_X1; $Points [] = $YPos1 + $this->GArea_Y1; $this->canvas->drawFilledPolygon($Points, 4, $color, $Alpha); } $LastYPos1 = $YPos1; $LastYPos2 = $YPos2; $LastXPos = $XPos; $XPos = $XPos + $this->DivisionWidth; } } /** * This function write the values of the specified series */ function writeValues($Data, $DataDescription, $Series) { /* Validate the Data and DataDescription array */ $this->validateDataDescription ( "writeValues", $DataDescription ); $this->validateData ( "writeValues", $Data ); if (! is_array ( $Series )) { $Series = array ($Series ); } foreach ( $Series as $Serie ) { $ColorID = $DataDescription->getColumnIndex($Serie); $XPos = $this->GArea_X1 + $this->GAreaXOffset; foreach ( $Data as $Values ) { if (isset ( $Values[$Serie] ) && is_numeric ( $Values[$Serie] )) { $Value = $Values[$Serie]; $YPos = $this->GArea_Y2 - (($Value - $this->VMin) * $this->DivisionRatio); $Positions = imagettfbbox ( $this->FontSize, 0, $this->FontName, $Value ); $Width = $Positions [2] - $Positions [6]; $XOffset = $XPos - ($Width / 2); $YOffset = $YPos - 4; $this->canvas->drawText($this->FontSize, 0, new Point($XOffset, $YOffset), $this->palette->colors[$ColorID], $this->FontName, $Value, ShadowProperties::NoShadow()); } $XPos = $XPos + $this->DivisionWidth; } } } /** * @brief Draws a line graph where the data gives Y values for a * series of regular positions along the X axis */ function drawLineGraph($Data, $DataDescription, $SerieName = "") { /* Validate the Data and DataDescription array */ $this->validateDataDescription ( "drawLineGraph", $DataDescription ); $this->validateData ( "drawLineGraph", $Data ); $GraphID = 0; foreach ( $DataDescription->values as $ColName ) { $ColorID = $DataDescription->getColumnIndex($ColName); if ($SerieName == "" || $SerieName == $ColName) { $XPos = $this->GArea_X1 + $this->GAreaXOffset; $XLast = - 1; foreach ( $Data as $Values ) { if (isset ( $Values[$ColName] )) { $Value = $Values[$ColName]; $YPos = $this->GArea_Y2 - (($Value - $this->VMin) * $this->DivisionRatio); /* Save point into the image map if option activated */ if ($this->BuildMap) $this->addToImageMap ( $XPos - 3, $YPos - 3, $XPos + 3, $YPos + 3, $DataDescription->description[$ColName], $Values[$ColName] . $DataDescription->getYUnit(), "Line" ); if (! is_numeric ( $Value )) { $XLast = - 1; } if ($XLast != - 1) $this->canvas->drawLine(new Point($XLast, $YLast), new Point($XPos, $YPos), $this->palette->colors[$ColorID], $this->LineWidth, $this->LineDotSize, $this->shadowProperties, new Point($this->GArea_X1, $this->GArea_Y1), new Point($this->GArea_X2, $this->GArea_Y2)); $XLast = $XPos; $YLast = $YPos; if (! is_numeric ( $Value )) { $XLast = - 1; } } $XPos = $XPos + $this->DivisionWidth; } $GraphID ++; } } } /** * @brief Draws a line graph where one series of data defines the * X position and another the Y position */ function drawXYGraph($Data, $YSerieName, $XSerieName, $PaletteID = 0) { $graphAreaMin = new Point($this->GArea_X1, $this->GArea_Y1); $graphAreaMax = new Point($this->GArea_X2, $this->GArea_Y2); $lastPoint = null; foreach ($Data as $Values) { if (isset ( $Values[$YSerieName] ) && isset ( $Values[$XSerieName] )) { $X = $Values[$XSerieName]; $Y = $Values[$YSerieName]; $Y = $this->GArea_Y2 - (($Y - $this->VMin) * $this->DivisionRatio); $X = $this->GArea_X1 + (($X - $this->VXMin) * $this->XDivisionRatio); $currentPoint = new Point($X, $Y); if ($lastPoint != null) { $this->canvas->drawLine($lastPoint, $currentPoint, $this->palette->colors[$PaletteID], $this->LineWidth, $this->LineDotSize, $this->shadowProperties, $graphAreaMin, $graphAreaMax); } $lastPoint = $currentPoint; } } } /** * This function draw a cubic curve */ function drawCubicCurve(pData $data, $Accuracy = .1, $SerieName = "") { /* Validate the Data and DataDescription array */ $this->validateDataDescription("drawCubicCurve", $data->getDataDescription()); $this->validateData ( "drawCubicCurve", $data->getData() ); $graphAreaMin = new Point($this->GArea_X1, $this->GArea_Y1); $graphAreaMax = new Point($this->GArea_X2, $this->GArea_Y2); $GraphID = 0; foreach ( $data->getDataDescription()->values as $ColName ) { if ($SerieName == "" || $SerieName == $ColName) { /** @todo The next section of code has been duplicated by * copy & paste */ $XIn = array(); $YIn = array(); $Yt = ""; $U = ""; $ColorID = $data->getDataDescription()->getColumnIndex($ColName); $Index = 1; $XLast = - 1; $Missing = array(); $data->getXYMap($ColName, $XIn, $YIn, $Missing, $Index); assert(count($XIn) == count($YIn)); assert($Index + 1 >= count($XIn)); $Yt [0] = 0; $Yt [1] = 0; $U [1] = 0; $this->calculateCubicCurve($Yt, $XIn, $YIn, $U, $Index); $Yt [$Index] = 0; for($k = $Index - 1; $k >= 1; $k --) $Yt [$k] = $Yt [$k] * $Yt [$k + 1] + $U [$k]; $XPos = $this->GArea_X1 + $this->GAreaXOffset; for($X = 1; $X <= $Index; $X = $X + $Accuracy) { /* I believe here we're searching for the integral * value k such that $X lies between $XIn[k] and * $XIn[k-1] */ $klo = 1; $khi = $Index; $k = $khi - $klo; while ( $k > 1 ) { $k = $khi - $klo; If ($XIn [$k] >= $X) $khi = $k; else $klo = $k; } $klo = $khi - 1; /* These assertions are to check my understanding * of the code. If they fail, it is my fault and * not a bug */ assert($khi = $klo + 1); assert($XIn[$klo] < $X); assert($X <= $XIn[$khi]); $h = $XIn [$khi] - $XIn [$klo]; $a = ($XIn [$khi] - $X) / $h; $b = ($X - $XIn [$klo]) / $h; /** * I believe this is the actual cubic Bezier * calculation. In parametric form: * * B(t) = (1-t)^3 * B0 * + 3 (1-t)^2 * t * B1 * + 3 (1-t) * t^2 * B2 * + t^3 B3 */ $Value = $a * $YIn [$klo] + $b * $YIn [$khi] + (($a * $a * $a - $a) * $Yt [$klo] + ($b * $b * $b - $b) * $Yt [$khi]) * ($h * $h) / 6; $YPos = $this->GArea_Y2 - (($Value - $this->VMin) * $this->DivisionRatio); if ($XLast != - 1 && ! isset ( $Missing [floor ( $X )] ) && ! isset ( $Missing [floor ( $X + 1 )] )) $this->canvas->drawLine(new Point($XLast, $YLast), new Point($XPos, $YPos), $this->palette->colors[$ColorID], $this->LineWidth, $this->LineDotSize, $this->shadowProperties, $graphAreaMin, $graphAreaMax); $XLast = $XPos; $YLast = $YPos; $XPos = $XPos + $this->DivisionWidth * $Accuracy; } // Add potentialy missing values $XPos = $XPos - $this->DivisionWidth * $Accuracy; if ($XPos < ($this->GArea_X2 - $this->GAreaXOffset)) { $YPos = $this->GArea_Y2 - (($YIn [$Index] - $this->VMin) * $this->DivisionRatio); $this->canvas->drawLine(new Point($XLast, $YLast), new Point($this->GArea_X2 - $this->GAreaXOffset, $YPos), $this->palette->colors[$ColorID], $this->LineWidth, $this->LineDotSize, $this->shadowProperties, $graphAreaMin, $graphAreaMax); } $GraphID ++; } } } /** * @todo I haven't figured out exactly what this bit of code does, * it's just an attempt to reduce code duplication */ private function calculateCubicCurve(array & $Yt, array $XIn, array $YIn, array & $U, $Index) { for($i = 2; $i <= $Index - 1; $i ++) { /* Typically $Sig will be 0.5, since each X value will be * one unit past the last. If there is missing data then * this ratio will change */ $Sig = ($XIn [$i] - $XIn [$i - 1]) / ($XIn [$i + 1] - $XIn [$i - 1]); $p = $Sig * $Yt [$i - 1] + 2; /* This Y value will nearly always be negative, thanks to * $Sig being 0.5 */ $Yt [$i] = ($Sig - 1) / $p; /** @todo No idea what the following code is doing */ $U [$i] = ($YIn [$i + 1] - $YIn [$i]) / ($XIn [$i + 1] - $XIn [$i]) - ($YIn [$i] - $YIn [$i - 1]) / ($XIn [$i] - $XIn [$i - 1]); $U [$i] = (6 * $U [$i] / ($XIn [$i + 1] - $XIn [$i - 1]) - $Sig * $U [$i - 1]) / $p; } } /** * This function draw a filled cubic curve */ function drawFilledCubicCurve(pData $data, $Accuracy = .1, $Alpha = 100, $AroundZero = FALSE) { /* Validate the Data and DataDescription array */ $this->validateDataDescription("drawFilledCubicCurve", $data->getDataDescription()); $this->validateData ( "drawFilledCubicCurve", $data->getData() ); $LayerWidth = $this->GArea_X2 - $this->GArea_X1; $LayerHeight = $this->GArea_Y2 - $this->GArea_Y1; $YZero = $LayerHeight - ((0 - $this->VMin) * $this->DivisionRatio); if ($YZero > $LayerHeight) { $YZero = $LayerHeight; } $GraphID = 0; foreach ($data->getDataDescription()->values as $ColName) { $XIn = array(); $YIn = array(); $Yt = array(); $U = array(); $ColorID = $data->getDataDescription()->getColumnIndex($ColName); $numElements = 1; $XLast = - 1; $Missing = array(); $data->getXYMap($ColName, $XIn, $YIn, $Missing, $numElements); $Yt [0] = 0; $Yt [1] = 0; $U [1] = 0; $this->calculateCubicCurve($Yt, $XIn, $YIn, $U, $numElements); $Yt [$numElements] = 0; for($k = $numElements - 1; $k >= 1; $k --) $Yt [$k] = $Yt [$k] * $Yt [$k + 1] + $U [$k]; $Points = ""; $Points [] = $this->GAreaXOffset + $this->GArea_X1; $Points [] = $LayerHeight + $this->GArea_Y1; $YLast = NULL; $XPos = $this->GAreaXOffset; $PointsCount = 2; for($X = 1; $X <= $numElements; $X = $X + $Accuracy) { $klo = 1; $khi = $numElements; $k = $khi - $klo; while ( $k > 1 ) { $k = $khi - $klo; If ($XIn [$k] >= $X) $khi = $k; else $klo = $k; } $klo = $khi - 1; $h = $XIn [$khi] - $XIn [$klo]; $a = ($XIn [$khi] - $X) / $h; $b = ($X - $XIn [$klo]) / $h; $Value = $a * $YIn [$klo] + $b * $YIn [$khi] + (($a * $a * $a - $a) * $Yt [$klo] + ($b * $b * $b - $b) * $Yt [$khi]) * ($h * $h) / 6; $YPos = $LayerHeight - (($Value - $this->VMin) * $this->DivisionRatio); if ($YLast != NULL && $AroundZero && ! isset ( $Missing [floor ( $X )] ) && ! isset ( $Missing [floor ( $X + 1 )] )) { $aPoints = ""; $aPoints [] = $XLast + $this->GArea_X1; $aPoints [] = min($YLast + $this->GArea_Y1, $this->GArea_Y2); $aPoints [] = $XPos + $this->GArea_X1; $aPoints [] = min($YPos + $this->GArea_Y1, $this->GArea_Y2); $aPoints [] = $XPos + $this->GArea_X1; $aPoints [] = $YZero + $this->GArea_Y1; $aPoints [] = $XLast + $this->GArea_X1; $aPoints [] = $YZero + $this->GArea_Y1; $this->canvas->drawFilledPolygon($aPoints, 4, $this->palette->colors[$ColorID], $alpha); } if (! isset ( $Missing [floor ( $X )] ) || $YLast == NULL) { $PointsCount ++; $Points [] = $XPos + $this->GArea_X1; $Points [] = min($YPos + $this->GArea_Y1, $this->GArea_Y2); } else { $PointsCount ++; $Points [] = $XLast + $this->GArea_X1; $Points [] = min($LayerHeight + $this->GArea_Y1, $this->GArea_Y2);; } $YLast = $YPos; $XLast = $XPos; $XPos = $XPos + $this->DivisionWidth * $Accuracy; } // Add potentialy missing values $XPos = $XPos - $this->DivisionWidth * $Accuracy; if ($XPos < ($LayerWidth - $this->GAreaXOffset)) { $YPos = $LayerHeight - (($YIn [$numElements] - $this->VMin) * $this->DivisionRatio); if ($YLast != NULL && $AroundZero) { $aPoints = ""; $aPoints [] = $XLast + $this->GArea_X1; $aPoints [] = max($YLast + $this->GArea_Y1, $this->GArea_Y1); $aPoints [] = $LayerWidth - $this->GAreaXOffset + $this->GArea_X1; $aPoints [] = max($YPos + $this->GArea_Y1, $this->GArea_Y1); $aPoints [] = $LayerWidth - $this->GAreaXOffset + $this->GArea_X1; $aPoints [] = max($YZero + $this->GArea_Y1, $this->GArea_Y1); $aPoints [] = $XLast + $this->GArea_X1; $aPoints [] = max($YZero + $this->GArea_Y1, $this->GArea_Y1); $this->canvas->drawFilledPolygon($aPoints, 4, $this->palette->colors[$ColorID], $alpha); } if ($YIn [$klo] != "" && $YIn [$khi] != "" || $YLast == NULL) { $PointsCount ++; $Points [] = $LayerWidth - $this->GAreaXOffset + $this->GArea_X1; $Points [] = $YPos + $this->GArea_Y1; } } $Points [] = $LayerWidth - $this->GAreaXOffset + $this->GArea_X1; $Points [] = $LayerHeight + $this->GArea_Y1; if (! $AroundZero) { $this->canvas->drawFilledPolygon($Points, $PointsCount, $this->palette->colors[$ColorID], $Alpha); } $this->drawCubicCurve($data, $Accuracy, $ColName ); $GraphID ++; } } /** * This function draw a filled line graph */ function drawFilledLineGraph($Data, $DataDescription, $Alpha = 100, $AroundZero = FALSE) { $Empty = - 2147483647; /* Validate the Data and DataDescription array */ $this->validateDataDescription ( "drawFilledLineGraph", $DataDescription ); $this->validateData ( "drawFilledLineGraph", $Data ); $LayerWidth = $this->GArea_X2 - $this->GArea_X1; $LayerHeight = $this->GArea_Y2 - $this->GArea_Y1; $GraphID = 0; foreach ( $DataDescription->values as $ColName ) { $ColorID = $DataDescription->getColumnIndex($ColName); $aPoints = array(); $aPoints [] = $this->GAreaXOffset + $this->GArea_X1; $aPoints [] = $LayerHeight + $this->GArea_Y1; $XPos = $this->GAreaXOffset; $XLast = - 1; $PointsCount = 2; $YZero = $LayerHeight - ((0 - $this->VMin) * $this->DivisionRatio); if ($YZero > $LayerHeight) { $YZero = $LayerHeight; } $YLast = $Empty; foreach (array_keys($Data) as $Key) { $Value = $Data [$Key] [$ColName]; $YPos = $LayerHeight - (($Value - $this->VMin) * $this->DivisionRatio); /* Save point into the image map if option activated */ if ($this->BuildMap) $this->addToImageMap ( $XPos - 3, $YPos - 3, $XPos + 3, $YPos + 3, $DataDescription->description[$ColName], $Data [$Key] [$ColName] . $DataDescription->getYUnit(), "FLine" ); if (! is_numeric ( $Value )) { $PointsCount ++; $aPoints [] = $XLast + $this->GArea_X1; $aPoints [] = $LayerHeight + $this->GArea_Y1; $YLast = $Empty; } else { $PointsCount ++; if ($YLast != $Empty) { $aPoints [] = $XPos + $this->GArea_X1; $aPoints [] = $YPos + $this->GArea_Y1; } else { $PointsCount ++; $aPoints [] = $XPos + $this->GArea_X1; $aPoints [] = $LayerHeight + $this->GArea_Y1; $aPoints [] = $XPos + $this->GArea_X1; $aPoints [] = $YPos + $this->GArea_Y1; } if ($YLast != $Empty && $AroundZero) { $Points = ""; $Points [] = $XLast + $this->GArea_X1; $Points [] = $YLast + $this->GArea_Y1; $Points [] = $XPos + $this->GArea_X1; $Points [] = $YPos + $this->GArea_Y1; $Points [] = $XPos + $this->GArea_X1; $Points [] = $YZero + $this->GArea_Y1; $Points [] = $XLast + $this->GArea_X1; $Points [] = $YZero + $this->GArea_Y1; $this->canvas->drawFilledPolygon($Points, 4, $this->palette->colors[$ColorID], $Alpha); } $YLast = $YPos; } $XLast = $XPos; $XPos = $XPos + $this->DivisionWidth; } $aPoints [] = $LayerWidth - $this->GAreaXOffset + $this->GArea_X1; $aPoints [] = $LayerHeight + $this->GArea_Y1; if ($AroundZero == FALSE) { $this->canvas->drawFilledPolygon($aPoints, $PointsCount, $this->palette->colors[$ColorID], $Alpha); } $GraphID ++; $this->drawLineGraph ( $Data, $DataDescription, $ColName ); } } /** * This function draws a bar graph */ function drawOverlayBarGraph($Data, $DataDescription, $Alpha = 50) { /* Validate the Data and DataDescription array */ $this->validateDataDescription ( "drawOverlayBarGraph", $DataDescription ); $this->validateData ( "drawOverlayBarGraph", $Data ); $LayerHeight = $this->GArea_Y2 - $this->GArea_Y1; $GraphID = 0; foreach ( $DataDescription->values as $ColName ) { $ColorID = $DataDescription->getColumnIndex($ColName); $XWidth = $this->DivisionWidth / 4; $XPos = $this->GAreaXOffset; $YZero = $LayerHeight - ((0 - $this->VMin) * $this->DivisionRatio); foreach (array_keys($Data) as $Key) { if (isset ( $Data [$Key] [$ColName] )) { $Value = $Data [$Key] [$ColName]; if (is_numeric ( $Value )) { $YPos = $LayerHeight - (($Value - $this->VMin) * $this->DivisionRatio); $this->canvas->drawFilledRectangle(new Point(floor($XPos - $XWidth + $this->GArea_X1), floor($YPos + $this->GArea_Y1)), new Point(floor($XPos + $XWidth + $this->GArea_X1), floor($YZero + $this->GArea_Y1)), $this->palette->colors[$GraphID], ShadowProperties::NoShadow(), false, $Alpha); $X1 = floor ( $XPos - $XWidth + $this->GArea_X1 ); $Y1 = floor ( $YPos + $this->GArea_Y1 ) + .2; $X2 = floor ( $XPos + $XWidth + $this->GArea_X1 ); $Y2 = $this->GArea_Y2 - ((0 - $this->VMin) * $this->DivisionRatio); if ($X1 <= $this->GArea_X1) { $X1 = $this->GArea_X1 + 1; } if ($X2 >= $this->GArea_X2) { $X2 = $this->GArea_X2 - 1; } /* Save point into the image map if option activated */ if ($this->BuildMap) $this->addToImageMap ( $X1, min ( $Y1, $Y2 ), $X2, max ( $Y1, $Y2 ), $DataDescription->description[$ColName], $Data [$Key] [$ColName] . $DataDescription->getYUnit(), "oBar" ); $this->canvas->drawLine(new Point($X1, $Y1), new Point($X2, $Y1), $this->palette->colors[$ColorID], $this->LineWidth, $this->LineDotSize, $this->shadowProperties, new Point($this->GArea_X1, $this->GArea_Y1), new Point($this->GArea_X2, $this->GArea_Y2)); } } $XPos = $XPos + $this->DivisionWidth; } $GraphID ++; } } /** * This function draw a bar graph */ function drawBarGraph($Data, $DataDescription, $Alpha = 100) { /* Validate the Data and DataDescription array */ $this->validateDataDescription ( "drawBarGraph", $DataDescription ); $this->validateData ( "drawBarGraph", $Data ); $Series = count ( $DataDescription->values); $SeriesWidth = $this->DivisionWidth / ($Series + 1); $SerieXOffset = $this->DivisionWidth / 2 - $SeriesWidth / 2; $YZero = $this->GArea_Y2 - ((0 - $this->VMin) * $this->DivisionRatio); if ($YZero > $this->GArea_Y2) { $YZero = $this->GArea_Y2; } $SerieID = 0; foreach ( $DataDescription->values as $ColName ) { $ColorID = $DataDescription->getColumnIndex($ColName); $XPos = $this->GArea_X1 + $this->GAreaXOffset - $SerieXOffset + $SeriesWidth * $SerieID; foreach (array_keys($Data) as $Key) { if (isset ( $Data [$Key] [$ColName] )) { if (is_numeric ( $Data [$Key] [$ColName] )) { $Value = $Data [$Key] [$ColName]; $YPos = $this->GArea_Y2 - (($Value - $this->VMin) * $this->DivisionRatio); /* Save point into the image map if option activated */ if ($this->BuildMap) { $this->addToImageMap ( $XPos + 1, min ( $YZero, $YPos ), $XPos + $SeriesWidth - 1, max ( $YZero, $YPos ), $DataDescription->description[$ColName], $Data [$Key] [$ColName] . $DataDescription->getYUnit(), "Bar" ); } if ($Alpha == 100) { $this->canvas->drawRectangle(new Point($XPos + 1, $YZero), new Point($XPos + $SeriesWidth - 1, $YPos), new Color(25, 25, 25), $this->LineWidth, $this->LineDotSize, $this->shadowProperties); } $this->canvas->drawFilledRectangle(new Point($XPos + 1, $YZero), new Point($XPos + $SeriesWidth - 1, $YPos), $this->palette->colors[$ColorID], $this->shadowProperties, TRUE, $Alpha); } } $XPos = $XPos + $this->DivisionWidth; } $SerieID ++; } } /** * This function draw a stacked bar graph */ function drawStackedBarGraph($Data, $DataDescription, $Alpha = 50, $Contiguous = FALSE) { /* Validate the Data and DataDescription array */ $this->validateDataDescription ( "drawBarGraph", $DataDescription ); $this->validateData ( "drawBarGraph", $Data ); if ($Contiguous) $SeriesWidth = $this->DivisionWidth; else $SeriesWidth = $this->DivisionWidth * .8; $YZero = $this->GArea_Y2 - ((0 - $this->VMin) * $this->DivisionRatio); if ($YZero > $this->GArea_Y2) { $YZero = $this->GArea_Y2; } $SerieID = 0; $LastValue = ""; foreach ( $DataDescription->values as $ColName ) { $ColorID = $DataDescription->getColumnIndex($ColName); $XPos = $this->GArea_X1 + $this->GAreaXOffset - $SeriesWidth / 2; foreach (array_keys($Data) as $Key) { if (isset ( $Data [$Key] [$ColName] )) { if (is_numeric ( $Data [$Key] [$ColName] )) { $Value = $Data [$Key] [$ColName]; if (isset ( $LastValue [$Key] )) { $YPos = $this->GArea_Y2 - ((($Value + $LastValue [$Key]) - $this->VMin) * $this->DivisionRatio); $YBottom = $this->GArea_Y2 - (($LastValue [$Key] - $this->VMin) * $this->DivisionRatio); $LastValue [$Key] += $Value; } else { $YPos = $this->GArea_Y2 - (($Value - $this->VMin) * $this->DivisionRatio); $YBottom = $YZero; $LastValue [$Key] = $Value; } /* Save point into the image map if option activated */ if ($this->BuildMap) $this->addToImageMap ( $XPos + 1, min ( $YBottom, $YPos ), $XPos + $SeriesWidth - 1, max ( $YBottom, $YPos ), $DataDescription->description[$ColName], $Data [$Key] [$ColName] . $DataDescription->getYUnit(), "sBar" ); $this->canvas->drawFilledRectangle(new Point($XPos + 1, $YBottom), new Point($XPos + $SeriesWidth - 1, $YPos), $this->palette->colors[$ColorID], $this->shadowProperties, TRUE, $Alpha); } } $XPos = $XPos + $this->DivisionWidth; } $SerieID ++; } } /** * This function draw a limits bar graphs */ function drawLimitsGraph($Data, $DataDescription, Color $color = null) { if ($color == null) { $color = new Color(0, 0, 0); } /* Validate the Data and DataDescription array */ $this->validateDataDescription ( "drawLimitsGraph", $DataDescription ); $this->validateData ( "drawLimitsGraph", $Data ); $XWidth = $this->DivisionWidth / 4; $XPos = $this->GArea_X1 + $this->GAreaXOffset; $graphAreaMin = new Point($this->GArea_X1, $this->GArea_Y1); $graphAreaMax = new Point($this->GArea_X2, $this->GArea_Y2); foreach (array_keys($Data) as $Key) { $Min = $Data [$Key] [$DataDescription->values[0]]; $Max = $Data [$Key] [$DataDescription->values[0]]; $GraphID = 0; $MaxID = 0; $MinID = 0; foreach ( $DataDescription->values as $ColName ) { if (isset ( $Data [$Key] [$ColName] )) { if ($Data [$Key] [$ColName] > $Max && is_numeric ( $Data [$Key] [$ColName] )) { $Max = $Data [$Key] [$ColName]; $MaxID = $GraphID; } } if (isset ( $Data [$Key] [$ColName] ) && is_numeric ( $Data [$Key] [$ColName] )) { if ($Data [$Key] [$ColName] < $Min) { $Min = $Data [$Key] [$ColName]; $MinID = $GraphID; } $GraphID ++; } } $YPos = $this->GArea_Y2 - (($Max - $this->VMin) * $this->DivisionRatio); $X1 = floor ( $XPos - $XWidth ); $Y1 = floor ( $YPos ) - .2; $X2 = floor ( $XPos + $XWidth ); if ($X1 <= $this->GArea_X1) { $X1 = $this->GArea_X1 + 1; } if ($X2 >= $this->GArea_X2) { $X2 = $this->GArea_X2 - 1; } $YPos = $this->GArea_Y2 - (($Min - $this->VMin) * $this->DivisionRatio); $Y2 = floor ( $YPos ) + .2; $this->canvas->drawLine(new Point(floor ( $XPos ) - .2, $Y1 + 1), new Point(floor ( $XPos ) - .2, $Y2 - 1), $color, $this->LineWidth, $this->LineDotSize, $this->shadowProperties, $graphAreaMin, $graphAreaMax); $this->canvas->drawLine(new Point(floor ( $XPos ) + .2, $Y1 + 1), new Point(floor ( $XPos ) + .2, $Y2 - 1), $color, $this->LineWidth, $this->LineDotSize, $this->shadowProperties, $graphAreaMin, $graphAreaMax); $this->canvas->drawLine(new Point($X1, $Y1), new Point($X2, $Y1), $this->palette->colors[$MaxID], $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $this->canvas->drawLine(new Point($X1, $Y2), new Point($X2, $Y2), $this->palette->colors[$MinID], $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $XPos = $XPos + $this->DivisionWidth; } } /** * This function draw radar axis centered on the graph area */ function drawRadarAxis($Data, $DataDescription, $Mosaic = TRUE, $BorderOffset = 10, Color $colorA = null, Color $colorS = null, $MaxValue = -1) { if ($colorA == null) { $colorA = new Color(60, 60, 60); } if ($colorS == null) { $colorS = new Color(200, 200, 200); } /* Validate the Data and DataDescription array */ $this->validateDataDescription ( "drawRadarAxis", $DataDescription ); $this->validateData ( "drawRadarAxis", $Data ); /* Draw radar axis */ $Points = count ( $Data ); $Radius = ($this->GArea_Y2 - $this->GArea_Y1) / 2 - $BorderOffset; $XCenter = ($this->GArea_X2 - $this->GArea_X1) / 2 + $this->GArea_X1; $YCenter = ($this->GArea_Y2 - $this->GArea_Y1) / 2 + $this->GArea_Y1; /* Search for the max value */ if ($MaxValue == - 1) { foreach ( $DataDescription->values as $ColName ) { foreach (array_keys($Data) as $Key) { if (isset ( $Data [$Key] [$ColName] )) if ($Data [$Key] [$ColName] > $MaxValue) { $MaxValue = $Data [$Key] [$ColName]; } } } } /* Draw the mosaic */ if ($Mosaic) { $RadiusScale = $Radius / $MaxValue; for($t = 1; $t <= $MaxValue - 1; $t ++) { $TRadius = $RadiusScale * $t; $LastX1 = - 1; for($i = 0; $i <= $Points; $i ++) { $Angle = - 90 + $i * 360 / $Points; $X1 = cos ( $Angle * M_PI / 180 ) * $TRadius + $XCenter; $Y1 = sin ( $Angle * M_PI / 180 ) * $TRadius + $YCenter; $X2 = cos ( $Angle * M_PI / 180 ) * ($TRadius + $RadiusScale) + $XCenter; $Y2 = sin ( $Angle * M_PI / 180 ) * ($TRadius + $RadiusScale) + $YCenter; if ($t % 2 == 1 && $LastX1 != - 1) { $Plots = ""; $Plots [] = $X1; $Plots [] = $Y1; $Plots [] = $X2; $Plots [] = $Y2; $Plots [] = $LastX2; $Plots [] = $LastY2; $Plots [] = $LastX1; $Plots [] = $LastY1; $this->canvas->drawFilledPolygon($Plots, (count ( $Plots ) + 1) / 2, new Color(250, 250, 250)); } $LastX1 = $X1; $LastY1 = $Y1; $LastX2 = $X2; $LastY2 = $Y2; } } } /* Draw the spider web */ for($t = 1; $t <= $MaxValue; $t ++) { $TRadius = ($Radius / $MaxValue) * $t; $LastX = - 1; for($i = 0; $i <= $Points; $i ++) { $Angle = - 90 + $i * 360 / $Points; $X = cos ( $Angle * M_PI / 180 ) * $TRadius + $XCenter; $Y = sin ( $Angle * M_PI / 180 ) * $TRadius + $YCenter; if ($LastX != - 1) $this->canvas->drawDottedLine(new Point($LastX, $LastY), new Point($X, $Y), 4, 1, $colorS, $this->shadowProperties); $LastX = $X; $LastY = $Y; } } /* Draw the axis */ for($i = 0; $i <= $Points; $i ++) { $Angle = - 90 + $i * 360 / $Points; $X = cos ( $Angle * M_PI / 180 ) * $Radius + $XCenter; $Y = sin ( $Angle * M_PI / 180 ) * $Radius + $YCenter; $this->canvas->drawLine(new Point($XCenter, $YCenter), new Point($X, $Y), $colorA, $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $XOffset = 0; $YOffset = 0; if (isset ( $Data [$i] [$DataDescription->getPosition()] )) { $Label = $Data [$i] [$DataDescription->getPosition()]; $Positions = imagettfbbox ( $this->FontSize, 0, $this->FontName, $Label ); $Width = $Positions [2] - $Positions [6]; $Height = $Positions [3] - $Positions [7]; if ($Angle >= 0 && $Angle <= 90) $YOffset = $Height; if ($Angle > 90 && $Angle <= 180) { $YOffset = $Height; $XOffset = - $Width; } if ($Angle > 180 && $Angle <= 270) { $XOffset = - $Width; } $this->canvas->drawText($this->FontSize, 0, new Point($X + $XOffset, $Y + $YOffset), $colorA, $this->FontName, $Label, ShadowProperties::NoShadow()); } } /* Write the values */ for($t = 1; $t <= $MaxValue; $t ++) { $TRadius = ($Radius / $MaxValue) * $t; $Angle = - 90 + 360 / $Points; $X1 = $XCenter; $Y1 = $YCenter - $TRadius; $X2 = cos ( $Angle * M_PI / 180 ) * $TRadius + $XCenter; $Y2 = sin ( $Angle * M_PI / 180 ) * $TRadius + $YCenter; $XPos = floor ( ($X2 - $X1) / 2 ) + $X1; $YPos = floor ( ($Y2 - $Y1) / 2 ) + $Y1; $Positions = imagettfbbox ( $this->FontSize, 0, $this->FontName, $t ); $X = $XPos - ($X + $Positions [2] - $X + $Positions [6]) / 2; $Y = $YPos + $this->FontSize; $this->canvas->drawFilledRoundedRectangle(new Point($X + $Positions [6] - 2, $Y + $Positions [7] - 1), new Point($X + $Positions [2] + 4, $Y + $Positions [3] + 1), 2, new Color(240, 240, 240), $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $this->canvas->drawRoundedRectangle(new Point($X + $Positions [6] - 2, $Y + $Positions [7] - 1), new Point($X + $Positions [2] + 4, $Y + $Positions [3] + 1), 2, new Color(220, 220, 220), $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $this->canvas->drawText($this->FontSize, 0, new Point($X, $Y), $colorA, $this->FontName, $t, ShadowProperties::NoShadow()); } } private function calculateMaxValue($Data, $DataDescription) { $MaxValue = -1; foreach ( $DataDescription->values as $ColName ) { foreach (array_keys($Data) as $Key) { if (isset ( $Data [$Key] [$ColName] )) if ($Data [$Key] [$ColName] > $MaxValue && is_numeric($Data[$Key][$ColName])) { $MaxValue = $Data [$Key] [$ColName]; } } } return $MaxValue; } /** * This function draw a radar graph centered on the graph area */ function drawRadar($Data, $DataDescription, $BorderOffset = 10, $MaxValue = -1) { /* Validate the Data and DataDescription array */ $this->validateDataDescription ( "drawRadar", $DataDescription ); $this->validateData ( "drawRadar", $Data ); $Points = count ( $Data ); $Radius = ($this->GArea_Y2 - $this->GArea_Y1) / 2 - $BorderOffset; $XCenter = ($this->GArea_X2 - $this->GArea_X1) / 2 + $this->GArea_X1; $YCenter = ($this->GArea_Y2 - $this->GArea_Y1) / 2 + $this->GArea_Y1; /* Search for the max value */ if ($MaxValue == - 1) { $MaxValue = $this->calculateMaxValue($Data, $DataDescription); } $GraphID = 0; foreach ( $DataDescription->values as $ColName ) { $ColorID = $DataDescription->getColumnIndex($ColName); $Angle = - 90; $XLast = - 1; foreach (array_keys($Data) as $Key) { if (isset ( $Data [$Key] [$ColName] )) { $Value = $Data [$Key] [$ColName]; $Strength = ($Radius / $MaxValue) * $Value; $XPos = cos ( $Angle * M_PI / 180 ) * $Strength + $XCenter; $YPos = sin ( $Angle * M_PI / 180 ) * $Strength + $YCenter; if ($XLast != - 1) $this->canvas->drawLine(new Point($XLast, $YLast), new Point($XPos, $YPos), $this->palette->colors[$ColorID], $this->LineWidth, $this->LineDotSize, $this->shadowProperties); if ($XLast == - 1) { $FirstX = $XPos; $FirstY = $YPos; } $Angle = $Angle + (360 / $Points); $XLast = $XPos; $YLast = $YPos; } } $this->canvas->drawLine(new Point($XPos, $YPos), new Point($FirstX, $FirstY), $this->palette->colors[$ColorID], $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $GraphID ++; } } /** * This function draw a radar graph centered on the graph area */ function drawFilledRadar($Data, $DataDescription, $Alpha = 50, $BorderOffset = 10, $MaxValue = -1) { /* Validate the Data and DataDescription array */ $this->validateDataDescription ( "drawFilledRadar", $DataDescription ); $this->validateData ( "drawFilledRadar", $Data ); $Points = count ( $Data ); $Radius = ($this->GArea_Y2 - $this->GArea_Y1) / 2 - $BorderOffset; $XCenter = ($this->GArea_X2 - $this->GArea_X1) / 2; $YCenter = ($this->GArea_Y2 - $this->GArea_Y1) / 2; /* Search for the max value */ if ($MaxValue == - 1) { $MaxValue = $this->calculateMaxValue($Data, $DataDescription); } $GraphID = 0; foreach ( $DataDescription->values as $ColName ) { $ColorID = $DataDescription->getColumnIndex($ColName); $Angle = - 90; $XLast = - 1; $Plots = array(); foreach (array_keys($Data) as $Key) { if (isset ( $Data [$Key] [$ColName] )) { $Value = $Data [$Key] [$ColName]; if (! is_numeric ( $Value )) { $Value = 0; } $Strength = ($Radius / $MaxValue) * $Value; $XPos = cos ( $Angle * M_PI / 180 ) * $Strength + $XCenter; $YPos = sin ( $Angle * M_PI / 180 ) * $Strength + $YCenter; $Plots [] = $XPos + $this->GArea_X1; $Plots [] = $YPos + $this->GArea_Y1; $Angle = $Angle + (360 / $Points); $XLast = $XPos; } } if (isset ( $Plots [0] )) { $Plots [] = $Plots [0]; $Plots [] = $Plots [1]; $this->canvas->drawFilledPolygon($Plots, (count ( $Plots ) + 1) / 2, $this->palette->colors[$ColorID], $Alpha); for($i = 0; $i <= count ( $Plots ) - 4; $i = $i + 2) $this->canvas->drawLine(new Point($Plots [$i], $Plots [$i + 1]), new Point($Plots [$i + 2], $Plots [$i + 3]), $this->palette->colors[$ColorID], $this->LineWidth, $this->LineDotSize, $this->shadowProperties); } $GraphID ++; } } /** * This function can be used to set the background color */ function drawBackground(Color $color) { $C_Background = $this->canvas->allocateColor($color); imagefilledrectangle ( $this->canvas->getPicture(), 0, 0, $this->XSize, $this->YSize, $C_Background ); } private function drawGradient(Point $point1, Point $point2, Color $color, $decay) { /* Positive gradient */ if ($decay > 0) { $YStep = ($point2->getY() - $point1->getY() - 2) / $decay; for($i = 0; $i <= $decay; $i ++) { $color = $color->addRGBIncrement(-1); $Yi1 = $point1->getY() + ($i * $YStep); $Yi2 = ceil ( $Yi1 + ($i * $YStep) + $YStep ); if ($Yi2 >= $Yi2) { $Yi2 = $point2->getY() - 1; } $this->canvas->drawFilledRectangle(new Point($point1->getX(), $Yi1), new Point($point2->getX(), $Yi2), $color, ShadowProperties::NoShadow()); } } /* Negative gradient */ if ($decay < 0) { $YStep = ($point2->getY() - $point1->getY() - 2) / - $decay; $Yi1 = $point1->getY(); $Yi2 = $point1->getY() + $YStep; for($i = - $decay; $i >= 0; $i --) { $color = $color->addRGBIncrement(1); $this->canvas->drawFilledRectangle(new Point($point1->getX(), $Yi1), new Point($point2->getX(), $Yi2), $color, ShadowProperties::NoShadow()); $Yi1 += $YStep; $Yi2 += $YStep; if ($Yi2 >= $Yi2) { $Yi2 = $point2->getY() - 1; } } } } /** * This function can be used to set the background color */ private function drawGraphAreaGradient(BackgroundStyle $style) { if (!$style->useGradient()) { return; } $this->drawGradient(new Point($this->GArea_X1 + 1, $this->GArea_Y1 + 1), new Point($this->GArea_X2 - 1, $this->GArea_Y2), $style->getGradientStartColor(), $style->getGradientDecay()); } public function drawBackgroundGradient(Color $color, $decay) { $this->drawGradient(new Point(0, 0), new Point($this->XSize, $this->YSize), $color, $decay); } /** * This function will draw an ellipse */ function drawEllipse($Xc, $Yc, $Height, $Width, Color $color) { $this->canvas->drawCircle(new Point($Xc, $Yc), $Height, $color, $this->shadowProperties, $Width ); } /** * This function will draw a filled ellipse */ function drawFilledEllipse($Xc, $Yc, $Height, $Width, Color $color) { $this->canvas->drawFilledCircle(new Point($Xc, $Yc), $Height, $color, $this->shadowProperties, $Width ); } /** * Load a PNG file and draw it over the chart */ function drawFromPNG($FileName, $X, $Y, $Alpha = 100) { $this->drawFromPicture ( 1, $FileName, $X, $Y, $Alpha ); } /** * Load a GIF file and draw it over the chart */ function drawFromGIF($FileName, $X, $Y, $Alpha = 100) { $this->drawFromPicture ( 2, $FileName, $X, $Y, $Alpha ); } /** * Load a JPEG file and draw it over the chart */ function drawFromJPG($FileName, $X, $Y, $Alpha = 100) { $this->drawFromPicture ( 3, $FileName, $X, $Y, $Alpha ); } /** * Generic loader function for external pictures */ function drawFromPicture($PicType, $FileName, $X, $Y, $Alpha = 100) { if (file_exists ( $FileName )) { $Infos = getimagesize ( $FileName ); $Width = $Infos [0]; $Height = $Infos [1]; if ($PicType == 1) { $Raster = imagecreatefrompng ( $FileName ); } if ($PicType == 2) { $Raster = imagecreatefromgif ( $FileName ); } if ($PicType == 3) { $Raster = imagecreatefromjpeg ( $FileName ); } imagecopymerge ( $this->canvas->getPicture(), $Raster, $X, $Y, 0, 0, $Width, $Height, $Alpha ); imagedestroy ( $Raster ); } } /** * Add a border to the picture * * @todo This hasn't been updated to the new API yet */ function addBorder($Size = 3, $R = 0, $G = 0, $B = 0) { $Width = $this->XSize + 2 * $Size; $Height = $this->YSize + 2 * $Size; $Resampled = imagecreatetruecolor ( $Width, $Height ); $C_Background = imagecolorallocate($Resampled, $R, $G, $B); imagefilledrectangle ( $Resampled, 0, 0, $Width, $Height, $C_Background ); imagecopy ( $Resampled, $this->canvas->getPicture(), $Size, $Size, 0, 0, $this->XSize, $this->YSize ); imagedestroy ( $this->canvas->getPicture() ); $this->XSize = $Width; $this->YSize = $Height; $this->canvas->setPicture(imagecreatetruecolor ( $this->XSize, $this->YSize )); $C_White = $this->canvas->allocate(new Color(255, 255, 255)); imagefilledrectangle ( $this->canvas->getPicture(), 0, 0, $this->XSize, $this->YSize, $C_White ); imagecolortransparent ( $this->canvas->getPicture(), $C_White ); imagecopy ( $this->canvas->getPicture(), $Resampled, 0, 0, 0, 0, $this->XSize, $this->YSize ); } /** * Render the current picture to a file */ function Render($FileName) { if ($this->ErrorReporting) $this->printErrors ( $this->ErrorInterface ); /* Save image map if requested */ if ($this->BuildMap) $this->SaveImageMap (); imagepng ( $this->canvas->getPicture(), $FileName ); } /** * Render the current picture to STDOUT */ function Stroke() { if ($this->ErrorReporting) $this->printErrors ( "GD" ); /* Save image map if requested */ if ($this->BuildMap) $this->SaveImageMap (); header ( 'Content-type: image/png' ); imagepng ( $this->canvas->getPicture() ); } /** * Validate data contained in the description array * * @todo Should this be a method on DataDescription? */ protected function validateDataDescription($FunctionName, DataDescription &$DataDescription, $DescriptionRequired = TRUE) { if ($DataDescription->getPosition() == '') { $this->Errors [] = "[Warning] " . $FunctionName . " - Y Labels are not set."; $DataDescription->setPosition("Name"); } if ($DescriptionRequired) { if (! isset ( $DataDescription->description)) { $this->Errors [] = "[Warning] " . $FunctionName . " - Series descriptions are not set."; foreach ( $DataDescription->values as $key => $Value ) { $DataDescription->description[$Value] = $Value; } } if (count ( $DataDescription->description) < count ( $DataDescription->values )) { $this->Errors [] = "[Warning] " . $FunctionName . " - Some series descriptions are not set."; foreach ( $DataDescription->values as $key => $Value ) { if (! isset ( $DataDescription->description[$Value] )) $DataDescription->description[$Value] = $Value; } } } } /** * Validate data contained in the data array */ protected function validateData($FunctionName, &$Data) { $DataSummary = array (); foreach ( $Data as $key => $Values ) { foreach ( $Values as $key2 => $Value ) { if (! isset ( $DataSummary [$key2] )) $DataSummary [$key2] = 1; else $DataSummary [$key2] ++; } } if (empty($DataSummary)) $this->Errors [] = "[Warning] " . $FunctionName . " - No data set."; foreach ( $DataSummary as $key => $Value ) { if ($Value < max ( $DataSummary )) { $this->Errors [] = "[Warning] " . $FunctionName . " - Missing data in serie " . $key . "."; } } } /** * Print all error messages on the CLI or graphically */ function printErrors($Mode = "CLI") { if (count ( $this->Errors ) == 0) return (0); if ($Mode == "CLI") { foreach ( $this->Errors as $key => $Value ) echo $Value . "\r\n"; } elseif ($Mode == "GD") { $MaxWidth = 0; foreach ( $this->Errors as $key => $Value ) { $Position = imageftbbox ( $this->ErrorFontSize, 0, $this->ErrorFontName, $Value ); $TextWidth = $Position [2] - $Position [0]; if ($TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; } } $this->canvas->drawFilledRoundedRectangle(new Point($this->XSize - ($MaxWidth + 20), $this->YSize - (20 + (($this->ErrorFontSize + 4) * count ( $this->Errors )))), new Point($this->XSize - 10, $this->YSize - 10), 6, new Color(233, 185, 185), $this->lineWidth, $this->lineDotSize, $this->shadowProperties); $this->canvas->drawRoundedRectangle(new Point($this->XSize - ($MaxWidth + 20), $this->YSize - (20 + (($this->ErrorFontSize + 4) * count ( $this->Errors )))), new Point($this->XSize - 10, $this->YSize - 10), 6, new Color(193, 145, 145), $this->LineWidth, $this->LineDotSize, $this->shadowProperties); $YPos = $this->YSize - (18 + (count ( $this->Errors ) - 1) * ($this->ErrorFontSize + 4)); foreach ( $this->Errors as $key => $Value ) { $this->canvas->drawText($this->ErrorFontSize, 0, new Point($this->XSize - ($MaxWidth + 15), $YPos), new Color(133, 85, 85), $this->ErrorFontName, $Value, ShadowProperties::NoShadow()); $YPos = $YPos + ($this->ErrorFontSize + 4); } } } /** * Activate the image map creation process */ function setImageMap($Mode = TRUE, $GraphID = "MyGraph") { $this->BuildMap = $Mode; $this->MapID = $GraphID; } /** * Add a box into the image map */ function addToImageMap($X1, $Y1, $X2, $Y2, $SerieName, $Value, $CallerFunction) { if ($this->MapFunction == NULL || $this->MapFunction == $CallerFunction) { $this->ImageMap [] = round ( $X1 ) . "," . round ( $Y1 ) . "," . round ( $X2 ) . "," . round ( $Y2 ) . "," . $SerieName . "," . $Value; $this->MapFunction = $CallerFunction; } } /** * Load and cleanup the image map from disk */ function getImageMap($MapName, $Flush = TRUE) { /* Strip HTML query strings */ $Values = $this->tmpFolder . $MapName; $Value = explode ( "\?", $Values ); $FileName = $Value [0]; if (file_exists ( $FileName )) { $Handle = fopen ( $FileName, "r" ); $MapContent = fread ( $Handle, filesize ( $FileName ) ); fclose ( $Handle ); echo $MapContent; if ($Flush) unlink ( $FileName ); exit (); } else { header ( "HTTP/1.0 404 Not Found" ); exit (); } } /** * Save the image map to the disk */ function SaveImageMap() { if (! $this->BuildMap) { return (- 1); } if ($this->ImageMap == NULL) { $this->Errors [] = "[Warning] SaveImageMap - Image map is empty."; return (- 1); } $Handle = fopen ( $this->tmpFolder . $this->MapID, 'w' ); if (! $Handle) { $this->Errors [] = "[Warning] SaveImageMap - Cannot save the image map."; return (- 1); } else { foreach ( $this->ImageMap as $Value ) fwrite ( $Handle, htmlentities ( $Value ) . "\r" ); } fclose ( $Handle ); } /** * Set date format for axis labels */ function setDateFormat($Format) { $this->DateFormat = $Format; } /** * Convert TS to a date format string */ function ToDate($Value) { return (date ( $this->DateFormat, $Value )); } /** * Check if a number is a full integer (for scaling) */ static private function isRealInt($Value) { if ($Value == floor ( $Value )) return (TRUE); return (FALSE); } /** * @todo I don't know what this does yet, I'm refactoring... */ public function calculateScales(& $Scale, & $Divisions) { /* Compute automatic scaling */ $ScaleOk = FALSE; $Factor = 1; $MinDivHeight = 25; $MaxDivs = ($this->GArea_Y2 - $this->GArea_Y1) / $MinDivHeight; if ($this->VMax <= $this->VMin) { throw new Exception("Impossible to calculate scales when VMax <= VMin"); } if ($this->VMin == 0 && $this->VMax == 0) { $this->VMin = 0; $this->VMax = 2; $Scale = 1; $Divisions = 2; } elseif ($MaxDivs > 1) { while ( ! $ScaleOk ) { $Scale1 = ($this->VMax - $this->VMin) / $Factor; $Scale2 = ($this->VMax - $this->VMin) / $Factor / 2; if ($Scale1 > 1 && $Scale1 <= $MaxDivs && ! $ScaleOk) { $ScaleOk = TRUE; $Divisions = floor ( $Scale1 ); $Scale = 1; } if ($Scale2 > 1 && $Scale2 <= $MaxDivs && ! $ScaleOk) { $ScaleOk = TRUE; $Divisions = floor ( $Scale2 ); $Scale = 2; } if (! $ScaleOk) { if ($Scale2 > 1) { $Factor = $Factor * 10; } if ($Scale2 < 1) { $Factor = $Factor / 10; } } } if (floor ( $this->VMax / $Scale / $Factor ) != $this->VMax / $Scale / $Factor) { $GridID = floor ( $this->VMax / $Scale / $Factor ) + 1; $this->VMax = $GridID * $Scale * $Factor; $Divisions ++; } if (floor ( $this->VMin / $Scale / $Factor ) != $this->VMin / $Scale / $Factor) { $GridID = floor ( $this->VMin / $Scale / $Factor ); $this->VMin = $GridID * $Scale * $Factor; $Divisions ++; } } else /* Can occur for small graphs */ $Scale = 1; } /* * Returns the resource */ public function getPicture() { return $this->canvas->getPicture(); } static private function computeAutomaticScaling($minCoord, $maxCoord, &$minVal, &$maxVal, &$Divisions) { $ScaleOk = FALSE; $Factor = 1.0; $MinDivHeight = 25; $MaxDivs = ($maxCoord - $minCoord) / $MinDivHeight; if ($minVal == 0 && $maxVal == 0){ $minVal = 0; $maxVal = 2; $Scale = 1; $Divisions = 2; }elseif ($minVal == $maxVal){ $minVal = 0; $maxVal = $maxVal*2; $Scale = 1; $Divisions = 2; } elseif ($MaxDivs > 1) { while ( ! $ScaleOk ) { if($Factor == 0) { die(); } $Scale1 = ($maxVal - $minVal) / $Factor; $Scale2 = ($maxVal - $minVal) / $Factor / 2; if ($Scale1 > 1 && $Scale1 <= $MaxDivs && ! $ScaleOk) { $ScaleOk = TRUE; $Divisions = floor ( $Scale1 ); $Scale = 1; } if ($Scale2 > 1 && $Scale2 <= $MaxDivs && ! $ScaleOk) { $ScaleOk = TRUE; $Divisions = floor ( $Scale2 ); $Scale = 2; } if (! $ScaleOk) { if ($Scale2 > 1) { $Factor = $Factor * 10; } if ($Scale2 < 1) { $Factor = $Factor / 10; } } } if (floor ( $maxVal / $Scale / $Factor ) != $maxVal / $Scale / $Factor) { $GridID = floor ( $maxVal / $Scale / $Factor ) + 1; $maxVal = $GridID * $Scale * $Factor; $Divisions ++; } if (floor ( $minVal / $Scale / $Factor ) != $minVal / $Scale / $Factor) { $GridID = floor ( $minVal / $Scale / $Factor ); $minVal = $GridID * $Scale * $Factor; $Divisions ++; } } else /* Can occurs for small graphs */ $Scale = 1; if (! isset ( $Divisions )) $Divisions = 2; if (self::isRealInt ( ($maxVal - $minVal) / ($Divisions - 1) )) $Divisions --; elseif (self::isRealInt ( ($maxVal - $minVal) / ($Divisions + 1) )) $Divisions ++; } static private function convertValueForDisplay($value, $format, $unit) { if ($format == "number") return $value . $unit; if ($format == "time") return ConversionHelpers::ToTime ( $value ); if ($format == "date") return $this->ToDate ( $value ); if ($format == "metric") return ConversionHelpers::ToMetric ( $value ); if ($format == "currency") return ConversionHelpers::ToCurrency ( $value ); } } /** * * @param $Message */ function RaiseFatal($Message) { echo "[FATAL] " . $Message . "\r\n"; exit (); } ?>