I want to draw a spiral, a circle where the radius steadily increases with the angle.
When searching for similar questions, the circle/spiral is always approximated by drawing a lot of lines with the lineTo
method. The more lines are used, the more the circle/spiral is approximated. But using a lot of lines has a performance disadvantage, so the programmer needs to weight up "performance" vs "appearance".
The bezierCurveTo
on the other hand could draw a perfect spiral, without a huge performance disadvantage, but I don’t know how to calculate the control points for the bezier method.
Playground for drawing the spiral with the lineTo
method: JSFiddle Playground
context.save();
context.beginPath();
let offset = initialRadius;
for (let turn = 0; turn < turnCount; turn++) {
for (let step = 0; step < stepCount; step++) {
context.lineTo(offset, 0);
offset += growthPerTurn / stepCount;
context.rotate(360 / stepCount * Math.PI / 180);
}
}
context.lineWidth = 3;
context.strokeStyle = "yellow";
context.stroke();
context.restore();
Here is my first attempt to approximate the spiral with bezier: JSFiddle Playground
(determined the value for temp
through trial and error)
context.moveTo(initialRadius, 0);
const temp = 1.25;
for (let turn = 0; turn < turnCount; turn++) {
for (let step = 1; step <= stepsPerTurn; step++) {
const points = [
[temp * calculateOffset(turn, step -0.66) * Math.cos(calculateAngle(step - 0.66)), temp * calculateOffset(turn, step -0.66) * Math.sin(calculateAngle(step - 0.66))],
[temp * calculateOffset(turn, step -0.33) * Math.cos(calculateAngle(step - 0.33)), temp * calculateOffset(turn, step -0.33) * Math.sin(calculateAngle(step - 0.33))],
[calculateOffset(turn, step) * Math.cos(calculateAngle(step)),calculateOffset(turn, step) * Math.sin(calculateAngle(step))],
];
context.bezierCurveTo(points[0][0], points[0][1], points[1][0], points[1][1], points[2][0], points[2][1]);
}
}
2
Answers
How about a more mathematical approach…
We can think of a spiral as a circle with a variable diameter that keeps getting smaller
A circle
We can draw a circle with many dot and a bit of trigonometry
A spiral
Now that we know how to draw a circle we can make reduce the diameter on every frame
… and another example
For best fit use line segments
Using a bezier will likely be more of an approximation than using line segments..
If you set the length of each line segment to be only a few pixels long you will get a very accurate spiral that at most will be only a fraction of a pixel out.
In the snippet the change in angle is half the line segment length divided by the radius times PI.
The radius is the current angle times a slope value
radiusChange
To prevent infinite loop ensure the
lineSegLength
is not zeroTo prevent a divide by zero check that the changing radius does not = zero
Beziers are not suited to this task.
To use a bezier of length > 2 pixels will be complicated and rather imprecise. Just as you can not draw a perfect circle using beziers I very much doubt they can create a good spiral.