



オウムガイの螺旋模様は黄金螺旋といわれている。このほか、自然界には黄金螺旋が見られるが、なぜその比率になっているかは分かっていない。

今回は、PHPを使って黄金螺旋を描くプログラムを作ってみる。
サンプル・プログラム:実行例


実行時に、いくつかのURLパラメータを渡すことができる。
【例】GoldenSpiral.php?trans=1&n=12
背景を透明にして、12段階の螺旋を描く。
パラメータ | 内容 | 範 囲 | デフォルト値 |
---|---|---|---|
type | 描画モード | 1:黄金長方形のみ描く,2:黄金螺旋のみ描く,3:両方を描く | 3 |
inspect | 検証モード | 0:通常モード,1:検証モード | 0 |
margin | 左右マージン(ピクセル) | 0~80 | 1 |
trans | 背景を透過するかどうか | 0:背景色で塗りつぶす,1:透過する | 0 |
width | 描画領域の横幅(ピクセル) | 100~1000 | 600 |
n | 頂点の数 | 0~15 | 10 |
thick1 | 黄金長方形の線の太さ | 1~10 | 1 |
thick2 | 黄金螺旋の線の太さ | 1~10 | 3 |
bgcolor | 背景色 | 000000~FFFFFF | FFFFFF |
color1 | 黄金長方形の描画色 | 000000~FFFFFF | 000000 |
color2 | 黄金螺旋の描画色 | 000000~FFFFFF | FF0000 |
サンプル・プログラム
GoldenSpiral.php | サンプル・プログラム本体 |
準備:定数
0033: //デフォルト表示幅(ピクセル)
0034: define('WIDTH', 600);
0035:
0036: //黄金比
0037: define('RATIO', ((1 + sqrt(5)) / 2));
0038:
0039: //検証モードで使うカラー
0040: define('INSPECT_COLOR1', '000000'); //描画済み線分
0041: define('INSPECT_COLOR2', '0000FF'); //矩形
0042: define('INSPECT_COLOR3', 'FF0000'); //円弧
0043: define('INSPECT_COLOR4', 'AAFFFF'); //塗りつぶし
0044: //検証モードの線分の太さ
0045: define('INSPECT_THICK1', 1); //描画済み
0046: define('INSPECT_THICK2', 3); //矩形・円弧
検証モードで使うカラーおよび線分の太さは、INSPECT_ ではじまる定数に記述する。
これらの値は自由に変更できる。
解説:各種パラメータ
0164: /**
0165: * $_GET または $_POST から正規化した数値を取り出す
0166: * @param string $type int:整数 float:浮動小数
0167: * @param string $key キー
0168: * @param mixed $min, $max 最小値、最大値
0169: * @param string $errmsg エラーメッセージを格納する
0170: * @return mixed 取得値/NULL:エラー
0171: */
0172: function getParameters(&$items) {
0173: static $tbl_val = array(
0174: //描画モード
0175: 'mode'=>array('type'=>'int', 'min'=>1, 'max'=>3, 'def'=>3),
0176: //検証モード
0177: 'inspect'=>array('type'=>'int', 'min'=>0, 'max'=>1, 'def'=>0),
0178: //左右マージン(ピクセル)
0179: 'margin'=>array('type'=>'int', 'min'=>0, 'max'=>80, 'def'=>1),
0180: //背景を透過するかどうか
0181: 'trans'=>array('type'=>'int', 'min'=>0, 'max'=>1, 'def'=>0),
0182: //描画領域の横幅(ピクセル)
0183: 'width'=>array('type'=>'float', 'min'=>100, 'max'=>1000, 'def'=>WIDTH),
0184: //頂点の数
0185: 'n'=>array('type'=>'int', 'min'=>0, 'max'=>15, 'def'=>10),
0186: //黄金長方形の線の太さ
0187: 'thick1'=>array('type'=>'int', 'min'=>1, 'max'=>10, 'def'=>1),
0188: //黄金螺旋の線の太さ
0189: 'thick2'=>array('type'=>'int', 'min'=>1, 'max'=>10, 'def'=>3)
0190: );
0191: static $tbl_str = array(
0192: //背景色
0193: 'bgcolor'=>array('pat'=>'/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i', 'def'=>'FFFFFF'),
0194: //黄金長方形の描画色
0195: 'color1'=>array('pat'=>'/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i', 'def'=>'000000'),
0196: //黄金螺旋の描画色
0197: 'color2'=>array('pat'=>'/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i', 'def'=>'FF0000')
0198: );
0199: foreach ($tbl_val as $key=>$arr) {
0200: $items[$key] = getValidNumber($key, $arr['type'], $arr['min'], $arr['max'], $arr['def']);
0201: }
0202: foreach ($tbl_str as $key=>$arr) {
0203: $items[$key] = getValidString($key, $arr['pat'], $arr['def']);
0204: }
0205: }
配列 $tbl_val および $tbl_str を変更することで、パラメータの範囲やデフォルト値を変更できる。
解説:頂点パラメータを計算
0219: /**
0220: * 頂点パラメータを計算
0221: * @param int $n 頂点の数
0222: * @return array 頂点パラメータ
0223: * [$i][0] X座標(0.0以上1.0以下)
0224: * [$i][1] Y座標(0.0以上1.0以下)
0225: * [$i][2] 辺の長さ(0.0以上1.0以下)
0226: * [$i][3] 方向(0度以上360度以下)
0227: */
0228: function calcApexes($n) {
0229: $k = RATIO;
0230: $p = array();
0231: if ($n == 0) return $p;
0232:
0233: //始点
0234: $p[0][0] = 1.0;
0235: $p[0][1] = 0.0;
0236: $p[0][2] = 1.0;
0237: $p[0][3] = 90;
0238:
0239: //頂点座標の計算
0240: for ($i = 1; $i < $n; $i++) {
0241: $p[$i][2] = $p[$i - 1][2] * ($k - 1);
0242: switch ($p[$i - 1][3]) {
0243: case 90:
0244: $p[$i][0] = $p[$i - 1][0];
0245: $p[$i][1] = $p[$i - 1][1] + $p[$i - 1][2] - $p[$i][2];
0246: $p[$i][3] = 0;
0247: break;
0248: case 0;
0249: $p[$i][0] = $p[$i - 1][0] + $p[$i - 1][2] - $p[$i][2];
0250: $p[$i][1] = $p[$i - 1][1];
0251: $p[$i][3] = 270;
0252: break;
0253: case 270;
0254: $p[$i][0] = $p[$i - 1][0];
0255: $p[$i][1] = $p[$i - 1][1] - $p[$i - 1][2] + $p[$i][2];
0256: $p[$i][3] = 180;
0257: break;
0258: case 180;
0259: $p[$i][0] = $p[$i - 1][0] - $p[$i - 1][2] + $p[$i][2];
0260: $p[$i][1] = $p[$i - 1][1];
0261: $p[$i][3] = 90;
0262: break;
0263: }
0264: }
0265: return $p;
0266: }
ユーザー関数 calcApexes は、指定した数だけ頂点の座標を計算して配列に格納する。あわせて、黄金長方形の1辺の長さ(=黄金螺旋の半径)、次の正方形を配置する方向(=螺旋の描画開始角度)を計算し、同じ配列に格納する。

