views:

194

answers:

1

I am trying to create a spinning wheel with text on it. I have created the wheel and it is populating perfectly based on the colors that I supply to it. Now I am trying to add text to each of the parts of the wheel but am running into some problems. I cannot seem to get the text to display properly within each of the colors. I was hoping someone could help me get this to work properly. The place where I am trying to get the text to work is in the _drawSlice function. I am trying to figure out the logic to get it to work properly. Any help is appreciated. Here is my code:

package {
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.text.TextFormat;
import flash.text.TextField;
import flash.display.BitmapData;
import flash.display.Bitmap;

public class PieChart extends MovieClip{
    //settings
    private var _radius:Number = new Number(100);

    //storage
    private var slices:Array = new Array();
    private var _startAngle:Number = new Number(0);

    public function PieChart() {
        this._setup(_radius);

        for(var i:int=0;i<2;i++){
            this.addSlice(0xCF5351,'Maroon'); //maroon
            this.addSlice(0x3DA261,'Green'); //green
            this.addSlice(0x4485C3,'Blue'); //blue
            this.addSlice(0xF8F66D,'Yellow'); //yellow
            this.addSlice(0x9D499B,'Purple'); //purple
            this.addSlice(0xF99F44,'Orange'); //orange
        }
        this.x = 150;
        this.y = 150;
        this.draw();
    }

    public function addSlice(color:Number,text:String):void {
        var slice:Array = ["slice"+slices.length,color,text];
        slices.push(slice);
    }

    public function draw():void {
        var angle:Number=((100 / slices.length)*360)/100;

        for(var i:int=0;i<slices.length;i++){
            this._drawSlice(_radius,_startAngle,slices[i][1],1,angle,slices[i][2]);                         
            _startAngle-=angle;
        }
    }

    private function _drawSlice(radius:Number,angle:Number,color:Number,alpha:Number,arc:Number,txt:String):void {
        var sprite:Sprite = new Sprite();

        sprite.graphics.beginFill(color,alpha);

        //setup the variables
        var segAngle:Number, theta:Number, angle:Number, angleMid:Number, segs:Number, ax:Number, ay:Number, bx:Number, by:Number, cx:Number, cy:Number;

        //start at point 0,0
        sprite.graphics.moveTo(0, 0);
        //get the number of segments
        segs = Math.ceil(Math.abs(arc)/45);
        // Now calculate the sweep of each segment.
        segAngle = arc/segs;
        // The math requires radians rather than degrees. To convert from degrees
        // use the formula (degrees/180)*Math.PI to get radians.
        theta = -(segAngle/180)*Math.PI;
        // convert angle _startAngle to radians
        angle = -(_startAngle/180)*Math.PI;
        // draw the curve in segments no larger than 45 degrees.
        if (segs>0) {
            // draw a line from the center to the start of the curve
            ax = Math.cos(_startAngle/180*Math.PI)*radius;
            ay = Math.sin(-_startAngle/180*Math.PI)*radius;
            sprite.graphics.lineTo(ax, ay);

            // Loop for drawing curve segments
            for (var i:int = 0; i<segs; i++) {
                angle += theta;
                angleMid = angle-(theta/2);
                bx = Math.cos(angle)*radius;
                by = Math.sin(angle)*radius;
                cx = Math.cos(angleMid)*(radius/Math.cos(theta/2));
                cy = Math.sin(angleMid)*(radius/Math.cos(theta/2));
                sprite.graphics.curveTo(cx, cy, bx, by);
            }

            // close the wedge by drawing a line to the center
            sprite.graphics.lineTo(0, 0);
        }

        var txtFormat:TextFormat = new TextFormat();
        txtFormat.color = 0xFFFFFF;
        txtFormat.size = 16;

        var txtField:TextField = new TextField;
        txtField.text = txt;
        txtField.setTextFormat(txtFormat);

        var bmpData:BitmapData = new BitmapData(sprite.width,sprite.height,true,0x000000);
        bmpData.draw(txtField);

        var bmp:Bitmap = new Bitmap(bmpData,"auto",true);
        bmp.rotation = (_startAngle*-1)-20;
        bmp.y -= 20;

        sprite.addChild(bmp);
        this.addChild(sprite);
    }

    private function _setup(radius:Number):void {
        this._radius = radius;
    }

}

}

+1  A: 

For simplicity's sake, I'm using embed fonts, but you can easily change that :) This is a quick solution based around your code, but the thinking was the same as explained earlier, I would favor creating a slice object with the text in the right position , then add the slice objects to form the wheel. Within your code I tried to replicate this, so I set a position for the textfield which is then added to a small container whose rotation is proportional to the numbers of slides. This can be improved but you should have enough elements to turn this into nicer code:

package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.text.TextField;
 import flash.text.TextFormat;

