A Flash Developer Resource Site

# Thread: problem: drawing circle with curveTo()

1. Am making a little drawing tablet application using the new drawing API and everything was working great - straight lines, free lines, boxes, filled boxes, even triangles worked great. Then I tried to make a circle tool. Using the instructions straight out of the manual for the curveTo() function, what I get looks like a ball that's been squashed both vertically and horizontally.

Their example works like this:

If you have a movie clip 100 pix by 100, and want a circle of the same dimensions:

moveTo(50, 0)
circleTo(100, 0, 100, 50)
circleTo(100, 100, 50, 100)
circleTo(0, 100, 0, 50)
circleTo(0, 0, 50, 0)

Now this should give a perfect circle, but instead it gives the flattened round object I described above.

I've tried a kludge where I move all the control points in a few percent on both x and y axes. But this isn't a real solution, since it's still not really a circle and has very slight points on all four sides. Hrmmp.

Is there any way to make this work for real?

--PG

2. woops - I mean curveTo in my example, not circleTo

3. ## Been There Done That.

Yes, I was at this place last week and came to the conclusion, after looking through my OpenGL code, that it just wasn't worth trying to figure out.
Instead I drew a polygon that simulates the circle.

Here's my first version w/ bugs:
Code:
```_root.createEmptyMovieClip ("arc", 1);
with (_root.arc)
// Inputs
var x = 100;
var y = 100;
var startAngle = 0;
var endAngle = 320;
var lineColor = 0x000000;
var fillColor = 0xFFFF00;
var Fill = true;

// Holders
var increment = 1;
var circlePoints = new Array();
var cosAngle;
var sinAngle;
var numberOfDegrees;
{
if (!circlePoints) {
return;
}
lineStyle( 4, lineColor, 100 );
if (Fill == true) {
beginFill(fillColor, 100);
}
var i ;

if(startAngle > endAngle) {
numberOfDegrees = (360 - startAngle + endAngle) / increment;
} else {
numberOfDegrees = endAngle - startAngle / increment;
}
for(i = 0; i < numberOfDegrees+1; i++){
if(Math.abs(cosAngle) < .01) {
cosAngle = 0.0;
}
if(Math.abs(sinAngle) < .01) {
sinAngle = 0.0;
}
if (i == 0) {
}
startAngle += increment;
}
if (Fill == true) {
lineTo(x, y);
endFill();
}
}```
I say with bugs because it doesn't remove the line from center to start when you have a complete circle, among other less obvious problems. I'm can't post the complete updated version because it's for a commercial product and we have sourcecode restrictions, but this is a good start for anyone. This will make a PacMan BTW.
[Edited by SCDYNE on 04-08-2002 at 12:32 PM]

4. This code will draw an ellipse. Just set a = b for a circle:

Code:
```var a = 200;
var b = 100;

var j = a * 0.70711;
var n = b * 0.70711;
var i = j -(b-n)*a/b;
var m = n -(a-j)*b/a;

var x = Stage.width/2;
var y = Stage.height/2;

lineStyle( 1, 0, 100 );
moveTo( x + a, y );
curveTo( x + a, y - m, x + j, y - n );
curveTo( x + i, y - b, x    , y - b );
curveTo( x - i, y - b, x - j, y - n );
curveTo( x - a, y - m, x - a, y     );

curveTo( x - a, y + m, x - j, y + n );
curveTo( x - i, y + b, x    , y + b );
curveTo( x + i, y + b, x + j, y + n );
curveTo( x + a, y + m, x + a, y     );```

5. Thank you both!

--PG

6. Tried nmain's solution as being the more straightforward, and it worked great. Thank you again

7. awsome.

8. ## Bezier Circle baby!

While it's not possible to draw a perfect circle with a Bezier curve, visually it is close enough that you can't tell the difference.

My example allows for input of multiple aspects of the arc including start angle and end angle, using lineTo(), but it's very slow.
Nmain's example draws either a circle or ellipse with the use of curveTo()and it's very fast, but practically it can't be a open arc.

The fastest and best solution would be to use nmain's example and calculate the remainder of the partial segment making up the last curveTo() to complete an arc of X total degrees. Does that make sense?

9. Interesting point SCDYNE. Before I got an answer yesterday I also posted the same question to Macromedia's flash usenet list. Here's one really good answer I got.

I think it works, although it looks slightly less elegant than nmain's. What it does is handle open arcs as well as circles, which I didn't need for my app.

