xxxxxxxxxx
649
let canvasW = 400;
let canvasH = 400;
let hudHeight = 40;
let minRadius = 20;
let maxRadius;
let minCenterX;
let maxCenterX;
let minCenterY;
let maxCenterY;
let centerX = canvasW / 2;
let centerY = 0;
let radius = 0;
let diameter;
let diameterSq;
let x0 = 50;
let y0 = 10 + hudHeight;
let x1 = canvasW - x0;
let y1 = y0;
let x;
let y;
let lineHalfLength;
let activeHandle = 0;
let handleRadius = 6;
let hovering = false;
let pressing = false;
let holding = false;
let downDX;
let downDY;
let intersectionRadius = 4;
let i0X;
let i0Y;
let i1X;
let i1Y;
let currentChordLength;
let longestChordX0;
let longestChordY0;
let longestChordX1;
let longestChordY1;
let longestChordLength;
let textAngle = 0;
let textRotationSnappiness = 0.3;
let white;
let lineColor;
let defaultHandleColor;
let hoveringHandleColor;
let holdingHandleColor;
let chordColor;
let ghostChordColor;
let intersectionColor;
let shadowColor;
let hudColor;
let backgroundColor;
let toCenterX;
let toCenterY;
let toRadius;
let fromCenterX;
let fromCenterY;
let fromRadius;
let easing = true;
let easeCounter = 0;
let easeFrames = 20;
function setup()
{
createCanvas(canvasW, canvasH);
if (canvasW === canvasH || min(canvasW, canvasH) === canvasH)
maxRadius = (canvasH - hudHeight) / 2 - handleRadius;
else
maxRadius = canvasW / 2 - handleRadius;
lineHalfLength = sqrt(canvasW * canvasW + canvasH * canvasH);
white = color(255);
lineColor = color(255, 100);
defaultHandleColor = white;
hoveringHandleColor = color(20, 200, 120);
holdingHandleColor = color(250, 40, 60);
chordColor = color(250, 220, 20);
ghostChordColor = color(250, 220, 20, 100);
intersectionColor = color(40, 200, 250);
shadowColor = color(0, 200);
hudColor = color(0, 100);
backgroundColor = color(40);
Begin();
}
function draw()
{
background(backgroundColor);
let mouseDX0 = mouseX - x0;
let mouseDY0 = mouseY - y0;
let mouseDSq0 = mouseDX0 * mouseDX0 + mouseDY0 * mouseDY0;
let mouseDX1 = mouseX - x1;
let mouseDY1 = mouseY - y1;
let mouseDSq1 = mouseDX1 * mouseDX1 + mouseDY1 * mouseDY1;
// The active handle can only change if you're not holding either handle
if (!holding)
activeHandle = int(mouseDSq0 > mouseDSq1);
let mouseDSq;
if (activeHandle === 0)
{
x = x0;
y = y0;
mouseDSq = mouseDSq0;
}
else
{
x = x1;
y = y1;
mouseDSq = mouseDSq1;
}
hovering = mouseDSq <= handleRadius * handleRadius;
if (easing)
{
Ease();
}
else
{
if (pressing)
{
if (!(mouseIsPressed && mouseButton === LEFT))
{
EndPressing();
}
}
else
{
if (mouseIsPressed && mouseButton === LEFT)
{
BeginPressing();
}
}
if (holding)
{
if (pressing)
{
// move the active handle
if (activeHandle === 0)
{
x0 = mouseX - downDX;
y0 = mouseY - downDY;
if (x0 < 0)
x0 = 0;
else if (x0 > canvasW)
x0 = canvasW;
if (y0 < hudHeight)
y0 = hudHeight;
else if (y0 > canvasH)
y0 = canvasH;
}
else
{
x1 = mouseX - downDX;
y1 = mouseY - downDY;
if (x1 < 0)
x1 = 0;
else if (x1 > canvasW)
x1 = canvasW;
if (y1 < hudHeight)
y1 = hudHeight;
else if (y1 > canvasH)
y1 = canvasH;
}
}
else
{
EndHolding();
}
}
else
{
if (pressing)
{
let downDSq = downDX * downDX + downDY * downDY;
if (downDSq <= handleRadius * handleRadius)
{
BeginHolding();
}
}
}
}
LineVsCircle();
currentChordLength = undefined;
if (i0X !== undefined)
{
let chordX = i1X - i0X;
let chordY = i1Y - i0Y;
let chordLengthSq = chordX * chordX + chordY * chordY;
currentChordLength = round(sqrt(chordLengthSq), 2);
if (!easing && currentChordLength >= longestChordLength)
{
longestChordX0 = i0X;
longestChordY0 = i0Y;
longestChordX1 = i1X;
longestChordY1 = i1Y;
longestChordLength = currentChordLength;
}
}
if (!easing)
{
AttemptDrawGhostChord();
}
DrawLine();
DrawCircle();
DrawIntersections();
DrawHandles();
AttemptPrintCurrentChordLength();
DrawHud();
}
function keyPressed()
{
if (easing)
return;
if (keyCode === 32)
Begin();
}
function Begin()
{
toRadius = round(random(minRadius, maxRadius), 2);
minCenterX = toRadius + handleRadius;
maxCenterX = canvasW - minCenterX;
minCenterY = toRadius + hudHeight + handleRadius;
maxCenterY = canvasH - (toRadius + handleRadius);
toCenterX = random(minCenterX, maxCenterX);
toCenterY = random(minCenterY, maxCenterY);
longestChordLength = 0;
currentChordLength = undefined;
i0X = undefined;
i0Y = undefined;
i1X = undefined;
i1Y = undefined;
longestChordX0 = undefined;
longestChordY0 = undefined;
longestChordX1 = undefined;
longestChordY1 = undefined;
BeginEasing();
}
function AttemptDrawGhostChord()
{
if (longestChordX0 === undefined)
return;
DrawGhostChord();
}
function DrawGhostChord()
{
DrawGhostSegment(longestChordX0, longestChordY0,
longestChordX1, longestChordY1);
}
function DrawCircle()
{
push();
noFill();
stroke(lineColor);
strokeWeight(2);
circle(centerX, centerY, radius * 2);
stroke(white);
strokeWeight(4);
point(centerX, centerY);
pop();
}
function DrawLine()
{
let dX = x1 - x0;
let dY = y1 - y0;
let distance = dist(x0, y0, x1, y1);
let dirX = dX / distance;
let dirY = dY / distance;
let startX = x0 - dirX * lineHalfLength;
let startY = y0 - dirY * lineHalfLength;
let endX = x1 + dirX * lineHalfLength;
let endY = y1 + dirY * lineHalfLength;
// If there are no intersections at all...
if (i0X === undefined && i1X === undefined)
{
// ... Then the line is fully outside of the circle
DrawOutsideSegment(startX, startY, endX, endY);
}
else // Otherwise, if there ARE intersections...
{
// If both intersections are the same...
if (i0X === i1X && i0Y === i1Y)
{
// ... then the line is tangent, and the whole thing is thus outside
DrawOutsideSegment(startX, startY, endX, endY);
}
else // Otherwise, if the intersections are NOT the same...
{
// ... then because this is a line and not a segment, there
// will necessarily be two intersections, so we can just
// draw start to i0 as an outside segment,
// draw i0 to i1 as an inside segment, and
// draw i1 to end as an outside segment
DrawOutsideSegment(startX, startY, i0X, i0Y);
DrawInsideSegment(i0X, i0Y, i1X, i1Y);
DrawOutsideSegment(i1X, i1Y, endX, endY);
}
}
}
function AttemptPrintCurrentChordLength()
{
if (i0X === undefined)
return;
PrintCurrentChordLength();
}
function PrintCurrentChordLength()
{
let dX = x1 - x0;
let dY = y1 - y0;
let angle = atan2(dY, dX);
if (angle < -HALF_PI)
angle += PI;
else if (angle > HALF_PI)
angle -= PI;
textAngle = lerp(textAngle, angle, textRotationSnappiness);
let chordMidX = (i0X + i1X) / 2;
let chordMidY = (i0Y + i1Y) / 2;
let chordLengthText = round(currentChordLength, 1).toLocaleString(
undefined, {minimumFractionDigits: 1});
push();
textAlign(CENTER, CENTER);
textSize(16);
translate(chordMidX, chordMidY);
rotate(textAngle);
ShadowText(chordLengthText, 0, 0);
pop();
}
function DrawInsideSegment(startX, startY, endX, endY)
{
push();
stroke(chordColor);
strokeWeight(5);
line(startX, startY, endX, endY);
pop();
}
function DrawOutsideSegment(startX, startY, endX, endY)
{
push();
stroke(lineColor);
strokeWeight(2);
line(startX, startY, endX, endY);
pop();
}
function DrawGhostSegment(startX, startY, endX, endY)
{
push();
stroke(ghostChordColor);
strokeWeight(3);
line(startX, startY, endX, endY);
pop();
}
function DrawIntersections()
{
push();
stroke(intersectionColor);
strokeWeight(intersectionRadius * 2);
if (i0X !== undefined)
point(i0X, i0Y);
if (i1X !== undefined)
point(i1X, i1Y);
pop();
}
function DrawHandles()
{
if (activeHandle === 0)
{
if (holding)
DrawHoldingHandle(x0, y0);
else if (hovering)
DrawHoveringHandle(x0, y0);
else
DrawDefaultHandle(x0, y0);
DrawDefaultHandle(x1, y1);
}
else
{
DrawDefaultHandle(x0, y0);
if (holding)
DrawHoldingHandle(x1, y1);
else if (hovering)
DrawHoveringHandle(x1, y1);
else
DrawDefaultHandle(x1, y1);
}
}
function DrawHud()
{
let left = canvasW / 3;
let right = canvasW - left;
let chordString = longestChordLength.toLocaleString(
undefined, {minimumFractionDigits: 2});
let radiusString = radius.toLocaleString(
undefined, {minimumFractionDigits: 2});
push();
noStroke();
fill(hudColor);
rect(0, 0, canvasW, hudHeight);
textAlign(CENTER, CENTER);
textSize(12);
ShadowText("Longest Chord Length", left, 12);
ShadowText("Circle Radius", right, 12);
textSize(16);
if (!easing)
ShadowText(chordString, left, 28, chordColor);
ShadowText(radiusString, right, 28, chordColor);
pop();
}
function DrawDefaultHandle(x, y)
{
push();
stroke(defaultHandleColor);
strokeWeight(handleRadius * 2);
point(x, y);
pop();
}
function DrawHoveringHandle(x, y)
{
push();
stroke(hoveringHandleColor);
strokeWeight(handleRadius * 2 + 2);
point(x, y);
pop();
}
function DrawHoldingHandle(x, y)
{
push();
stroke(holdingHandleColor);
strokeWeight(handleRadius * 2);
point(x, y);
pop();
}
function BeginPressing()
{
pressing = true;
downDX = mouseX - x;
downDY = mouseY - y;
}
function EndPressing()
{
pressing = false;
downDX = undefined;
downDY = undefined;
}
function BeginHolding()
{
holding = true;
}
function EndHolding()
{
holding = false;
}
function BeginEasing()
{
easing = true;
easeCounter = 0;
fromCenterX = centerX;
fromCenterY = centerY;
fromRadius = radius;
}
function Ease()
{
function a(t) { return pow(t, 2.0); }
function b(t) { return 1 - a(1 - t); }
++easeCounter;
let t = easeCounter / easeFrames;
let interpolant = t < 0.5 ? a(2 * t) / 2 : b(2 * t - 1) / 2 + 0.5;
centerX = lerp(fromCenterX, toCenterX, interpolant);
centerY = lerp(fromCenterY, toCenterY, interpolant);
radius = lerp(fromRadius, toRadius, interpolant);
diameter = 2 * radius;
diameterSq = diameter * diameter;
if (easeCounter >= easeFrames)
EndEasing();
}
function EndEasing()
{
easing = false;
centerX = toCenterX;
centerY = toCenterY;
radius = toRadius;
diameter = 2 * radius;
diameterSq = diameter * diameter;
}
function DebugPrint()
{
push();
textSize(10);
noStroke();
fill(chordColor);
text("hovering: " + hovering, 4, 10);
text("pressing: " + pressing, 4, 20);
text("holding: " + holding , 4, 30);
text("down delta: (" + downDX + ", " + downDY + ")", 4, 40);
pop();
}
function ShadowText(str, x, y, textColor = white)
{
textStyle(BOLD);
fill(shadowColor);
stroke(shadowColor);
strokeWeight(4);
text(str, x + 1, y + 1);
fill(textColor);
noStroke();
text(str, x, y);
}
function LineVsCircle()
{
let dX = x1 - x0;
let dY = y1 - y0;
let mX = x0 - centerX;
let mY = y0 - centerY;
let mDotD = mX * dX + mY * dY;
let mSq = mX * mX + mY * mY;
let rSq = radius * radius;
let a = dX * dX + dY * dY;
let b = 2 * mDotD;
let c = mSq - rSq;
let discriminant = b * b - 4 * a * c;
if (discriminant < 0)
{
i0X = undefined;
i0Y = undefined;
i1X = undefined;
i1Y = undefined;
return;
}
if (discriminant === 0)
{
let t = -b / (2 * a);
i0X = x0 + t * dX;
i0Y = y0 + t * dY;
i1X = i0X;
i1Y = i0Y;
return;
}
let root = sqrt(discriminant);
let denominator = 2 * a;
let t0 = (-b - root) / denominator;
let t1 = (-b + root) / denominator;
i0X = x0 + t0 * dX;
i0Y = y0 + t0 * dY;
i1X = x0 + t1 * dX;
i1Y = y0 + t1 * dY;
}