Chapter 68

反復関数系

miku
miku
2021.11.19に更新

前章では再帰という形でフラクタルの描画方法を扱ったが、再帰関数の内部で行っているのは回転や移動を行うというものだった。そうすると、回転や移動のような変換の種類と、30度や100pxのような変換の値をパラメータとしてまとめたものを関数に指定してもフラクタルを描画できそうだ。そのような形式の実現方法として反復関数系やL-Systemというものがあり、前者は本章で、後者は次の章で扱う。

反復関数系

for (let i = 0; i < n; i++) {
  const rule = getRule();
  pos = f(rule, pos);
  point(pos.x, pos.y);
}

反復関数系は「48章 - 行列・アフィン変換」で扱ったアフィン変換を利用した描画方法になる。アフィン変換に必要なパラメータである a, b, c, d, tx, tyweight と呼ぶ値を結合したものをルールと呼び、そのルールをランダムに取り出してアフィン変換を反復して繰り返す。ランダムは完全なランダムではなく、weight を重みとした取り出し方になる。

反復関数系の作例

let rules, x, y;

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

  rules = [
    {
      a: 0.05,
      b: 0,
      c: 0,
      d: 0.6,
      tx: 0,
      ty: 0,
      weight: 0.17,
    },
    {
      a: 0.05,
      b: 0,
      c: 0,
      d: -0.5,
      tx: 0,
      ty: 1,
      weight: 0.17,
    },
    {
      a: 0.46,
      b: -0.321,
      c: 0.386,
      d: 0.383,
      tx: 0,
      ty: 0.6,
      weight: 0.17,
    },
    {
      a: 0.47,
      b: -0.154,
      c: 0.171,
      d: 0.423,
      tx: 0,
      ty: 1.1,
      weight: 0.17,
    },
    {
      a: 0.433,
      b: 0.275,
      c: -0.25,
      d: 0.476,
      tx: 0,
      ty: 1,
      weight: 0.16,
    },
    {
      a: 0.421,
      b: 0.257,
      c: -0.353,
      d: 0.306,
      tx: 0,
      ty: 0.7,
      weight: 0.16,
    },
  ];

  stroke(240);
  noFill();
  x = random();
  y = random();
}

function draw() {
  translate(width / 2, height);

  for (let i = 0; i < 100; i++) {
    const rule = getRule();
    const tx = x;
    const ty = y;
    x = tx * rule.a + ty * rule.b + rule.tx;
    y = tx * rule.c + ty * rule.d + rule.ty;

    point(x * 350, -y * 350);
  }
}

function getRule() {
  let rand = random();
  for (const rule of rules) {
    if (rand < rule.weight) {
      return rule;
    }
    rand -= rule.weight;
  }
}

反復関数系の作例 その2

let rules, x, y;

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

  rules = [
    {
      a: 0.85,
      b: 0.04,
      c: -0.04,
      d: 0.85,
      tx: 0,
      ty: 1.6,
      weight: 0.65,
    },
    {
      a: -0.15,
      b: 0.28,
      c: 0.26,
      d: 0.24,
      tx: 0,
      ty: 0.44,
      weight: 0.07,
    },
    {
      a: 0.2,
      b: -0.26,
      c: 0.23,
      d: 0.22,
      tx: 0,
      ty: 1.6,
      weight: 0.07,
    },
    {
      a: 0,
      b: 0,
      c: 0,
      d: 0.16,
      tx: 0,
      ty: 0,
      weight: 0.21,
    },
  ];

  stroke(240);
  noFill();
  x = random();
  y = random();
}

function draw() {
  translate(width / 2, height);

  for (let i = 0; i < 100; i++) {
    const rule = getRule();
    const tx = x;
    const ty = y;
    x = tx * rule.a + ty * rule.b + rule.tx;
    y = tx * rule.c + ty * rule.d + rule.ty;

    point(x * 50, -y * 50);
  }
}

function getRule() {
  let rand = random();
  for (const rule of rules) {
    if (rand < rule.weight) {
      return rule;
    }
    rand -= rule.weight;
  }
}