Chapter 07

クランプ

miku
miku
2021.11.10に更新

max()

max(x, y);

max() は渡された引数のうち大きい方を返す関数だ。これはそのとおりに使うのが普通なのだが、もうひとつ大事な使い方がある。

const x = 100;
const y = 任意の値;
const v = max(x, y);

x の値は固定だが、y には任意の値を入れることができるとする。xymax() を取った値を v とすると、v はどういう値の範囲をとるのかを考えてみよう。

実は v は常に x 以上の値になる。

const x = 100;

max(x, 90); // 100
max(x, 100); // 100
max(x, 110); // 110

xy が同じ場合」 ... x(y) を返す。
xy より小さい場合」 ... (x より大きい数である) y を返す。
xy より大きい場合」 ... x を返す。

これを絵で表現すると下記のとおりになる。

コード全体を見る
const x = 100;

function setup() {
  createCanvas(windowWidth, windowHeight);
  noFill();
}

function draw() {
  clear();

  strokeWeight(1);
  stroke(255, 0, 0);
  line(x, 0, x, height);

  strokeWeight(2);
  stroke(240);
  const mx = max(x, mouseX);
  line(mx, 0, mx, height);
}
const x = 100; // 線のx座標
const mx = max(x, mouseX); // 線とマウスのx座標の大きい方を返す
// line(x1, y1, x2, y2)で(x1, y1)から(x2, y2)まで直線を引くことができる。
line(mx, 0, mx, height);

赤の縦線がx座標を固定している線で、白の縦線がマウスで動かすことができる線になる。白の縦線のx座標は max(x, mouseX) にしているので、赤線より左側に移動することができない。

ここまでをまとめると、x を固定した状態で max(x, y) を取ると、y がどんな値でも、必ず x 以上の値を返す。

min()

min(x, y);

min() は渡された引数のうち小さい方を返す関数だ。

const x = 100;
const y = 任意の値;
const v = max(x, y);

先ほどと同じように、x の値は固定だが、y には任意の値を入れることができるとする。xymin() を取った値を v とすると、v はどういう値の範囲をとるのかを考えてみよう。

もう想像が付いているかもしれないが、v は常に x 以下の値になる。

const x = 100;

min(x, 90); // 90
min(x, 100); // 100
min(x, 110); // 100
コード全体を見る
let x;

function setup() {
  createCanvas(windowWidth, windowHeight);
  noFill();

  x = width - 100;
}

function draw() {
  clear();

  strokeWeight(1);
  stroke(255, 0, 0);
  line(x, 0, x, height);

  strokeWeight(2);
  stroke(240);
  const mx = min(x, mouseX);
  line(mx, 0, mx, height);
}
const x = width - 100; // 線のx座標
const mx = min(x, mouseX);
line(mx, 0, mx, height);

マウスで白線を動かしてみると、赤線より右側に移動できないことが確認できる。

ここまでをまとめると、x を固定した状態で min(x, y) を取ると、y がどんな値でも、必ず x 以下の値を返す。

max() と min() の組み合わせ

コード全体を見る
let left, right;

function setup() {
  createCanvas(windowWidth, windowHeight);
  noFill();

  left = 100;
  right = width - 100;
}

function draw() {
  clear();

  strokeWeight(1);
  stroke(255, 0, 0);
  line(left, 0, left, height);
  line(right, 0, right, height);

  strokeWeight(2);
  stroke(240);
  const mx = max(left, min(right, mouseX));
  line(mx, 0, mx, height);
}
const left = 100; // 左の境界
const right = width - 100; // 右の境界
const mx = max(left, min(right, mouseX));
line(mx, 0, mx, height);

max()min() を組み合わせると、max() で固定した値以上、min() で固定した値以下の範囲に値を制限できる。

コード例では min(), max() の順番で mouseX の制限を行っているが、これが逆になっても結果は変わらない。

クランプ

function clamp(value, min, max) {
  return max(min(value, max), min);
}

上記で行った max()min() の組み合わせをクランプと呼ぶ。クランプは、値・最小値・最大値を渡して、値が最小値~最大値の範囲を超えないように調整を行う。

p5.jsでは同機能の constrain() という関数があるので今後はこちらを利用する。

constrain(-50, 0, 100); // 0
constrain(50, 0, 100); // 50
constrain(150, 0, 100); // 100

正規化・線形補間・マップの範囲制限

前章で扱った正規化・線形補間・マップは、場合によっては範囲外の値を返す可能性があったが、クランプによって範囲内に値を収めることができる。

たとえば正規化の場合、v の値を constrain(v, a, b) としておけば、正規化に渡す v が必ず a~b の範囲に収まるようになり、変換先で範囲外である0未満または1より大きい値になることはない。線形補間とマップの場合も同様である。

正規化の制限

const v = 0;
const a = 100;
const b = 200;

// norm()に渡す前にvをa~bの範囲に収めることで、必ず0~1の範囲の値が返る
norm(constrain(v, a, b), a, b);

線形補間の制限

const v = 2;
const a = 100;
const b = 200;

// lerp()に渡す前にvを0~1の範囲に収めることで、必ずa~bの範囲の値が返る
lerp(a, b, constrain(v, 0, 1));

マップの制限

const v = 2;
const a = 100;
const b = 200;
const c = 1000;
const d = 2000;

// map()に渡す前にvをa~bの範囲に収めることで、必ずc~dの範囲の値が返る
map(constrain(v, a, b), a, b, c, d);