Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

trig example: finding ellipse radii. intrinsic angle, and AABB #105

Open
Pomax opened this issue Aug 9, 2024 · 0 comments
Open

trig example: finding ellipse radii. intrinsic angle, and AABB #105

Pomax opened this issue Aug 9, 2024 · 0 comments

Comments

@Pomax
Copy link
Owner

Pomax commented Aug 9, 2024

function draw() {
  clear(`white`);
  const { P, Q } = drawInitialSetup();

  // Let's get to work. Step 1: rotate P to P'
  setColor(`#933`);
  const Pprime = new Point(-P.y, P.x);
  showPointFromCenter(Pprime, `P'`);

  // Step 2: get the midpoint D = Q--P'
  setColor(`#333`);
  const D = new Point((Q.x + Pprime.x) / 2, (Q.y + Pprime.y) / 2);
  showPoint(D, `D`);
  line(Pprime, Q);

  // Then in a "new panel", step 3: get A and B
  translate(width / 3, 0);
  setColor(`red`);
  showPoint(Q, `Q`);
  setColor(`#333`);
  showPointFromCenter(D, `D`);
  noFill();

  setStroke(`#0909`);
  const Dr = dist(C, D);
  circle(D, Dr);

  setColor(`#060`);
  let T = atan2(Q.y - D.y, Q.x - D.x);
  const A = new Point(D.x + Dr * cos(T), D.y + Dr * sin(T));
  const B = new Point(D.x - Dr * cos(T), D.y - Dr * sin(T));
  showPoint(A, `A`);
  showPoint(B, `B`);

  // step 4: get lengths a and b
  const a = dist(Q, A);
  const b = dist(Q, B);

  // and let's highlight those lengths:
  setStroke(`#2DF`);
  line(B, Q);
  setStroke(`#22D`);
  line(A, Q);

  // step 5: get our ellipse radii!
  const Alen = dist(C, A);
  const Blen = dist(C, B);
  const v1 = new Point((B.x / Blen) * a, (B.y / Blen) * a);
  const v2 = new Point((A.x / Alen) * b, (A.y / Alen) * b);

  // And let's highlight those same identities
  setColor(`#22D`);
  showPointFromCenter(v1, ``);
  setColor(`#2DF`);
  showPointFromCenter(v2, ``);

  setStroke(`#AAA`);
  line(v1, B);
  line(v2, A);

  // Then to finish up, let's show the things we set out to find.
  translate(width / 3, 0);

  // Show the major/minor radius:
  setColor(`black`);
  showPointFromCenter(v1, `v1`);
  showPointFromCenter(v2, `v2`);

  // Show the ellipse's intrinsic rotation:
  setColor(`#9095`);
  const phi = atan2(v2.y, v2.x);
  line(polarPoint(phi, -1000), polarPoint(phi, 1000));

  // and show the AABB (see https://stackoverflow.com/questions/87734)
  let ux = b * cos(phi);
  let uy = b * sin(phi);
  let vx = a * cos(phi + PI / 2);
  let vy = a * sin(phi + PI / 2);
  let w = sqrt(ux * ux + vx * vx);
  let h = sqrt(uy * uy + vy * vy);

  setColor(`#111`);
  line(-w, -h, w, -h);
  line(-w, h, w, h);
  line(-w, -h, -w, h);
  line(w, -h, w, h);
}

// ----------helper functions past this point----------

const C = new Point(0, 0);
const radius = 100;
let shearMatrix, scaleMatrix, rotateMatrix;

function setup() {
  setSize(900, 400);
  setBorder(1, `black`);
  setGrid(20, `grey`);
  addSlider(`sx`, { min: 0, max: 4, value: 1, step: 0.01 });
  addSlider(`sy`, { min: 0, max: 4, value: 1, step: 0.01 });
  addSlider(`shx`, { min: -3, max: 3, value: 1, step: 0.01 });
  addSlider(`shy`, { min: -3, max: 3, value: 0, step: 0.01 });
  addSlider(`angle`, { min: 0, max: TAU, value: 0.3, step: 0.01 });
}

function drawInitialSetup() {
  setColor(`black`);
  text(`Panel 1`, 11, 23);
  line(300, 0, 300, height);
  text(`Panel 2`, 311, 23);
  line(600, 0, 600, height);
  text(`Panel 3`, 611, 23);

  // Set up our transforms and draw both our original circle,
  // and the ellipse we get from transforming that circle:
  shearMatrix = [1, shx, shy, 1];
  scaleMatrix = [sx, 0, 0, sy];
  rotateMatrix = [cos(angle), -sin(angle), sin(angle), cos(angle)];
  drawBaseShapes();

  // Show the original "axes" and the corresponding
  // post-transform conjugated half-diameters:
  const P = new Point(...transformCoords(radius, 0));
  const Q = new Point(...transformCoords(0, radius));

  setStroke(`#00F3`);
  line(0, 0, 0, radius);
  line(0, 0, radius, 0);

  setColor(`red`);
  showPointFromCenter(P, `P`);
  showPointFromCenter(Q, `Q`);
  return { P, Q };
}

function showPoint(p, label, offset) {
  point(p);
  offsetText(label, p, offset);
}

function showPointFromCenter(p, label) {
  line(C, p);
  showPoint(p, label);
}

function drawBaseShapes() {
  for (let t = 0, x, y; t <= TAU; t += 0.01) {
    resetTransform();
    translate(width / 6, height / 2);
    x = radius * cos(t);
    y = radius * sin(t);

    setStroke(`#00F3`);

    circle(x, y, 0.05);
    translate(width / 3, 0);
    circle(x, y, 0.05);
    translate(width / 3, 0);
    circle(x, y, 0.05);

    resetTransform();
    translate(width / 6, height / 2);
    let [nx, ny] = transformCoords(x, y);
    setStroke(`red`);
    circle(nx, ny, 0.05);
    translate(width / 3, 0);
    circle(nx, ny, 0.05);
    translate(width / 3, 0);
    circle(nx, ny, 0.05);
  }

  setColor(`red`);
  resetTransform();
  translate(width / 6, height / 2);
  showPoint(C, `C`, -10);
  translate(width / 3, 0);
  showPoint(C, `C`, -10);
  translate(width / 3, 0);
  showPoint(C, `C`, -10);

  resetTransform();
  translate(width / 6, height / 2);
}

function polarPoint(a, r) {
  return new Point(r * cos(a), r * sin(a));
}

function transformCoords(x, y) {
  return mul(...mul(...mul(x, y, shearMatrix), scaleMatrix), rotateMatrix);
}

function mul(x, y, m) {
  return [m[0] * x + m[1] * y, m[2] * x + m[3] * y];
}

function offsetText(label, p, offset = 20) {
  const a = atan2(p.y, p.x);
  const r = dist(0, 0, p.x, p.y) + offset;
  text(label, r * cos(a) - 5, 5 + r * sin(a));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant