Smooth Curve Graphs with PHP and GD. Today I have new article for PHP. I will tell you about drawing graphs with GD. Plus – we will smooth our graph with curve lines using cubic splines interpolation method. You can read more about method at Wiki.
[sociallocker]
[/sociallocker]
Now – download the source files and lets start coding !
Step 1. HTML
Here are HTML layout for our example page:
index.html
04 |
<meta charset= "utf-8" /> |
05 |
<title>Smooth Curve Graphs with PHP and GD | Script Tutorials</title> |
06 |
<link href= "css/main.css" rel= "stylesheet" type= "text/css" /> |
09 |
<div class = "container" > |
10 |
<img src= "smooth_graph.php" alt= "" /> |
13 |
<h2>Smooth Curve Graphs with PHP and GD</h2> |
Step 2. CSS
Now, lets define all used styles:
css/main.css
06 |
background-repeat : no-repeat ; |
07 |
background-color : #bababa ; |
08 |
background-image : -webkit-radial-gradient( 600px 200px , circle , #eee , #bababa 40% ); |
09 |
background-image : -moz-radial-gradient( 600px 200px , circle , #eee , #bababa 40% ); |
10 |
background-image : -o-radial-gradient( 600px 200px , circle , #eee , #bababa 40% ); |
11 |
background-image : radial-gradient( 600px 200px , circle , #eee , #bababa 40% ); |
13 |
font : 14px / 1.3 Arial , sans-serif ; |
17 |
background-color : #212121 ; |
19 |
box-shadow: 0 -1px 2px #111111 ; |
36 |
footer a.stuts,a.stuts:visited{ |
43 |
margin : 23px 0 0 110px ; |
53 |
border : 3px #111 solid ; |
60 |
-moz-border-radius: 15px ; |
61 |
-webkit-border-radius: 15px ; |
And first step and second – not very important of course, most important will now:
Step 3. PHP
This is our image generator (of graph):
smooth_graph.php
03 |
define( 'GRAPH_WIDTH' , 500); |
04 |
define( 'GRAPH_HEIGHT' , 400); |
05 |
include_once ( 'classes/Plot.php' ); |
06 |
include_once ( 'classes/CubicSplines.php' ); |
08 |
$dx = (GRAPH_WIDTH - 40) / ( $iPoints - 1); |
10 |
for ( $i = 0; $i < $iPoints ; $i ++) { |
11 |
$y = rand(20, GRAPH_HEIGHT - 20); |
15 |
$vImagegHeight = GRAPH_HEIGHT + 30; |
16 |
$vImage = imagecreatetruecolor(GRAPH_WIDTH + 50, $vImagegHeight ); |
17 |
$vBgColor = imagecolorallocate( $vImage , 160, 160, 160); |
18 |
$vTextColor = imagecolorallocate( $vImage , 0, 0, 0); |
19 |
$vAxisColor = imagecolorallocate( $vImage , 0, 0, 0); |
20 |
$vDotColor = imagecolorallocate( $vImage , 192, 64, 64); |
21 |
imagefill( $vImage , 0, 0, $vBgColor ); |
22 |
$oPlot = new Plot( $aCoords ); |
23 |
$oPlot ->drawDots( $vImage , $vDotColor , 10, GRAPH_HEIGHT, 8); |
24 |
$oCurve = new CubicSplines(); |
25 |
$vColor = imagecolorallocate( $vImage , 225, 64, 64); |
26 |
$iStart = microtime(1); |
28 |
$oCurve ->setInitCoords( $aCoords , 1); |
29 |
$r = $oCurve ->processCoords(); |
31 |
$curveGraph = new Plot( $r ); |
37 |
$curveGraph ->drawLine( $vImage , $vColor , 10, GRAPH_HEIGHT); |
39 |
$sTime = sprintf( "%1.4f" , microtime(1) - $iStart ); |
40 |
imagefilledrectangle( $vImage , 0, GRAPH_HEIGHT, GRAPH_WIDTH + 50, $vImagegHeight , $vBgColor ); |
41 |
$oPlot ->drawAxis( $vImage , $vAxisColor , 10, GRAPH_HEIGHT); |
42 |
$iPanelY = GRAPH_HEIGHT; |
43 |
imagefilledrectangle( $vImage , 10, $iPanelY + 10, 20, $iPanelY + 20, $vColor ); |
44 |
imagerectangle( $vImage , 10, $iPanelY + 10, 20, $iPanelY + 20, $vAxisColor ); |
45 |
imagettftext( $vImage , 10, 0, 30, $iPanelY + 20, $vTextColor , 'Ds-digib.ttf' , 'Cubic splines in PHP for graphs: ' . $sTime . ' sec' ); |
46 |
header( "Content-type: image/png" ); |
48 |
imagedestroy( $vImage ); |
In this file I using 2 another classes (as separated service classes):
classes/Plot.php
04 |
function __construct(& $aCoords ) { |
05 |
$this ->aCoords = & $aCoords ; |
07 |
public function drawLine( $vImage , $vColor , $iPosX = 0, $iPosY = false) { |
09 |
$iPosY = imagesy( $vImage ); |
10 |
reset( $this ->aCoords); |
11 |
list( $iPrevX , $iPrevY ) = each( $this ->aCoords); |
12 |
while (list ( $x , $y ) = each( $this ->aCoords)) { |
13 |
imageline( $vImage , $iPosX + round ( $iPrevX ), $iPosY - round ( $iPrevY ), $iPosX + round ( $x ), $iPosY - round ( $y ), $vColor ); |
18 |
public function drawDots( $vImage , $vColor , $iPosX = 0, $iPosY = false, $iDotSize = 1) { |
20 |
$iPosY = imagesy( $vImage ); |
21 |
$vBorderColor = imagecolorallocate( $vImage , 0, 0, 0); |
22 |
foreach ( $this ->aCoords as $x => $y ) { |
23 |
imagefilledellipse( $vImage , $iPosX + round ( $x ), $iPosY - round ( $y ), $iDotSize , $iDotSize , $vColor ); |
24 |
imageellipse( $vImage , $iPosX + round ( $x ), $iPosY - round ( $y ), $iDotSize , $iDotSize , $vBorderColor ); |
27 |
public function drawAxis( $vImage , $vColor , $iPosX = 0, $iPosY = false) { |
29 |
$iPosY = imagesy( $vImage ); |
30 |
$vImageWidth = imagesx( $vImage ); |
31 |
imageline( $vImage , $iPosX , $iPosY , $iPosX , 0, $vColor ); |
32 |
imageline( $vImage , $iPosX , $iPosY , $vImageWidth , $iPosY , $vColor ); |
33 |
imagefilledpolygon( $vImage , array ( $iPosX , 0, $iPosX - 3, 5, $iPosX + 3, 5), 3, $vColor ); |
34 |
imagefilledpolygon( $vImage , array ( $vImageWidth , $iPosY , $vImageWidth - 5, $iPosY - 3, $vImageWidth - 5, $iPosY + 3), 3, $vColor ); |
and classes/CubicSplines.php
06 |
protected $aSplines = array (); |
10 |
protected function prepareCoords(& $aCoords , $iStep , $iMinX = -1, $iMaxX = -1) { |
11 |
$this ->aCrdX = array (); |
12 |
$this ->aCrdY = array (); |
13 |
$this ->aCoords = array (); |
15 |
foreach ( $aCoords as $x => $y ) { |
19 |
$this ->iMinX = $iMinX ; |
20 |
$this ->iMaxX = $iMaxX ; |
21 |
if ( $this ->iMinX == -1) |
22 |
$this ->iMinX = min( $this ->aCrdX); |
23 |
if ( $this ->iMaxX == -1) |
24 |
$this ->iMaxX = max( $this ->aCrdX); |
25 |
$this ->iStep = $iStep ; |
27 |
public function setInitCoords(& $aCoords , $iStep = 1, $iMinX = -1, $iMaxX = -1) { |
28 |
$this ->aSplines = array (); |
29 |
if ( count ( $aCoords ) < 4) { |
32 |
$this ->prepareCoords( $aCoords , $iStep , $iMinX , $iMaxX ); |
33 |
$this ->buildSpline( $this ->aCrdX, $this ->aCrdY, count ( $this ->aCrdX)); |
35 |
public function processCoords() { |
36 |
for ( $x = $this ->iMinX; $x <= $this ->iMaxX; $x += $this ->iStep) { |
37 |
$this ->aCoords[ $x ] = $this ->funcInterp( $x ); |
39 |
return $this ->aCoords; |
41 |
private function buildSpline( $x , $y , $n ) { |
42 |
for ( $i = 0; $i < $n ; ++ $i ) { |
43 |
$this ->aSplines[ $i ][ 'x' ] = $x [ $i ]; |
44 |
$this ->aSplines[ $i ][ 'a' ] = $y [ $i ]; |
46 |
$this ->aSplines[0][ 'c' ] = $this ->aSplines[ $n - 1][ 'c' ] = 0; |
47 |
$alpha [0] = $beta [0] = 0; |
48 |
for ( $i = 1; $i < $n - 1; ++ $i ) { |
49 |
$h_i = $x [ $i ] - $x [ $i - 1]; |
50 |
$h_i1 = $x [ $i + 1] - $x [ $i ]; |
52 |
$C = 2.0 * ( $h_i + $h_i1 ); |
54 |
$F = 6.0 * (( $y [ $i + 1] - $y [ $i ]) / $h_i1 - ( $y [ $i ] - $y [ $i - 1]) / $h_i ); |
55 |
$z = ( $A * $alpha [ $i - 1] + $C ); |
56 |
$alpha [ $i ] = - $B / $z ; |
57 |
$beta [ $i ] = ( $F - $A * $beta [ $i - 1]) / $z ; |
59 |
for ( $i = $n - 2; $i > 0; -- $i ) { |
60 |
$this ->aSplines[ $i ][ 'c' ] = $alpha [ $i ] * $this ->aSplines[ $i + 1][ 'c' ] + $beta [ $i ]; |
62 |
for ( $i = $n - 1; $i > 0; -- $i ) { |
63 |
$h_i = $x [ $i ] - $x [ $i - 1]; |
64 |
$this ->aSplines[ $i ][ 'd' ] = ( $this ->aSplines[ $i ][ 'c' ] - $this ->aSplines[ $i - 1][ 'c' ]) / $h_i ; |
65 |
$this ->aSplines[ $i ][ 'b' ] = $h_i * (2.0 * $this ->aSplines[ $i ][ 'c' ] + $this ->aSplines[ $i - 1][ 'c' ]) / 6.0 + ( $y [ $i ] - $y [ $i - 1]) / $h_i ; |
68 |
private function funcInterp( $x ) { |
69 |
$n = count ( $this ->aSplines); |
70 |
if ( $x <= $this ->aSplines[0][ 'x' ]) { |
71 |
$s = $this ->aSplines[1]; |
73 |
if ( $x >= $this ->aSplines[ $n - 1][ 'x' ]) { |
74 |
$s = $this ->aSplines[ $n - 1]; |
79 |
$k = $i + ( $j - $i ) / 2; |
80 |
if ( $x <= $this ->aSplines[ $k ][ 'x' ]) { |
86 |
$s = $this ->aSplines[ $j ]; |
90 |
return $s [ 'a' ] + ( $s [ 'b' ] + ( $s [ 'c' ] / 2.0 + $s [ 'd' ] * $dx / 6.0) * $dx ) * $dx ; |
Conclusion
I hope that you got interesting lesson for today. Good luck in your work!