Drawing Curved Lines Simplified

I’m bad at drawing curved lines in ActionScript. I’m always too lazy to figure out how to calculate exactly where the control point should go when using curveTo. Because of this, most of my experiments have relied on drawing a lot of short straight line segments to simulate curves, rather than figuring out how to draw the curves properly. For example, my tree experiments are drawn entirely with straight line segments.

While revisiting my grass simulations I decided to bite the bullet and figure out an easy way to work with curves. The result is a couple of “spikes” or isolated test cases that demonstrate the logic behind converting a series of straight lines into a nice looking curve.

The basic concept is to bisect each of the straight lines, then draw a curve between the bisections, using the original points as the curve’s control point. Nothing new I’m sure, but it works really well for me, because I can continue to think in straight lines, but draw in curves.

Click to add points. Click and drag points to move them.

I also extended this logic to work with fills, which is handy for drawing for drawing things like grass, seaweed, and tentacle monsters (I’m looking at you Peter Organa). To do this, you need to calculate the average angle for each point based on the next and previous points, then draw your curves along points tangential to that angle. If that doesn’t make a lot of sense, the example below should clear it up. It’s not perfect (still need to deal with the “flip” point that results in crossed lines), but it’s been good enough for some of my recent experiments.

You can download the test files here.

Grant Skinner

The "g" in gskinner. Also the "skinner".

@gskinner

