Chapter 25

可視化

miku
miku
2021.11.12に更新

この章では、物体の見えない情報をあえて数値や形にして可視化することについて学ぶ。

たとえばゲームでは、体力をHPとして表示する、プレイヤーの可視範囲を表示する、ステルスゲームで敵の可視範囲を表示する、などのように内部の情報を数値や絵として表現することが多い。

他にも、少し先の情報を可視化することがあり、たとえば投げ物のような仕組みがある場合はたいてい放物線に投げることになるので、その軌道を前もって表示したり、ワープができるなら移動先の位置をフォーカスしたり、投げ物の場合と同じように移動ルートを出す、最短経路の情報をプレイヤーの少し先の位置まで表示するなどの活用方法がある。

ベクトルの可視化

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

function draw() {
  const d = dist(mouseX, mouseY, width / 2, height / 2);
  const angle = atan2(mouseY - height / 2, mouseX - width / 2);

  clear();
  translate(width / 2, height / 2);
  rotate(angle);
  line(0, 0, d, 0);
  line(d, 0, d - 10, -10);
  line(d, 0, d - 10, 10);
  resetMatrix();
}

ベクトルの向きと大きさを、矢印として可視化する作例。

三角関数の可視化

const r = 150;
let angle;

function setup() {
  createCanvas(windowWidth, windowHeight);
  angleMode(DEGREES);
  stroke(240);
  noFill();

  angle = 0;
}

function draw() {
  clear();

  strokeWeight(1);
  stroke(100);
  circle(width / 2, height / 2, r * 2);

  const x = width / 2 + cos(angle) * r;
  const y = height / 2 + sin(angle) * r;

  strokeWeight(3);
  stroke(208, 70, 72);
  line(width / 2, height / 2, x, height / 2);
  stroke(89, 125, 206);
  line(x, height / 2, x, y);
  stroke(240);
  line(width / 2, height / 2, x, y);

  angle++;
  angle %= 360;
}

三角関数による成分の分解を可視化する作例。

半径の可視化

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

  for (let i = 0; i < 50; i++) {
    const x = random(width);
    const y = random(height);
    const r = random(10, 50);

    stroke(240);
    noFill();
    circle(x, y, r * 2);

    noStroke();
    fill(240);
    text(r.toFixed(0), x, y);
  }
}

円の半径を円の中央に描画して可視化する作例。

向きの可視化

const n = 30;
let arrows;

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

  arrows = [];
  for (let i = 0; i < n; i++) {
    const x = random(width);
    const y = random(height);
    const vx = random([-2, 2]);
    const vy = random([-2, 2]);

    arrows.push({ x, y, vx, vy });
  }
}

function draw() {
  clear();

  for (let arrow of arrows) {
    arrow.x += arrow.vx;
    arrow.y += arrow.vy;

    if (arrow.x < 0) {
      arrow.x = 0;
      arrow.vx *= -1;
    } else if (arrow.x >= width) {
      arrow.x = width - 1;
      arrow.vx *= -1;
    }

    if (arrow.y < 0) {
      arrow.y = 0;
      arrow.vy *= -1;
    } else if (arrow.y >= height) {
      arrow.y = height - 1;
      arrow.vy *= -1;
    }

    translate(arrow.x, arrow.y);
    scale(0.3);
    rotate(atan2(arrow.vy, arrow.vx));
    drawArrow();
    resetMatrix();
  }
}

function drawArrow() {
  beginShape();
  vertex(-40, -20);
  vertex(30, -20);
  vertex(30, -50);
  vertex(90, 0);
  vertex(30, 50);
  vertex(30, 20);
  vertex(-40, 20);
  endShape();
}

ベクトルの向きを可視化する作例。

矢印のような複雑な図形を描く場合は vertex() を利用する。beginShape()endShape() の間に複数の vertex(x座標, y座標) を入れると、書いた vertex() の順番通りに頂点を繋いだ図形を描画する。

向きの可視化 その2

let tx, ty;
let circles;
const r = 30;

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

  tx = width / 2;
  ty = height / 2;

  circles = [];
  for (let i = 0; i < 50; i++) {
    const x = random(width);
    const y = random(height);
    circles.push({ x, y });
  }
}

function draw() {
  clear();

  ty++;
  ty %= height;

  circles.forEach((c) => {
    drawCircle(c);
  });

  drawTarget();
}

function drawCircle(c) {
  strokeWeight(2);
  stroke(240);
  noFill();
  circle(c.x, c.y, r * 2);
  const rad = atan2(ty - c.y, tx - c.x);
  line(c.x, c.y, c.x + cos(rad) * r, c.y + sin(rad) * r);
}

function drawTarget() {
  strokeWeight(2);
  stroke(240);
  line(tx - 10, ty, tx + 10, ty);
  line(tx, ty - 10, tx, ty + 10);
}

円は回転しているかが見た目ではわからないので、角度の方向に線を入れることにより、向きの可視化をした作例。

向きの可視化 その3

const d = 300;
let angle;

function setup() {
  createCanvas(windowWidth, windowHeight);
  angleMode(DEGREES);
  strokeWeight(3);

  angle = 0;
}

function draw() {
  clear();

  translate(width / 2, height / 2);

  noStroke();
  fill(240);
  circle(0, 0, d);

  stroke(240);
  noFill();
  arc(0, 0, d + 30, d + 30, angle - 10, angle + 340);

  angle++;
}

円の外側に少し穴が空いた円弧を描き、円の角度に合わせて穴が空いた部分を移動させて、円の角度を可視化させる作例。

arc(x, y, w, h, start, stop) で、中心座標が (x, y)、幅と高さが (w, h)start~stop の角度まで時計回りに孤を描く。start, stop はラジアンの指定だが angleMode(DEGREES) で度に変更できる。

回転の可視化

const d = 300;
const n = 3;
let arcs, baseAngle;

function setup() {
  createCanvas(windowWidth, windowHeight);
  angleMode(DEGREES);
  strokeWeight(3);

  arcs = [];
  for (let i = 0; i < n; i++) {
    const start = random(360);
    const stop = start + random(50, 360);
    arcs.push({ d: d + (i + 1) * 15, start, stop });
  }

  baseAngle = 0;
}

function draw() {
  clear();

  translate(width / 2, height / 2);
  noStroke();
  fill(240);
  circle(0, 0, d);

  stroke(240);
  noFill();

  for (const a of arcs) {
    arc(0, 0, a.d, a.d, baseAngle + a.start, baseAngle + a.stop);
  }

  baseAngle += 1;
}

具体的な向きはわからないが、円が回転していることを、周りの弧で表現している作例。

速度の可視化

let x, y, vx, vy;

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

  x = width / 2;
  y = height / 2;
  vx = 10;
}

function draw() {
  clear();

  x += vx;

  translate(x, y);
  line(10, 0, -10, -7);
  line(-10, -7, -10, 7);
  line(-10, 7, 10, 0);
  line(-10, 0, -10 - vx * 3, 0);
  resetMatrix();

  if (x >= width + 10) {
    x = 0;
  }
}

速度を線の長さで可視化する作例。