PHP

Рисуем плавную кривую в PHP с использованием GD

В данном уроке мы выведем график на PHP с использованием библиотеки GD. Кривая будет сглаживаться с помощью интерполяции кубическими сплайнами.

Шаг 1. Разметка HTML

Разметка для демонстрации достаточно проста.
 	<!DOCTYPE html>
 	<html lang="ru" >
 	    <head>
 	        <meta charset="utf-8" />
 	        <title>Плавная кривая на PHP и GD | Материалы сайта RUSELLER.COM</title>
 	 
 	        <link href="css/main.css" rel="stylesheet" type="text/css" />
 	    </head>
 	    <body>
 	        <div class="container">
 	            <img src="smooth_graph.php" alt="" />
	        </div>
 	        <footer>
 	            <h2>Плавная кривая на PHP и GD</h2>
 	            <a href="http://www.ruseller.com" target="_blank" class="stuts">Материалы сайта <span>RUSELLER.COM</span></a>
 	        </footer>
 	    </body>
 	</html>

В качестве источника для элемента изображения выступает PHP файл, который генерирует график.

Шаг 2. CSS

Для демонстрации используются обычные правила:
css/main.css
 	*{
 	    margin:0;
 	    padding:0;
 	}
	 
 	body {
 	    background-repeat:no-repeat;
 	    background-color:#bababa;
	    background-image: -webkit-radial-gradient(600px 200px, circle, #eee, #bababa 40%);
 	    background-image: -moz-radial-gradient(600px 200px, circle, #eee, #bababa 40%);
 	    background-image: -o-radial-gradient(600px 200px, circle, #eee, #bababa 40%);
 	    background-image: radial-gradient(600px 200px, circle, #eee, #bababa 40%);
 	    color:#fff;
	    font:14px/1.3 Arial,sans-serif;
 	    min-height:600px;
 	}
 	 
	footer {
 	    background-color:#212121;
 	    bottom:0;
 	    box-shadow: 0 -1px 2px #111111;
 	    display:block;
 	    height:70px;
 	    left:0;
 	    position:fixed;
 	    width:100%;
 	    z-index:100;
 	}
 	 
 	footer h2{
 	    font-size:22px;
 	    font-weight:normal;
 	    left:50%;
 	    margin-left:-400px;
 	    padding:22px 0;
 	    position:absolute;
 	    width:540px;
 	}
 	 
 	footer a.stuts,a.stuts:visited{
 	    border:none;
 	    text-decoration:none;
 	    color:#fcfcfc;
 	    font-size:14px;
 	    left:50%;
 	    line-height:31px;
 	    margin:23px 0 0 110px;
 	    position:absolute;
 	    top:0;
 	}
 	 
 	footer .stuts span {
 	    font-size:22px;
 	    font-weight:bold;
 	    margin-left:5px;
 	}
 	 
 	.container {
 	    border:3px #111 solid;
 	    margin:20px auto;
 	    padding:20px;
 	    position:relative;
 	    width:550px;
 	    height:430px;
 	 
 	    border-radius:15px;
 	    -moz-border-radius:15px;
 	    -webkit-border-radius:15px;
 	}

Шаг 3. PHP

График генерируется серверной стороной:
smooth_graph.php
 	<?php
 	set_time_limit(100);
 	 
 	define('GRAPH_WIDTH',  500);
 	define('GRAPH_HEIGHT', 400);
 	 
 	include_once ('classes/Plot.php');
 	include_once ('classes/CubicSplines.php');
 	 
 	$iPoints = 15;
 	$dx = (GRAPH_WIDTH - 40) / ($iPoints - 1);
 	$x = 20;
 	 
 	for ($i = 0; $i < $iPoints; $i++) {
 	    $y = rand(20, GRAPH_HEIGHT - 20);
 	    $aCoords[$x] = $y;
	    $x+= $dx;
 	}
 	 
 	$vImagegHeight = GRAPH_HEIGHT + 30;
 	$vImage = imagecreatetruecolor(GRAPH_WIDTH + 50, $vImagegHeight);
 	 
 	$vBgColor = imagecolorallocate($vImage, 160, 160, 160);
 	$vTextColor = imagecolorallocate($vImage, 0, 0, 0);
 	$vAxisColor = imagecolorallocate($vImage, 0, 0, 0);
 	$vDotColor  = imagecolorallocate($vImage, 192, 64, 64);
 	 
 	imagefill($vImage, 0, 0, $vBgColor);
 	 
 	$oPlot = new Plot($aCoords);
 	$oPlot->drawDots($vImage, $vDotColor, 10, GRAPH_HEIGHT, 8);
 	 
 	$oCurve = new CubicSplines();
 	$vColor = imagecolorallocate($vImage, 225, 64, 64);
 	 
 	$iStart = microtime(1);
 	if ($oCurve) {
	    $oCurve->setInitCoords($aCoords, 1);
 	    $r = $oCurve->processCoords();
 	    if ($r)
 	        $curveGraph = new Plot($r);
 	    else
 	        continue;
 	} else {
 	    $curveGraph = $oPlot;
 	}
 	 
 	$curveGraph->drawLine($vImage, $vColor, 10, GRAPH_HEIGHT);
 	 
 	// unset($oCurve);
	$sTime = sprintf("%1.4f", microtime(1) - $iStart);
 	 
 	imagefilledrectangle($vImage, 0, GRAPH_HEIGHT, GRAPH_WIDTH + 50, $vImagegHeight, $vBgColor);
 	$oPlot->drawAxis($vImage, $vAxisColor, 10, GRAPH_HEIGHT);
 	$iPanelY = GRAPH_HEIGHT;
 	 
 	imagefilledrectangle($vImage, 10, $iPanelY + 10, 20, $iPanelY + 20, $vColor);
 	imagerectangle($vImage, 10, $iPanelY + 10, 20, $iPanelY + 20, $vAxisColor);
	imagettftext($vImage, 10, 0, 30, $iPanelY + 20, $vTextColor, 'Ds-digib.ttf', 'Кубические сплайны в PHP:         ' . $sTime . ' sec');
 	 
 	header("Content-type: image/png");
	imagepng($vImage);
 	imagedestroy($vImage);
 	 
 	?>

Формируется массив $aCoords точек, который заполняется случайным образом. Затем данный массив используется для построения плавной кривой, которая проходит через все его точки.

В файле используются 2 класса, которые оформлены в отдельных файлах.

Класс для вывода элементов графики (линий, точек и осей):
classes/Plot.php
 	<?php
 	 
 	class Plot {
 	    private $aCoords;
 	 
 	    function __construct(&$aCoords) {
 	        $this->aCoords = &$aCoords;
 	    }
 	 
 	    public function drawLine($vImage, $vColor, $iPosX = 0, $iPosY = false) {
	        if ($iPosY === false)
 	            $iPosY = imagesy($vImage);
 	 
 	        reset($this->aCoords);
	        list($iPrevX, $iPrevY) = each($this->aCoords);
 	 
 	        while (list ($x, $y) = each($this->aCoords)) {
 	            imageline($vImage, $iPosX + round($iPrevX), $iPosY - round($iPrevY), $iPosX + round($x), $iPosY - round($y), $vColor);
 	            $iPrevX = $x;
 	            $iPrevY = $y;
 	        }
 	    }
 	 
 	    public function drawDots($vImage, $vColor, $iPosX = 0, $iPosY = false, $iDotSize = 1) {
 	        if ($iPosY === false)
 	            $iPosY = imagesy($vImage);
 	 
 	        $vBorderColor = imagecolorallocate($vImage, 0, 0, 0);
 	        foreach ($this->aCoords as $x => $y) {
 	            imagefilledellipse($vImage, $iPosX + round($x), $iPosY - round($y), $iDotSize, $iDotSize, $vColor);
 	            imageellipse($vImage, $iPosX + round($x), $iPosY - round($y), $iDotSize, $iDotSize, $vBorderColor);
 	        }
 	    }
 	 
 	    public function drawAxis($vImage, $vColor, $iPosX = 0, $iPosY = false) {
 	        if ($iPosY === false)
 	            $iPosY = imagesy($vImage);
 	 
 	        $vImageWidth = imagesx($vImage);
 	        imageline($vImage, $iPosX, $iPosY, $iPosX, 0, $vColor);
 	        imageline($vImage, $iPosX, $iPosY, $vImageWidth, $iPosY, $vColor);
 	 
 	        imagefilledpolygon($vImage, array($iPosX, 0, $iPosX - 3, 5, $iPosX + 3, 5), 3, $vColor);
 	        imagefilledpolygon($vImage, array($vImageWidth, $iPosY, $vImageWidth - 5, $iPosY - 3, $vImageWidth - 5, $iPosY + 3), 3, $vColor);
 	    }
 	}
 	 
 	?>

И класс для формирования плавной кривой с помощью кубических сплайнов:
classes/CubicSplines.php
 	<?php
 	 
 	class CubicSplines {
 	    protected $aCoords;
 	    protected $aCrdX;
 	    protected $aCrdY;
 	    protected $aSplines = array();
 	    protected $iMinX;
 	    protected $iMaxX;
 	    protected $iStep;
 	 
 	    protected function prepareCoords(&$aCoords, $iStep, $iMinX = -1, $iMaxX = -1) {
 	        $this->aCrdX = array();
 	        $this->aCrdY = array();
 	        $this->aCoords = array();
 	 
 	        ksort($aCoords);
 	        foreach ($aCoords as $x => $y) {
 	            $this->aCrdX[] = $x;
 	            $this->aCrdY[] = $y;
 	        }
 	 
 	        $this->iMinX = $iMinX;
 	        $this->iMaxX = $iMaxX;
 	 
 	        if ($this->iMinX == -1)
 	            $this->iMinX = min($this->aCrdX);
 	        if ($this->iMaxX == -1)
 	            $this->iMaxX = max($this->aCrdX);
	 
 	        $this->iStep = $iStep;
 	    }
 	 
 	    public function setInitCoords(&$aCoords, $iStep = 1, $iMinX = -1, $iMaxX = -1) {
 	        $this->aSplines = array();
 	 
 	        if (count($aCoords) < 4) {
 	            return false;
 	        }
 	 
 	        $this->prepareCoords($aCoords, $iStep, $iMinX, $iMaxX);
	        $this->buildSpline($this->aCrdX, $this->aCrdY, count($this->aCrdX));
 	    }
 	 
 	    public function processCoords() {
	        for ($x = $this->iMinX; $x <= $this->iMaxX; $x += $this->iStep) {
	            $this->aCoords[$x] = $this->funcInterp($x);
 	        }
 	 
 	        return $this->aCoords;
 	    }
 	 
 	    private function buildSpline($x, $y, $n) {
 	        for ($i = 0; $i < $n; ++$i) {
 	            $this->aSplines[$i]['x'] = $x[$i];
 	            $this->aSplines[$i]['a'] = $y[$i];
 	        }
 	 
 	        $this->aSplines[0]['c'] = $this->aSplines[$n - 1]['c'] = 0;
 	        $alpha[0] = $beta[0] = 0;
 	        for ($i = 1; $i < $n - 1; ++$i) {
	            $h_i = $x[$i] - $x[$i - 1];
 	            $h_i1 = $x[$i + 1] - $x[$i];
	            $A = $h_i;
 	            $C = 2.0 * ($h_i + $h_i1);
 	            $B = $h_i1;
 	            $F = 6.0 * (($y[$i + 1] - $y[$i]) / $h_i1 - ($y[$i] - $y[$i - 1]) / $h_i);
 	            $z = ($A * $alpha[$i - 1] + $C);
 	            $alpha[$i] = - $B / $z;
	            $beta[$i] = ($F - $A * $beta[$i - 1]) / $z;
	        }
 	 
 	        for ($i = $n - 2; $i > 0; --$i) {
 	            $this->aSplines[$i]['c'] = $alpha[$i] * $this->aSplines[$i + 1]['c'] + $beta[$i];
 	        }
 	 
 	        for ($i = $n - 1; $i > 0; --$i) {
 	            $h_i = $x[$i] - $x[$i - 1];
 	            $this->aSplines[$i]['d'] = ($this->aSplines[$i]['c'] - $this->aSplines[$i - 1]['c']) / $h_i;
 	            $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;
 	        }
 	    }
 	 
 	    private function funcInterp($x) {
 	        $n = count($this->aSplines);
 	        if ($x <= $this->aSplines[0]['x'])  {
 	            $s = $this->aSplines[1];
 	        } else {
 	            if ($x >= $this->aSplines[$n - 1]['x']) {
 	                $s = $this->aSplines[$n - 1];
 	            } else {
 	                $i = 0;
	                $j = $n - 1;
 	                while ($i + 1 < $j) {
 	                    $k = $i + ($j - $i) / 2;
 	                    if ($x <= $this->aSplines[$k]['x']) {
 	                        $j = $k;
 	                    } else {
 	                        $i = $k;
 	                    }
 	                }
 	 
 	                $s = $this->aSplines[$j];
 	            }
	        }
	 
 	        $dx = ($x - $s['x']);
 	        return $s['a'] + ($s['b'] + ($s['c'] / 2.0 + $s['d'] * $dx / 6.0) * $dx) * $dx;
 	    }
 	}
 	 
 	?>

Добавлено: 18 Августа 2013 10:12:44 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...