座標計算は、方向に応じて場合分けすることで計算する。1辺の長さは、黄金比に応じて小さくなってゆく。








解説:黄金長方形を描く
0268: /**
0269: * 黄金長方形を描く
0270: * @param array $p 頂点パラメータ
0271: * @param obj $image イメージストリーム
0272: * @param int $width 描画領域の横幅(ピクセル)
0273: * @param int $margin 左右マージン(ピクセル)
0274: * @param int $color 色ID
0275: * @param int $thick 太さ(省略時=1)
0276: * @param bool $inspect FALSE:通常モード(デフォルト)/TRUE:検証モード
0277: * @return bool TRUE:成功/FALSE:失敗
0278: */
0279: function drawRect($p, $image, $width, $margin, $color, $thick=1, $inspect=FALSE) {
0280: $res = TRUE;
0281: $w = (double)($width - $margin * 2) / RATIO;
0282:
0283: //一番外側の長方形
0284: imagesetthickness($image, INSPECT_THICK2);
0285: imagerectangle($image, $margin, (int)($margin / RATIO), $width - $margin, (int)($margin / RATIO + floor($w)), INSPECT_COLOR1);
0286:
0287: //黄金長方形
0288: $n = count($p) - 1;
0289: foreach ($p as $i=>$val) {
0290: //始点
0291: $x1 = $x0 = $val[0];
0292: $y1 = $y0 = $val[1];
0293: $len = $val[2];
0294: $dir = $val[3];
0295: //正方形を描く
0296: for ($j = 0; $j < 4; $j++) {
0297: switch ($dir) {
0298: case 90:
0299: $x2 = $x1;
0300: $y2 = $y1 + $len;
0301: $dir = 180;
0302: break;
0303: case 180;
0304: $x2 = $x1 - $len;
0305: $y2 = $y1;
0306: $dir = 270;
0307: break;
0308: case 270;
0309: $x2 = $x1;
0310: $y2 = $y1 - $len;
0311: $dir = 0;
0312: break;
0313: case 0;
0314: $x2 = $x1 + $len;
0315: $y2 = $y1;
0316: $dir = 90;
0317: break;
0318: }
0319: if ($x2 < $x0) $x0 = $x2;
0320: if ($y2 < $y0) $y0 = $y2;
0321: if (! $inspect) {
0322: $tck = $thick;
0323: $col = $color;
0324: } else {
0325: if ($i != $n) {
0326: $tck = INSPECT_THICK1;
0327: $arr = color2rgb(INSPECT_COLOR1);
0328: $col = imagecolorallocate($image, $arr[0], $arr[1], $arr[2]);
0329: } else {
0330: $tck = INSPECT_THICK2;
0331: $arr = color2rgb(INSPECT_COLOR2);
0332: $col = imagecolorallocate($image, $arr[0], $arr[1], $arr[2]);
0333: }
0334: }
0335: imagesetthickness($image, $tck);
0336: $res = imageline($image, (int)($margin + $x1 * $w), (int)($margin / RATIO + $y1 * $w), (int)($margin + $x2 * $w), (int)($margin / RATIO + $y2 * $w), $col);
0337: if ($res == FALSE) break;
0338: $x1 = $x2;
0339: $y1 = $y2;
0340: }
0341: //塗りつぶし(検証モード)
0342: if ($inspect && ($n == $i)) {
0343: $arr = color2rgb(INSPECT_COLOR4);
0344: $col2 = imagecolorallocate($image, $arr[0], $arr[1], $arr[2]);
0345: $res = imagefill($image, $margin + ($x0 + $len / 2) * $w, $margin + ($y0 + $len / 2) * $w, $col2);
0346: }
0347: }
0348: return $res;
0349: }

