点と点を線で結んでグラフにする時、点同士の間を補完する必要があります。
単純に直線でつなぐだけの線形補間でもグラフにはできますが、全ての点を通る滑らかな曲線を描画するのであれば3次スプライン曲線を利用するのが一般的です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | <?php $points = array (); $width = 300; $height = 100; for ( $x =0; $x <= $width ; $x +=50){ $y = rand(0,100); $points [] = array ( $x , $y ); } $spline = new CubicSpline( $points ); $spline ->draw( $width , $height ); class CubicSpline { private $xx = array (); private $yy = array (); private $q = array (); private $r = array (); private $s = array (); private $n ; private $points ; function __construct( $points ){ $this ->points = $points ; $this ->n = $n = count ( $points ) - 1; // 区間の数 $h = array (); $b = array (); // Step1 for ( $i =0; $i < $n ; $i ++){ $h [ $i ] = $points [ $i + 1][0] - $points [ $i ][0]; } for ( $i =1; $i < $n ; $i ++){ $b [ $i ] = 2.0 * ( $h [ $i ] + $h [ $i - 1]); $d [ $i ] = 3.0 * (( $points [ $i + 1][1] - $points [ $i ][1]) / $h [ $i ] - ( $points [ $i ][1] - $points [ $i - 1][1]) / $h [ $i - 1]); } // Step2 $g [1] = $h [1] / $b [1]; for ( $i =2; $i < $n - 1; $i ++){ $g [ $i ] = $h [ $i ] / ( $b [ $i ] - $h [ $i - 1] * $g [ $i - 1]); } $u [1] = $d [1] / $b [1]; for ( $i =2; $i < $n ; $i ++){ $u [ $i ] = ( $d [ $i ] - $h [ $i - 1] * $u [ $i - 1]) / ( $b [ $i ] - $h [ $i - 1] * $g [ $i - 1]); } // Step3 $this ->r[0] = $this ->r[ $n ] = 0.0; $this ->r[ $n - 1] = $u [ $n - 1]; for ( $i = $n - 2; $i >= 1; $i --){ $this ->r[ $i ] = $u [ $i ] - $g [ $i ] * $this ->r[ $i + 1]; } //Step4 for ( $i =0; $i < $n ; $i ++){ $this ->q[ $i ] = ( $points [ $i +1][1] - $points [ $i ][1]) / $h [ $i ] - $h [ $i ] * ( $this ->r[ $i +1] + 2.0 * $this ->r[ $i ]) / 3.0; $this ->s[ $i ] = ( $this ->r[ $i + 1] - $this ->r[ $i ]) / (3.0 * $h [ $i ]); } return ; } function value( $x ){ $points = $this ->points; $n = $this ->n; $i = -1; // 区間の決定 for ( $i1 =1; $i1 < $n && $i < 0; $i1 ++){ if ( $x < $points [ $i1 ][0]){ $i = $i1 - 1; } } if ( $i < 0){ $i = $n - 1; } // 計算 $x2 = $x - $points [ $i ][0]; $y = $points [ $i ][1] + $x2 * ( $this ->q[ $i ] + $x2 * ( $this ->r[ $i ] + $this ->s[ $i ] * $x2 )); return $y ; } public function draw( $width , $height , $hMargin =10, $vMargin =30 ){ $end = end ( $this ->points); $image = imagecreatetruecolor( $width + $hMargin * 2, $height + $vMargin * 2); $bg = imagecolorallocate( $image , 255, 255, 255); $axis = imagecolorallocate( $image , 180, 180,180); $color1 = imagecolorallocate( $image , 30, 30, 30); $color2 = imagecolorallocate( $image , 50, 150, 255); imagefill( $image , 0, 0, $bg ); imageline( $image , $hMargin , $height + $vMargin , $hMargin + $width , $height + $vMargin , $axis ); imageline( $image , $hMargin , $vMargin , $hMargin , $height + $vMargin , $axis ); $x1 = $y1 = $x2 = $y2 = 0; for ( $x =0; $x < $end [0]; $x ++){ $x1 = $x2 ; $y1 = $y2 ; $x2 = $hMargin + $x ; $y2 = ( $height + $vMargin ) - $this ->value( $x ); if ( $x == 0) continue ; imageline( $image , $x1 , $y1 , $x2 , $y2 , $color1 ); } foreach ( $this ->points as $point ){ list( $x , $y ) = $point ; $x = $hMargin + $x ; $y = ( $height + $vMargin ) - $y ; imagefilledrectangle( $image , $x - 2, $y - 2, $x + 2, $y + 2, $color2 ); } header( 'Content-Type: image/jpeg' ); imagejpeg( $image , null, 90); imagedestroy( $image ); exit ; } } |
サンプルではランダムに点をプロットしてそれらの点を通るスプライン曲線を描画しています。
クラスをインスタンス化する際に座標 array(x, y) を配列にしたものを渡し、draw() で画像として描画します。
このプログラムは下記参考 URL のソースコードを元に PHP で書きなおしたものです。
参考:
http://www.sist.ac.jp/~suganuma/cpp/2-bu/7-sho/7-sho.htm#e-7-42