Pomax / custom-graphics-element

Because what if you could just... write graphics sketches? On the web? Like, directly?
MIT License
17 stars 1 forks source link

implement homogeneous projection. #97

Open Pomax opened 3 months ago

Pomax commented 3 months ago
function setup() {
  setSize(600, 400);
  setBorder(1, `black`);
  setGrid(20, `grey`);
  play();
}

function draw() {
  clear(`#333`);
  randomSeed(3);
  center();
  drawCube(100, ([x, y, z]) =>
    hproject(
      x, y, z,
      frame / 100, frame / 150, frame / 200,
      250
    )
  );
}

function drawCube(s, pproject) {
  const top = [[-s, s, s], [-s, -s, s], [s, -s, s], [s, s, s]];
  const bottom = [[-s, s, -s], [-s, -s, -s], [s, -s, -s], [s, s, -s]];
  const cube = [top, bottom].map(pts => pts.map(pproject));
  cube.forEach(pts => {
    setColor(randomColor());
    pts.forEach((p, i) => {
      point(...p);
      const q = pts[(i + 1) % pts.length];
      line(...p, ...q);
    })
  });
  setColor(randomColor());
  range(0, top.length, 1, i => line(...cube[0][i], ...cube[1][i]));
}

function hproject(Vx, Vy, Vz, yaw = 0, pitch = 0, roll = 0, w = Infinity, Tx = 0, Ty = 0, Tz = 0, Sx = 1, Sy = 1, Sz = 1) {
  const [y, p, r] = [yaw, pitch, roll];
  const cy = cos(y), sy = sin(y);
  const cp = cos(p), sp = sin(p);
  const cr = cos(r), sr = sin(r);

  // homogenous affine transform matrix
  const M = [
    Sx * cp * cy, Sx * (cy * sp * sr - cr * sy), Sx * (cr * cy * sp + sr * sy), Tx,
    Sy * cp * sy, Sy * (cr * cy + sp * sr * sy), Sy * (cr * sp * sy - cy * sr), Ty,
    -Sz * sp, Sz * cp * sr, Sz * cp * cr, Tz,
    sp / w, -cp * sr / w, -cp * cr / w, 1,
  ];

  // homogeneous transformed input vector
  const P = [
    Vx * M[0] + Vy * M[1] + Vz * M[2] + M[3],
    Vx * M[4] + Vy * M[5] + Vz * M[6] + M[7],
    Vx * M[8] + Vy * M[9] + Vz * M[10] + M[11],
    Vx * M[12] + Vy * M[13] + Vz * M[14] + M[15],
  ]

  // screen coordinate
  return [P[0] / P[3], P[1] / P[3]];
}
Pomax commented 3 months ago

Partly done. Arc: in 3d:

function setup() {
  setSize(600, 400);
  setBorder(1, `black`);
  setProjector(HOMOGENEOUS).setInfinity(Infinity);
  rotateProjector(0.1, -0.2, -0.8);
  play();
}

function draw() {
  setCursor(`none`);
  clear(`white`);
  center();
  noFill();
  setStroke(`black`);

  rotateProjector(0.1, -0.2, frame/100);

  const d = 100;
  line(-d,0,0, d,0,0);
  line(0,-d,0, 0,d,0);
  line(0,0,-d, 0,0,d);

  point(0,0,0)
  point(d,0,0)
  point(0,d,0)
  point(0,0,d)

  const a = [50, 20, 0];
  const b = [20, 80, 50];
  const pts = [a];
  for (let t=0; t<=TAU; t+=0.01) {
    pts.push(lerp3d(t, a, b));
  }
  poly(pts);

  setStroke(`lightgrey`);
  poly(pts.map(p => [p[0],p[1],0]));
  poly(pts.map(p => [p[0],0,p[2]]));
  poly(pts.map(p => [0,p[1],p[2]]));
}

function pointerMove() {
  redraw();
}

function lerp3d(t, a, b) {
  return [
    cos(t) * a[0] + sin(t) * b[0],
    cos(t) * a[1] + sin(t) * b[1],
    cos(t) * a[2] + sin(t) * b[2],
  ]
}