まず、一番外側の長方形を組み込み関数 imagerectangle を使って描く。
黄金長方形は、実際には正方形を描いてゆくのだが、頂点が必ずしも正方形の左上端とはかぎらないので、方向の値を見ながら正方形の頂点を計算し、組み込み関数 imageline を使って4辺を描画する。
解説:黄金螺旋を描く
0351: /**
0352: * 黄金螺旋を描く
0353: * @param array $p 頂点パラメータ
0354: * @param obj $image イメージストリーム
0355: * @param int $width 描画領域の横幅(ピクセル)
0356: * @param int $margin 左右マージン(ピクセル)
0357: * @param int $color 色ID
0358: * @param int $thick 太さ(省略時=1)
0359: * @param bool $inspect FALSE:通常モード(デフォルト)/TRUE:検証モード
0360: * @return bool TRUE:成功/FALSE:失敗
0361: */
0362: function drawSpiral($p, $image, $width, $margin, $color, $thick=1, $inspect=FALSE) {
0363: $res = TRUE;
0364: $w = (double)($width - $margin * 2) / RATIO;
0365:
0366: $n = count($p) - 1;
0367: foreach ($p as $i=>$val) {
0368: if (! $inspect) {
0369: $tck = $thick;
0370: $col = $color;
0371: } else {
0372: if ($i != $n) {
0373: $tck = INSPECT_THICK1;
0374: $arr = color2rgb(INSPECT_COLOR1);
0375: $col = imagecolorallocate($image, $arr[0], $arr[1], $arr[2]);
0376: } else {
0377: $tck = INSPECT_THICK2;
0378: $arr = color2rgb(INSPECT_COLOR3);
0379: $col = imagecolorallocate($image, $arr[0], $arr[1], $arr[2]);
0380: }
0381: }
0382: imagesetthickness($image, $tck);
0383: $res = imagearc($image, (int)($margin + $val[0] * $w), (int)($margin / RATIO + $val[1] * $w), (int)($val[2] * $w * 2), (int)($val[2] * $w * 2), $val[3], $val[3] + 90, $col);
0384: if ($res == FALSE) break;
0385: }
0386: return $res;
0387: }



参考サイト
- 黄金比の描き方をまとめました:ALICEMIX
- PHPで雪の結晶を描く:ぱふぅ家のホームページ
- オウムガイ - 鳥羽水族館:ぱふぅ家のホームページ
- 日本橋で化石探検:ぱふぅ家のホームページ