ブログ |

PROMPT → WEB
#010
STRAIGHTEDGE & COMPASS
CLAUDE CODE × CANVAS 2D / GEOMETRY

そのWeb演出、Claude Codeで作れます。クリエイティブWeb表現のプロンプト集 #10 Straightedge & Compass

株式会社グルーヴサインの米沢です。

AIコーディングでクリエイティブなWeb表現を実際に作り、そのプロンプトごと公開していく「PROMPT → WEB」の第10回です。

PROMPT → WEB #010

Straightedge & Compass

INTRODUCTION

前回(#09)の最後に、次は「幾何の証明が組み上がっていく線」と書きました。今回がそれです。

これまでの9回は、どれも見た目をどう作るかの話でした。今回だけは少し性格が違って、主役は正しさです。

題材は、定規とコンパスによる作図。2300年ほど前に『原論』で整理された手続きで、使える操作はたった3つしかありません。

① 与えられた2点を通る直線を引く
② 与えられた中心と半径で円を描く
③ すでに引いた線どうしの交点を取る

この3つだけで、正三角形も、正方形も、正六角形も、そして正五角形も描けます。五角形というのが面白いところで、頂点の位置は 72° ずつですが、作図では角度を一度も測りません。 円と円の交点を取っていくうちに、勝手に72°になります。

今回のコードには Math.cos も Math.sin も、頂点を求める場所には1行も出てきません。 出てくるのは交点の計算だけです。それで本当に正しい正五角形になる、というのを画面で見てもらうのが今回の趣旨です。

RESULT

まず現物です。下のボタンを押してみてください。

クリックすると、この画面の上で Straightedge & Compass が再生されます(何度でも再生可能)。ESC または CLOSE で終了します。

暗い紫紺の空間に、金色の円がひとつ描かれるところから始まります。コンパスの弧が光跡を引いて走り、交点が星のようにきらめき、最後に図形が結ばれて明滅します。描いている先端からは光の粒が散り、完成の瞬間には頂点から光が弾けます。画面下に、いま何をしているかの手順が出ます。操作は2つです。

しばらく触らずにいると、点 P がひとりでにゆっくり回り、作図が繰り返されます。ESC または CLOSE で終了します。

WHAT WE MADE

技術的に分解すると、次のようになります。

HOW IT WORKS

1. 円と円の交点

いちばん使うのがこれです。2つの円の中心と半径から、交点を求めます。

function circleCircle(c1, r1, c2, r2) {
  const d = dist(c1, c2);
  if (d < 1e-9 || d > r1 + r2 + 1e-9 || d < Math.abs(r1 - r2) - 1e-9) return [];
  const a = (r1 * r1 - r2 * r2 + d * d) / (2 * d);   // 中心線に沿った距離
  const h = Math.sqrt(Math.max(0, r1 * r1 - a * a)); // そこからの垂線の長さ
  const px = c1.x + a * (c2.x - c1.x) / d;
  const py = c1.y + a * (c2.y - c1.y) / d;
  return [{ x: px - h * (c2.y - c1.y) / d, y: py + h * (c2.x - c1.x) / d },
          { x: px + h * (c2.y - c1.y) / d, y: py - h * (c2.x - c1.x) / d }];
}

離れすぎ・近すぎの判定を省くと Math.sqrt が NaN を返し、そこから先の作図が静かに全部壊れます。交点が「無い」場合があることを、最初に書いておくのが大事です。

2. 垂直二等分線が、すべての基本

2点 A・B から等距離の点の集まりが、垂直二等分線です。作図では、A と B を中心に同じ半径の円を描き、その2つの交点を結ぶだけで引けます。

これが引けると、中点(二等分線と線分ABの交点)と直角(二等分線そのもの)が手に入ります。今回の正方形も正五角形も、出発点はここです。

3. 正五角形の一辺を、角度を使わずに出す

いちばんの山場です。半径 r の円に内接する正五角形の一辺は 2r·sin36° ですが、作図ではこの式を使いません。代わりにこうします。

① 円の中心 O、円周上の点 A(上)
② A を通る直径を引き、反対側の交点 A' を取る
③ AA' の垂直二等分線を引き、円との交点 Q を取る(=直角が手に入る)
④ OQ の垂直二等分線から、OQ の中点 M を取る
⑤ M を中心に、半径 MA の円を描く
⑥ その円が直径 QQ' を切る点 N を取る
⑦ 線分 AN の長さが、正五角形の一辺

本当にそうなるのか、数値で確かめました。半径1で計算すると、

求めた一辺 AN   1.175570505
2·sin(36°)      1.175570505   ← 誤差 0.00e+0

一致します。あとはこの長さをコンパスに取り、円周を5回刻めば5頂点です。刻んだ結果の各辺を測ると、5本とも 352.6712(半径300pxのとき)で揃い、最後の点が最初の点にぴったり戻ります。角度を一度も計算していないのに、72°ずつ並んでいるわけです。

4. 交点は2つ出る ── どちらへ進むかを決める

円周をコンパスで刻むとき、円と円の交点は毎回2つ出ます。片方は進みたい方向、もう片方は1つ前に戻る方向です。素直に書くと、行って戻ってを繰り返して図形が閉じません。

進行方向は外積の符号で決めました。中心から見て、いまの点から左回りか右回りかを判定しています。

const cross = (z) => (from.x - O.x) * (z.y - O.y) - (from.y - O.y) * (z.x - O.x);
return candidates.sort((p, q) => cross(q) - cross(p))[0];

作図の本には「順に取っていく」としか書かれていない部分で、コードにするとここが一番引っかかります。

5. 手順をデータとして持つ

作図を手続きのコードとして書くのではなく、描くものの配列として組み立てています。

steps.push({ kind: 'circle', c: M, r: dist(M, A), aux: true, cap: 'M を中心に半径 MA の円を描く' });
steps.push({ kind: 'point',  p: N, name: 'N',     cap: '直径との交点 N を取る' });

この形にしておくと、順番に途中まで描く(=アニメーション)、いまの手順を説明文として出す、手順数を数えるが、全部ただで手に入ります。描画側は配列を順に描くだけで、作図の中身を知る必要がありません。

6. 光に見せる

線をそのまま引くと、ただの図形です。発光させるのに filter: blur() のような重い手は使わず、同じ線を3回、太さと濃さを変えて加算合成で重ねています。

const passes = [[6.5, 0.085], [2.6, 0.22], [1, 1]];   // [太さ倍率, 濃さ倍率]
ctx.globalCompositeOperation = 'lighter';
for (const [wm, am] of passes) { /* 同じ線を引く */ }

外側の太く薄い1枚が滲み、芯の1枚が明るく締まる。これだけで線が光って見えます。交点の印も、放射グラデーションに十字のきらめきを足しただけです。

線には、ごくわずかな揺らぎも入れています。ここで注意が要りました。揺れを毎フレーム乱数で引き直すと、線が小刻みに波打ちます。 位置と線の番号から決まる固定のハッシュを使い、同じ線なら常に同じ揺れになるようにしています。

const hash = (i, seed) => {
  const x = Math.sin(i * 127.1 + seed * 311.7) * 43758.5453;
  return (x - Math.floor(x)) * 2 - 1;
};

7. 見た目を差し替えても、幾何は1行も変わらない

最初に作ったとき、この作品は黒板とチョークでした。白い線、緑の板、粉っぽい質感。作図の記録としては悪くなかったのですが、見ていて心が躍るかというと、そうでもない。

そこで魔法陣に変えました。このとき、幾何のコードは1行も触っていません。

手順3で書いたとおり、作図は「描くものの配列」として組み立ててあります。描画側は配列を順に描くだけなので、chalkPath() を glowPath() に差し替え、色を決め直し、光の粒を足しただけで終わりました。作図と描画が分かれていると、見た目の作り直しが怖くなくなります。

考えてみれば、魔法陣はもともと定規とコンパスの作図です。描いているものは最初から同じでした。

THE PROMPT

ここが本題です。上のデモを作るためにClaude Codeへ渡せる、汎用プロンプト全文を掲載します。右上の COPY ボタンでそのままコピーできます。

あなたは計算幾何とCreative Codingに精通したフロントエンドエンジニアです。
「Straightedge & Compass」= 光の魔法陣として、定規とコンパスだけの操作から正多角形が
組み上がっていく表現を、Canvas 2D で実装してください。
(幾何ライブラリは使わない。交点の計算は自前で書く)

# 最重要の制約
- 使ってよい操作は3つだけ: 直線を引く / 円を描く / すでに引いた線どうしの交点を取る
- 【厳守】正多角形の頂点を三角関数で求めないこと。
  Math.cos / Math.sin で頂点を並べるのは作図ではない。
  頂点は必ず「円と円の交点」「直線と円の交点」として導くこと
  (円弧を描画する時だけは cos/sin を使ってよい。あくまで頂点の導出の話)

# 置き換える変数
- GROUND  = "#140c1d"  // 深い紫紺の夜
- INK     = "#ffe9b8"  // 光で引く線
- AUX     = "rgba(236,200,130,0.30)"  // 補助線は淡い金
- 仕上げの図形は作図ごとに色を変える:
    正三角形 "#ffd98a" / 正方形 "#ff9fc4" / 正六角形 "#8ef0d8" / 正五角形 "#c9a6ff"
- LABEL = "STRAIGHTEDGE & COMPASS"

# 幾何の核(この3つだけ書けばよい)
- circleCircle(c1,r1,c2,r2) … 離れすぎ/近すぎ/同心を必ず弾く。弾かないとNaNが伝播して
  以降の作図が静かに全部壊れる
- lineLine(a,b,c,d)         … 平行は null を返す
- lineCircle(a,b,c,r)       … 判別式が負なら空配列
- bisector(a,b) … 垂直二等分線。a,b を中心に半径|ab|の円を2つ描き、その交点2つを返す。
  中点も直角も、すべてここから導く

# 4つの作図(クリックで切り替え)
1. 正三角形 … 同じ半径の2円の交点
2. 正方形   … 直径と、その垂直二等分線が円を切る2点
3. 正六角形 … 半径をそのままコンパスに取り、円周を刻む(半径=一辺)
4. 正五角形 … ①直径AA' ②AA'の垂直二等分線→円との交点Q ③OQの中点M
               ④M中心・半径MAの円が直径を切る点N ⑤線分ANが一辺 ⑥ANで円周を5回刻む
- 【注意】円周を刻むとき交点は毎回2つ出る。進行方向を外積の符号で選ばないと
  行ったり来たりして図形が閉じない

# 作図は「手順のデータ」として持つ
- { kind:'circle'|'line'|'point'|'figure', …座標…, aux:bool, cap:'この手順の説明' } の配列を作る
- 描画側は配列を順に描くだけにする。こうすると、途中まで描くアニメーション・
  手順の説明表示・手順数の表示が、追加の実装なしで手に入る

# 描画(光の魔法陣)
- 線は発光させる。filter:blur は使わず、同じ線を3回重ねる:
    [太さ×6.5, 濃さ×0.085] → [×2.6, ×0.22] → [×1, ×1]、合成は 'lighter'
- 線にごくわずかな揺らぎを入れる。
  【罠】揺れを毎フレーム乱数で引き直すと線が小刻みに波打つ。
  点の番号と線の番号から決まる固定のハッシュを使い、同じ線は常に同じ揺れにすること
- 補助線は淡い金で薄く、仕上げの図形だけ太く鮮やかに。完成後はゆっくり明滅させる
- 交点は「放射グラデ+十字のきらめき」で星のように光らせ、記号(A, B, M, N …)を添える
- 【注意】補助点の記号と頂点の記号を衝突させない(同じ図に B が2つ出ると読めなくなる)
- 光の粒を散らす: ①描いている先端から火花 ②完成の瞬間に頂点から弾ける
  ③何もしていない間も、ゆっくり漂う粒を少しずつ足して空気を光らせる
- 背景は作図の中心から立ちのぼる紫のオーラと、周辺光量落ち、ごく薄い粒子
- 画面下に、いまの手順の説明文を明朝系で1行

# インタラクション
- ポインタを動かすと円周上の点Pが動き、【作図全体をその場で引き直す】。
  毎フレーム全部やり直しても計算は数マイクロ秒で済む(録画でないことが伝わる)
- クリック/タップで次の作図へ。手順は最初から再生する
- 数秒操作が無ければ、点Pをゆっくり回して作図を繰り返す

# 品質要件(production)
- 全画面オーバーレイ。CLOSE ボタンと ESC で終了。多重起動を防ぐ
- レスポンシブ: リサイズで座標を組み直す。devicePixelRatio は最大2でクランプ
- prefers-reduced-motion: アニメーションせず、完成した図を1枚だけ描いて静止させる
- 終了時に requestAnimationFrame を停止し、全リスナを解除し、CanvasをDOMから除去する
- Canvas 2D が使えない環境では静かに何もしない。本文の閲覧を妨げない

CUSTOMIZE

PERFORMANCE

今回は、計算より描画のほうが重いという珍しい回です。

WHAT AI DID / WHAT HUMAN DIRECTION MATTERS

幾何の公式そのものは、AIの得意分野です。円と円の交点も、垂直二等分線も、正五角形の作図手順も、聞けば正確に返ってきます。 境界条件(離れすぎた円、平行な直線)の処理まで含めて、ここは完全にAIの領分でした。

ただ、何も指定しないとAIは頂点を三角関数で並べます。 それはそれで正しい正五角形が描けるので、画面だけ見れば区別がつきません。しかしそれは作図ではなく、今回の記事は成立しません。

ですから今回の人間の仕事は、ほとんどが制約を課すことでした。

面白いのは、縛りを入れたほうがコードが短くなったことです。三角関数で頂点を並べる実装より、交点だけで組み立てる実装のほうが、関数が3つで済みました。制約は窮屈にするものだと思われがちですが、設計を決めてくれることのほうが多い。AIに何を禁じるかが、そのまま作品の輪郭になるという回でした。

NEXT

粒子、文字、線、物理、計算、製版、音、分割、光と影、そして今回の作図。ちょうど10回になりました。

次回はスクロール連動か、歪像(ある一点から見たときだけ像を結ぶ絵)あたりを考えています。

今回のプロンプトも丸ごとコピーして使えます。色を自社のブランドカラーに置き換えて、Claude Codeに渡してみてください。角度を測らずに五角形が閉じる瞬間は、何度見ても気持ちがいいものです。

レガシーPHPシステムの保守・引き継ぎのご相談を受け付けています。
初回のご相談・ヒアリングは無料です。

無料相談・お問い合わせはこちら

株式会社グルーヴサイン 代表取締役 米沢忠司。PHP開発歴約20年。他社開発システムの保守・引き継ぎを主力に、2007年の設立から19期目。