Files
2026-09-03 11:24:19 +02:00

1985 lines
51 KiB
PHP

<?php
/*=========================================================================*/
/* Name: XY_Plot.php */
/* Uses: Creates a trend with X/Y data */
/* Date: 4/12/2007 */
/* Author: Andrew Que (http://www.DrQue.net/) */
/* Revisions: */
/* 0.1 - 4/12/2007 - QUE - Creation */
/* 1.0 - 4/29/2007 - QUE - Base functionality */
/* 1.1 - 11/24/2007 - QUE - Added linear regression */
/* 1.1.1 - 12/15/2007 - QUE - Added line clipping */
/* 1.1.2 - 12/24/2007 - QUE - PHPDoc documentation added */
/* 1.1.3 - 12/28/2007 - QUE - Small bug fixes */
/* 1.2 - 12/29/2007 - QUE - Grid lines and lables */
/* 1.2.1 - 1/1/2008 - QUE - Added circle clipping */
/* */
/* Website: http://xyplot.drque.net/ */
/* The newest version and information about this project are on the */
/* website. */
/* ----------------------------------------------------------------------- */
/* */
/* X/Y trend with linear regression script */
/* Copyright (C) 2007 Andrew Que */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/* ----------------------------------------------------------------------- */
/* */
/* (C) Copyright 2007-2008 */
/* Andrew Que */
/* �|> */
/*=========================================================================*/
/**
* X/Y plot
*
* <p>
* Used for creating graphs of X/Y data and includes functions for
* drawing mean average and linear regression. Functions include:
* <ul>
* <li>Line drawing. Lines are drawen between each point</li>
* <li>Point drawing. Each point is charted</li>
* <li>Mean average. A line denoting the average</li>
* <li>Linear regression. Linear regression line</li>
* <li>Grid lines and lables.</li>
* </ul>
*
* @package XY_Plot
* @author Andrew Que ({@link http://www.DrQue.net/})
* @copyright Copyright (c) 2007-2008, Andrew Que
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/
/**
* Requirenments
*/
require_once('./polynomial/ClipLine.php');
require_once('./polynomial/ClipCircle.php');
/**
* A rectangular area define by the upper left corner and the lower right
* @package XY_Plot
*/
class Window
{
protected $x;
protected $y;
protected $xx;
protected $yy;
/**
* Constructor
*
* Create a new window with specified area
* @param $x left edge
* @param $y top edge
* @param $xx right edge
* @param $yy bottom edge
*/
public function __construct( $x , $y , $xx , $yy )
{
$this->x = $x;
$this->y = $y;
$this->xx = $xx;
$this->yy = $yy;
}
/**
* Resize window
*
* Change the size of the window to the new area
* @param $x left edge
* @param $y top edge
* @param $xx right edge
* @param $yy bottom edge
*/
public function Resize( $x , $y , $xx , $yy )
{
$this->x = $x;
$this->y = $y;
$this->xx = $xx;
$this->yy = $yy;
}
/**
* Get left coordinate
*/
public function GetX()
{
return $this->x;
}
/**
* Get top coordinate
*/
public function GetY()
{
return $this->y;
}
/**
* Get right coordinate
*/
public function GetXX()
{
return $this->xx;
}
/**
* Get bottom coordinate
*/
public function GetYY()
{
return $this->yy;
}
/**
* Return width (delta x)
*/
public function GetWidth()
{
return $this->xx - $this->x;
}
/**
* Return height (delta y)
*/
public function GetHeigth()
{
return $this->yy - $this->y;
}
}
/**
* X/Y plot class
*
* <p>
* Used to create a plot of X/Y data
* <p>
* Examples of use:
* <code>
* $Plot = new XY_PlotClass( $Image ); // <- Initilize
* $Plot->AddData( 1 , 5 ); // <- Add some data
* $Plot->AddData( 3 , 2 );
* $Plot->AddData( 4 , 4 );
* $Plot->SetColor( $Black ); // <- Line color
* $Plot->RenderWithLines(); // <- Draw lines into $Image
* </code>
* @package XY_Plot
*/
class XY_PlotClass
{
/**#@+ @ignore */
protected $Image;
protected $Window;
protected $X_Max = -1;
protected $X_Min = -1;
protected $Y_Min = 0;
protected $Y_Max = 100;
protected $Color = 0;
protected $LinearRegressionColor = 0;
protected $LinearRegressionThickness = 4;
protected $LinearAverageColor = 0;
protected $Data = array();
protected $WindowedData = array();
protected $RenderMethod = "Points";
protected $Slope;
protected $Correlation;
protected $Y_Intercept;
protected $Average;
protected $MaxX_Distance = 0;
protected $CircleSize = 5;
protected $AverageWidth = 1;
protected $IsDataCalculated = false;
protected $Y_MajorDivisionExtention = 0;
protected $Y_MajorDivisionScale = 1;
protected $Y_MajorDivisionColor;
protected $Y_MajorDivisionTextColor;
protected $Y_MajorTextCallback = null;
protected $Y_MajorTextFontSize = 2;
protected $Y_MinorDivisionScale = 1;
protected $Y_MinorDivisionColor;
protected $X_MajorDivisionExtention = 0;
protected $X_MajorDivisionScale = 1;
protected $X_MajorDivisionColor;
protected $X_MajorDivisionTextColor;
protected $X_MajorTextCallback = null;
protected $X_MajorTextFontSize = 2;
protected $X_MajorCustomStart = null;
protected $X_MajorCustomIncrement = null;
protected $X_MajorCustomEnd = null;
protected $X_MinorDivisionScale = 1;
protected $X_MinorDivisionColor;
protected $X_MinorTextFontSize = 2;
protected $X_MinorCustomStart = null;
protected $X_MinorCustomIncrement = null;
protected $X_MinorCustomEnd = null;
/**#@-*/
/**
* Constructor
* @param $Image The image to use for the plot
*/
public function __construct( $Image )
{
$this->Image = $Image;
// Assume graph uses entire window
$this->Window =
new Window(
0 , 0 ,
imagesx( $this->Image ) , imagesy( $this->Image ) );
}
/**
* Set the width of the mean average plot
*
* Set the width of the mean average plot line
* @param $Value New width
*/
public function SetAverageWidth( $Value )
{
$this->AverageWidth = $Value;
}
/**
* Get mean average width
*
* Return mean average width
* @return mean average width
*/
public function GetAverageWidth()
{
return $this->AverageWidth;
}
/**
* Set the max and max X-span
*
* The values for X-min and X-max further narrow down the data added
* to the plot. This specifies a range of X values that will be on the
* the chart.
* @param $X_Min The lowest X-value to include in chart
* @param $X_Max The highest X-value to include in chart
*/
public function SetX_Span( $X_Min , $X_Max )
{
$this->X_Max = $X_Max;
$this->X_Min = $X_Min;
$IsDataCalculated = false;
}
/**
* Limit Y-axis range
*
* Set the range for Y values. Values above and below the min and
* max values will be clipped.
* @param $Y_Min The lowest Y-value to include in chart
* @param $Y_Max The highest Y-value to include in chart
*/
public function SetY_Span( $Y_Min , $Y_Max )
{
$this->Y_Min = $Y_Min;
$this->Y_Max = $Y_Max;
}
/**
* Resize the viewing window
* @param $x left edge
* @param $y top edge
* @param $xx right edge
* @param $yy bottom edge
*/
public function SizeWindow( $x , $y , $xx , $yy )
{
$this->Window->Resize( $x , $y , $xx , $yy );
}
/**
* Set the color of the line plot
*
* This is the color of the line created by <b>RenderWithLines()</b>.
* @param $Color Color of the line
*/
public function SetColor( $Color )
{
$this->Color = $Color;
}
/**
* Set color of the linear regression line
*
* This is the color of the line created by <b>RenderLinearRegression()</b>.
* @param $Color Color of the line
*/
public function SetLinearRegressionColor( $Color )
{
$this->LinearRegressionColor = $Color;
}
/**
* Set color of the average line
*
* This is the color of the line created by <b>RenderMeanPlot()</b>.
* @param $Color Color of the line
*/
public function SetAverageColor( $Color )
{
$this->LinearAverageColor = $Color;
}
/**
* Flush all data
*
* This will remove add data points from the plot. Useful for adding more
* then one plot to an image.
*/
public function ResetData()
{
$this->Data = array();
$IsDataCalculated = false;
}
/**
* Add a data point
*
* This called for every data point to be part of the chart.
* @param $x X-cord of data point
* @param $y Y value of point at X
*/
public function AddData( $x , $y )
{
$this->Data[ "$x" ] = (float)$y;
$IsDataCalculated = false;
}
/**
* Max gab between x values
*
* Set the maximum gap between x values before a line no longer
* connects them. Useful when areas of data are missing. Set to
* 0 to disable (default).
* @param $Distance Max distance between x values
*/
public function SetMaxX_Distance( $Distance )
{
$this->MaxX_Distance = $Distance;
}
/**
* Get the max gab between x balues
*/
public function GetMaxX_Distance( $Distance )
{
return $this->MaxX_Distance;
}
/**
* Linear regression line thickness
*
* Set thickness of linear regression line. Default is 4 pixels.
* @param $Value Thickness
*/
public function SetLinearRegressionThickness( $Value )
{
$this->LinearRegressionThickness = $Value;
}
/**
* Get Linear regression line thickness
*
* Return thickness of linear regression line.
* @return Thickness
*/
public function GetLinearRegressionThickness()
{
return $this->LinearRegressionThickness;
}
/**
* Set circle size for data points
*
* The circle size is a parameter for the <b>RenderPoints()</b>
* method. The size is the dimeter of the circle used to denote a point
* on the chart. Default size is 5 pixels.
* @param $Size Dimeter of circle
*/
public function SetCircleSize( $Size )
{
$this->CircleSize = $Size;
}
/**
* Return the lowest Y data scale
*/
public function GetY_Min()
{
return $this->Y_Min;
}
/**
* Return the highest Y data scale
*/
public function GetY_Max()
{
return $this->Y_Max;
}
/**
* Return the lowest X data scale
*/
public function GetX_Min()
{
return $this->X_Min;
}
/**
* Return the highest x data scale
*/
public function GetX_Max()
{
return $this->X_Max;
}
/**
* Computed slope
*
* <p>
* Return the slope computer through linear regression. Requires a call
* to <b>CalculateLinearRegression()</b> before containing
* valid data.
* <p>
* This is the <i>m</i> part of the standard line function <i>y = mx + b</i>
* @return Slope of linear regression line
*/
public function GetSlope()
{
return $this->Slope;
}
/**
* Computed Y-intercept
*
* <p>
* Returns the Y-intercept computed through linear regression. Requires
* a call to <b>CalculateLinearRegression()</b> before containing
* valid data.
* <p>
* This is the <i>b</i> part of the standard line function <i>y = mx + b</i>
* @return Y-intercept
*/
public function GetY_Intercept()
{
return $this->Y_Intercept;
}
/**
* Correlation coefficient
*
* Returns the correlation coefficient computed through linear regression. Requires
* a call to <b>CalculateLinearRegression()</b> before containing
* valid data.
* @return Correlation coefficient
*/
public function GetCorrelation()
{
return $this->Correlation;
}
/**
* Mean average
*
* Return the mean average of data within X span. Requires a call
* to <b>CalculateMeanAverage</b> before containing valid data.
* @return Mean average
*/
public function GetAverage()
{
return $this->Average;
}
//------------------------------------------------------------------------
/**
* Build an array narrowed in on the x-frame
*/
private function BuildWindowArray()
{
// Have we built this array already?
if ( ! $IsDataCalculated )
{
// Sort data
ksort( $this->Data );
// Clear array
$this->WindowedData = array();
// For all samples
foreach ( $this->Data as $x => $y )
{
// Within sample window?
if ( ( $x < $this->X_Min )
|| ( $x > $this->X_Max ) )
continue;
// Add to windowed data
$this->WindowedData[] =
array( "x" => $x , "y" => $y );
}
// Data is now calculated
$IsDataCalculated = true;
}
return $this->WindowedData;
}
/**
* Scale a Y data point into the Y window scale
*
* This function will translate the value in $y to a verticle
* pixel location in the image window. This function can be useful
* for drawing grid lines.
* @param $y The Y-value to translate
* @return translated Y-value
*/
public function ScaleY( $y )
{
// Compute delta Y
$Y_Delta = $this->Y_Max - $this->Y_Min;
// Strip minimum value
$y -= $this->Y_Min;
// Scale data
if ( $Y_Delta != 0 )
$y = ( $y * $this->Window->GetHeigth() ) / $Y_Delta;
else
$y = 0;
// Adjust for window height
$y = $this->Window->GetHeigth() - $y;
return $y;
}
/**
* Scale a X data point into the X window scale
*
* This function will translate the value in $x to a horizontal
* pixel location in the image window.
* @since verion 1.2
* @param $x The X-value to translate
* @return translated X-value
*/
public function ScaleX( $x )
{
// Scale x
$x -= $this->X_Min;
$x = $x * $this->Window->GetWidth();
$x /= $this->X_Max - $this->X_Min;
$x = round( $x );
return $x;
}
/**
* Calculate the lowest value of Y data
*
* @return Lowest Y value in data
*/
public function FindY_Min()
{
$WindowArray = $this->BuildWindowArray();
$Min = $WindowArray[ 0 ][ "y" ];
// For all data
for ( $Index = 0; $Index < count( $WindowArray ); ++$Index )
$Min = Min( $Min , $WindowArray[ $Index ][ "y" ] );
return $Min;
}
/**
* Calculate the highest value of Y data
*
* @return Highest Y value in data
*/
public function FindY_Max()
{
$WindowArray = $this->BuildWindowArray();
$Max = $WindowArray[ 0 ][ "y" ];
// For all data
for ( $Index = 0; $Index < count( $WindowArray ); ++$Index )
$Max = Max( $Max , $WindowArray[ $Index ][ "y" ] );
return $Max;
}
/**
* Number of data points
*
* Find the number of data points in the window
* @return Number of data points
*/
public function FindNumberOfDataPoints()
{
$WindowArray = $this->BuildWindowArray();
return count( $WindowArray );
}
/**
* Calculate the lowest value of X data
*
* @return Lowest X value in data
*/
public function FindX_Min()
{
$Min = min( array_keys( $this->Data ) );
return $Min;
}
/**
* Calculate the highest value of X data
* @return Highest X value in data
*/
public function FindX_Max()
{
$Max = max( array_keys( $this->Data ) );
return $Max;
}
/**
* Automaticlly adjust the min and max values
*
* Scale Y min/max to fit all the data. This will find the min and
* max values of Y and set Y-min and Y-max accordingly. A rounding
* paremters can be used to round down/up and min/max values. For
* example, a round value of 0.1 will round to the nearest 10th. If
* Y-max was .22 and Y-min was .15, the values used with be .3 and .1
* @param $Round Decimal to round to
*/
public function AutoScaleY_MinMax( $Round )
{
// Fetch data
$WindowArray = $this->BuildWindowArray();
$First = current( $WindowArray );
$Y_Max = $Y_Min = $First[ "y" ];
// For all data points
foreach ( $WindowArray as $Data )
{
$Y_Min = Min( $Y_Min , $Data[ "y" ] );
$Y_Max = Max( $Y_Max , $Data[ "y" ] );
}
// If rounding
if ( $Round > 0 )
{
$Round = 1 / $Round;
$Y_Min = floor( $Y_Min * $Round ) / $Round;
$Y_Max = ceil( $Y_Max * $Round ) / $Round;
}
// Set this as the new span
$this->SetY_Span( $Y_Min , $Y_Max );
}
/**
* Automaticlly adjust the Y min and max values
*
* @deprecated Use AutoScaleY_MinMax
*/
public function AdjustMinMax( $Round )
{
$this->AutoScaleY_MinMax( $Round );
}
/**
* Automaticlly adjust the X min and max values
*
* Scale X min/max to fit all the data. This will find the min and
* max values of X and set X-min and X-max accordingly. A rounding
* paremters can be used to round down/up and min/max values. For
* example, a round value of 0.1 will round to the nearest 10th. If
* X-max was .22 and X-min was .15, the values used with be .3 and .1
* @param $Round Decimal to round to
*/
public function AutoScaleX_MinMax( $Round = 0 )
{
$X_Min = min( array_keys( $this->Data ) );
$X_Max = max( array_keys( $this->Data ) );
// If rounding
if ( $Round > 0 )
{
$Round = 1 / $Round;
$X_Min = floor( $X_Min * $Round ) / $Round;
$X_Max = ceil( $X_Max * $Round ) / $Round;
}
// Set this as the new span
$this->SetX_Span( $X_Min , $X_Max );
}
/**
* Calculate mean average
*
* Calculate the mean average of data inside the plot
* @return Average
*/
public function CalculateMeanAverage()
{
// Fetch data
$WindowArray = $this->BuildWindowArray();
$Count = $n = count( $WindowArray );
// Do all sumations
$Average = 0.0;
for ( $Index = 0; $Index < $Count; ++$Index )
$Average += $WindowArray[ $Index ][ "y" ];
$Average /= $Count;
$this->Average = $Average;
}
/**
* Calculate slope and y-intercept of least square linear regression
*
* This function is needed if the data from the linear regression
* calculation is needed but the linear regression plot hasn't been drawn.
* After this function is called, <b>GetSlope</b> and <b>GetY_Intercept</b>
* will return valid information.
*/
public function CalculateLinearRegression()
{
$DeltaX = $this->X_Max - $this->X_Min;
// Fetch data
$WindowArray = $this->BuildWindowArray();
$Count = $n = count( $WindowArray );
// No data?
if ( $Count == 0 )
{
$this->Slope = $m = 0;
$this->Y_Intercept = $b = 0;
return;
}
// Do all sumations
$SumX = $SumY = $SumXY = $SumXX = $SumYY = 0.0;
for ( $Index = 0; $Index < $Count; ++$Index )
{
$x = $WindowArray[ $Index ][ "x" ] - $this->X_Min;
$y = $WindowArray[ $Index ][ "y" ];
$SumX += $x; // � x
$SumY += $y; // � y
$SumXY += $x * $y; // � xy
$SumXX += $x * $x; // � x^2
$SumYY += $y * $y; // � y^2
}
$SumX_SumX = $SumX * $SumX; // ( �x )^2
$SumY_SumY = $SumY * $SumY; // ( �y )^2
// Slope:
// n �xy - �x �y
// m = -------------------
// n �x^2 - ( �x )^2
//
$Numerator = ( $n * $SumXY ) - ( $SumX * $SumY );
$Denominator = ( $n * $SumXX ) - $SumX_SumX;
if ( $Denominator )
$m = $Numerator / $Denominator;
else
$m = 0;
// Y-intercept:
// �y - m �x
// b = -----------
// n
//
$b = ( $SumY - ( $m * $SumX ) ) / $n;
// Correlation coefficient
// n �xy - �x �y
// r = ---------------------------------------------
// �[ n �x^2 - ( �x )^2 ][ n �y^2 - ( �y )^2 ]
//
$Numerator = ( $n * $SumXY ) - ( $SumX * $SumY );
$AccumulatorX = ( $n * $SumXX ) - $SumX_SumX;
$AccumulatorY = ( $n * $SumYY ) - $SumY_SumY;
$Denominator = sqrt( $AccumulatorX * $AccumulatorY );
if ( $Denominator )
$r = $Numerator / $Denominator;
else
$r = 0;
// Save information
$this->Slope = $m;
$this->Y_Intercept = $b;
$this->Correlation = $r;
}
//------------------------------------------------------------------------
/**
* Draw points for each data point given
*
* This creates a point plot in the image specified during creation of
* the class. Each point is of the size specified by <b>SetCircleSize</b>
* and of the color specified by <b>SetColor</b>
*/
public function RenderPoints()
{
// Margins
$MarginLeft = $this->Window->GetX();
$MarginTop = $this->Window->GetY();
$MarginRight = $this->Window->GetXX();
$MarginBottom = $this->Window->GetYY();
$Height = $this->Window->GetHeigth();
// Delta Y
$Y_Delta = $this->Y_Max - $this->Y_Min;
// Delta X
$DeltaX = $this->X_Max - $this->X_Min;
// Get data points
$WindowArray = $this->BuildWindowArray();
// Number of data points
$IndexMax = count( $WindowArray ) ;
// For each point
for ( $Index = 0; $Index < $IndexMax; ++$Index )
{
// Scale x
$x = $this->ScaleX( $WindowArray[ $Index ][ "x" ] );
// Scale y
$y = $this->ScaleY( $WindowArray[ $Index ][ "y" ] );
// Draw point
ClippedFilledCircle(
$this->Image ,
$MarginLeft + $x ,
$MarginTop + $y ,
$this->CircleSize ,
$this->Color ,
$MarginLeft ,
$MarginTop ,
$MarginRight ,
$MarginBottom
);
}
}
/**
* Draw lines from point to point
*
* This creates a line plot in the image specified during creation of
* the class. Lines will connect all the points in the graph, sorted by
* their x-values. A gap in the chart will be left if the distance
* between two points is over the values specified with <b>SetMaxX_Distance</b>.
* The color of the lines is specified with <b>SetColor</b>
*/
public function RenderWithLines()
{
// Margins
$MarginLeft = $this->Window->GetX();
$MarginTop = $this->Window->GetY();
$MarginRight = $this->Window->GetXX();
$MarginBottom = $this->Window->GetYY();
// Delta Y
$Y_Delta = $this->Y_Max - $this->Y_Min;
// Delta X
$DeltaX = $this->X_Max - $this->X_Min;
// Get data points
$WindowArray = $this->BuildWindowArray();
// Number of data points
$IndexMax = count( $WindowArray ) ;
$LastY = null;
$LastX = 0;
$LastRawX = null;
// For each point
for ( $Index = 0; $Index < $IndexMax; ++$Index )
{
// Scale x
$x = $this->ScaleX( $WindowArray[ $Index ][ "x" ] );
// Scale y
$y = $this->ScaleY( $WindowArray[ $Index ][ "y" ] );
// Delta X between this sample and last
$X_Span = $WindowArray[ $Index ][ "x" ] - $LastRawX;
// Is this distance too great?
if ( ( $LastY === null )
|| ( ( $this->MaxX_Distance != 0 )
&& ( $X_Span > $this->MaxX_Distance ) ) )
{
$LastX = $x;
$LastY = $y;
$LastRawX = $WindowArray[ $Index ][ "x" ];
// Skip plotting this line
continue;
}
// Locations of line
$PointX = $MarginLeft + $LastX;
$PointY = $MarginTop + $LastY;
$PointXX = $MarginLeft + $x;
$PointYY = $MarginTop + $y;
// Draw clipped line
DrawClippedLine(
$this->Image ,
$PointX , $PointY ,
$PointXX , $PointYY ,
$MarginLeft , $MarginTop ,
$MarginRight , $MarginBottom ,
$this->Color );
// Set last coordinates to current
$LastX = $x;
$LastY = $y;
$LastRawX = $WindowArray[ $Index ][ "x" ];
}
}
/**
* Draw linear regression plot
*
* This function will compute and draw a line representing the mean
* square linear regression of the data within the window.
*/
public function RenderLinearRegression()
{
// Margins
$MarginLeft = $this->Window->GetX();
$MarginTop = $this->Window->GetY();
$MarginRight = $this->Window->GetXX();
$MarginBottom = $this->Window->GetYY();
// Calculate linear regression data
$this->CalculateLinearRegression();
$DeltaX = $this->X_Max - $this->X_Min;
$m = $this->Slope;
$b = $this->Y_Intercept;
// Standard line function is y = mx + b
$y = $this->ScaleY( $b , false );
$yy = $this->ScaleY( ( $m * $DeltaX ) + $b , false );
$x = $MarginLeft;
$y = $MarginTop + $y + $Index;
$xx = $MarginRight;
$yy = $MarginTop + $yy + $Index;
// Draw line
for ( $Index = 0; $Index < $this->LinearRegressionThickness; ++$Index )
{
$Y_Offset = floor( $Index - ( $this->LinearRegressionThickness / 2 ) );
DrawClippedLine(
$this->Image ,
$x , $y + $Y_Offset ,
$xx , $yy + $Y_Offset ,
$MarginLeft , $MarginTop ,
$MarginRight , $MarginBottom ,
$this->LinearRegressionColor );
}
}
/**
* Draw mean average line
*
* This creates a line denoting the mean average in the image specified
* during creation of the class.
*/
public function RenderMeanPlot()
{
// Margins
$MarginLeft = $this->Window->GetX();
$MarginTop = $this->Window->GetY();
$MarginRight = $this->Window->GetXX();
// Calculate average
$this->CalculateMeanAverage();
// Standard line function is y = mx + b
$y = $this->ScaleY( $this->Average );
// Draw average
for ( $Index = 0; $Index < $this->AverageWidth; ++$Index )
imageline(
$this->Image ,
$MarginLeft , $MarginTop + $y + $Index ,
$MarginRight , $MarginTop + $y + $Index ,
$this->LinearAverageColor );
}
//------------------------------------------------------------------------
// Grids
//------------------------------------------------------------------------
/**
* Font size for verticle major divisions
*
* This sets the size of the font (PHP's internal fonts for GD) used
* for verticle lables
* @param $Value new font size must be between 1 and 5
* @since verion 1.2
*/
function SetY_MajorTextFontSize( $Value )
{
assert( $Value >= 1 );
assert( $Value <= 5 );
$this->Y_MajorTextFontSize = $Value;
}
/**
* Get font size for verticle divisions
*
* Return the font size for verticle divisison
* @return font size for verticle divisions
* @since verion 1.2
*/
function GetY_MajorTextFontSize()
{
return $this->Y_MajorTextFontSize;
}
/**
* Major verticle division extention
*
* Number of pixels past the margin to extend major verticle divisions
* Default is 0.
* @param $Value Pixel to extend
* @since verion 1.2
*/
function SetY_MajorDivisionExtention( $Value )
{
$this->Y_MajorDivisionExtention = $Value;
}
/**
* Major verticle division extention
*
* Current number of pixels past the margin to extend major verticle
* divisions
* @return verticle division extention
* @since verion 1.2
*/
function GetY_MajorDivisionExtention()
{
return $this->Y_MajorDivisionExtention;
}
/**
* Major verticle scale
*
* These are the increments in which the major verticle scale
* is labled. For example, a value of 50 would place divides every
* 50 units.
* @param $Value Scale
* @since verion 1.2
*/
function SetY_MajorDivisionScale( $Value )
{
$this->Y_MajorDivisionScale = $Value;
}
/**
* Get major verticle scale
*
* Return increments in which the major verticle scale
* is labled.
* @return increments of scale
* @since verion 1.2
*/
function GetY_MajorDivisionScale()
{
return $this->Y_MajorDivisionScale;
}
/**
* Color of major verticle divisions
*
* Set the color of major verticle divisions
* @param $Value Allocated color in the image
* @since verion 1.2
*/
function SetY_MajorDivisionColor( $Value )
{
$this->Y_MajorDivisionColor = $Value;
}
/**
* Get color of major verticle divisions
*
* Return the color of major verticle divisions
* @return color
* @since verion 1.2
*/
function GetY_MajorDivisionColor()
{
return $this->Y_MajorDivisionColor;
}
/**
* Color of major verticle division lables
*
* Set color of major verticle divisions lables
* @param $Value Allocated color in the image
* @since verion 1.2
*/
function SetY_MajorDivisionTextColor( $Value )
{
$this->Y_MajorDivisionTextColor = $Value;
}
/**
* Get color of major verticle division lables
*
* Return the current color of text lables for verticle division lables
* @return color
* @since verion 1.2
*/
function GetY_MajorDivisionTextColor()
{
return $this->Y_MajorDivisionTextColor;
}
/**
* Text callback for verticle division lables
*
* <p>
* Assigns a custom callback for creating a string for verticle
* lables. The callback function is passed 3 parameters: a value,
* x location and y location. The callback function can do one of two
* things.
* <p>
* The callback function may return a string with the value properly
* formatted.
* <code>
* function FormatCallback( $Value )
* {
* return "$" . number_format( $Value , 2 );
* }
* </code>
* <p>
* This function is an example of a simple callback, formatting the
* number as a dollor amount.
* <p>
* The other option is to make place custom text at the location
* <code>
* function FormatCallback( $Value , $x , $y )
* {
* // Price for this division
* imagestring(
* $Image ,
* $FontSize ,
* $x + $CenterBias , $y ,
* $String ,
* $ColorMap[ "Black" ] );
*
* return "";
* }
* </code>
* <p>
* This example (which is not functional) shows how such a function might
* look. The parameters of $x and $y are the location of the point at
* which the text is to be placed. By returning nothing, XY Plot will
* print nothing to the chart.
* @param $Value callback function
* @since verion 1.2
*/
function SetY_MajorTextCallback( $Value )
{
$this->Y_MajorTextCallback = $Value;
}
/**
* Get text callback for verticle division lables
*
* Return the callback function for verticle division lables
* @return function name
* @since verion 1.2
*/
function GetY_MajorTextCallback()
{
return $this->Y_MajorTextCallback;
}
/**
* Major verticle divisions
*
* Draw major verticle (top to bottom) divisions and add text lables
* @since verion 1.2
*/
function DrawY_MajorDivisions()
{
// Margins
$MarginLeft = $this->Window->GetX();
$MarginTop = $this->Window->GetY();
$MarginRight = $this->Window->GetXX();
$MarginBottom = $this->Window->GetYY();
// Round off start and end of horizontal divisions
$Round = 1.0 / $this->Y_MajorDivisionScale;
$Y_MinStart = floor( $this->Y_Min * $Round ) / $Round;
$Y_MaxStart = ceil( $this->Y_Max * $Round ) / $Round;
$Y_Position = $Y_MaxStart;
// While more divisions remain to be drawn
while ( $Y_Position >= $Y_MinStart )
{
// Get vertical location of line
$y = round( $this->ScaleY( $Y_Position , false ) );
$y += $MarginTop;
// Is this within the margins?
if ( ( $y < $MarginTop )
|| ( $y > $MarginBottom ) )
{
$Y_Position -= $this->Y_MajorDivisionScale;
continue;
}
$x = $MarginRight + $this->Y_MajorDivisionExtention;
// Division line
imageline(
$this->Image ,
$MarginLeft ,
$y ,
$x ,
$y ,
$this->Y_MajorDivisionColor );
$Callback = $this->Y_MajorTextCallback;
// Lable for division
if ( $Callback )
$String = $Callback( $Y_Position , $x , $y );
else
$String = number_format( $Y_Position , 0 );
// If there is a string to print
if ( $String != "" )
{
$CenterBias = ImageFontHeight( $this->Y_MajorTextFontSize );
$CenterBias /= 2;
$CenterBias = -round( $CenterBias );
// Price for this division
imagestring(
$this->Image ,
$this->Y_MajorTextFontSize ,
$x + 4 , $y + $CenterBias ,
$String ,
$this->Y_MajorDivisionTextColor );
}
// Next...
$Y_Position -= $this->Y_MajorDivisionScale;
}
}
/**
* Minor verticle scale
*
* These are the increments in which the minor verticle scale
* is labled. For example, a value of 50 would place divides every
* 50 units.
* @param $Value Scale
* @since verion 1.2
*/
function SetY_MinorDivisionScale( $Value )
{
$this->Y_MinorDivisionScale = $Value;
}
/**
* Get minor verticle scale
*
* Return increments in which the minor verticle scale
* is labled.
* @return increments of scale
* @since verion 1.2
*/
function GetY_MinorDivisionScale()
{
return $this->Y_MinorDivisionScale;
}
/**
* Color of minor verticle divisions
*
* Set the color of minor verticle divisions
* @param $Value Allocated color in the image
* @since verion 1.2
*/
function SetY_MinorDivisionColor( $Value )
{
$this->Y_MinorDivisionColor = $Value;
}
/**
* Get color of minor verticle divisions
*
* Return the color of minor verticle divisions
* @return color
* @since verion 1.2
*/
function GetY_MinorDivisionColor()
{
return $this->Y_MinorDivisionColor;
}
/**
* Minor verticle divisions
*
* Draw minor verticle (top to bottom) divisions
* @since verion 1.2
*/
function DrawY_MinorDivisions()
{
// Margins
$MarginLeft = $this->Window->GetX();
$MarginTop = $this->Window->GetY();
$MarginRight = $this->Window->GetXX();
$MarginBottom = $this->Window->GetYY();
// Round off start and end of horizontal divisions
$Round = 1.0 / $this->Y_MinorDivisionScale;
$Y_MinStart = floor( $this->Y_Min * $Round ) / $Round;
$Y_MaxStart = ceil( $this->Y_Max * $Round ) / $Round;
$Y_Position = $Y_MaxStart;
// While more divisions remain to be drawn
while ( $Y_Position >= $Y_MinStart )
{
// Get vertical location of line
$y = round( $this->ScaleY( $Y_Position , false ) );
$y += $MarginTop;
// Is this within the margins?
if ( ( $y < $MarginTop )
|| ( $y > $MarginBottom ) )
{
$Y_Position -= $this->Y_MinorDivisionScale;
continue;
}
$x = $MarginRight + $this->Y_MinorDivisionExtention;
// Division line
imageline(
$this->Image ,
$MarginLeft ,
$y ,
$x ,
$y ,
$this->Y_MinorDivisionColor );
// Next...
$Y_Position -= $this->Y_MinorDivisionScale;
}
}
/**
* Custom major horizontal start value
*
* This is the value from which to start the grid lines.
* Set to null to disable (default).
* @param $Value Value to start from
* @since verion 1.2
*/
function SetX_MajorCustomStart( $Value )
{
$this->X_MajorCustomStart = $Value;
}
/**
* Return custom start value
* @return Custom start value
* @since verion 1.2
*/
function GetX_MajorCustomStart()
{
return $this->X_MajorCustomStart;
}
/**
* Custom major horizontal division increment function
*
* The custom increment function must take a value and return
* the next value. This function is useful if the graph is not
* split up linearly, such as with logarithmic divisions or divisions
* by month.
* @param $Value A custom increment function
* @since verion 1.2
*/
function SetX_MajorCustomIncrement( $Value )
{
$this->X_MajorCustomIncrement = $Value;
}
/**
* Get custom major horizontal division increment function
*
* @return major horizontal division increment function
* @since verion 1.2
*/
function GetX_MajorCustomIncrement()
{
return $this->X_MajorCustomIncrement;
}
/**
* Custom major horizontal division end value
*
* A value at which to stop making division marks. This can be useful
* if the division lines stop before the end of the chat
* @param $Value X value at which to stop
* @since verion 1.2
*/
function SetX_MajorCustomEnd( $Value )
{
$this->X_MajorCustomEnd = $Value;
}
/**
* Get custom major horizontal division end value
* @return custom major horizontal division end value
* @since verion 1.2
*/
function GetX_MajorCustomEnd()
{
return $this->X_MajorCustomEnd;
}
/**
* Font size for horizontal major divisions
*
* This sets the size of the font (PHP's internal fonts for GD) used
* for horizontal lables
* @param $Value new font size (must be between 1 and 5)
* @since verion 1.2
*/
function SetX_MajorTextFontSize( $Value )
{
assert( $Value >= 1 );
assert( $Value <= 5 );
$this->X_MajorTextFontSize = $Value;
}
/**
* Get font size for horizontal divisions
*
* Return the font size for horizontal divisison
* @return font size for horizontal divisions
* @since verion 1.2
*/
function GetX_MajorTextFontSize()
{
return $this->X_MajorTextFontSize;
}
/**
* Major horizontal division extention
*
* Number of pixels past the margin to extend major horizontal divisions
* Default is 0.
* @param $Value Pixel to extend
* @since verion 1.2
*/
function SetX_MajorDivisionExtention( $Value )
{
$this->X_MajorDivisionExtention = $Value;
}
/**
* Major horizontal division extention
*
* Current number of pixels past the margin to extend major horizontal
* divisions
* @return horizontal division extention
* @since verion 1.2
*/
function GetX_MajorDivisionExtention()
{
return $this->X_MajorDivisionExtention;
}
/**
* Major horizontal scale
*
* These are the increments in which the major horizontal scale
* is labled. For example, a value of 50 would place divides every
* 50 units.
* @param $Value Scale
* @since verion 1.2
*/
function SetX_MajorDivisionScale( $Value )
{
$this->X_MajorDivisionScale = $Value;
}
/**
* Get major horizontal scale
*
* Return increments in which the major horizontal scale
* is labled.
* @return increments of scale
* @since verion 1.2
*/
function GetX_MajorDivisionScale()
{
return $this->X_MajorDivisionScale;
}
/**
* Color of horizontal divisions
*
* Set the color of major horizontal divisions
* @param $Value Allocated color in the image
* @since verion 1.2
*/
function SetX_MajorDivisionColor( $Value )
{
$this->X_MajorDivisionColor = $Value;
}
/**
* Get color of major horitontal divisions
*
* Return the color of major horitontal divisions
* @return color
* @since verion 1.2
*/
function GetX_MajorDivisionColor()
{
return $this->X_MajorDivisionColor;
}
/**
* Color of major horitontal division lables
*
* Set color of major horitontal divisions lables
* @param $Value Allocated color in the image
* @since verion 1.2
*/
function SetX_MajorDivisionTextColor( $Value )
{
$this->X_MajorDivisionTextColor = $Value;
}
/**
* Get color of major horitontal division lables
*
* Return the current color of text lables for horitontal division lables
* @return color
* @since verion 1.2
*/
function GetX_MajorDivisionTextColor()
{
return $this->X_MajorDivisionTextColor;
}
/**
* Text callback for horitontal division lables
*
* <p>
* Assigns a custom callback for creating a string for verticle
* lables. The callback function is passed 3 parameters: a value,
* x location and y location. The callback function can do one of two
* things.
* <p>
* The callback function may return a string with the value properly
* formatted.
* <code>
* function FormatCallback( $Value )
* {
* return "$" . number_format( $Value , 2 );
* }
* </code>
* <p>
* This function is an example of a simple callback, formatting the
* number as a dollor amount.
* <p>
* The other option is to make place custom text at the location
* <code>
* function FormatCallback( $Value , $x , $y )
* {
* // Price for this division
* imagestring(
* $Image ,
* $FontSize ,
* $x + $CenterBias , $y ,
* $String ,
* $ColorMap[ "Black" ] );
*
* return "";
* }
* </code>
* <p>
* This example (which is not functional) shows how such a function might
* look. The parameters of $x and $y are the location of the point at
* which the text is to be placed. By returning nothing, XY Plot will
* print nothing to the chart.
* @param $Value callback function
* @since verion 1.2
*/
function SetX_MajorTextCallback( $Value )
{
$this->X_MajorTextCallback = $Value;
}
/**
* Get text callback for horitontal division lables
*
* Return the callback function for horitontal division lables
* @return function name
* @since verion 1.2
*/
function GetX_MajorTextCallback()
{
return $this->X_MajorTextCallback;
}
/**
* Major horizontal divisions
*
* Draw major horizontal (left to right) divisions and add text lables
* @since verion 1.2
*/
function DrawX_MajorDivisions()
{
// Margins
$MarginLeft = $this->Window->GetX();
$MarginTop = $this->Window->GetY();
$MarginRight = $this->Window->GetXX();
$MarginBottom = $this->Window->GetYY();
// Round off start and end of horizontal divisions
$Round = 1.0 / $this->X_MajorDivisionScale;
// Start
if ( $this->X_MajorCustomStart === null )
$X_Position = $X_MinStart = floor( $this->X_Min * $Round ) / $Round;
else
$X_Position = $this->X_MajorCustomStart;
// End
if ( $this->X_MajorCustomEnd === null )
$X_MaxStart = ceil( $this->X_Max * $Round ) / $Round;
else
$X_MaxStart = $this->X_MajorCustomEnd;
$IncFunction = $this->X_MajorCustomIncrement;
// While more divisions remain to be drawn
while ( $X_Position <= $X_MaxStart )
{
// Get vertical location of line
$x = round( $this->ScaleX( $X_Position , false ) );
$x += $MarginLeft;
// Is this within the margins?
if ( ( $x < $MarginLeft )
|| ( $x > $MarginRight ) )
{
if ( $IncFunction === null )
$X_Position += $this->X_MajorDivisionScale;
else
$X_Position = $IncFunction( $X_Position );
continue;
}
$y = $MarginBottom + $this->X_MajorDivisionExtention;
// Division line
imageline(
$this->Image ,
$x ,
$MarginTop ,
$x ,
$y ,
$this->X_MajorDivisionColor );
$Callback = $this->X_MajorTextCallback;
// Lable for division
if ( $Callback )
$String = $Callback( $X_Position , $x , $y );
else
$String = number_format( $X_Position , 0 );
if ( $String != "" )
{
// Center the text
$CenterBias = strlen( $String );
$CenterBias *= ImageFontWidth( $this->X_MajorTextFontSize );
$CenterBias /= 2;
$CenterBias = -round( $CenterBias );
// Price for this division
imagestring(
$this->Image ,
$this->X_MajorTextFontSize ,
$x + $CenterBias , $y ,
$String ,
$this->X_MajorDivisionTextColor );
}
// Next...
if ( $IncFunction === null )
$X_Position += $this->X_MajorDivisionScale;
else
$X_Position = $IncFunction( $X_Position );
}
}
/**
* Custom Minor horizontal start value
*
* This is the value from which to start the grid lines.
* Set to null to disable (default).
* @param $Value Value to start from
* @since verion 1.2
*/
function SetX_MinorCustomStart( $Value )
{
$this->X_MinorCustomStart = $Value;
}
/**
* Return custom start value
* @return Custom start value
* @since verion 1.2
*/
function GetX_MinorCustomStart()
{
return $this->X_MinorCustomStart;
}
/**
* Custom Minor horizontal division increment function
*
* The custom increment function must take a value and return
* the next value. This function is useful if the graph is not
* split up linearly, such as with logarithmic divisions or divisions
* by month.
* @param $Value A custom increment function
* @since verion 1.2
*/
function SetX_MinorCustomIncrement( $Value )
{
$this->X_MinorCustomIncrement = $Value;
}
/**
* Get custom Minor horizontal division increment function
*
* @return Minor horizontal division increment function
* @since verion 1.2
*/
function GetX_MinorCustomIncrement()
{
return $this->X_MinorCustomIncrement;
}
/**
* Custom Minor horizontal division end value
*
* A value at which to stop making division marks. This can be useful
* if the division lines stop before the end of the chat
* @param $Value X value at which to stop
* @since verion 1.2
*/
function SetX_MinorCustomEnd( $Value )
{
$this->X_MinorCustomEnd = $Value;
}
/**
* Get custom Minor horizontal division end value
* @return custom Minor horizontal division end value
* @since verion 1.2
*/
function GetX_MinorCustomEnd()
{
return $this->X_MinorCustomEnd;
}
/**
* Minor horizontal scale
*
* These are the increments in which the minor horizontal scale
* is labled. For example, a value of 50 would place divides every
* 50 units.
* @param $Value Scale
* @since verion 1.2
*/
function SetX_MinorDivisionScale( $Value )
{
$this->X_MinorDivisionScale = $Value;
}
/**
* Get minor horizontal scale
*
* Return increments in which the minor horizontal scale
* is labled.
* @return increments of scale
* @since verion 1.2
*/
function GetX_MinorDivisionScale()
{
return $this->X_MinorDivisionScale;
}
/**
* Color of horizontal divisions
*
* Set the color of minor horizontal divisions
* @param $Value Allocated color in the image
* @since verion 1.2
*/
function SetX_MinorDivisionColor( $Value )
{
$this->X_MinorDivisionColor = $Value;
}
/**
* Get color of minor horitontal divisions
*
* Return the color of minor horitontal divisions
* @return color
* @since verion 1.2
*/
function GetX_MinorDivisionColor()
{
return $this->X_MinorDivisionColor;
}
/**
* Minor horizontal divisions
*
* Draw minor horizontal (left to right) divisions
* @since verion 1.2
*/
function DrawX_MinorDivisions()
{
// Margins
$MarginLeft = $this->Window->GetX();
$MarginTop = $this->Window->GetY();
$MarginRight = $this->Window->GetXX();
$MarginBottom = $this->Window->GetYY();
// Round off start and end of horizontal divisions
$Round = 1.0 / $this->X_MinorDivisionScale;
// Start
if ( $this->X_MinorCustomStart === null )
$X_Position = $X_MinStart = floor( $this->X_Min * $Round ) / $Round;
else
$X_Position = $this->X_MinorCustomStart;
// End
if ( $this->X_MinorCustomEnd === null )
$X_MaxStart = ceil( $this->X_Max * $Round ) / $Round;
else
$X_MaxStart = $this->X_MinorCustomEnd;
$IncFunction = $this->X_MinorCustomIncrement;
// While more divisions remain to be drawn
while ( $X_Position <= $X_MaxStart )
{
// Get vertical location of line
$x = round( $this->ScaleX( $X_Position , false ) );
$x += $MarginLeft;
// Is this within the margins?
if ( ( $x < $MarginLeft )
|| ( $x > $MarginRight ) )
{
if ( $IncFunction === null )
$X_Position += $this->X_MinorDivisionScale;
else
$X_Position = $IncFunction( $X_Position );
continue;
}
$y = $MarginBottom + $this->X_MinorDivisionExtention;
// Division line
imageline(
$this->Image ,
$x ,
$MarginTop ,
$x ,
$y ,
$this->X_MinorDivisionColor );
// Next...
if ( $IncFunction === null )
$X_Position += $this->X_MinorDivisionScale;
else
$X_Position = $IncFunction( $X_Position );
}
}
}
// Hardware: the parts of a computer that can be kicked. ~Jeff Pesis
?>