Open Pomax opened 3 months ago
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)); }