PHPで黄金螺旋を描く

(1/1)
パルテノン神殿と黄金比
パルテノン神殿と黄金比
黄金比とは、$ \displaystyle 1:\frac{1 + \sqrt{5}}{2} \approx 1:1.618 $ であり、ヨーロッパでは古くから最も美しい長方形として親しまれてきた。古代ギリシアのパルテノン神殿やミロのビーナス、パリの凱旋門に黄金比が用いられている。
凱旋門と黄金比
凱旋門と黄金比
黄金比は、安定した美しさをもつ比率とされ、美術品や、さまざまな工業デザインに取り入れられている。
オウムガイと黄金比
オウムガイと黄金比
黄金比を使って螺旋を描いたものを黄金螺旋と呼ぶ。
オウムガイの螺旋模様は黄金螺旋といわれている。このほか、自然界には黄金螺旋が見られるが、なぜその比率になっているかは分かっていない。

今回は、PHPを使って黄金螺旋を描くプログラムを作ってみる。
(2026年3月1日)PHP8.5対応:double,imagedestroyを使わない, pahooInputData.php導入

目次

サンプル・プログラム:実行例

PHPで黄金螺旋を描く
PNG画像としてブラウザに表示する。

実行時に、いくつかの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サンプル・プログラム本体
pahooInputData.phpデータ入力に関わる関数群。
使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。
GoldenSpiral.php 更新履歴
バージョン 更新日 内容
1.2.0 2026/03/01 PHP8.5対応:double,imagedestroyを使わない, pahooInputData.php導入
1.1 2022/04/24 PHP8対応,リファラ・チェック改良
1.0 2019/04/10
pahooInputData.php 更新履歴
バージョン 更新日 内容
2.0.1 2025/08/11 getParam() bug-fix
2.0.0 2025/08/11 pahooLoadEnv() 追加
1.9.0 2025/07/26 getParam() 引数に$trim追加
1.8.1 2025/03/15 validRegexPattern() debug
1.8.0 2024/11/12 validRegexPattern() 追加

準備:pahooInputData 関数群

PHPのバージョンや入力データのバリデーションなど、汎用的に使う関数群を収めたファイル "pahooInputData.php" が同梱されているが、include_path が通ったディレクトリに配置してほしい。他のプログラムでも "pahooInputData.php" を利用するが、常に最新のファイルを1つ配置すればよい。

また、各種クラウドサービスに登録したときに取得するアカウント情報、アプリケーションパスワードなどを登録した .pahooEnv ファイルから読み込む関数 pahooLoadEnv を備えている。こちらについては、「各種クラウド連携サービス(WebAPI)の登録方法」をご覧いただきたい。

準備:各種定数など

stationsearch.php

  56: // 各種定数(START) ===========================================================
  57: 
  58: // 地図描画サービスの選択
  59: //    0:Google
  60: //    2:地理院地図・OSM
  61: define('MAPSERVICE', 2);
  62: 
  63: // 住所検索サービスの選択
  64: //    0:Google
  65: //    1:Yahoo!ジオコーダAPI
  66: //   11:HeartRails Geo API
  67: //   12:OSM Nominatim Search API
  68: //   13:国土地理院ジオコーディングAPI
  69: define('GEOSERVICE', 1);
  70: 
  71: // 逆ジオコーディングサービスの選択
  72: //    0:Google
  73: //    1:Yahoo!JAPAN
  74: //   11:HeartRails Geo API
  75: //   21:簡易ジオコーディングサービス
  76: define('REVGEOSERVICE', 1);
  77: 
  78: // マップの表示サイズ(単位:ピクセル)
  79: define('MAP_WIDTH',  600);
  80: define('MAP_HEIGHT', 400);
  81: // マップID
  82: define('MAPID', 'map_id');
  83: // マップ中心マーカーのURL;表示したくなければNULLにする
  84: define('CENTER_MARKER', 'https://maps.google.com/mapfiles/ms/micons/ltblu-pushpin.png');
  85: 
  86: // 初期値
  87: define('DEF_LATITUDE',  35.7);          // 緯度
  88: define('DEF_LONGITUDE', 139.7);         // 経度
  89: define('DEF_TYPE',      'roadmap');     // マップタイプ
  90: define('DEF_ZOOM',      13);            // ズーム
  91: define('DEF_CATEGORY', 'address');      // カテゴリ
  92: 
  93: // 検索キーの最小文字長
  94: define('QUERY_MIN_LEN', 3);
  95: 
  96: // 検索キーの最大文字長
  97: define('QUERY_MAX_LEN', 99);
  98: 
  99: // 各種定数(END) ===============================================================

