Wednesday, October 19, 2011

More About Approximating Circular Arcs With a Cubic Bezier Path


Back in April of this year I wrote an article called Approximating a Circular Arc With a Cubic Bezier Path which demonstrated how one could do the job with the latest version of Adobe Flex and Actionscript.  The article was based on a paper written by Aleksas Riskus called Approximation of a Cubic Bezier Curve by Circular Arcs and Vice Versa which explains how a single cubic Bezier curve can be used to approximate an arc of 90 degrees or less.  The paper actually presents two formulas for doing this, in sections (7) and (9), and I'd started with the latter because it seemed like the ActionScript version would be easier to write.  The fact that the formulas didn't work as written was probably for the best, because it forced me to read the paper carefully enough to roughly understand the derivations, at least for section (7).   Which is what I ended up implementing.  I also sent a note to Aleksas Riškus, who is a Mathematics professor in Lithuania, and not long after I'd published my article, he replied with corrections for the equations in section (9):

x2 = xc + ax – k2*ay,
y2 = yc + ay + k2*ax,
x3 = xc + bx + k2*by,                                  (9)
y3 = yc + by – k2*bx


Back then I was grateful for the reply and planned to write a follow-up blog that included a revised version of the ActionScript code.

Then I forgot about it.

Just recently Martin Fox sent me an email about the blog entry.  He pointed out that the circular arcs my demo application rendered were a bit flat when the swept angle was small, and that this was likely due to the constant tangent magnitude ( k*R in section (7) of the Riskus paper).  Simply recoding the method that computed the arcs in terms of the equations in section (9) corrected the problem.   And having reacquainted myself with this material I decided to factor the code for computing arcs into a utility class.   The utility class. ArcPathUtils.as, includes methods for creating spark.primitives.Path objects that render some useful variations on the basic arc:

createArcPathData(
    xc:Number, yc:Number, r:Number, a1:Number, a2:Number):String
createTriangularWedgePathData(
    xc:Number, yc:Number, r:Number, a1:Number, a2:Number):String

createRectangularSegmentPathData(
    xc:Number, yc:Number, r1:Number, r2:Number, a1:Number, a2:Number,
    caps:String="||"):String
createRectangularSegmentPathData(
    xc:Number, yc:Number, r1:Number, r2:Number, a1:Number, a2:Number,
    caps:String="()"):String

createRectangularSegmentPathData(
    xc:Number, yc:Number, r1:Number, r2:Number, a1:Number, a2:Number,
    caps:String="))"):String

createRectangularSegmentPathData(
    xc:Number, yc:Number, r1:Number, r2:Number, a1:Number, a2:Number,
    caps:String=")("):String



This set of static PathArcUtils methods compute the String value for a s:Path's data property.  They all create an arc centered at xc,yc that sweeps from angle a1, to a2.  The center of the arc is specified in pixel coordinates, and the sweep angles in degrees.  The r parameter for createArcPathData() and createTriangularWedgePathData() is the arc's radius, also in pixels.   The createRectangularSegmentPathData() method creates an rectangular arc "segment" whose ends are specified by the final caps parameter.    The arc segment has an inner and outer radius specified by the r1 and r2 parameters respectively.

The PathArcUtils class also includes a createArc() method, similar to createArcPathData(), which just returns the coordinates for the bezier curves as objects.

The application below demonstrates the s:Path arc variations.  The source code for the application can be found here:


You can try the different arc types, and "rectangular arc segment" end cap types, by clicking the buttons in the top row.   There are some limits to the relative values for the start and end angle, and for the inner and outer radius, to prevent the Path from self-intersecting enough to distort the expected shape (see the source code for the details).

4 comments:

  1. I was asked to formally grant permission to use the example code. I've added a Creative Commons license to the source code:


    This work is licensed under the Creative Commons Attribution 3.0
    Unported License. To view a copy of this license, visit
    http://creativecommons.org/licenses/by/3.0/ or send a letter to
    Creative Commons, 444 Castro Street, Suite 900, Mountain View,
    California, 94041, USA.

    ReplyDelete
  2. Very useful, thanks! URL of the second file is:

    https://sites.google.com/site/hansmuller/flex-blog/ArcButton.mxml

    @Hans
    maybe you can fix the URL

    ReplyDelete
    Replies
    1. Thanks for the feedback, it's (finally) fixed now.

      Delete
  3. You sir, made my day. I've been having some issues with SVG scaling using Inkscape to create pie charts, and was looking for a way to generate bezier curves with code.

    *tips hat*

    ReplyDelete