fract()

function fract(x) {
  return x - floor(x);
}
fract(12) // 0
fract(12.5) // 0.5
fract(13.7) // 0.7

fract() は引数に渡された数の小数部を取り出す関数だ。p5.jsでも同名同機能の関数が実装されている。

繰り返し

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

  for (let x = 0; x < width; x++) {
    fill((x / width) * 255, 0, 0);
    rect(x, 0, 1, height);
  }
}

前章で扱ったグラデーションのコードを見てみよう。x座標によって赤の値が変化する作例だが、x / width0~1 の範囲の割合に直したあと、その値を赤の最大値にかけることにより、x座標における赤の値を取得することができる。

では上記のようにグラデーションを繰り返して描く場合はどのようにしてコードを書けばいいのか。書き方は色々あるだろうが、ここで fract() を利用した作例を紹介したい。

const ratio = x / width;
fract(ratio * 2);

まず x / width で幅に対する割合を取るのは一緒だ。次にその割合を2倍にして 0~2 の範囲に変換したあと、fract() で小数部を取りだす。

ratio * 2 0 0.5 0.999 1 1.5 1.999
fract(ratio * 2) 0 0.5 0.999 0 0.5 0.999

fract() を取る前と後の比較が上記である。小数部は0以上1未満の範囲を取るので、1未満までは fract() を取る取らないに関わらず値は同じになるが、1 だと 0 に、1.5 だと 0.5 に、1.999 だと 0.999 と、ある整数おきにまた0以上1未満の値を取るようになる。

つまり、0~2の範囲の小数部は0~1の範囲を2回繰り返すという意味になる。小数部は正規化されていると考えることができるので、そのまま加工せず、赤の最大値にかけ合わせればいい。

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

  for (let x = 0; x < width; x++) {
    const ratio = fract((x / width) * 2);
    stroke(ratio * 255, 0, 0);
    rect(x, 0, 1, height);
  }
}

グラデーションを横に繰り返して描画する

const n = 10;

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

  for (let x = 0; x < width; x++) {
    const ratio = fract((x / width) * n);
    stroke(ratio * 255, 255, 255);
    rect(x, 0, 1, height);
  }
}

n 回繰り返すグラデーションの作例。x / width で求めた割合を n 倍して 0~n の範囲にする。ここから小数部を取り出すと、0~1 の範囲が n 回繰り返されることになる。

グラデーションを縦横に繰り返して描画する

function setup() {
  createCanvas(windowWidth, windowHeight);
  background(0);
  loadPixels();

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      const rx = fract((x / width) * 10);
      const ry = fract((y / width) * 10);
      const c = [rx * 255, ry * 255, 255];
      setPixel(x, y, c);
    }
  }
  
  updatePixels();
}

function setPixel(x, y, c) {
  const i = (y * width + x) * 4;
  pixels[i + 0] = c[0];
  pixels[i + 1] = c[1];
  pixels[i + 2] = c[2];
}

領域内の距離によって色を変える

function setup() {
  createCanvas(windowWidth, windowHeight);
  background(0);
  loadPixels();

  const maxD = dist(0, 0, 0.5, 0.5);

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      const rx = fract((x / width) * 10);
      const ry = fract((y / width) * 10);
      const d = dist(0.5, 0.5, rx, ry);
      const c = map(d, 0, maxD, 0, 255);
      const p = [c, c, c];
      setPixel(x, y, p);
    }
  }

  updatePixels();
}

function setPixel(x, y, p) {
  const i = (y * width + x) * 4;
  pixels[i + 0] = p[0];
  pixels[i + 1] = p[1];
  pixels[i + 2] = p[2];
}

fract()0~1 の領域を複数作るとき、領域の中にあるピクセルが、領域の中央から離れているほど白に近づける作例。

fract() による特性で、領域の中央の値は計算しなくても 0.5 だとわかること、値の範囲は 0~1 と自然に正規化されているので範囲変換が行いやすいのが特徴である。