27 Comments

  1. whoa, this is crazy timing…i was just experimenting with drawing curves! thanks again for the source, im excited to check it out.

  2. Thanks for saving the day! We just needed this for our project! Thanks for sharing!

  3. what can i say? it’s beautiful…

  4. Great stuff. One quick question though. When I grab the control point at the top left of the “G” and drag it toward the top left corner, there is a point at which the lines cross. Any reason for this behavior?

  5. I was also experimenting with something like this, trying to figure out how to manage the control points in the curveTo.

    So this is very timely. Thanks for sharing.

  6. Joseph Sikorski May 31, 2008 at 1:30am

    I just recently finished a smooth painter implementation which works essetially like this. I already knew how to go about dividing up the lines, though.

    For the smooth painter, though, I couldn’t use curveTo because of the way the brush worked, actively sampling the colors below it. So I ended up making a quick point interpolation thing. Same principle as what you made here, though.

    I also decided to deprive my self of sleep and play with the curve thing a bit… http://www.box.net/shared/t9mxnbio8w

  7. jeremy, as I mentioned above, the point at which the tangent points flip needs to be fixed. They should flip at the point at which the angle in and out are reversed (180 deg apart), but they currently flip at a fixed rotation. I’m planning to fix this, just haven’t had a chance yet. If anyone gets to it before I do, please post back here.

  8. Joseph Sikorski May 31, 2008 at 2:29pm

    I’m not exactly sure how to solve the flipping with how you did it, though I’m sure it’s trivial. Here’s how I wrote the draw function, though. And it automatically flips correctly, too.

    function draw():void

    {

    var g:Graphics = graphics;

    g.clear();

    var prevMidpta:Point = null;

    var prevMidptb:Point = null;

    var prevNpa:Point = null;

    var prevNpb:Point = null;

    var l:Number = pts.length;

    for (var i:Number=0; i

  9. Hello Grant!

    In the context of your experiments, a pretty little problem to solve : how would you draw circle segments with curveTo ?

    What I was doing is split the circle curve in 4 or 5 arcs, then interpolate point coords in the last incomplete section … but i never got to a trully accurate solution.

    Any ideas ?

  10. hi,

    I desperately search a method/algorithm for drawing an arc of bezier (partial bezier)

    ex:

    drawBezierArc(pointA:Point,pointB:Point,pointCtrl:Point,distance:Number)

    where distance

  11. Oh, wow! Grant Skinner mentioned me in a blog post. My heart is aflutter!

    Alright Grant, I’ll give my tentacle monster another shot, this time with better math!

    Thanks for posting the code!

    I just finished playing with Flickr/as3flickrlib so this will probably be my next project.

  12. Joseph Sikorski June 3, 2008 at 5:48pm

    laz9: Quadratic splines can’t be used to make a perfect circle. Old truetype fonts (which use them) usually had circles represented by an octogon.

    xeo: Does it have to be by distance or will t value suffice?

  13. This reminds me of the “Alt+Drag line” feature to pull/push curves from lines…in the Macromedia Freehand program (RIP). I wanted do something similar with ActionScript. Thanks for posting!

  14. Good stuff, Grant.

    By clicking on your demo, I added a point which controls the curve. However my long-time wish list has been a little different: is there anyway to make it so that by clicking, I can add a point where the curve will go through? I don’t care which direction it curves to. 🙂

    Thanks for the code again.

  15. hi! i am not able to open the .fla test files ..i am using flash professional8 in win xp..dunno wats wrong..i cant open it in adobe flash player also..i really need to test this code. can ne1 twll me wats wrong?

  16. hi! i cant seem to open the .fla files of the test files..i am using flash8 in win xp..m neitehr able to open it in flash player. can ne1 tell me wats wrong? i realy need to test this code..

  17. You will need Flash CS3 to open the FLA.

  18. Hi Grant,

    I am an old fan of your works. We used your UML designer to generate our class hierarchy for our one of very prestigiuos product and am a regular reader of your blog as well.

    I must say this demo is “Speechless” stunning and absolutely fabulous.

    I have learned a lot from your blog and dont wanna mis the opportunity to say “Thanks .. Thanks a lot”

    God bless you. keep writing

    Anand

  19. Anthony Pace July 7, 2008 at 8:39pm

    This is nice work, and can be the starting point for a lot of people with regard to smoothing; yet, what would really be interesting to me, is if you could reverse the construction of the quadratic bezier, knowing just the vertex and two additional points on the line, to find the control point; now if you can do that your math_fu is strong.

    I have been trying to perfect my line smoothing code for a while now; although, I have been able to find the the control point for the quadratic bezier/parabola if the outermost points are an equal distance from the vertex at any degree; however, I am finding it very difficult to find any documentation on how to find it if the outermost points of the cage are not equidistant from the vertex.

    People have gone so far as to tell me it is impossible; to that I respond that the bezier would be impossible if this were true. I have actually reversed the construction for a certain type of triangle that has a degree greater than 90 for one of its corners.

    Your still the god of trees in my book; however, could you also be the god of quadratics?

  20. Very nice GS, thanks for sharing! I struggled w/ this for a while, and finally found a nice bezier curve-fitting function in the Tweener package.

    http://labs.zeh.com.br/blog/?p=104#, the closed shape is something new though. very cool.

  21. This is an amazing example and thank you so much for posting the code. As I attempt to fill in the double-lined shape with a solid color, I am getting undesirable results. Would you please offer a code solution to how to fill in this shape? Thank you so much.

  22. That is one cool application of action script. Wow.

    Great job and thanks for sharing

  23. REFACTORED FOR USE WITH FILL AND CLOSED EDGES

    preview:

    http://labs.review.one-agency.be/labs/curvetest/

    ps( movieclips on stage are named points now);

    /**

    * CurveTests by Grant Skinner. May 30, 2008

    * Visit http://www.gskinner.com/blog for documentation, updates and more free code.

    *

    * revisioned by Jelger Muylaert. Jan 6 2009

    *

    * You may distribute and modify this code freely.

    *

    */

    // for unloading:

    function halt():void {

    removeEventListener(MouseEvent.MOUSE_DOWN,handlePress);

    stage.removeEventListener(MouseEvent.MOUSE_MOVE,doDrag);

    stage.removeEventListener(MouseEvent.MOUSE_UP,endDrag);

    }

    //

    var pts:Array = [point1, point2, point3, point4, point5, point6];

    var r:Number = point1.width/2;

    var curvePoints:Array = [];

    var g:Graphics = graphics;

    var color:uint = 0x66ffff;

    draw();

    function draw():void

    {

    getCurvePointsBottomUp();

    drawCurve();

    }

    function getCurvePointsBottomUp():void

    {

    g.clear();

    var prevMidpta:Point = null;

    var prevMidptb:Point = null;

    var prevNpa:Point = null;

    var prevNpb:Point = null;

    var l:Number = pts.length;

    var pt1:Object;

    var pt2:Object;

    var pt3:Object;

    var n1:Point;

    var n3:Point;

    var npa:Point;

    var npb:Point;

    var scaledR:Number;

    curvePoints = [];

    for (var i:uint=0; i

  24. Hi, Grant, I’ve been looking for a lasso tool example source code for my work but I can’t find any so I’m trying to develop one DIY & ur code is really helpful. Also thanks to the code enhanced by Joseph Sikorski & Jelger.

    Has any one already developed a lasso tool before? Care to give me some help especially the ant-moving animation & how to find all points inside the lasso curved polygon?

  25. hello. it modifies for several lines. a punteada line is possible thanks

    function halt():void {

    removeEventListener(MouseEvent.MOUSE_OVER,handlePress);

    stage.removeEventListener(MouseEvent.MOUSE_MOVE,doDrag);

    stage.removeEventListener(MouseEvent.MOUSE_UP,endDrag);

    }

    var pts:Array = [pt1,pt2,pt3,pt4,pt5,pt6];

    draw();

    function draw():void {

    var g:Graphics = graphics;

    g.clear();

    var prevMidpta:Point = null;

    var prevMidptb:Point = null;

    var prevMidptc:Point = null;

    var prevMidptd:Point = null;

    var prevMidpte:Point = null;

    var prevMidptf:Point = null;

    var prevMidptg:Point = null;

    var prevMidpth:Point = null;

    var l:Number = pts.length;

    for (var i:Number=1;i

  26. hey grant, thanks for sharing!

  27. Hello., Skinner
    you can do this example with dashed line
    thanks

Leave a Reply

Your email address will not be published. Required fields are marked *