xxxxxxxxxx
107
/**
* == Optimizations ==
* - A segment now consists of a list of points. The old approach rotated a lot of duplicate points
* as the start of one segment is the same as the end of the previous segment.
* - Seperating fixed points from points that still need to be moved. (That way we don't have to check
* if the fixed points still need to be moved.)
* - Track the rotation of the segment inside the segment. That way we don't need to keep the original
* list of point around.
* - Purge points if they are too close together. If we zoom out and you can't spot the difference between
* two points why render both of them? (Creates a spikier curve)
*/
let movingSegment;
let zoom = 1;
let targetZoom = 1;
let fixedPoints = [];
const ROTATION_SPEED = 0.05;
const STROKE_WIDTH = 2;
let usePurging = false;
function setup() {
createCanvas(640, 360);
const checkbox = createCheckbox(
"Use purging (will create a spiky curve but faster)"
);
checkbox.mousePressed((e) => {
usePurging = !checkbox.checked();
});
// Define the start and end points of the initial segment (which don't move)
const start = createVector(0, 0);
const end = createVector(0, 100);
fixedPoints = fixedPoints.concat([start, end]);
movingSegment = new Segment(fixedPoints, end);
frameRate(25);
}
function nextGeneration() {
// Copy all points in reversed order from the moving segment to the fixed points.
// The code starts off at 2 because we want to skip the last point (this is the rotation point)
for (let i = movingSegment.points.length - 2; i >= 0; i--) {
fixedPoints.push(movingSegment.points[i]);
}
let newFixedPoints;
if (usePurging) {
newFixedPoints = [fixedPoints[0]];
for (let i = 1; i < fixedPoints.length; i++) {
const currentPoint = fixedPoints[i];
const lastPoint = newFixedPoints[newFixedPoints.length - 1];
// Get the difference in x and y in terms of screen coordinates
const normalizedX = abs(currentPoint.x - lastPoint.x) * zoom;
const normalizedY = abs(currentPoint.y - lastPoint.y) * zoom;
// If they have a distance in x or y that is greater than the stroke
// then there is a visible distinction and we keep it.
if (normalizedX > 1 || normalizedY > 1) {
newFixedPoints.push(currentPoint);
}
}
// Always keep the last one (as we skipped it in the purging process)
newFixedPoints.push(fixedPoints[fixedPoints.length - 1]);
} else {
newFixedPoints = fixedPoints;
}
// Create a new moving segment of all the new fixed points.
movingSegment = new Segment(
newFixedPoints,
newFixedPoints[newFixedPoints.length - 1]
);
}
function draw() {
background(255);
noFill();
translate(width / 2, height / 2);
// Interpolate the zoom level towards the target zoom
scale(lerp(zoom, targetZoom, movingSegment.getProgress()));
// Loop through the fixed points and show them
stroke(0);
strokeWeight(STROKE_WIDTH / zoom);
beginShape();
for (let point of fixedPoints) {
vertex(point.x, point.y);
}
endShape();
// Animate and show the moving part
movingSegment.update();
movingSegment.show();
// If the progress of the animation is 100% then create next generation
if (movingSegment.getProgress() === 1) {
zoom = targetZoom;
targetZoom = zoom / sqrt(2);
nextGeneration();
}
}