<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Contour</title><link>https://mobiusone-org.github.io/hugo-contour/posts/</link><description>Recent content in Posts on Contour</description><generator>Hugo</generator><language>ja-JP</language><lastBuildDate>Fri, 12 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://mobiusone-org.github.io/hugo-contour/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Marching Squares で等高線地図を描く</title><link>https://mobiusone-org.github.io/hugo-contour/posts/marching-squares/</link><pubDate>Fri, 12 Jun 2026 00:00:00 +0000</pubDate><guid>https://mobiusone-org.github.io/hugo-contour/posts/marching-squares/</guid><description>&lt;h2 id="スカラー場をつくる"&gt;&lt;i&gt;01&lt;/i&gt;スカラー場をつくる&lt;/h2&gt;&lt;p&gt;等高線を引くには、まず「各地点の標高」を返す関数が必要になる。ここでは置換テーブルを使った
value noise を 4 オクターブ重ねた fBm を土台に、低周波の山塊をドメインワープで歪ませてから
細部ノイズを混ぜている。座標をそのままノイズに通すだけだと均質な「さざ波」にしかならないが、
ワープを挟むと尾根や盆地らしい大きな構造が現れる。&lt;/p&gt;
&lt;p&gt;海面の決め方にも一工夫ある。固定のしきい値ではなく、標高分布の分位点を海面とすることで、
どんな地形が生成されても水域の面積比が常に 40〜60% に収まる。
構図が毎回それなりに成立するのはこのためだ。&lt;/p&gt;
&lt;p&gt;&lt;span class="plate" role="figure" aria-label="海面 — 標高 0 m の等値線"&gt;
 &lt;span class="plate-frame"&gt;
 &lt;span class="mark tl" aria-hidden="true"&gt;&lt;/span&gt;
&lt;span class="mark tr" aria-hidden="true"&gt;&lt;/span&gt;
&lt;span class="mark bl" aria-hidden="true"&gt;&lt;/span&gt;
&lt;span class="mark br" aria-hidden="true"&gt;&lt;/span&gt;
 &lt;img src="https://mobiusone-org.github.io/hugo-contour/posts/marching-squares/waves.webp" alt="夕暮れの浜に崩れる波" width="1280" height="1831" loading="lazy" decoding="async"&gt;
 &lt;/span&gt;&lt;span class="plate-caption coords"&gt;海面 — 標高 0 m の等値線&lt;/span&gt;
&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;しきい値というと抽象的だが、実物はいつも目の前にある。海面は標高 0 m の等値線そのもので、
波打ち際は地形と水位が交差する場所を刻々と描き直している。marching squares がやっているのは、
要するにこの汀線をグリッドの上で見つけることだ。&lt;/p&gt;
&lt;h2 id="セルを-16-通りに分類する"&gt;&lt;i&gt;02&lt;/i&gt;セルを 16 通りに分類する&lt;/h2&gt;&lt;p&gt;標高場をグリッドにサンプリングしたら、隣接する 4 点を 1 セルとして、各頂点が等高線レベル
&lt;code&gt;t&lt;/code&gt; より上か下かを 4 ビットにまとめる。これで各セルは 16 通りのいずれかに分類され、
線分をどの辺からどの辺へ通すかが一意に（鞍点の 2 ケースを除いて）決まる。&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;let c = 0;
if (tl &amp;gt; t) c |= 8;
if (tr &amp;gt; t) c |= 4;
if (br &amp;gt; t) c |= 2;
if (bl &amp;gt; t) c |= 1;
if (c === 0 || c === 15) continue; // 全点が同じ側なら線は通らない

switch (c) {
 case 1: case 14: seg(left, bottom); break;
 case 2: case 13: seg(bottom, right); break;
 case 3: case 12: seg(left, right); break;
 case 4: case 11: seg(top, right); break;
 case 6: case 9: seg(top, bottom); break;
 case 7: case 8: seg(left, top); break;
 case 5: seg(left, top); seg(bottom, right); break;
 case 10: seg(left, bottom); seg(top, right); break;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;マーチングキューブ（3D 版）と違ってルックアップテーブルは
この &lt;code&gt;switch&lt;/code&gt; 文ひとつに収まる。レベル数 54 本 × 全セルを総当たりしても、
フルスクリーンで数ミリ秒のオーダーで終わる。&lt;/p&gt;</description></item></channel></rss>