public class PieChart extends MovieClip{
//settings
private var _radius:Number = new Number(100);


[Embed(source="fonts/Arial.ttf", 
    fontName="PCArial",
    mimeType="application/x-font-truetype",
    embedAsCFF= "false")]
public var PCArial:Class; 

//storage
private var slices:Array = new Array();
private var _startAngle:Number = new Number(0);

public function PieChart() {
    this._setup(_radius);

    for(var i:int=0;i<2;i++){
        this.addSlice(0xCF5351,'Maroon'); //maroon
        this.addSlice(0x3DA261,'Green'); //green
        this.addSlice(0x4485C3,'Blue'); //blue
        this.addSlice(0xF8F66D,'Yellow'); //yellow
        this.addSlice(0x9D499B,'Purple'); //purple
        this.addSlice(0xF99F44,'Orange'); //orange
    }
    this.x = 150;
    this.y = 150;
    this.draw();
}

public function addSlice(color:Number,text:String):void {
    var slice:Array = ["slice"+slices.length,color,text];
    slices.push(slice);
}

public function draw():void {
    var angle:Number=((100 / slices.length)*360)/100;

    for(var i:int=0;i< slices.length;i++){
        this._drawSlice(_radius,_startAngle,slices[i][1],1,angle,slices[i][2] , i);                         
        _startAngle-=angle;
    }
}

private function _drawSlice(radius:Number,angle:Number,color:Number,alpha:Number,arc:Number,txt:String , j:int):void {
    var sprite:Sprite = new Sprite();

    sprite.graphics.beginFill(color,alpha);

    //setup the variables
    var segAngle:Number, theta:Number, angle:Number, angleMid:Number, segs:Number, ax:Number, ay:Number, bx:Number, by:Number, cx:Number, cy:Number;

    //start at point 0,0
    sprite.graphics.moveTo(0, 0);
    //get the number of segments
    segs = Math.ceil(Math.abs(arc)/45);
    // Now calculate the sweep of each segment.
    segAngle = arc/segs;
    // The math requires radians rather than degrees. To convert from degrees
    // use the formula (degrees/180)*Math.PI to get radians.
    theta = -(segAngle/180)*Math.PI;
    // convert angle _startAngle to radians
    angle = -(_startAngle/180)*Math.PI;
    // draw the curve in segments no larger than 45 degrees.
    if (segs>0) {
        // draw a line from the center to the start of the curve
        ax = Math.cos(_startAngle/180*Math.PI)*radius;
        ay = Math.sin(-_startAngle/180*Math.PI)*radius;
        sprite.graphics.lineTo(ax, ay);

        // Loop for drawing curve segments
        for (var i:int = 0; i<segs; i++) {
            angle += theta;
            angleMid = angle-(theta/2);
            bx = Math.cos(angle)*radius;
            by = Math.sin(angle)*radius;
            cx = Math.cos(angleMid)*(radius/Math.cos(theta/2));
            cy = Math.sin(angleMid)*(radius/Math.cos(theta/2));
            sprite.graphics.curveTo(cx, cy, bx, by);
        }

        // close the wedge by drawing a line to the center
        sprite.graphics.lineTo(0, 0);
    }

    var txtFormat:TextFormat = new TextFormat("PCArial");
    txtFormat.color = 0xFFFFFF;
    txtFormat.size = 16;

    var txtField:TextField = new TextField;
    txtField.text = txt;
    txtField.x = 30;
    txtField.y = -18;
    txtField.rotation = -18 ;
    txtField.embedFonts = true;
    txtField.setTextFormat(txtFormat);

    var txtSprite:Sprite = new Sprite();

    txtSprite.rotation = (360/slices.length * j);
    txtSprite.addChild(txtField);

    sprite.addChild(txtSprite);
    this.addChild(sprite);
}

private function _setup(radius:Number):void {
    this._radius = radius;
}
   }

}
PatrickS
I dont want to use embedded fonts because they bloat the program from what I was reading. It works fine and the text is displaying and rotating. The problem I am having is that I cant seem to get the angles right with the text. The colors are fine but the text isnt in the right spot. Any help with that?
ngreenwood6
Some fonts are not that big, maybe 30kb-50kb ,but then again it depends on what you need to achieve. Properly or not in the right spot is too vague for me to give you an answer, can you narrow it down? Is it possible to see what you have so far?
PatrickS
The text isnt being displayed in the correct spot. It should be within the color area at an angle like the colors. If you create a new flash file and then link a class to it (name it PieChart) you can copy and paste the code from my post into that class overriding what is there. It will show you exactly what I am working with.
ngreenwood6
The code should work as is in your DocumentClass, just make sure to add a fonts folder with Arial.ttf when you first test it ;)
PatrickS
Thanks that worked pretty good. However, if there are less slices then the calculation seems to be off, do you know of a way to calculate the x and y based on the number of elements or something similar. I noticed in your description up above you said something about creating slices and adding them that way. Can you give me an example of what you are talking about? By the way thanks for all the help so far.
ngreenwood6
If you're able to draw one slice, you should be able to create a self contained Slice object. I don't have a code example it's just the approach I would take. At the simplest , a Slice object would be a Sprite that takes a color argument and some size parameters dependent on the number of slices you wish to create. In that Slice object you could then position your TextField, so the TextField rotation and x, y coordinates wouldn't be dependent on the Slice position within the PieChart. With the above example , you have to tweak the TextField coordinates if you change the slices number.
PatrickS
That is how it currently is. If you look at the function _drawSlice it creates a new Sprite object. It then adds the txtSprite that you added to it to the sprite. It then adds that sprite to the main movieClip which is how the class is based. However, the calculations do not appear to be relative to the sprite it is added to and rather the movieclip.
ngreenwood6
PatrickS
Thanks for all of your help so far. However, I am confused by this last comment. Any way you can give me an example of what you mean becuase I thought I was already adding the textfield to the sprite and rotating it relative to it.
ngreenwood6
Sorry, you're right... For some reason I got confused about it all. I'm not so great at trigonometry so I can't pull a ready made solution for you, but if I had to deal with this. I would try to calculate the coordinates for a median axis , a line that would split the slice in two halves and add my textfield accordingly. Probably would make this line a Sprite which I'd use to add the TextField
PatrickS