A Flash Developer Resource Site

Results 1 to 9 of 9

Thread: problem: drawing circle with curveTo()

  1. #1
    Junior Member
    Join Date
    Apr 2002
    Posts
    6
    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. #2
    Junior Member
    Join Date
    Apr 2002
    Posts
    6
    woops - I mean curveTo in my example, not circleTo

  3. #3
    Junior Member
    Join Date
    Dec 2001
    Posts
    14

    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 radius = 80;
     var startAngle = 0;
     var endAngle = 320;
     var lineColor = 0x000000;
     var fillColor = 0xFFFF00;
     var Fill = true;
    		
     // Holders
     var increment = 1;
     var circlePoints = new Array();
     var degToRadConst = 0.0174444444444;
     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++){
       cosAngle = Math.cos(startAngle * degToRadConst);
       sinAngle = Math.sin(startAngle * degToRadConst);
        if(Math.abs(cosAngle) < .01) {
         cosAngle = 0.0;
        }
        if(Math.abs(sinAngle) < .01) {
         sinAngle = 0.0;
        }
        if (i == 0) {
         moveTo(((radius * cosAngle) + x), ((radius * sinAngle) + y));
        }
         lineTo(((radius * cosAngle) + x), ((radius * sinAngle) + y));
         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. #4
    Senior Member
    Join Date
    Jul 2000
    Posts
    126
    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. #5
    Junior Member
    Join Date
    Apr 2002
    Posts
    6
    Thank you both!

    --PG

  6. #6
    Junior Member
    Join Date
    Apr 2002
    Posts
    6
    Tried nmain's solution as being the more straightforward, and it worked great. Thank you again

  7. #7
    Beyond the Sea
    Join Date
    Mar 2000
    Posts
    997
    awsome.

    good stuff. had questions about this as well.

  8. #8
    Junior Member
    Join Date
    Dec 2001
    Posts
    14

    resolved 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. #9
    Junior Member
    Join Date
    Apr 2002
    Posts
    6
    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
    // r = radius;
    // 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
    radians
    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
    // adjsutments
    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
  •  




Click Here to Expand Forum to Full Width

HTML5 Development Center