Help us understand the problem. What is going on with this article?

格子を描画するための数式を実装する

More than 1 year has passed since last update.

1. 概要

データ可視化などの際に格子状の模様を描画したくなる場合がある。自明な方法としては

  • 縦横に等間隔の直線を描画
  • 格子点に点をプロット

などが考えられるが、もう少しだけ工夫を要する手法を紹介する。

なお、以下で紹介するのは
「平面上の座標 $(x, y)$ が与えられたとき、その地点はどのような色で塗られるべきか」という情報を与える関数 $f(x, y)$
であることに注意。形状ベースではなくピクセル塗り潰しベースの考え方であり、このような $f(x, y)$ から実際に描画をする際には

などを利用すれば良い。

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

とすれば良い。例えば2で割った余りの差の絶対値をとる。
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なら以下のように実装できる

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()に書き換えるだけで、以下のようにも描画できる。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away