1985 lines
51 KiB
PHP
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
|
|
|
|
?>
|