Flash CurveTo Visualization Code
If you’ve ever wondered how Adobe Flash draws a curve when issued a “curveTo” command this article will help you visualize the process.
As you probably know, Flash requires three points to draw an exponential curve.
- A Starting Point. Wherever the Flash drawing pen is currently located. If you have not previously moved the pen with “lineTo”, “curveTo”, or “moveTo” the pen defaults to the origin (0, 0).
- A Control Point. A point which the the Flash drawing pen will head toward before changing direction and heading toward the end point. The pen generally does not go through the control point unless your “curve” is nearly straight.
- An End Point. Where the Flash drawing pen will come to rest when the curve is complete.
But how exactly does the control point “control” the shape of the curve? How does Flash precisely map the vague concept of heading towards a point and then land precisely at another point? Take a look at the SWF file below. The red dot represents the Starting Point, the green dot represents the Control Point and the blue dot represents the End Point. The magenta (purple) dot represents various points along the “curveTo” path. So how does Flash determine where the magenta dot should be?
- You can drag the red, green and blue dots.
- Pressing the "Q" key freezes the curveTo function one Quarter of the way along its path.
Here is how to visualize the process...
- Determine where you want to be along the curved path. This can be represented as a ratio, fraction or percentage of the total distance needed to travel along the path from the Start Point to the End Point. For this example, we'll use 1/4 (same as .25 or 25%).
- Draw a yellow line from the red Starting Point to the green Control Point.
- Draw a yellow dot on the yellow line 1/4 of the distance from the red Start Point to the green Control point.
- Draw a cyan line from the green Control Point to the blue End Point.
- Draw a cyan dot on the cyan line 1/4 of the distance from the green Control Point to the blue End Point.
- Draw a magenta line from the yellow dot to the cyan dot.
- Draw a magenta dot on the magenta line 1/4 of the distance from the yellow dot to the cyan dot. The magenta dot now represents a point 1/4 of the way along the curveTo path.
- To find all other points on the path, repeat this process with different ratios (or fractions or percentages). The example above repeatedly goes from .01 to .99 and then bounces back to .01.
So when Flash draws a curve with the "curveTo" command it essentially goes through the same process as above for a series of points along the curve's path and then connects those points with straight line segments. So now you know.
If you'd like to dig through the code used to generate the above SWF file, here it is...
// Red Dot (Start Point)
createEmptyMovieClip("P1", 10);
P1.lineStyle(20, 0xFF0000);
P1.lineTo(.5, 0);
P1._x = 50;
P1._y = 50;
//
// Green Dot (Control Point)
createEmptyMovieClip("P2", 20);
P2.lineStyle(20, 0x00FF00);
P2.lineTo(.5, 0);
P2._x = 450;
P2._y = 50;
//
// Blue Dot (End Point)
createEmptyMovieClip("P3", 30);
P3.lineStyle(20, 0x0000FF);
P3.lineTo(.5, 0);
P3._x = 50;
P3._y = 350;
//
// Yellow Dot
createEmptyMovieClip("P12", 40);
P12.lineStyle(10, 0xFFFF00);
P12.lineTo(.5, 0);
//
// Cyan Dot
createEmptyMovieClip("P23", 50);
P23.lineStyle(10, 0x00FFFF);
P23.lineTo(.5, 0);
//
// Magenta Dot
createEmptyMovieClip("P123", 60);
P123.lineStyle(10, 0xFF00FF);
P123.lineTo(.5, 0);
//
// Red Arc
createEmptyMovieClip("PartLineA", 70);
PartLineA.lineStyle(2, 0xFF0000);
PartLineA.moveTo(P1._x, P1._y);
PartLineA.curveTo(P2._x, P2._y, P3._x, P3._y);
//
// Blue Arc
createEmptyMovieClip("PartLineB", 80);
PartLineB.lineStyle(2, 0x000FF);
PartLineB.moveTo(P1._x, P1._y);
PartLineB.curveTo(P2._x, P2._y, P3._x, P3._y);
//
// Magenta Line
createEmptyMovieClip("GuideLine", 5);
GuideLine.lineStyle(2, 0xFF00FF);
GuideLine.moveTo(P12._x, P12._y);
GuideLine.lineTo(P23._x, P23._y);
//
// Yellow Line (Connects Starting Point to Control Point).
createEmptyMovieClip("GuideLine2", 6);
GuideLine2.lineStyle(1, 0xFFFF00);
GuideLine2.moveTo(P1._x, P1._y);
GuideLine2.lineTo(P2._x, P2._y);
//
// Cyan Line (Connects Control Point to End Point).
createEmptyMovieClip("GuideLine3", 7);
GuideLine3.lineStyle(1, 0x00FFFF);
GuideLine3.moveTo(P2._x, P2._y);
GuideLine3.lineTo(P3._x, P3._y);
//
// Animate the line by bouncing the values of "ratio" between 0 and 1.
var step = .0025;
var curveRatio = 0;
//
function showCurveTo(ratio) {
//
P12._x = P1._x+(P2._x-P1._x)*ratio;
P23._x = P2._x+(P3._x-P2._x)*ratio;
P123._x = P12._x+(P23._x-P12._x)*ratio;
P12._y = P1._y+(P2._y-P1._y)*ratio;
P23._y = P2._y+(P3._y-P2._y)*ratio;
P123._y = P12._y+(P23._y-P12._y)*ratio;
//
PartLineA.clear();
PartLineA.lineStyle(2, 0xFF0000);
PartLineA.moveTo(P1._x, P1._y);
PartLineA.curveTo(P12._x, P12._y, P123._x, P123._y);
//
PartLineB.clear();
PartLineB.lineStyle(2, 0x0000FF);
PartLineB.moveTo(P3._x, P3._y);
PartLineB.curveTo(P23._x, P23._y, P123._x, P123._y);
//
GuideLine.clear();
GuideLine.lineStyle(2, 0xFF00FF);
GuideLine.moveTo(P12._x, P12._y);
GuideLine.lineTo(P23._x, P23._y);
}
function bounceCurve (){
curveRatio += step;
if(curveRatio>1 || curveRatio<0) step*=-1;
showCurveTo(curveRatio);
}
function sameCurve(){
showCurveTo(curveRatio)
}
function toggleQuarter(){
if(Key.getCode() == 81){// "Q"
if(onEnterFrame == sameCurve){
onEnterFrame = bounceCurve;
}else{
curveRatio = .25;
onEnterFrame = sameCurve;
}
}
}
onEnterFrame = bounceCurve;
onKeyDown = toggleQuarter;
Key.addListener(this);
//
// Allow dots to be dragged and update lines.
var pressed = null;
P1.onPress = P2.onPress = P3.onPress = function(){
pressed = this;
onMouseMove = movePressed;
}
P1.onRelease = P2.onRelease = P3.onRelease =
P1.onReleaseOutside = P2.onReleaseOutside = P3.onReleaseOutside = function(){
pressed = null;
onMouseMove = null;
}
movePressed = function(){
pressed._x = _xmouse;
pressed._y = _ymouse;
GuideLine2.clear();
GuideLine2.lineStyle(1, 0xFFFF00);
GuideLine2.moveTo(P1._x, P1._y);
GuideLine2.lineTo(P2._x, P2._y);
GuideLine3.clear();
GuideLine3.lineStyle(1, 0x00FFFF);
GuideLine3.moveTo(P2._x, P2._y);
GuideLine3.lineTo(P3._x, P3._y);
updateAfterEvent();
}
//
//
Great article and explanation. I’ve been looking at math sites about curves and shaking my head in confusion. Been a while since trig etc. The visual explanation really made sense to me and now I get it. Thanks!
Great article. Thanks very much.
Hey.. this is awesome.. Im just trying to figure out how to draw curved lines between two mc’s that can be anywhere on the stage.
This code is too complex for me, are you able to guide me at all?
Thanks!
And can you email me? I dont think I can subscribe here?
Hey, man! You are super slick!
Could you modify your demo to also display the starting, control and end point?
[…] http://www.pixelwit.com/blog/2007/11/01/curveto-visualization […]
For example, points on a map. Those points can be anywhere in relation to each other so the control point used to connect one to the other using curveTo is not constant. I suspect, there is some equation that can be used to determine what that control point would be on a dynamic basis, but alas, I only suspect and I know not how to determine such a thing. Does that make sense? Because then I want to make the curves draw onto the page instead of just appearing. Or have I set too lofty of a goal for myself?
RONINdub, I’m not sure I understand what you’re trying to do.
Thanks for the explanation. I’m new to the drawing API so I have another question that I believe is related. If I wanted to keep the curve constant, no matter where the RED and BLUE dots were located, how would I figure that out and implement it in AS?