mheavers
mheavers

Reputation: 30158

flash as3 draw a donut wedge (arc with inner and outer radius) using curveTo

I'm trying to draw an arc in flash using a minimal amount of code / iterations. I ported this method below from an old AS2 example, but it requires a loop through a number of steps to make it look smooth, and I'd rather avoid that. I saw AS3 has a "curveTo" command, but it doesn't really draw a circular arc, it draws a bezier curve. Is there an arc command in flash? or a way to draw a segment of a circle?

Here's my old code:

function drawSolidArc (drawObj:Object, centerX:Number,centerY:Number,innerRadius:Number,outerRadius:Number,startAngle:Number,arcAngle:Number,steps:int=20):void {
    if (Math.abs(startAngle)>360)startAngle%=360
    if (Math.abs(arcAngle)>360)arcAngle%=360
    startAngle/=360,arcAngle/=360
    var twoPI:Number = 2 * Math.PI;
    var angleStep:Number = arcAngle/steps;
    var angle:Number, i:int, endAngle:Number;
    var xx:Number = centerX + Math.cos(startAngle * twoPI) * innerRadius;
    var yy:Number = centerY + Math.sin(startAngle * twoPI) * innerRadius;
    var xxInit:Number=xx;
    var yyInit:Number=yy;
    drawObj.graphics.moveTo(xx,yy);
    for(i=1; i<=steps; i++) {
        angle = (startAngle + i * angleStep) * twoPI;
        xx = centerX + Math.cos(angle) * innerRadius;
        yy = centerY + Math.sin(angle) * innerRadius;
        drawObj.graphics.lineTo(xx,yy);
    }
    endAngle = startAngle + arcAngle;
    for(i=0;i<=steps;i++) {
        angle = (endAngle - i * angleStep) * twoPI;
        xx = centerX + Math.cos(angle) * outerRadius;
        yy = centerY + Math.sin(angle) * outerRadius;
        drawObj.graphics.lineTo(xx,yy);
    }

    drawObj.graphics.lineTo(xxInit,yyInit);
};

var myArc:Shape = new Shape(); //or another DisplayObject

myArc.graphics.beginFill(0xcccccc, 0.50);
//objName, centerX,centerY, innerRadius, outerRadius, startAngle (12 o'clock is -90), arcAngle (degrees from startAngle), steps (smoothness)
drawSolidArc (myArc,250, 250, 180, 200, -90, 65, 100);
myArc.graphics.endFill();
this.addChild(myArc);

Upvotes: 3

Views: 3633

Answers (5)

mheavers
mheavers

Reputation: 30158

I did something similar to Jeremy's solution, although I didn't mask it. Apparently if you create another arc of a smaller radius from the same center point, it will subtract from the existing shape, so I just made another call to drawSolidArc after the first, and before endFill, with zero as the centerpoint, and the outer radius being the desired inner radius of the first wedge:

var myArc:Sprite = new Sprite();
myArc.graphics.beginFill(0xcccccc, 1);
drawArc(myArc, 275, 200, 60, 150, 35); //spriteName, startX, startY, innerRadius, radius, arcAngle, startAngle
drawArc(myArc, 275, 200, 0, 60, 35); //subtract out the middle by drawing a new circle over the top of it
myArc.graphics.endFill();
this.addChild(myArc);

Upvotes: 1

JeremyFromEarth
JeremyFromEarth

Reputation: 14344

Below is a link to few different shape drawing classes I've written. You can achieve the desired segment of a circle by drawing a wedge and masking out the center of it with a second circle shape.

https://github.com/makemachine/makemachine.actionscript/tree/master/src/makemachine/display/shapes

Upvotes: 1

joncys
joncys

Reputation: 1350

Then there's this implementation. But it's almost as messy, as that which you've posted. I'm afraid there's no elegant way (few lines or so) of drawing an arc.

Upvotes: 0

davivid
davivid

Reputation: 5960

This is how I have done it:

Draw a doughnut and mask it:

_foreground = new Sprite();
_foreground.graphics.beginFill(_colour)
_foreground.graphics.drawCircle(0, 0, _outerRadius);
_foreground.graphics.drawCircle(0, 0, _innerRadius);
_foreground.graphics.endFill();
_container.addChild(_foreground);

_mask = new Sprite();
_mask.graphics.beginFill(0xff0000);
_foreground.mask = _mask;

Then draw it using this method - by drawing a wedge into the mask

private function draw(e:Event):void //Called each frame (if animating)
{
// No need to draw more than 360
if (Math.abs(_arc) > _endAngle) 
{
    _arc = _endAngle;
    stop();
}

_numOfSegs = Math.ceil(Math.abs(_arc) / 45);
_segAngle = _arc / _numOfSegs;
_segAngle = (_segAngle / 180) * Math.PI;
_angle = (_startAngle / 180) * Math.PI;

// Calculate the start point
_ax = Math.cos(_angle) * _outerRadius;
_ay = Math.sin(-_angle) * _outerRadius;

// Draw the first line
_mask.graphics.lineTo(_ax, _ay);

for (var i:int=0; i<_numOfSegs; i++) 
{
    _angle += _segAngle;
    _angleMid = _angle - (_segAngle / 2);
    _bx = Math.cos(_angle) * _outerRadius;
    _by = Math.sin(_angle) * _outerRadius;
    _cx = Math.cos(_angleMid) * (_outerRadius / Math.cos(_segAngle / 2));
    _cy = Math.sin(_angleMid) * (_outerRadius / Math.cos(_segAngle / 2));
    _mask.graphics.curveTo(_cx, _cy, _bx, _by);
}

// Close the wedge
_mask.graphics.lineTo(0, 0);
_arc += 3.6; //This gives a 100 steps to complete the circle
}

Upvotes: 1

Kodiak
Kodiak

Reputation: 5978

Graphics.cubicCurveTo() will give you a better approximation of an arc than curveTo. But it's still not perfect.

Upvotes: 1

Related Questions