A spiral evolving into infinity.
Log in to post a comment.
let numPoints = 150; // min=20, max=300, step=5, Total number of points in the spiral
let a = 0; // min=0, max=50, step=0.1, Starting radius
let b = 0.28; // min=0, max=3, step=0.001, Controls the tightness of the spiral
let s = 1.88; // min=0, max=3, step=0.001, Controls the spacing of the spiral
// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(0.12);
// Global code will be evaluated once.
const turtle = new Turtle();
function drawRandomCubicHermiteSpline(pStart, pEnd, numPoints, aScale) {
// Generates a set of random control points between pStart and pEnd
function generateRandomControlPoints(pStart, pEnd, numPoints) {
let points = [pStart];
let dx = pEnd.x - pStart.x;
let dy = pEnd.y - pStart.y;
let dTotal = Math.sqrt(dx * dx + dy * dy);
let normalDirection = { x: -dy / dTotal, y: dx / dTotal };
for (let i = 1; i < numPoints - 1; i++) {
let t = i / (numPoints - 1);
let p = { x: pStart.x + dx * t, y: pStart.y + dy * t };
let f = Math.random() * 2 - 1;
let offsetMagnitude = Math.sin(t * Math.PI * f) * (Math.random() * dTotal * aScale);
p.x += offsetMagnitude * normalDirection.x;
p.y += offsetMagnitude * normalDirection.y;
points.push(p);
}
points.push(pEnd);
return points;
}
// Calculates tangent using two points.
function calculateTangent(p0, p1) {
return { x: p1.x - p0.x, y: p1.y - p0.y };
}
// Calculates tangents for each control point
function calculateTangents(points) {
let tangents = [];
for (let i = 0; i < points.length; i++) {
if (i == 0) {
tangents.push(calculateTangent(points[i], points[i + 1]));
} else if (i == points.length - 1) {
tangents.push(calculateTangent(points[i - 1], points[i]));
} else {
let tangent = calculateTangent(points[i - 1], points[i + 1]);
tangents.push({ x: tangent.x / 2, y: tangent.y / 2 });
}
}
return tangents;
}
// Computes a point on the Cubic Hermite spline
function hermiteInterpolate(p0, p1, t0, t1, t) {
let h00 = (2 * t ** 3) - (3 * t ** 2) + 1;
let h10 = (t ** 3) - (2 * t ** 2) + t;
let h01 = (-2 * t ** 3) + (3 * t ** 2);
let h11 = (t ** 3) - (t ** 2);
return {
x: h00 * p0.x + h10 * t0.x + h01 * p1.x + h11 * t1.x,
y: h00 * p0.y + h10 * t0.y + h01 * p1.y + h11 * t1.y
};
}
// Generate control points
let controlPoints = generateRandomControlPoints(pStart, pEnd, numPoints);
// Calculate tangents for control points
let tangents = calculateTangents(controlPoints);
// Draw the spline by interpolating between each pair of points
for (let i = 0; i < controlPoints.length - 1; i++) {
let p0 = controlPoints[i];
let p1 = controlPoints[i + 1];
let t0 = tangents[i];
let t1 = tangents[i + 1];
turtle.penup();
turtle.goto(p0.x, p0.y);
turtle.pendown();
for (let t = 0; t <= 1; t += 0.1) { // Smaller step for a smoother curve
let point = hermiteInterpolate(p0, p1, t0, t1, t);
turtle.goto(point.x, point.y);
}
turtle.penup();
}
}
// generate a spiral path
function generateSpiralPoints(numPoints, a, b, s = 0.1, yOffset = 0) {
let points = [];
for (let i = 0; i < numPoints; i++) {
// Angle increases linearly with each point
let theta = i * s; // Adjust the s multiplier to change spacing between points
// Radius increases with theta to create the spiral
let r = a + b * theta; // 'a' is the starting radius, 'b' controls how tightly the spiral winds
// Convert polar coordinates (r, theta) to Cartesian coordinates (x, y)
let x = r * Math.cos(theta);
let y = r * Math.sin(theta) + yOffset;
points.push({ x, y });
}
return points;
}
let spiralPoints = generateSpiralPoints(numPoints, a, b, s, -12);
// draw the figure below
for (let j = 0; j < 16; j++) {
for (let i = 0; i < j * 2 + 1; i++) {
let x = -90 + j * 12;
drawRandomCubicHermiteSpline({ x: x, y: 75 }, { x: x, y: 95 }, j * 2.5, 0.2);
}
}
// The walk function will be called until it returns false.
function walk(i) {
if (i < spiralPoints.length - 1) {
// Use the current and next point in the spiral as start and end points for the spline
let pStart = spiralPoints[i];
let pEnd = spiralPoints[i + 1];
let nCurves = i * 0.4;
// Call the function to draw the spline segment between these points
for (let k = 0; k < nCurves; k++) {
drawRandomCubicHermiteSpline(pStart, pEnd, i * 0.05, 0.1 * i / spiralPoints.length);
}
return true; // Continue walking
}
return false; // Stop walking once we've processed all points
}