Chapter 86

渦巻き

miku
miku
2021.11.19に更新
このチャプターの目次

実行例


解説

円形内の範囲を回転させるが、外側に行くほど回転が弱まるように設定する。

const radius = 200; // 半径
let maxRad = PI; // 最大回転のラジアン値

半径 radius 内が画像処理の対象で、円の中心部分に近づくにつれて回転が強まり、円の外側に近づくにつれて回転が弱まる。

(円の中心~外側がmaxRad~0ラジアンの回転に対応する)

const dest = createImage(img.width, img.height);
dest.copy(img, 0, 0, img.width, img.height, 0, 0, img.width, img.height);
dest.loadPixels();

あるピクセルを別のピクセルにコピーするアルゴリズムなので、入力と出力画像は分けておく。

// 円の中心位置
const cx = img.width / 2;
const cy = img.height / 2;

for (let y = 0; y < img.height; y++) {
  for (let x = 0; x < img.width; x++) {
    // 円の中心から対象位置までの距離を求める
    const d = dist(cx, cy, x, y);
    
    // 円の中心に近いほどthis.radの値に近づくようにする
    // 円の範囲外は回転する必要が無いので0を入れる
    const rad = max(0, map(d, 0, radius, maxRad, 0));

    // radラジアンが0より大きければ回転する必要があるので回転行列を使用して回転後の位置を求める
    if (rad > 0) {
      const tx = cx + round(cos(rad) * (x - cx) - sin(rad) * (y - cy));
      const ty = cy + round(sin(rad) * (x - cx) + cos(rad) * (y - cy));
      const c = getPixel(img, tx, ty);
      setPixel(dest, x, y, c);
    }
  }
}

ピクセルの抜け対策のため、回転後の座標から回転前の座標を回転行列で求める。

コード例

const radius = 180;
let img, maxRad;

function preload() {
  img = loadImage("./0.png");
}

function setup() {
  createCanvas(img.width, img.height);
  maxRad = PI;
  img.loadPixels();

  const dest = createImage(img.width, img.height);
  dest.copy(img, 0, 0, img.width, img.height, 0, 0, img.width, img.height);
  dest.loadPixels();

  const cx = img.width / 2;
  const cy = img.height / 2;

  for (let y = 0; y < img.height; y++) {
    for (let x = 0; x < img.width; x++) {
      const d = dist(cx, cy, x, y);
      const rad = max(0, map(d, 0, radius, maxRad, 0));

      if (rad > 0) {
        const tx = cx + round(cos(rad) * (x - cx) - sin(rad) * (y - cy));
        const ty = cy + round(sin(rad) * (x - cx) + cos(rad) * (y - cy));
        const c = getPixel(img, tx, ty);
        setPixel(dest, x, y, c);
      }
    }
  }

  dest.updatePixels();
  image(dest, 0, 0);
}

function getPixel(img, x, y) {
  const i = (y * img.width + x) * 4;
  return [img.pixels[i], img.pixels[i + 1], img.pixels[i + 2]];
}

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