Creating a Smooth Curve Graphs with PHP and GD

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.

Live Demo

[sociallocker]

download in package

[/sociallocker]


Now – download the source files and lets start coding !


Step 1. HTML

Here are HTML layout for our example page:

index.html

01 <!DOCTYPE html>
02 <html lang="en" >
03     <head>
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" />
07     </head>
08     <body>
09         <div class="container">
10             <img src="smooth_graph.php" alt="" />
11         </div>
12         <footer>
13             <h2>Smooth Curve Graphs with PHP and GD</h2>
14             <a href="https://www.script-tutorials.com/smooth-curve-graphs-with-php-and-gd/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
15         </footer>
16     </body>
17 </html>

Step 2. CSS

Now, lets define all used styles:

css/main.css

01 *{
02     margin:0;
03     padding:0;
04 }
05 body {
06     background-repeat:no-repeat;
07     background-color:#bababa;
08     background-image: -webkit-radial-gradient(600px 200pxcircle#eee#bababa 40%);
09     background-image: -moz-radial-gradient(600px 200pxcircle#eee#bababa 40%);
10     background-image: -o-radial-gradient(600px 200pxcircle#eee#bababa 40%);
11     background-image: radial-gradient(600px 200pxcircle#eee#bababa 40%);
12     color:#fff;
13     font:14px/1.3 Arial,sans-serif;
14     min-height:600px;
15 }
16 footer {
17     background-color:#212121;
18     bottom:0;
19     box-shadow: 0 -1px 2px #111111;
20     display:block;
21     height:70px;
22     left:0;
23     position:fixed;
24     width:100%;
25     z-index:100;
26 }
27 footer h2{
28     font-size:22px;
29     font-weight:normal;
30     left:50%;
31     margin-left:-400px;
32     padding:22px 0;
33     position:absolute;
34     width:540px;
35 }
36 footer a.stuts,a.stuts:visited{
37     border:none;
38     text-decoration:none;
39     color:#fcfcfc;
40     font-size:14px;
41     left:50%;
42     line-height:31px;
43     margin:23px 0 0 110px;
44     position:absolute;
45     top:0;
46 }
47 footer .stuts span {
48     font-size:22px;
49     font-weight:bold;
50     margin-left:5px;
51 }
52 .container {
53     border:3px #111 solid;
54     margin:20px auto;
55     padding:20px;
56     position:relative;
57     width:550px;
58     height:430px;
59     border-radius:15px;
60     -moz-border-radius:15px;
61     -webkit-border-radius:15px;
62 }

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

01 <?php
02 set_time_limit(100);
03 define('GRAPH_WIDTH',  500);
04 define('GRAPH_HEIGHT', 400);
05 include_once ('classes/Plot.php');
06 include_once ('classes/CubicSplines.php');
07 $iPoints = 15;
08 $dx = (GRAPH_WIDTH - 40) / ($iPoints - 1);
09 $x = 20;
10 for ($i = 0; $i $iPoints$i++) {
11     $y = rand(20, GRAPH_HEIGHT - 20);
12     $aCoords[$x] = $y;
13     $x+= $dx;
14 }
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);
27 if ($oCurve) {
28     $oCurve->setInitCoords($aCoords, 1);
29     $r $oCurve->processCoords();
30     if ($r)
31         $curveGraph new Plot($r);
32     else
33         continue;
34 else {
35     $curveGraph $oPlot;
36 }
37 $curveGraph->drawLine($vImage$vColor, 10, GRAPH_HEIGHT);
38 // unset($oCurve);
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");
47 imagepng($vImage);
48 imagedestroy($vImage);
49 ?>

In this file I using 2 another classes (as separated service classes):

classes/Plot.php

01 <?php
02 class Plot {
03     private $aCoords;
04     function __construct(&$aCoords) {
05         $this->aCoords = &$aCoords;
06     }
07     public function drawLine($vImage$vColor$iPosX = 0, $iPosY = false) {
08         if ($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);
14             $iPrevX $x;
15             $iPrevY $y;
16         }
17     }
18     public function drawDots($vImage$vColor$iPosX = 0, $iPosY = false, $iDotSize = 1) {
19         if ($iPosY === false)
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);
25         }
26     }
27     public function drawAxis($vImage$vColor$iPosX = 0, $iPosY = false) {
28         if ($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($vImagearray($iPosX, 0, $iPosX - 3, 5, $iPosX + 3, 5), 3, $vColor);
34         imagefilledpolygon($vImagearray($vImageWidth$iPosY$vImageWidth - 5, $iPosY - 3, $vImageWidth - 5, $iPosY + 3), 3, $vColor);
35     }
36 }
37 ?>

and classes/CubicSplines.php

01 <?php
02 class CubicSplines {
03     protected $aCoords;
04     protected $aCrdX;
05     protected $aCrdY;
06     protected $aSplines array();
07     protected $iMinX;
08     protected $iMaxX;
09     protected $iStep;
10     protected function prepareCoords(&$aCoords$iStep$iMinX = -1, $iMaxX = -1) {
11         $this->aCrdX = array();
12         $this->aCrdY = array();
13         $this->aCoords = array();
14         ksort($aCoords);
15         foreach ($aCoords as $x => $y) {
16             $this->aCrdX[] = $x;
17             $this->aCrdY[] = $y;
18         }
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;
26     }
27     public function setInitCoords(&$aCoords$iStep = 1, $iMinX = -1, $iMaxX = -1) {
28         $this->aSplines = array();
29         if (count($aCoords) < 4) {
30             return false;
31         }
32         $this->prepareCoords($aCoords$iStep$iMinX$iMaxX);
33         $this->buildSpline($this->aCrdX, $this->aCrdY, count($this->aCrdX));
34     }
35     public function processCoords() {
36         for ($x $this->iMinX; $x <= $this->iMaxX; $x += $this->iStep) {
37             $this->aCoords[$x] = $this->funcInterp($x);
38         }
39         return $this->aCoords;
40     }
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];
45         }
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];
51             $A $h_i;
52             $C = 2.0 * ($h_i $h_i1);
53             $B $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;
58         }
59         for ($i $n - 2; $i > 0; --$i) {
60             $this->aSplines[$i]['c'] = $alpha[$i] * $this->aSplines[$i + 1]['c'] + $beta[$i];
61         }
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;
66         }
67     }
68     private function funcInterp($x) {
69         $n count($this->aSplines);
70         if ($x <= $this->aSplines[0]['x'])  {
71             $s $this->aSplines[1];
72         else {
73             if ($x >= $this->aSplines[$n - 1]['x']) {
74                 $s $this->aSplines[$n - 1];
75             else {
76                 $i = 0;
77                 $j $n - 1;
78                 while ($i + 1 < $j) {
79                     $k $i + ($j $i) / 2;
80                     if ($x <= $this->aSplines[$k]['x']) {
81                         $j $k;
82                     else {
83                         $i $k;
84                     }
85                 }
86                 $s $this->aSplines[$j];
87             }
88         }
89         $dx = ($x $s['x']);
90         return $s['a'] + ($s['b'] + ($s['c'] / 2.0 + $s['d'] * $dx / 6.0) * $dx) * $dx;
91     }
92 }
93 ?>

Live Demo

Conclusion

I hope that you got interesting lesson for today. Good luck in your work!