格子を描画するための数式を実装する
格子を描画するための数式を実装する:
データ可視化などの際に格子状の模様を描画したくなる場合がある。自明な方法としては
なお、以下で紹介するのは
「平面上の座標 $(x, y)$ が与えられたとき、その地点はどのような色で塗られるべきか」という情報を与える関数 $f(x, y)$
であることに注意。形状ベースではなくピクセル塗り潰しベースの考え方であり、このような $f(x, y)$ から実際に描画をする際には
まずは単純なチェッカーボード(市松模様)を描画するための関数を考える。
各正方形の長さはいったん「1」とし、任意の地点 $(x, y)$ に対して0または1の値を返す $f(x, y)$ を用意したい。
この場合、$(x, y)$ の整数部分$\lfloor x \rfloor, \lfloor y \rfloor$ について
JavaScriptなら以下のように実装する。
これを利用し、各ピクセルについて「
※ 正方形の大きさを1以外にしたい場合は $x, y$ のスケール変換を行う。
続いて、以下のようにグラデーションで描画を行う方法を考える。
ここでも正方形の大きさは1とする。
考え方
点$(x, y)$と近くの格子との距離を計算し、0-1の範囲で格子との距離が近いほど大きな値をとるような関数を作り出す。
ひとつの方法
複素関数$f(z)$による写像 $f(x + iy) = u + iv$ は、正則ならば等角写像になることが知られている。
上述の
格子を等角写像で変換しているため、線と線の交わる部分が直角になっていることがわかると思う。
また、プログラム中で
1. 概要
データ可視化などの際に格子状の模様を描画したくなる場合がある。自明な方法としては- 縦横に等間隔の直線を描画
- 格子点に点をプロット
なお、以下で紹介するのは
「平面上の座標 $(x, y)$ が与えられたとき、その地点はどのような色で塗られるべきか」という情報を与える関数 $f(x, y)$
であることに注意。形状ベースではなくピクセル塗り潰しベースの考え方であり、このような $f(x, y)$ から実際に描画をする際には
- Pythonであればmatplotlib.pyplot.imshow()
- JavaScriptであればCanvasのピクセル値を指定する方法
2. 2色のチェッカーボード
まずは単純なチェッカーボード(市松模様)を描画するための関数を考える。各正方形の長さはいったん「1」とし、任意の地点 $(x, y)$ に対して0または1の値を返す $f(x, y)$ を用意したい。
この場合、$(x, y)$ の整数部分$\lfloor x \rfloor, \lfloor y \rfloor$ について
- $\lfloor x \rfloor, \lfloor y \rfloor$ が(偶数,偶数)なら0
- $\lfloor x \rfloor, \lfloor y \rfloor$ が(偶数,奇数)なら1
- $\lfloor x \rfloor, \lfloor y \rfloor$ が(奇数,偶数)なら1
- $\lfloor x \rfloor, \lfloor y \rfloor$ が(奇数,奇数)なら0
JavaScriptなら以下のように実装する。
JavaScript
function checkerBoard(x, y) { return Math.abs(Math.abs(Math.floor(x) % 2) - Math.abs(Math.floor(y) % 2)); }
checkerBoard(x, y)
が0なら白, 1なら黒」のような描画を行えばチェッカーボードが描画できる。※ 正方形の大きさを1以外にしたい場合は $x, y$ のスケール変換を行う。
3. 格子からの距離でグラデーション
続いて、以下のようにグラデーションで描画を行う方法を考える。ここでも正方形の大きさは1とする。
考え方
点$(x, y)$と近くの格子との距離を計算し、0-1の範囲で格子との距離が近いほど大きな値をとるような関数を作り出す。
ひとつの方法
- $x$ 方向、$y$方向それぞれについて、最寄りの格子からの距離$dx, dy$を計算する
- => 四捨五入した値との差の絶対値を利用
- この距離をそのまま使うのではなく、格子から離れるにつれて急激に減少するようにしたいので、対数を取った上で-1倍(符号を+に)する。そのままだと距離=0の格子上で無限大になるため、小さな数$\alpha$ を使って$-log(\alpha + dx)$のようにする
- => $\alpha$が小さな値になるほどシャープなグラデーションになる
- 値の範囲を0-1に正規化する。正規化の係数は$\alpha$に依存している。
JavaScript
function latticeProximity(x, y, alpha=0.01) { // alpha: prevent divergence of log - the smaller the sharper const dx = - Math.log(alpha + Math.abs(x - Math.round(x))); const dy = - Math.log(alpha + Math.abs(y - Math.round(y))); // Normalize const minVal = - 2 * Math.log(alpha + 0.5); const maxVal = - 2 * Math.log(alpha); // range of return value: [0, 1] return (dx + dy - minVal) / (maxVal - minVal); }
latticeProximity(x, y)
は点$(x, y)$ について格子からの距離に依存する0-1の値を返してくれる。各ピクセルについてこの返り値をカラースケール関数に渡して色を決めていけば良い。
4. 活用例
複素関数$f(z)$による写像 $f(x + iy) = u + iv$ は、正則ならば等角写像になることが知られている。上述の
latticeProximity()
を使って、ピクセル$(x, y)$ の色をlatticeProximity(u, v)
に基づいて決めていくと、以下のような図(ここでは$f(z) = sin(z)$)を描くことができる。格子を等角写像で変換しているため、線と線の交わる部分が直角になっていることがわかると思う。
また、プログラム中で
latticeProximity()
をcheckerBoard()
に書き換えるだけで、以下のようにも描画できる。
コメント
コメントを投稿