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!