AS3 – Parametric path drawing

Partial path drawing Last time I gently coded a script for “rounding” the corners of a path. I think the script is really fine, handles nicely closed paths, but, it’s “static”.

So here’s the new, innocent, question: what if you want to have an animated drawing of the path?

Innocent answer: the drawing method would need a “ratio” parameter, so you can draw only a part of the path.

In other words, you need parametric drawing of lines and curves. Just like the well known parametric easing functions from Robert Penner which have been since the AS1-days included in each and every Flash tweening engine.

Looks like it’s time for some math

For this job we need 2 things:

  • measuring the total length of the path (composed of straight lines and curves),
  • parametric drawing of straight lines and curves.

Measuring curves length

Measuring a straight line is easy, but measuring the length of a quadratic Bézier curve (that’s what you get with graphics.curveTo) is not a simple operation. The question has already been covered with talent by Jim “The Algorithmist” Armstrong: Quadratic Bezier Arc Length. This is a must read with solid explanations and references.

To summarize, there are various approaches on measuring a quadratic Bézier arc length:

  • linear approximation: dividing the curve into a finite number of linear segments and summing their length. This can provide a pretty decent approximation (apparently used in Degrafa framework for instance).
  • numerical integration: this brute force solution obviously provides a very precise result – this kind of stuff reminds me how I need to re-learn a lot of math, I got really rusty after many years producing “entertainment” websites…
  • Gauss-Legendre quadrature: this method can provide a good approximation with less computations than the other methods – an implementation can be found in The Algorithmist’s AS 3 Parametric Curve Library.

For this test I’ve chosen the numerical integration method. In case you wonder why, I think that’s a good compromise: it may not be the most efficient method, but it’s compact (ie. a few lines of code), quick to implement and it doesn’t add any external dependency. Feel free to reuse The Algoritmist’s library if you have performance problems to solve, but I believe it will be ok for most uses.

Fun with Bézier curves

Bézier curve construction This one is easier: Bézier curves have amazing geometric qualities and “cutting” a quadratic Bézier curve is actually fun. I am pathetic when it comes to algebra, but I do get geometry (useful for doing Flash stuff, isn’t it?).

Any point of a Bézier curve can be constructed geometrically. Look at the picture to the right: that’s a quadratic Bézier curve.

To construct C’ at the wanted ratio you first construct A’ and B’ by placing them at the given ratio on the AC and CB sides. Then C’ is placed at the same ratio on the new A’B’ side.

Now look closer: don’t you recognize that AA’C’ and C’B’B are actually 2 new quadratic Bézier curves?

That’s right, this geometric way of constructing a point of a Bézier curve for an arbitrary ratio basically gives you the coordinates of the curve split at this point. Isn’t that fantastic?

Now show some code!

Ok, so here’s a little “Segment” class which can represent either a straight line or a quadratic Bézier. It exposes a few useful methods for measuring and splitting.

package
{
import flash.geom.Point;

/**
 * Line/curve geometry and helpers
 * @author Philippe / http://philippe.elsass.me
 */
public class Segment
{
	public var start:Point;
	public var end:Point;
	public var control:Point;

	public function Segment(start:Point, end:Point, control:Point=null)
	{
		this.start = start;
		this.end = end;
		this.control = control;
	}

	// split at provided ratio 0 < k < 1
	public function subdivide(k:Number):Segment
	{
		var _end:Point;
		if (control)
		{
			var k1:Number = 1.0 - k;

			var _control:Point = new Point(
				k * control.x + k1 * start.x,
				k * control.y + k1 * start.y);

			var temp:Point = new Point(
				k * end.x + k1 * control.x,
				k * end.y + k1 * control.y);

			_end = new Point(
				k * temp.x + k1 * _control.x,
				k * temp.y + k1 * _control.y);

			return new Segment(start, _end, _control);
		}
		else
		{
			_end = new Point(
				start.x + k * (end.x - start.x),
				start.y + k * (end.y - start.y));
			return new Segment(start, _end);
		}
	}

	public function get length():Number
	{
		if (control)
		{
			// code credit: The Algorithmist
			var ax:Number = start.x - 2*control.x + end.x;
			var ay:Number = start.y - 2*control.y + end.y;
			var bx:Number = 2 * control.x - 2 * start.x;
			var by:Number = 2 * control.y - 2 * start.y;

			var a:Number = 4 * (ax * ax + ay * ay);
			var b:Number = 4 * (ax * bx + ay * by);
			var c:Number = bx * bx + by * by;

			var abc:Number = 2 * Math.sqrt(a + b + c);
			var a2:Number  = Math.sqrt(a);
			var a32:Number = 2 * a * a2;
			var c2:Number  = 2 * Math.sqrt(c);
			var ba:Number  = b / a2;

			return (a32 * abc + a2 * b * (abc - c2)
			  + (4 * c * a - b * b)
			  * Math.log((2 * a2 + ba + abc) / (ba + c2)))
			  / (4 * a32);
		}
		else return end.subtract(start).length;
	}
}
}

Interactive demo with source

I also modified the test project with a more advanced (and optimized) helper class for computing and rendering a rounded path.

Now you can create a path and use the mouse wheel to change the drawing ratio:

5 thoughts on “AS3 – Parametric path drawing

  1. Your code was time saver and life saver. Thanks a ton

    Can I use your code for commercial project. If Yes, Do you have any license file. Kindly let me know.

  2. Default license for code on this blog is: MIT license. Feel free to use 😉

  3. Pingback: basics in generative art #3 : LINES 2/2 « HIDIHO!

Comments are closed.