定数 RATIO黄金比を定義する。
検証モードで使うカラーおよび線分の太さは、INSPECT_ ではじまる定数に記述する。
これらの値は自由に変更できる。

解説:各種パラメータを取得する

GoldenSpiral.php

  96: /**
  97:  * $_GET または $_POST から正規化したデータを取り出す
  98:  * @param   array  $items  データを格納する配列
  99:  * @param   string $errmsg エラーメッセージを格納する
 100:  * @return  bool TRUE:取得成功/FALSE:失敗
 101: */
 102: function getParameters(&$items, &$errmsg) {
 103:     static $tbl_val = array(
 104:         // 描画モード
 105:         'mode'=>array('type'=>TRUE, 'min'=>1, 'max'=>3, 'def'=>3),
 106:         // 検証モード
 107:         'inspect'=>array('type'=>TRUE, 'min'=>0, 'max'=>1, 'def'=>0),
 108:         // 左右マージン(ピクセル)
 109:         'margin'=>array('type'=>TRUE, 'min'=>0, 'max'=>80, 'def'=>1),
 110:         // 背景を透過するかどうか
 111:         'trans'=>array('type'=>TRUE, 'min'=>0, 'max'=>1, 'def'=>0),
 112:         // 描画領域の横幅(ピクセル)
 113:         'width'=>array('type'=>FALSE, 'min'=>100, 'max'=>1000, 'def'=>WIDTH),
 114:         // 頂点の数
 115:         'n'=>array('type'=>TRUE, 'min'=>0, 'max'=>15, 'def'=>10),
 116:         // 黄金長方形の線の太さ
 117:         'thick1'=>array('type'=>TRUE, 'min'=>1, 'max'=>10, 'def'=>1),
 118:         // 黄金螺旋の線の太さ
 119:         'thick2'=>array('type'=>TRUE, 'min'=>1, 'max'=>10, 'def'=>3)
 120:     );
 121:     static $tbl_str = array(
 122:         // 背景色
 123:         'bgcolor'=>array('pat'=>'/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i', 'def'=>'FFFFFF'),
 124:         // 黄金長方形の描画色
 125:         'color1'=>array('pat'=>'/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i', 'def'=>'000000'),
 126:         // 黄金螺旋の描画色
 127:         'color2'=>array('pat'=>'/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i', 'def'=>'FF0000')
 128:     );
 129:     $errmsg = '';
 130:     foreach ($tbl_val as $key=>$arr) {
 131:         $items[$key] = getValidNumber($key, $errmsg, $arr['def'], $arr['type'], $arr['min'], $arr['max']);
 132:         if ($errmsg !'')  return FALSE;
 133:     }
 134:     foreach ($tbl_str as $key=>$arr) {
 135:         $items[$key] = getValidString($key, $errmsg, $arr['def'], 1, 9999, TRUE, array($arr['pat']));
 136:         if ($errmsg !'')  return FALSE;
 137:     }
 138:     return TRUE;
 139: }

上述のURLパラメータを取り出すのがユーザー関数 getParameters である。「PHPで雪の結晶を描く」で紹介したものと同じである。
配列 $tbl_val および $tbl_str を変更することで、パラメータの範囲やデフォルト値を変更できる。

解説:頂点パラメータを計算