Cheers,
Patrick

------------------------------------------------

Here's something from another list. I haven't actually tried it, but
it got some great response. I think the author is Ric Ewing...

Let me know how this works. If you like it, give Ric credit and post it to
the flash group =)

Scott

MovieClip.prototype.arcTo = function(x, y, r, a1, arc, obj) {
// ==============
// x, y = This should be where the current pen is... other values will
cause problems
// a = starting angle in degrees.
// arc = sweep of the arc. Negative values draw clockwise.
// obj = string for a varavle to store the end point of the arc
[optional]
// ==============
//
// Init vars
var remainder, theta, halftheta, angle, angleMid, segs, ax, ay, bx, by,
cx, cy, remainder, lastArc;
//
// Make a1 positive;
if (a1<0) {
a1 += 360;
}
//
// no sense in drawing more than is needed
if (arc>360) {
arc = 360;
}
//
// Flash uses 8 segments per circle, to match that, we draw in 45 degree
segments.
// First we calculate how many segments are needed for our arc.
segs = Math.floor(Math.abs(arc)/45);
//
// Now find what is left over.
remainder = Math.abs(arc)%45;
//
// The math requires radians rather than degrees. To convert from
degrees
// use the formula (degrees/180)*Math.PI to get radians. Because we know
// that we will be using 45 degree segments, we can reduce calculations
// by making theta = -0.785398163397448 because that is 45 degrees in
theta = -0.785398163397448;
//
// theta/2 is also used in several places, so we save onthat as well by
// entering the numbers directly.
halftheta = -0.392699081698724;
//
// convert angle a1 to radians
angle = -(a1/180)*Math.PI;
//
// find our starting points (ax,ay) relative to the secified x,y
ax = x-Math.cos(angle)*r;
ay = y-Math.sin(angle)*r;
//
// first see which way we're drawing and make the appropriate
if (arc<0) {
theta *= -1;
halftheta *= -1;
}
//
// if our arc is larger than 45 degrees, draw as 45 degree segments
// so that we match Flash's native circle routines.
if (segs>0) {
//
// to draw the segment, we need the angle halfway between our
// starting angle (a1) and our ending angle (a1+45). Because
// we have a loop for drawing these segments, we can simplify
// our process by pretending that our starting angle is a previous
// ending angle and then set angleMid to 1/2 theta less than angle.
// This means that we can simply increment both angle and angleMid
// by theta to get the next position.
angleMid = angle-halftheta;
//
// Loop for drawing arc segments
for (var i = 0; i<segs; i++) {
//
// increment our angles
angle += theta;
angleMid += theta;
//
// calculate our end point
bx = ax+Math.cos(angle)*r;
by = ay+Math.sin(angle)*r;
//
// calculate our control point
cx = ax+Math.cos(angleMid)*(r/Math.cos(halftheta));
cy = ay+Math.sin(angleMid)*(r/Math.cos(halftheta));
//
// draw the arc segment
this.curveTo(cx, cy, bx, by);
}
}
//
// if our arc doesn't evenly divide into 45 degree segments,
// draw the remaining segment.
if (remainder>0) {
//
// calculate the remaining segment arc
lastArc = Math.abs(arc)-(segs*45);
if (arc<0) {
lastArc *= -1;
}
//
// calculate our angles and points as above
angle = -((a+arc)/180)*Math.PI;
//
// a little tricky here... by dividing lastArc by 360 we
// save having to do ((lastArc/2)/180)*Math.PI.
angleMid = angle-(-(lastArc/360)*Math.PI);
bx = ax+Math.cos(angle)*r;
by = ay+Math.sin(angle)*r;
cx = ax+Math.cos(angleMid)*(r/Math.cos(angle-angleMid));
cy = ay+Math.sin(angleMid)*(r/Math.cos(angle-angleMid));
//
// draw the last curve
this.curveTo(cx, cy, bx, by);
}
//
// In the other draw routines the user must specify the end point
// which means that they always know where they are ending at, but
// here the endpoint is unknown unless the user calculates it themself.
// Lets be nice and let save them the hassle. If the user has provided
// a string for the obj argument, lets set it's x and y properties to
// our end point x & y.
if (obj != undefined) {
this[obj] = {x:bx, y:by};
}
//
// We're done.
};

#### Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•

 » Home » Movies » Tutorials » Submissions » Board » Links » Reviews » Feedback » Gallery » Fonts » The Lounge » Sound Loops » Sound FX » About FK » Sitemap