AS3 – Rounding corners
A few days ago, I was with a few students from Gobelins talking about their school projects. One group was working on some trip-assistant project where they would display itineraries on a city map.
Map itineraries most of the time are painted as straight lines with square corners and so one student suggested that it would look nicer with rounded corners: easy stuff, I said, just get each segment’s direction vector, multiply by the corner radius you want and lineTo/curveTo the path.
For some reason this didn’t seem to be absolutely obvious, so I launched FlashDevelop and quickly hacked the needed code to automatically round the corners.
Geometry basics
Let’s take a typical and unpleasant square corner. You get your ABC from a database and draw moveTo(A), lineTo(B), lineTo(C).
Now you admit it would please the eye to draw moveTo(A), lineTo(A’), curveTo(B, C’), lineTo(C). But how do you compute A’ and B’ automatically, with a nice R radius?
Well BA’ is simply BA (ie. A minus B) normalized to length R (ie. a vector on line BA of length R). Add BA’ to B and you get A’. Repeat with BC to get C’.
Back to ActionScript 3
Now let’s look at ActionScript 3’s Point class. Really, this class is great, it makes such computations so easy that you don’t even need to now your math.
// provided coordinates and radius var a:Point; var b:Point; var c:Point; var r:Number; // compute A' var ba:Point = a.subtract(b); ba = ba.normalize(r); var ap:Point = b.add(ba); // compute C' var bc:Point = c.subtract(b); bc = bc.normalize(r); var cp:Point = b.add(bc); // draw graphics.moveTo(a.x, a.y); graphics.lineTo(ap.x, ap.y); graphics.curveTo(b.x, b.y, cp.x, cp.y); graphics.lineTo(c.x, c.y);
Complete implementation
The final code is certainly trickier than just computing one corner – I’ve decided to give it a little more work to make it robust and efficient:
- radius should be adaptive because some points could be “too close”,
- computation should be efficient and minimize array access,
- provide a nice “closed path” option,
- use Vector.<T> (let’s get used to it).
And here’s the method
static public function drawRoundPath(g:Graphics, points:Vector.<Point>,
radius:Number = 20, closePath:Boolean = false):void
{
// code by Philippe / http://philippe.elsass.me
var count:int = points.length;
if (count < 2) return;
if (closePath && count < 3) return;
var p0:Point = points[0];
var p1:Point = points[1];
var p2:Point;
var pp0:Point;
var pp2:Point;
var last:Point;
if (!closePath)
{
g.moveTo(p0.x, p0.y);
last = points[count - 1];
}
var n:int = (closePath) ? count + 1 : count - 1;
for (var i:int = 1; i < n; i++)
{
p2 = points[(i + 1) % count];
var v0:Point = p0.subtract(p1);
var v2:Point = p2.subtract(p1);
var r:Number = Math.max(1, Math.min(radius,
Math.min(v0.length / 2, v2.length / 2)));
v0.normalize(r);
v2.normalize(r);
pp0 = p1.add(v0);
pp2 = p1.add(v2);
if (i == 1 && closePath)
{
g.moveTo(pp0.x, pp0.y);
last = pp0;
}
else g.lineTo(pp0.x, pp0.y);
g.curveTo(p1.x, p1.y, pp2.x, pp2.y);
p0 = p1;
p1 = p2;
}
g.lineTo(last.x, last.y);
}
Possible further development:
- create another helper method to produce a GraphicsPath collection for reusable drawing.
Interactive demo with source
Feel free to try out the little interactive demo – you can add/drag/remove/insert points and enjoy your rounded work of art:
haha quelle réactivité ! Merci msieur.
Really great post.
You and FlashDevelop code completion rock !!!
Hey Dude !
Thanks for the Great class.
Cheers
Hey Dude !
Thanks for the Great class.
Cheers
Very useful demo.. wish adobe would integrate something like this in their framework
Cheers
Sharing is Sexy
Thanks …