GoldenSpiral.php

 153: /**
 154:  * 頂点パラメータを計算
 155:  * @param   int $n 頂点の数
 156:  * @return  array  頂点パラメータ
 157:  *                  [$i][0] X座標(0.0以上1.0以下)
 158:  *                  [$i][1] Y座標(0.0以上1.0以下)
 159:  *                  [$i][2] 辺の長さ(0.0以上1.0以下)
 160:  *                  [$i][3] 方向(0度以上360度以下)
 161: */
 162: function calcApexes($n) {
 163:     $k = RATIO;
 164:     $p = array();
 165:     if ($n == 0)    return $p;
 166: 
 167:     // 始点
 168:     $p[0][0] = 1.0;
 169:     $p[0][1] = 0.0;
 170:     $p[0][2] = 1.0;
 171:     $p[0][3] = 90;
 172: 
 173:     // 頂点座標の計算
 174:     for ($i = 1$i < $n$i++) {
 175:         $p[$i][2] = $p[$i - 1][2* ($k - 1);
 176:         switch ($p[$i - 1][3]) {
 177:             case 90:
 178:                 $p[$i][0] = $p[$i - 1][0];
 179:                 $p[$i][1] = $p[$i - 1][1+ $p[$i - 1][2- $p[$i][2];
 180:                 $p[$i][3] = 0;
 181:                 break;
 182:             case 0:
 183:                 $p[$i][0] = $p[$i - 1][0+ $p[$i - 1][2- $p[$i][2];
 184:                 $p[$i][1] = $p[$i - 1][1];
 185:                 $p[$i][3] = 270;
 186:                 break;
 187:             case 270:
 188:                 $p[$i][0] = $p[$i - 1][0];
 189:                 $p[$i][1] = $p[$i - 1][1- $p[$i - 1][2+ $p[$i][2];
 190:                 $p[$i][3] = 180;
 191:                 break;
 192:             case 180:
 193:                 $p[$i][0] = $p[$i - 1][0- $p[$i - 1][2+ $p[$i][2];
 194:                 $p[$i][1] = $p[$i - 1][1];
 195:                 $p[$i][3] = 90;
 196:                 break;
 197:         }
 198:     }
 199:     return $p;
 200: }

黄金長方形の描画開始点、および黄金螺旋の中心点を頂点と呼ぶことにする。
ユーザー関数 calcApexes は、指定した数だけ頂点の座標を計算して配列に格納する。あわせて、黄金長方形の1辺の長さ(=黄金螺旋の半径)、次の正方形を配置する方向(=螺旋の描画開始角度)を計算し、同じ配列に格納する。

座標計算は、方向に応じて場合分けすることで計算する。1辺の長さは、黄金比に応じて小さくなってゆく。
黄金長方形
描画領域の横幅を w、黄金比をRとすると、1つめの頂点の座標は $ \displaystyle (\frac{w}{R},\ 0) $ となる。方向は90度(PHPのGDでは、3時方向を0度、時計回りに度数をカウント)、1辺の長さは $ \displaystyle \frac{w}{R} $ である。
黄金長方形
2つめの頂点の座標は、1つめの方向(90度)へ向かって、X座標は同じで、Y座標を $ \displaystyle \frac{w}{R} (R - 1) $ だけ増やした座標である。方向は180度、1辺の長さは $ \displaystyle \frac{w}{R} (R - 1) $ である。
黄金長方形
同様に、3つめ、4つめの黄金長方形を配置してゆく。
黄金長方形

解説:黄金長方形を描く

GoldenSpiral.php

 202: /**
 203:  * 黄金長方形を描く
 204:  * @param   array $p 頂点パラメータ
 205:  * @param   obj   $image イメージストリーム
 206:  * @param   int   $width 描画領域の横幅(ピクセル)
 207:  * @param   int   $margin  左右マージン(ピクセル)
 208:  * @param   int   $color 色ID
 209:  * @param   int   $thick 太さ(省略時=1)
 210:  * @param   bool  $inspect FALSE:通常モード(デフォルト)/TRUE:検証モード
 211:  * @return  bool TRUE:成功/FALSE:失敗
 212: */
 213: function drawRect($p, $image, $width, $margin, $color, $thick=1, $inspect=FALSE) {
 214:     $res = TRUE;
 215:     $w = (float)($width - $margin * 2) / RATIO;
 216: 
 217:     // 一番外側の長方形
 218:     imagesetthickness($image, INSPECT_THICK2);
 219:     imagerectangle($image, $margin, (int)($margin / RATIO), $width - $margin, (int)($margin / RATIO + floor($w)), INSPECT_COLOR1);
 220: 
 221:     // 黄金長方形
 222:     $n = count($p- 1;
 223:     foreach ($p as $i=>$val) {
 224:         // 始点
 225:         $x1 = $x0 = $val[0];
 226:         $y1 = $y0 = $val[1];
 227:         $len = $val[2];
 228:         $dir = $val[3];
 229:         // 正方形を描く
 230:         for ($j = 0$j < 4$j++) {
 231:             switch ($dir) {
 232:                 case 90:
 233:                     $x2  = $x1;
 234:                     $y2  = $y1 + $len;
 235:                     $dir = 180;
 236:                     break;
 237:                 case 180:
 238:                     $x2  = $x1 - $len;
 239:                     $y2  = $y1;
 240:                     $dir = 270;
 241:                     break;
 242:                 case 270:
 243:                     $x2  = $x1;
 244:                     $y2  = $y1 - $len;
 245:                     $dir = 0;
 246:                     break;
 247:                 case 0:
 248:                     $x2  = $x1 + $len;
 249:                     $y2  = $y1;
 250:                     $dir = 90;
 251:                     break;
 252:             }
 253:             if ($x2 < $x0)   $x0 = $x2;
 254:             if ($y2 < $y0)   $y0 = $y2;
 255:             if (! $inspect) {
 256:                 $tck = $thick;
 257:                 $col = $color;
 258:             } else {
 259:                 if ($i !$n) {
 260:                     $tck = INSPECT_THICK1;
 261:                     $arr = color2rgb(INSPECT_COLOR1);
 262:                     $col = imagecolorallocate($image, $arr[0], $arr[1], $arr[2]);
 263:                 } else {
 264:                     $tck = INSPECT_THICK2;
 265:                     $arr = color2rgb(INSPECT_COLOR2);
 266:                     $col = imagecolorallocate($image, $arr[0], $arr[1], $arr[2]);
 267:                 }
 268:             }
 269:             imagesetthickness($image, $tck);
 270:             $res = imageline($image, (int)($margin + $x1 * $w), (int)($margin / RATIO + $y1 * $w), (int)($margin + $x2 * $w), (int)($margin / RATIO + $y2 * $w), $col);
 271:             if ($res == FALSE)  break;
 272:             $x1  = $x2;
 273:             $y1  = $y2;
 274:         }
 275:         // 塗りつぶし(検証モード)
 276:         if ($inspect && ($n == $i)) {
 277:             $arr = color2rgb(INSPECT_COLOR4);
 278:             $col2 = imagecolorallocate($image, $arr[0], $arr[1], $arr[2]);
 279:             $res = imagefill($image, $margin + ($x0 + $len / 2* $w, $margin + ($y0 + $len / 2* $w, $col2);
 280:         }
 281:     }
 282:     return $res;
 283: }

calcApexes に代入された座標に従い、黄金長方形を描くのがユーザー関数 drawRect である。

まず、一番外側の長方形を組み込み関数 imagerectangle を使って描く。
黄金長方形は、実際には正方形を描いてゆくのだが、頂点が必ずしも正方形の左上端とはかぎらないので、方向の値を見ながら正方形の頂点を計算し、組み込み関数 imageline を使って4辺を描画する。

解説:黄金螺旋を描く

GoldenSpiral.php

 285: /**
 286:  * 黄金螺旋を描く
 287:  * @param   array $p 頂点パラメータ
 288:  * @param   obj   $image イメージストリーム
 289:  * @param   int   $width 描画領域の横幅(ピクセル)
 290:  * @param   int   $margin  左右マージン(ピクセル)
 291:  * @param   int   $color 色ID
 292:  * @param   int   $thick 太さ(省略時=1)
 293:  * @param   bool  $inspect FALSE:通常モード(デフォルト)/TRUE:検証モード
 294:  * @return  bool TRUE:成功/FALSE:失敗
 295: */
 296: function drawSpiral($p, $image, $width, $margin, $color, $thick=1, $inspect=FALSE) {
 297:     $res = TRUE;
 298:     $w = (float)($width - $margin * 2) / RATIO;
 299: 
 300:     $n = count($p- 1;
 301:     foreach ($p as $i=>$val) {
 302:         if (! $inspect) {
 303:             $tck = $thick;
 304:             $col = $color;
 305:         } else {
 306:             if ($i !$n) {
 307:                 $tck = INSPECT_THICK1;
 308:                 $arr = color2rgb(INSPECT_COLOR1);
 309:                 $col = imagecolorallocate($image, $arr[0], $arr[1], $arr[2]);
 310:             } else {
 311:                 $tck = INSPECT_THICK2;
 312:                 $arr = color2rgb(INSPECT_COLOR3);
 313:                 $col = imagecolorallocate($image, $arr[0], $arr[1], $arr[2]);
 314:             }
 315:         }
 316:         imagesetthickness($image, $tck);
 317:         $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);
 318:         if ($res == FALSE)  break;
 319:     }
 320:     return $res;
 321: }

calcApexes に代入された座標に従い、黄金螺旋を描くのがユーザー関数 drawSpiral である。
黄金螺旋
黄金螺旋は、頂点の座標を中心とし、半径は辺の長さ、方向から90度だけ円弧を描く。
黄金螺旋
この円弧を繋いでゆくと‥‥
黄金螺旋
黄金螺旋となる。

参考サイト

(この項おわり)
header