10

As part of an iOS app I’m making, I want to draw a decent approximation of an Archimedes spiral. The drawing library I’m using (CGPath in Quartz 2D, which is C-based) supports arcs as well as cubic and quadratic Bézier curves. What is a good method of approximating an Archimedes spiral using either of these path types? For example the wikipedia exemplar image says it was “drawn as a series of minimum-error Bézier segments.” How would one generate such segments?

My math background takes me through Calculus III plus some stuff I picked up from a classical mechanics class, but it’s been a couple of years so I’m rusty. What I have so far:

For a spiral r = a + b $\theta$, I used the information from this page to find that the cartesian slope at any point (r, $\theta$) is equal to

$$\frac{dy}{dx}=\frac{b\sin\theta\space+\space(a + b\theta)\cos\theta}{b\cos\theta\space-\space(a + b\theta)\sin\theta}$$

From here, I could use point-slope to find the equation of a tangent line at any point, but how do I go about finding the proper lengths of the handles (i.e. the positions of the middle two points) for the curve? Or would an approximation with circular arc segments be better/easier/faster?

If I can’t figure it out, I’ll just use a static image in the app, but it occurs to me that I don’t even know of a way to generate a high-quality image of an Archimedes spiral! The Spiral tool in Illustrator, for example, does only logarithmic spirals.

Zev Eisenberg
  • 225
  • 3
  • 11
  • Your drawing library can't evaluate trigonometric functions? – J. M. ain't a mathematician Aug 05 '12 at 05:21
  • You mean sin/cos/tan? I'm using C, so yes, I can. What are you getting at? – Zev Eisenberg Aug 05 '12 at 05:24
  • 1
    Then, why not just use the actual parametric equations for the Archimedean spiral instead of trying to draw a Bézier approximation? – J. M. ain't a mathematician Aug 05 '12 at 05:28
  • 1
    @J.M. Most drawing libraries outside of specialized math software only allow drawing primitives like line segments, ellipses, and cubic Bézier curves, but not arbitrary parametric curves. I expect Zev can evaluate trigonometric functions to pick the parameters for these primitives, but cannot make the curve follow an arbitrary parametric path. –  Aug 05 '12 at 05:41
  • Right you are @RahulNarain. Wish I could do that! – Zev Eisenberg Aug 05 '12 at 05:44
  • 1
    This comment doesn't answer the question as stated, but if I were you, @Zev, I'd just evaluate positions along the curve at small increments of $\theta$ and connect them with a series of line segments. –  Aug 05 '12 at 05:44
  • You know, it didn't even occur to me to see if I could get it looking smooth using straight line segments. Now that I've got information from the answer below, I want to try that, but I'll try this version as well. – Zev Eisenberg Aug 05 '12 at 05:51
  • 1
    Drawing lots of straight-line segments is a bad idea; in many graphics libraries it can cause rendering errors (e.g. because of overlapping antialiasing or rounding issues). Quartz doesn’t suffer so much from that, but under Quartz performance will be hideous because in order to avoid the antialiasing problem it needs to compute self-intersections. – al45tair Feb 03 '14 at 17:38
  • Just to add to my comment above, if you do need to decompose to line segments, it’s best to adaptively subdivide until the curve can be approximated by a straight line. This minimises rendering problems. – al45tair Feb 03 '14 at 17:39
  • FWIW, that page you reference shows formulae for dx/dθ and dy/dθ (i.e. how much x and y change, respectively, as you change θ). You can get your control points by multiplying dx/dθ and dy/dθ by some angle, and those will be your x and y offsets for your cubic bezier control points. The other approach is to just take a series of points from the spiral and then render it using some smoothing algorithm, such as Hermite spline. Again, super simple and renders a nice looking spiral. – Rob Nov 05 '19 at 00:22

2 Answers2

4

So it looks like the Wikipedia reference image uses 45 degree sections of these curves. You can use the equation for the spiral to give you the tangent line at the beginning and end of these curve sections. Evaluate the derivative at these two points to get the tangent line slope and then shift your line appropriately to hit the point used. The intersection Of these two lines should be your control point. Once you have found your control point you can put it in the function 'CGPathAddQuadCurveToPoint' for the cx, cy (I think) along with the point you want to go to (also from the spiral equation). For reference--check out the animation under 'quadratic curves' here

For extra speed, you only have to find 8 tangent lines max--just shift them out for the next cycle of the spiral and reuse them.

JoshRagem
  • 166
  • I think your Wikipedia link might work better if you use the syntax [link text](URL goes here). –  Aug 05 '12 at 05:46
  • So, if I understand you, I use the intersection of adjacent tangent lines as the control points for my quad curves? – Zev Eisenberg Aug 05 '12 at 05:46
  • Your wikipedia link is from the mobile site, and the Stack Exchange link parser doesn't like it. Here's the desktop link: http://en.wikipedia.org/wiki/Bézier_curve#Constructing_B.C3.A9zier_curves – Zev Eisenberg Aug 05 '12 at 05:49
  • 1
    @Zav Eisenberg the lines on the animation on Wikipedia are tangent lines, the define the control point. So yes, that is the point you use. – JoshRagem Aug 05 '12 at 05:49
  • Thanks for the link, I was typing on my phone so it's a little trickier. – JoshRagem Aug 05 '12 at 05:51
  • That worked, and it looks really good. Here’s a comparison of line segments vs. quadratic. It’s subtle, but if you view at 100% you can see the difference: https://i.sstatic.net/sftCv.png

    I had some mishaps along the way, however: https://i.sstatic.net/d8UdB.png

    – Zev Eisenberg Aug 05 '12 at 10:37
  • I posted the code I ended up creating, in case anyone wants to use it: https://github.com/ZevEisenberg/ZESpiral – Zev Eisenberg Feb 04 '14 at 00:59
2

Bézier cubic has eight parameters, four for x function, four for y function. Impose on the spiral segment that:

  • Endpoints go through correct places (four constraints);
  • Tangent at endpoints have correct angle (two constraints);
  • Curvatures at endpoints are correct (two constraints).

In practice, as tested, for 60° pieces, at any plausible resolution, that is indistinguishable from mathematical perfection.

There is an implementation in PostScript at github.com/jdaw1/placemat/, within which search for /ArchimedeanSpiralPath and /ApproximatingCurve.

This idea generalises beyond a spiral, and there is test output for multiple functions (Archimedes spiral on page 57), some of which approximate well, and others not. (The green line is the correct moveto lineto … lineto mathematical curve; the thin black is the Bézier cubic approximation.) It is ‘discussed’ on comp.lang.postscript.

Also see relevant PNG.

If drawing a circle with a Bézier cubic the error varies as the sixth power of the angle (details on comp.lang.postscript). I suspect, but haven’t bothered to prove, that the same is true here. Assuming that, reducing the angle size from 60° to 45° would reduce the error by a factor of ¾⁶ ≈ 0.178 ≈ 1 / 5.6, and reducing from 60° to 30° would be a 64-fold improvement.

jdaw1
  • 193