2019-07-17 20:31:04 +00:00
< ? php
2019-07-17 20:08:50 +00:00
/*=======================================================================
2019-07-17 20:31:04 +00:00
// File: JPGRAPH_REGSTAT.PHP
// Description: Regression and statistical analysis helper classes
// Created: 2002-12-01
// Ver: $Id: jpgraph_regstat.php 1131 2009-03-11 20:08:24Z ljp $
//
// Copyright (c) Aditus Consulting. All rights reserved.
//========================================================================
*/
2019-07-17 20:08:50 +00:00
//------------------------------------------------------------------------
// CLASS Spline
// Create a new data array from an existing data array but with more points.
// The new points are interpolated using a cubic spline algorithm
//------------------------------------------------------------------------
class Spline {
// 3:rd degree polynom approximation
private $xdata , $ydata ; // Data vectors
2019-07-17 20:31:04 +00:00
private $y2 ; // 2:nd derivate of ydata
2019-07-17 20:08:50 +00:00
private $n = 0 ;
2019-07-17 20:31:04 +00:00
function __construct ( $xdata , $ydata ) {
$this -> y2 = array ();
$this -> xdata = $xdata ;
$this -> ydata = $ydata ;
$n = count ( $ydata );
$this -> n = $n ;
if ( $this -> n !== count ( $xdata ) ) {
JpGraphError :: RaiseL ( 19001 );
//('Spline: Number of X and Y coordinates must be the same');
}
// Natural spline 2:derivate == 0 at endpoints
$this -> y2 [ 0 ] = 0.0 ;
$this -> y2 [ $n - 1 ] = 0.0 ;
$delta [ 0 ] = 0.0 ;
// Calculate 2:nd derivate
for ( $i = 1 ; $i < $n - 1 ; ++ $i ) {
$d = ( $xdata [ $i + 1 ] - $xdata [ $i - 1 ]);
if ( $d == 0 ) {
JpGraphError :: RaiseL ( 19002 );
//('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
}
$s = ( $xdata [ $i ] - $xdata [ $i - 1 ]) / $d ;
$p = $s * $this -> y2 [ $i - 1 ] + 2.0 ;
$this -> y2 [ $i ] = ( $s - 1.0 ) / $p ;
$delta [ $i ] = ( $ydata [ $i + 1 ] - $ydata [ $i ]) / ( $xdata [ $i + 1 ] - $xdata [ $i ]) -
( $ydata [ $i ] - $ydata [ $i - 1 ]) / ( $xdata [ $i ] - $xdata [ $i - 1 ]);
$delta [ $i ] = ( 6.0 * $delta [ $i ] / ( $xdata [ $i + 1 ] - $xdata [ $i - 1 ]) - $s * $delta [ $i - 1 ]) / $p ;
}
// Backward substitution
for ( $j = $n - 2 ; $j >= 0 ; -- $j ) {
$this -> y2 [ $j ] = $this -> y2 [ $j ] * $this -> y2 [ $j + 1 ] + $delta [ $j ];
}
2019-07-17 20:08:50 +00:00
}
// Return the two new data vectors
function Get ( $num = 50 ) {
2019-07-17 20:31:04 +00:00
$n = $this -> n ;
$step = ( $this -> xdata [ $n - 1 ] - $this -> xdata [ 0 ]) / ( $num - 1 );
$xnew = array ();
$ynew = array ();
$xnew [ 0 ] = $this -> xdata [ 0 ];
$ynew [ 0 ] = $this -> ydata [ 0 ];
for ( $j = 1 ; $j < $num ; ++ $j ) {
$xnew [ $j ] = $xnew [ 0 ] + $j * $step ;
$ynew [ $j ] = $this -> Interpolate ( $xnew [ $j ]);
}
return array ( $xnew , $ynew );
2019-07-17 20:08:50 +00:00
}
// Return a single interpolated Y-value from an x value
function Interpolate ( $xpoint ) {
2019-07-17 20:31:04 +00:00
$max = $this -> n - 1 ;
$min = 0 ;
2019-07-17 20:08:50 +00:00
2019-07-17 20:31:04 +00:00
// Binary search to find interval
while ( $max - $min > 1 ) {
$k = ( $max + $min ) / 2 ;
if ( $this -> xdata [ $k ] > $xpoint )
$max = $k ;
else
$min = $k ;
}
2019-07-17 20:08:50 +00:00
2019-07-17 20:31:04 +00:00
// Each interval is interpolated by a 3:degree polynom function
$h = $this -> xdata [ $max ] - $this -> xdata [ $min ];
2019-07-17 20:08:50 +00:00
2019-07-17 20:31:04 +00:00
if ( $h == 0 ) {
JpGraphError :: RaiseL ( 19002 );
//('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
}
2019-07-17 20:08:50 +00:00
2019-07-17 20:31:04 +00:00
$a = ( $this -> xdata [ $max ] - $xpoint ) / $h ;
$b = ( $xpoint - $this -> xdata [ $min ]) / $h ;
return $a * $this -> ydata [ $min ] + $b * $this -> ydata [ $max ] +
(( $a * $a * $a - $a ) * $this -> y2 [ $min ] + ( $b * $b * $b - $b ) * $this -> y2 [ $max ]) * ( $h * $h ) / 6.0 ;
2019-07-17 20:08:50 +00:00
}
}
//------------------------------------------------------------------------
// CLASS Bezier
// Create a new data array from a number of control points
//------------------------------------------------------------------------
class Bezier {
2019-07-17 20:31:04 +00:00
/**
* @ author Thomas Despoix , openXtrem company
* @ license released under QPL
* @ abstract Bezier interoplated point generation ,
* computed from control points data sets , based on Paul Bourke algorithm :
* http :// local . wasp . uwa . edu . au /~ pbourke / geometry / bezier / index2 . html
*/
2019-07-17 20:08:50 +00:00
private $datax = array ();
private $datay = array ();
private $n = 0 ;
2019-07-17 20:31:04 +00:00
function __construct ( $datax , $datay , $attraction_factor = 1 ) {
// Adding control point multiple time will raise their attraction power over the curve
$this -> n = count ( $datax );
if ( $this -> n !== count ( $datay ) ) {
JpGraphError :: RaiseL ( 19003 );
//('Bezier: Number of X and Y coordinates must be the same');
}
$idx = 0 ;
foreach ( $datax as $datumx ) {
for ( $i = 0 ; $i < $attraction_factor ; $i ++ ) {
$this -> datax [ $idx ++ ] = $datumx ;
}
}
$idx = 0 ;
foreach ( $datay as $datumy ) {
for ( $i = 0 ; $i < $attraction_factor ; $i ++ ) {
$this -> datay [ $idx ++ ] = $datumy ;
}
}
$this -> n *= $attraction_factor ;
2019-07-17 20:08:50 +00:00
}
2019-07-17 20:31:04 +00:00
/**
* Return a set of data points that specifies the bezier curve with $steps points
* @ param $steps Number of new points to return
* @ return array ( $datax , $datay )
*/
2019-07-17 20:08:50 +00:00
function Get ( $steps ) {
2019-07-17 20:31:04 +00:00
$datax = array ();
$datay = array ();
for ( $i = 0 ; $i < $steps ; $i ++ ) {
list ( $datumx , $datumy ) = $this -> GetPoint (( double ) $i / ( double ) $steps );
$datax [ $i ] = $datumx ;
$datay [ $i ] = $datumy ;
}
$datax [] = end ( $this -> datax );
$datay [] = end ( $this -> datay );
return array ( $datax , $datay );
2019-07-17 20:08:50 +00:00
}
2019-07-17 20:31:04 +00:00
/**
* Return one point on the bezier curve . $mu is the position on the curve where $mu is in the
* range 0 $mu < 1 where 0 is tha start point and 1 is the end point . Note that every newly computed
* point depends on all the existing points
*
* @ param $mu Position on the bezier curve
* @ return array ( $x , $y )
*/
2019-07-17 20:08:50 +00:00
function GetPoint ( $mu ) {
2019-07-17 20:31:04 +00:00
$n = $this -> n - 1 ;
$k = 0 ;
$kn = 0 ;
$nn = 0 ;
$nkn = 0 ;
$blend = 0.0 ;
$newx = 0.0 ;
$newy = 0.0 ;
$muk = 1.0 ;
$munk = ( double ) pow ( 1 - $mu ,( double ) $n );
for ( $k = 0 ; $k <= $n ; $k ++ ) {
$nn = $n ;
$kn = $k ;
$nkn = $n - $k ;
$blend = $muk * $munk ;
$muk *= $mu ;
$munk /= ( 1 - $mu );
while ( $nn >= 1 ) {
$blend *= $nn ;
$nn -- ;
if ( $kn > 1 ) {
$blend /= ( double ) $kn ;
$kn -- ;
}
if ( $nkn > 1 ) {
$blend /= ( double ) $nkn ;
$nkn -- ;
}
}
$newx += $this -> datax [ $k ] * $blend ;
$newy += $this -> datay [ $k ] * $blend ;
}
return array ( $newx , $newy );
2019-07-17 20:08:50 +00:00
}
}
// EOF
?>