Search code examples
flashactionscript-3actionscriptdrawingbezier

How can I modify my code to line through the bezier control points?


HI all -

I am using anchor points and control points to create a shape using curveTo. It's all working fine, but I cannot figure out how to get my lines to go through the center of the control points (blue dots) when the line is not straight.

Here is my code for drawing the shape:

           // clear old line and draw new / begin fill
        var g:Graphics = graphics;
        g.clear();
        g.lineStyle(2, 0, 1);
        g.beginFill(0x0099FF,.1);

        //move to starting anchor point
        var startX:Number = anchorPoints[0].x;
        var startY:Number = anchorPoints[0].y;
        g.moveTo(startX, startY);

        // Connect the dots
        var numAnchors:Number = anchorPoints.length;
        for (var i:Number=1; i<numAnchors; i++) {

            // curve to next anchor through control
            g.curveTo(controlPoints[i].x,controlPoints[i].y, anchorPoints[i].x, anchorPoints[i].y);

        }
        // Close the loop
        g.curveTo(controlPoints[0].x,controlPoints[0].y,startX,startY);

And the shape I'm drawing for reference:

How can I modify my code so that the lines go directly through the blue control points?

Thanks in advance!

b


Solution

  • Example project Source code

    If you're not interested in how this is derived, skip down to the answer section.

    Background Information


    Bezier curves are interesting and fun to work with. This animation shows how your Quadratic curve is being drawn between the two anchor points (P0 and P2) over time with respect to the control point (P1).

    What you need to do, instead of drawing the control point (P1), is draw the point on the curve at t=0.5:

    Point at t=0.5
    (source: whilenotnull.com)

    Luckily this is easy to do with the equation given on the Wikipedia page: http://en.wikipedia.org/wiki/Bezier_Curve#Quadratic_B.C3.A9zier_curves

    Here is the formula in Actionscript:

    public function calculatePoint(p0:Point, p1:Point, p2:Point, t:Number):Point
    {
        var p:Point = new Point(calculateTerm(p0.x, p1.x, p2.x, t), calculateTerm(p0.y, p1.y, p2.y, t));
        return p;
    }
    
    public function calculateTerm(p0:Number, p1:Number, p2:Number, t:Number):Number
    {
        var negT:Number = 1 - t;
    
        var a0:Number = Math.pow(negT, 2) * p0;
        var a1:Number = 2 * negT * t * p1;
        var a2:Number = Math.pow(t, 2) * p2;
    
        var pos:Number = a0 + a1 + a2;
        return pos;
    }
    

    So if you plug the three points: var t0:Point = calculatePoint(p0, p1, p2, 0.5); you'll get the point on the curve where you want to draw your "control point".


    Answer

    Now we can write a function that assumes the second parameter is a point on the curve and determine the co-ordinates of the control point:

    public function derivePoint(p0:Point, b1:Point, p2:Point, t:Number = 0.5):Point
    {
        var p:Point = new Point(deriveTerm(p0.x, b1.x, p2.x, t), deriveTerm(p0.y, b1.y, p2.y, t));
        return p;
    }
    
    public function deriveTerm(p0:Number, bt:Number, p2:Number, t:Number):Number
    {
        var negT:Number = 1 - t;
    
        var a0:Number = Math.pow(negT, 2) * p0;
        var a1:Number = 2 * negT * t;
        var a2:Number = Math.pow(t, 2) * p2;
    
        var p1:Number = (bt - a0 - a2) / a1;
        return p1;
    }
    

    From this I've updated your code snippet to (hopefully) draw the curve through your "control points":

    // clear old line and draw new / begin fill
    var g:Graphics = graphics;
    g.clear();
    g.lineStyle(2, 0, 1);
    g.beginFill(0x0099FF,.1);
    
    //move to starting anchor point
    var startX:Number = anchorPoints[0].x;
    var startY:Number = anchorPoints[0].y;
    g.moveTo(startX, startY);
    
    // Connect the dots
    var p0:Point = new Point(startX, startY);
    var p2:Point;
    
    var numAnchors:Number = anchorPoints.length;
    for (var i:Number=1; i<numAnchors; i++) {
    
        p2 = new Point(anchorPoints[i].x, anchorPoints[i].y);
    
        // curve to next anchor through control
        var b1:Point = new Point(controlPoints[i].x,controlPoints[i].y);
        var p1:Point = derivePoint(p0, b1, p2);
    
        g.curveTo(p1.x, p1.y, p2.x, p2.y);
    
        p0 = p2;
    
    }
    // Close the loop
    g.curveTo(controlPoints[0].x,controlPoints[0].y,startX,startY);
    

    Example project Source code