| 1 | package charts {
|
|---|
| 2 | import charts.series.pies.PieLabel;
|
|---|
| 3 | import flash.external.ExternalInterface;
|
|---|
| 4 | import string.Utils;
|
|---|
| 5 | import charts.series.Element;
|
|---|
| 6 | import charts.series.pies.PieSliceContainer;
|
|---|
| 7 | import charts.series.pies.DefaultPieProperties;
|
|---|
| 8 | import global.Global;
|
|---|
| 9 |
|
|---|
| 10 | import flash.display.Sprite;
|
|---|
| 11 |
|
|---|
| 12 | public class Pie extends Base
|
|---|
| 13 | {
|
|---|
| 14 |
|
|---|
| 15 | private var labels:Array;
|
|---|
| 16 | private var links:Array;
|
|---|
| 17 | private var colours:Array;
|
|---|
| 18 | private var gradientFill:String = 'true'; //toggle gradients
|
|---|
| 19 | private var border_width:Number = 1;
|
|---|
| 20 | private var label_line:Number;
|
|---|
| 21 | private var easing:Function;
|
|---|
| 22 | public var style:Object;
|
|---|
| 23 | public var total_value:Number = 0;
|
|---|
| 24 |
|
|---|
| 25 | // new:
|
|---|
| 26 | private var props:Properties;
|
|---|
| 27 | //
|
|---|
| 28 |
|
|---|
| 29 | public function Pie( json:Object )
|
|---|
| 30 | {
|
|---|
| 31 | this.labels = new Array();
|
|---|
| 32 | this.links = new Array();
|
|---|
| 33 | this.colours = new Array();
|
|---|
| 34 |
|
|---|
| 35 | this.style = {
|
|---|
| 36 | colours: ["#900000", "#009000"] // slices colours
|
|---|
| 37 | }
|
|---|
| 38 |
|
|---|
| 39 | object_helper.merge_2( json, this.style );
|
|---|
| 40 |
|
|---|
| 41 | for each( var colour:String in this.style.colours )
|
|---|
| 42 | this.colours.push( string.Utils.get_colour( colour ) );
|
|---|
| 43 |
|
|---|
| 44 | //
|
|---|
| 45 | //
|
|---|
| 46 | //
|
|---|
| 47 | this.props = new DefaultPieProperties(json);
|
|---|
| 48 | //
|
|---|
| 49 | //
|
|---|
| 50 | //
|
|---|
| 51 |
|
|---|
| 52 | this.label_line = 10;
|
|---|
| 53 |
|
|---|
| 54 | this.values = json.values;
|
|---|
| 55 | this.add_values();
|
|---|
| 56 | }
|
|---|
| 57 |
|
|---|
| 58 |
|
|---|
| 59 | //
|
|---|
| 60 | // Pie chart make is quite different to a normal make
|
|---|
| 61 | //
|
|---|
| 62 | public override function add_values():void {
|
|---|
| 63 | // this.Elements= new Array();
|
|---|
| 64 |
|
|---|
| 65 | //
|
|---|
| 66 | // Warning: this is our global singleton
|
|---|
| 67 | //
|
|---|
| 68 | var g:Global = Global.getInstance();
|
|---|
| 69 |
|
|---|
| 70 | var total:Number = 0;
|
|---|
| 71 | var slice_start:Number = this.props.get('start-angle');
|
|---|
| 72 | var i:Number;
|
|---|
| 73 | var val:Object;
|
|---|
| 74 |
|
|---|
| 75 | for each ( val in this.values ) {
|
|---|
| 76 | if( val is Number )
|
|---|
| 77 | total += val;
|
|---|
| 78 | else
|
|---|
| 79 | total += val.value;
|
|---|
| 80 | }
|
|---|
| 81 | this.total_value = total;
|
|---|
| 82 |
|
|---|
| 83 | i = 0;
|
|---|
| 84 | for each ( val in this.values ) {
|
|---|
| 85 |
|
|---|
| 86 | var value:Number = val is Number ? val as Number : val.value;
|
|---|
| 87 | var slice_angle:Number = value*360/total;
|
|---|
| 88 |
|
|---|
| 89 | if( slice_angle >= 0 )
|
|---|
| 90 | {
|
|---|
| 91 |
|
|---|
| 92 | var t:String = this.props.get('tip').replace('#total#', NumberUtils.formatNumber( this.total_value ));
|
|---|
| 93 | t = t.replace('#percent#', NumberUtils.formatNumber( value / this.total_value * 100 ) + '%');
|
|---|
| 94 |
|
|---|
| 95 | this.addChild(
|
|---|
| 96 | this.add_slice(
|
|---|
| 97 | i,
|
|---|
| 98 | slice_start,
|
|---|
| 99 | slice_angle,
|
|---|
| 100 | val, // <-- NOTE: val (object) NOT value (a number)
|
|---|
| 101 | t,
|
|---|
| 102 | this.colours[(i % this.colours.length)]
|
|---|
| 103 | )
|
|---|
| 104 | );
|
|---|
| 105 |
|
|---|
| 106 | // TODO: fix this and remove
|
|---|
| 107 | // tmp.make_tooltip( this.key );
|
|---|
| 108 | }
|
|---|
| 109 | i++;
|
|---|
| 110 | slice_start += slice_angle;
|
|---|
| 111 | }
|
|---|
| 112 | }
|
|---|
| 113 |
|
|---|
| 114 | private function add_slice( index:Number, start:Number, angle:Number, value:Object, tip:String, colour:String ): PieSliceContainer {
|
|---|
| 115 |
|
|---|
| 116 |
|
|---|
| 117 | // Properties chain:
|
|---|
| 118 | // pie-slice -> calculated-stuff -> pie
|
|---|
| 119 | //
|
|---|
| 120 | // calculated-stuff:
|
|---|
| 121 | var calculated_stuff:Properties = new Properties(
|
|---|
| 122 | {
|
|---|
| 123 | colour: colour, // <-- from the colour cycle array
|
|---|
| 124 | tip: tip, // <-- replaced the #total# & #percent# for this slice
|
|---|
| 125 | start: start, // <-- calculated
|
|---|
| 126 | angle: angle // <-- calculated
|
|---|
| 127 | },
|
|---|
| 128 | this.props );
|
|---|
| 129 |
|
|---|
| 130 | var tmp:Object = {};
|
|---|
| 131 | if ( value is Number )
|
|---|
| 132 | tmp.value = value;
|
|---|
| 133 | else
|
|---|
| 134 | tmp = value;
|
|---|
| 135 |
|
|---|
| 136 | var p:Properties = new Properties( tmp, calculated_stuff );
|
|---|
| 137 |
|
|---|
| 138 | // no user defined label?
|
|---|
| 139 | if ( !p.has('label') )
|
|---|
| 140 | p.set('label', p.get('value').toString());
|
|---|
| 141 |
|
|---|
| 142 | // tr.aces( 'value', p.get('value'), p.get('label'), p.get('colour') );
|
|---|
| 143 | return new PieSliceContainer( index, p );
|
|---|
| 144 | }
|
|---|
| 145 |
|
|---|
| 146 |
|
|---|
| 147 | public override function closest( x:Number, y:Number ): Object {
|
|---|
| 148 | // PIE charts don't do closest to mouse tooltips
|
|---|
| 149 | return { Element:null, distance_x:0, distance_y:0 };
|
|---|
| 150 | }
|
|---|
| 151 |
|
|---|
| 152 |
|
|---|
| 153 | public override function resize( sc:ScreenCoordsBase ): void {
|
|---|
| 154 | var radius:Number = this.style.radius;
|
|---|
| 155 | if (isNaN(radius)){
|
|---|
| 156 | radius = ( Math.min( sc.width, sc.height ) / 2.0 );
|
|---|
| 157 | var offsets:Object = {top:0, right:0, bottom:0, left:0};
|
|---|
| 158 | trace("sc.width, sc.height, radius", sc.width, sc.height, radius);
|
|---|
| 159 |
|
|---|
| 160 | var i:Number;
|
|---|
| 161 | var sliceContainer:PieSliceContainer;
|
|---|
| 162 |
|
|---|
| 163 | // loop to gather and merge offsets
|
|---|
| 164 | for ( i = 0; i < this.numChildren; i++ ) {
|
|---|
| 165 | sliceContainer = this.getChildAt(i) as PieSliceContainer;
|
|---|
| 166 | var pie_offsets:Object = sliceContainer.get_radius_offsets();
|
|---|
| 167 | for (var key:Object in offsets) {
|
|---|
| 168 | if ( pie_offsets[key] > offsets[key] ) {
|
|---|
| 169 | offsets[key] = pie_offsets[key];
|
|---|
| 170 | }
|
|---|
| 171 | }
|
|---|
| 172 | }
|
|---|
| 173 | var vRadius:Number = radius;
|
|---|
| 174 | // Calculate minimum radius assuming the contraint is vertical
|
|---|
| 175 | // Shrink radius by the largest top/bottom offset
|
|---|
| 176 | vRadius -= Math.max(offsets.top, offsets.bottom);
|
|---|
| 177 | // check to see if the left/right labels will fit
|
|---|
| 178 | if ((vRadius + offsets.left) > (sc.width / 2))
|
|---|
| 179 | {
|
|---|
| 180 | //radius -= radius + offsets.left - (sc.width / 2);
|
|---|
| 181 | vRadius = (sc.width / 2) - offsets.left;
|
|---|
| 182 | }
|
|---|
| 183 | if ((vRadius + offsets.right) > (sc.width / 2))
|
|---|
| 184 | {
|
|---|
| 185 | //radius -= radius + offsets.right - (sc.width / 2);
|
|---|
| 186 | vRadius = (sc.width / 2) - offsets.right;
|
|---|
| 187 | }
|
|---|
| 188 |
|
|---|
| 189 | // Make sure the radius is at least 10
|
|---|
| 190 | radius = Math.max(vRadius, 10);
|
|---|
| 191 | }
|
|---|
| 192 |
|
|---|
| 193 | var rightTopTicAngle:Number = 720;
|
|---|
| 194 | var rightTopTicIdx:Number = -1;
|
|---|
| 195 | var rightBottomTicAngle:Number = -720;
|
|---|
| 196 | var rightBottomTicIdx:Number = -1;
|
|---|
| 197 |
|
|---|
| 198 | var leftTopTicAngle:Number = 720;
|
|---|
| 199 | var leftTopTicIdx:Number = -1;
|
|---|
| 200 | var leftBottomTicAngle:Number = -720;
|
|---|
| 201 | var leftBottomTicIdx:Number = -1;
|
|---|
| 202 |
|
|---|
| 203 | // loop and resize
|
|---|
| 204 | for ( i = 0; i < this.numChildren; i++ )
|
|---|
| 205 | {
|
|---|
| 206 | sliceContainer = this.getChildAt(i) as PieSliceContainer;
|
|---|
| 207 | sliceContainer.pie_resize(sc, radius);
|
|---|
| 208 |
|
|---|
| 209 | // While we are looping through the children, we determine which
|
|---|
| 210 | // labels are the starting points in each quadrant so that we
|
|---|
| 211 | // move the labels around to prevent overlaps
|
|---|
| 212 | var ticAngle:Number = sliceContainer.getTicAngle();
|
|---|
| 213 | if (ticAngle >= 270)
|
|---|
| 214 | {
|
|---|
| 215 | // Right side - Top
|
|---|
| 216 | if ((ticAngle < rightTopTicAngle) || (rightTopTicAngle <= 90))
|
|---|
| 217 | {
|
|---|
| 218 | rightTopTicAngle = ticAngle;
|
|---|
| 219 | rightTopTicIdx = i;
|
|---|
| 220 | }
|
|---|
| 221 | // Just in case no tics in Right-Bottom
|
|---|
| 222 | if ((rightBottomTicAngle < 0) ||
|
|---|
| 223 | ((rightBottomTicAngle > 90) && (rightBottomTicAngle < ticAngle)))
|
|---|
| 224 | {
|
|---|
| 225 | rightBottomTicAngle = ticAngle;
|
|---|
| 226 | rightBottomTicIdx = i;
|
|---|
| 227 | }
|
|---|
| 228 | }
|
|---|
| 229 | else if (ticAngle <= 90)
|
|---|
| 230 | {
|
|---|
| 231 | // Right side - Bottom
|
|---|
| 232 | if ((ticAngle > rightBottomTicAngle) || (rightBottomTicAngle > 90))
|
|---|
| 233 | {
|
|---|
| 234 | rightBottomTicAngle = ticAngle;
|
|---|
| 235 | rightBottomTicIdx = i;
|
|---|
| 236 | }
|
|---|
| 237 | // Just in case no tics in Right-Top
|
|---|
| 238 | if ((rightTopTicAngle > 360) ||
|
|---|
| 239 | ((rightTopTicAngle <= 90) && (ticAngle < rightBottomTicAngle)))
|
|---|
| 240 | {
|
|---|
| 241 | rightTopTicAngle = ticAngle;
|
|---|
| 242 | rightTopTicIdx = i;
|
|---|
| 243 | }
|
|---|
| 244 | }
|
|---|
| 245 | else if (ticAngle <= 180)
|
|---|
| 246 | {
|
|---|
| 247 | // Left side - Bottom
|
|---|
| 248 | if ((leftBottomTicAngle < 0) || (ticAngle < leftBottomTicAngle))
|
|---|
| 249 | {
|
|---|
| 250 | leftBottomTicAngle = ticAngle;
|
|---|
| 251 | leftBottomTicIdx = i;
|
|---|
| 252 | }
|
|---|
| 253 | // Just in case no tics in Left-Top
|
|---|
| 254 | if ((leftTopTicAngle > 360) || (leftTopTicAngle < ticAngle))
|
|---|
| 255 | {
|
|---|
| 256 | leftTopTicAngle = ticAngle;
|
|---|
| 257 | leftTopTicIdx = i;
|
|---|
| 258 | }
|
|---|
| 259 | }
|
|---|
| 260 | else
|
|---|
| 261 | {
|
|---|
| 262 | // Left side - Top
|
|---|
| 263 | if ((leftTopTicAngle > 360) || (ticAngle > leftTopTicAngle))
|
|---|
| 264 | {
|
|---|
| 265 | leftTopTicAngle = ticAngle;
|
|---|
| 266 | leftTopTicIdx = i;
|
|---|
| 267 | }
|
|---|
| 268 | // Just in case no tics in Left-Bottom
|
|---|
| 269 | if ((leftBottomTicAngle < 0) || (leftBottomTicAngle > ticAngle))
|
|---|
| 270 | {
|
|---|
| 271 | leftBottomTicAngle = ticAngle;
|
|---|
| 272 | leftBottomTicIdx = i;
|
|---|
| 273 | }
|
|---|
| 274 | }
|
|---|
| 275 | }
|
|---|
| 276 |
|
|---|
| 277 | // Make a clockwise pass on right side of pie trying to move
|
|---|
| 278 | // the labels so that they do not overlap
|
|---|
| 279 | var childIdx:Number = rightTopTicIdx;
|
|---|
| 280 | var yVal:Number = sc.top;
|
|---|
| 281 | var bDone:Boolean = false;
|
|---|
| 282 | while ((childIdx >= 0) && (!bDone))
|
|---|
| 283 | {
|
|---|
| 284 | sliceContainer = this.getChildAt(childIdx) as PieSliceContainer;
|
|---|
| 285 | ticAngle = sliceContainer.getTicAngle();
|
|---|
| 286 | if ((ticAngle >= 270) || (ticAngle <= 90))
|
|---|
| 287 | {
|
|---|
| 288 | yVal = sliceContainer.moveLabelDown(sc, yVal);
|
|---|
| 289 |
|
|---|
| 290 | childIdx++;
|
|---|
| 291 | if (childIdx >= this.numChildren) childIdx = 0;
|
|---|
| 292 |
|
|---|
| 293 | bDone = (childIdx == rightTopTicIdx);
|
|---|
| 294 | }
|
|---|
| 295 | else
|
|---|
| 296 | {
|
|---|
| 297 | bDone = true;
|
|---|
| 298 | }
|
|---|
| 299 | }
|
|---|
| 300 |
|
|---|
| 301 | // Make a counter-clockwise pass on right side of pie trying to move
|
|---|
| 302 | // the labels so that they do not overlap
|
|---|
| 303 | childIdx = rightBottomTicIdx;
|
|---|
| 304 | yVal = sc.bottom;
|
|---|
| 305 | bDone = false;
|
|---|
| 306 | while ((childIdx >= 0) && (!bDone))
|
|---|
| 307 | {
|
|---|
| 308 | sliceContainer = this.getChildAt(childIdx) as PieSliceContainer;
|
|---|
| 309 | ticAngle = sliceContainer.getTicAngle();
|
|---|
| 310 | if ((ticAngle >= 270) || (ticAngle <= 90))
|
|---|
| 311 | {
|
|---|
| 312 | yVal = sliceContainer.moveLabelUp(sc, yVal);
|
|---|
| 313 |
|
|---|
| 314 | childIdx--;
|
|---|
| 315 | if (childIdx < 0) childIdx = this.numChildren - 1;
|
|---|
| 316 |
|
|---|
| 317 | bDone = (childIdx == rightBottomTicIdx);
|
|---|
| 318 | }
|
|---|
| 319 | else
|
|---|
| 320 | {
|
|---|
| 321 | bDone = true;
|
|---|
| 322 | }
|
|---|
| 323 | }
|
|---|
| 324 |
|
|---|
| 325 | // Make a clockwise pass on left side of pie trying to move
|
|---|
| 326 | // the labels so that they do not overlap
|
|---|
| 327 | childIdx = leftBottomTicIdx;
|
|---|
| 328 | yVal = sc.bottom;
|
|---|
| 329 | bDone = false;
|
|---|
| 330 | while ((childIdx >= 0) && (!bDone))
|
|---|
| 331 | {
|
|---|
| 332 | sliceContainer = this.getChildAt(childIdx) as PieSliceContainer;
|
|---|
| 333 | ticAngle = sliceContainer.getTicAngle();
|
|---|
| 334 | if ((ticAngle > 90) && (ticAngle < 270))
|
|---|
| 335 | {
|
|---|
| 336 | yVal = sliceContainer.moveLabelUp(sc, yVal);
|
|---|
| 337 |
|
|---|
| 338 | childIdx++;
|
|---|
| 339 | if (childIdx >= this.numChildren) childIdx = 0;
|
|---|
| 340 |
|
|---|
| 341 | bDone = (childIdx == leftBottomTicIdx);
|
|---|
| 342 | }
|
|---|
| 343 | else
|
|---|
| 344 | {
|
|---|
| 345 | bDone = true;
|
|---|
| 346 | }
|
|---|
| 347 | }
|
|---|
| 348 |
|
|---|
| 349 | // Make a counter-clockwise pass on left side of pie trying to move
|
|---|
| 350 | // the labels so that they do not overlap
|
|---|
| 351 | childIdx = leftTopTicIdx;
|
|---|
| 352 | yVal = sc.top;
|
|---|
| 353 | bDone = false;
|
|---|
| 354 | while ((childIdx >= 0) && (!bDone))
|
|---|
| 355 | {
|
|---|
| 356 | sliceContainer = this.getChildAt(childIdx) as PieSliceContainer;
|
|---|
| 357 | ticAngle = sliceContainer.getTicAngle();
|
|---|
| 358 | if ((ticAngle > 90) && (ticAngle < 270))
|
|---|
| 359 | {
|
|---|
| 360 | yVal = sliceContainer.moveLabelDown(sc, yVal);
|
|---|
| 361 |
|
|---|
| 362 | childIdx--;
|
|---|
| 363 | if (childIdx < 0) childIdx = this.numChildren - 1;
|
|---|
| 364 |
|
|---|
| 365 | bDone = (childIdx == leftTopTicIdx);
|
|---|
| 366 | }
|
|---|
| 367 | else
|
|---|
| 368 | {
|
|---|
| 369 | bDone = true;
|
|---|
| 370 | }
|
|---|
| 371 | }
|
|---|
| 372 | }
|
|---|
| 373 |
|
|---|
| 374 |
|
|---|
| 375 | public override function toString():String {
|
|---|
| 376 | return "Pie with "+ this.numChildren +" children";
|
|---|
| 377 | }
|
|---|
| 378 | }
|
|---|
| 379 | }
|
|---|