[7849] | 1 | package charts.series.pies {
|
---|
| 2 |
|
---|
| 3 | import charts.series.Element;
|
---|
| 4 | import flash.events.Event;
|
---|
| 5 | import caurina.transitions.Tweener;
|
---|
| 6 | import caurina.transitions.Equations;
|
---|
| 7 | import flash.geom.Point;
|
---|
| 8 | //import flash.events.Event;
|
---|
| 9 | import flash.events.MouseEvent;
|
---|
| 10 |
|
---|
| 11 | public class PieSliceContainer extends Element {
|
---|
| 12 |
|
---|
| 13 | private var TO_RADIANS:Number = Math.PI / 180;
|
---|
| 14 |
|
---|
| 15 | private var animating:Boolean;
|
---|
| 16 | private var pieSlice:PieSlice;
|
---|
| 17 | private var pieLabel:PieLabel;
|
---|
| 18 | private var pieRadius:Number;
|
---|
| 19 | private var tick_size:Number = 10;
|
---|
| 20 | private var tick_extension_size:Number = 4;
|
---|
| 21 | private var label_margin:Number = 2;
|
---|
| 22 | private var animationOffset:Number = 30;
|
---|
| 23 |
|
---|
| 24 | private var saveX:Number;
|
---|
| 25 | private var saveY:Number;
|
---|
| 26 | private var moveToX:Number;
|
---|
| 27 | private var moveToY:Number;
|
---|
| 28 |
|
---|
| 29 | private var original_alpha:Number;
|
---|
| 30 |
|
---|
| 31 |
|
---|
| 32 |
|
---|
| 33 | //
|
---|
| 34 | // this holds the slice and the text.
|
---|
| 35 | // we want to rotate the slice, but not the text, so
|
---|
| 36 | // this container holds both
|
---|
| 37 | //
|
---|
| 38 | public function PieSliceContainer( index:Number, value:Properties )
|
---|
| 39 | {
|
---|
| 40 | //
|
---|
| 41 | // replace magic in the label:
|
---|
| 42 | //
|
---|
| 43 | // value.set('label', this.replace_magic_values( value.get('label') ) );
|
---|
| 44 |
|
---|
| 45 |
|
---|
| 46 | tr.aces( 'pie', value.get('animate') );
|
---|
| 47 |
|
---|
| 48 | this.pieSlice = new PieSlice( index, value );
|
---|
| 49 | this.addChild( this.pieSlice );
|
---|
| 50 | var textlabel:String = value.get('label');
|
---|
| 51 |
|
---|
| 52 | //
|
---|
| 53 | // we set the alpha of the parent container
|
---|
| 54 | //
|
---|
| 55 | this.alpha = this.original_alpha = value.get('alpha');
|
---|
| 56 | //
|
---|
| 57 | if ( !value.has('label-colour') )
|
---|
| 58 | value.set('label-colour', value.get('colour'));
|
---|
| 59 |
|
---|
| 60 | var l:String = value.get('no-labels') ? '' : value.get('label');
|
---|
| 61 |
|
---|
| 62 | this.pieLabel = new PieLabel(
|
---|
| 63 | {
|
---|
| 64 | label: l,
|
---|
| 65 | colour: value.get('label-colour'),
|
---|
| 66 | 'font-size': value.get('font-size'),
|
---|
| 67 | 'on-click': value.get('on-click') } )
|
---|
| 68 | this.addChild( this.pieLabel );
|
---|
| 69 |
|
---|
| 70 | this.attach_events__(value);
|
---|
| 71 | this.animating = false;
|
---|
| 72 | }
|
---|
| 73 |
|
---|
| 74 | public function is_over():Boolean {
|
---|
| 75 | return this.pieSlice.is_over;
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | public function get_slice():Element {
|
---|
| 79 | return this.pieSlice;
|
---|
| 80 | }
|
---|
| 81 |
|
---|
| 82 | public function get_label():PieLabel {
|
---|
| 83 | return this.pieLabel;
|
---|
| 84 | }
|
---|
| 85 |
|
---|
| 86 |
|
---|
| 87 | //
|
---|
| 88 | // the axis makes no sense here, let's override with null and write our own.
|
---|
| 89 | //
|
---|
| 90 | public override function resize( sc:ScreenCoordsBase ): void {}
|
---|
| 91 |
|
---|
| 92 | public function is_label_on_screen( sc:ScreenCoordsBase, slice_radius:Number ): Boolean {
|
---|
| 93 |
|
---|
| 94 | return this.pieLabel.move_label(
|
---|
| 95 | slice_radius + 10,
|
---|
| 96 | sc.get_center_x(),
|
---|
| 97 | sc.get_center_y(),
|
---|
| 98 | this.pieSlice.angle+(this.pieSlice.slice_angle/2) );
|
---|
| 99 | }
|
---|
| 100 |
|
---|
| 101 | public function pie_resize( sc:ScreenCoordsBase, slice_radius:Number ): void {
|
---|
| 102 |
|
---|
| 103 | this.pieRadius = slice_radius; // save off value for later use
|
---|
| 104 | this.pieSlice.pie_resize(sc, slice_radius);
|
---|
| 105 |
|
---|
| 106 | var ticAngle:Number = this.getTicAngle();
|
---|
| 107 |
|
---|
| 108 | this.saveX = this.x;
|
---|
| 109 | this.saveY = this.y;
|
---|
| 110 | this.moveToX = this.x + (animationOffset * Math.cos(ticAngle * TO_RADIANS));
|
---|
| 111 | this.moveToY = this.y + (animationOffset * Math.sin(ticAngle * TO_RADIANS));
|
---|
| 112 |
|
---|
| 113 | if (this.pieLabel.visible)
|
---|
| 114 | {
|
---|
| 115 | var lblRadius:Number = slice_radius + this.tick_size;
|
---|
| 116 | var lblAngle:Number = ticAngle * TO_RADIANS;
|
---|
| 117 |
|
---|
| 118 | this.pieLabel.x = this.pieSlice.x + lblRadius * Math.cos(lblAngle);
|
---|
| 119 | this.pieLabel.y = this.pieSlice.y + lblRadius * Math.sin(lblAngle);
|
---|
| 120 |
|
---|
| 121 | if (this.isRightSide())
|
---|
| 122 | {
|
---|
| 123 | this.pieLabel.x += this.tick_extension_size + this.label_margin;
|
---|
| 124 | }
|
---|
| 125 | else
|
---|
| 126 | {
|
---|
| 127 | //if legend stands to the left side of the pie
|
---|
| 128 | this.pieLabel.x =
|
---|
| 129 | this.pieLabel.x -
|
---|
| 130 | this.pieLabel.width -
|
---|
| 131 | this.tick_extension_size -
|
---|
| 132 | this.label_margin -
|
---|
| 133 | 4;
|
---|
| 134 | }
|
---|
| 135 | this.pieLabel.y -= this.pieLabel.height / 2;
|
---|
| 136 |
|
---|
| 137 | this.drawTicLines();
|
---|
| 138 | }
|
---|
| 139 | }
|
---|
| 140 |
|
---|
| 141 | public override function get_tooltip():String {
|
---|
| 142 | return this.pieSlice.get_tooltip();
|
---|
| 143 | }
|
---|
| 144 |
|
---|
| 145 | public override function get_tip_pos():Object {
|
---|
| 146 | var p:flash.geom.Point = this.localToGlobal( new flash.geom.Point(this.mouseX, this.mouseY) );
|
---|
| 147 | return {x:p.x,y:p.y};
|
---|
| 148 | }
|
---|
| 149 |
|
---|
| 150 | //
|
---|
| 151 | // override this. I think this needs to be moved into an
|
---|
| 152 | // animation manager?
|
---|
| 153 | //
|
---|
| 154 | // BTW this is called attach_events__ because Element has an
|
---|
| 155 | // attach_events already. I guess we need to fix one of them
|
---|
| 156 | //
|
---|
| 157 | protected function attach_events__(value:Properties):void {
|
---|
| 158 |
|
---|
| 159 | //
|
---|
| 160 | // TODO: either move this into properties
|
---|
| 161 | // props.as(Array).get('moo');
|
---|
| 162 | // or get rid of type checking
|
---|
| 163 | //
|
---|
| 164 |
|
---|
| 165 | var animate:Object = value.get('animate');
|
---|
| 166 | if (!(animate is Array)) {
|
---|
| 167 | if ((animate == null) || (animate)) {
|
---|
| 168 | animate = [{"type":"bounce","distance":5}];
|
---|
| 169 | }
|
---|
| 170 | else {
|
---|
| 171 | animate = new Array();
|
---|
| 172 | }
|
---|
| 173 | }
|
---|
| 174 |
|
---|
| 175 | var anims:Array = animate as Array;
|
---|
| 176 | //
|
---|
| 177 | // end to do
|
---|
| 178 | //
|
---|
| 179 |
|
---|
| 180 | this.addEventListener(MouseEvent.MOUSE_OVER, this.mouseOver_first, false, 0, true);
|
---|
| 181 | this.addEventListener(MouseEvent.MOUSE_OUT, this.mouseOut_first, false, 0, true);
|
---|
| 182 |
|
---|
| 183 | for each( var a:Object in anims ) {
|
---|
| 184 | switch( a.type ) {
|
---|
| 185 |
|
---|
| 186 | case "bounce":
|
---|
| 187 | // weak references so the garbage collector will kill them:
|
---|
| 188 | this.addEventListener(MouseEvent.MOUSE_OVER, this.mouseOver_bounce_out, false, 0, true);
|
---|
| 189 | this.addEventListener(MouseEvent.MOUSE_OUT, this.mouseOut_bounce_out, false, 0, true);
|
---|
| 190 | this.animationOffset = a.distance;
|
---|
| 191 | break;
|
---|
| 192 |
|
---|
| 193 | default:
|
---|
| 194 | // weak references so the garbage collector will kill them:
|
---|
| 195 | this.addEventListener(MouseEvent.MOUSE_OVER, this.mouseOver_alpha, false, 0, true);
|
---|
| 196 | this.addEventListener(MouseEvent.MOUSE_OUT, this.mouseOut_alpha, false, 0, true);
|
---|
| 197 | break;
|
---|
| 198 | }
|
---|
| 199 | }
|
---|
| 200 | }
|
---|
| 201 |
|
---|
| 202 | //
|
---|
| 203 | // stop multiple tweens from running
|
---|
| 204 | //
|
---|
| 205 | public function mouseOver_first(event:Event):void {
|
---|
| 206 |
|
---|
| 207 | if ( this.animating ) return;
|
---|
| 208 |
|
---|
| 209 | this.animating = true;
|
---|
| 210 | Tweener.removeTweens(this);
|
---|
| 211 | }
|
---|
| 212 |
|
---|
| 213 | public function mouseOut_first(event:Event):void {
|
---|
| 214 | Tweener.removeTweens(this);
|
---|
| 215 | this.animating = false;
|
---|
| 216 | }
|
---|
| 217 |
|
---|
| 218 | public function mouseOver_bounce_out(event:Event):void {
|
---|
| 219 | Tweener.addTween(this, {x:this.moveToX, y:this.moveToY, time:0.4, transition:"easeOutBounce"} );
|
---|
| 220 | }
|
---|
| 221 |
|
---|
| 222 | public function mouseOut_bounce_out(event:Event):void {
|
---|
| 223 | Tweener.addTween(this, {x:this.saveX, y:this.saveY, time:0.4, transition:"easeOutBounce"} );
|
---|
| 224 | }
|
---|
| 225 |
|
---|
| 226 | public function mouseOver_alpha(event:Event):void {
|
---|
| 227 | Tweener.addTween(this, { alpha:1, time:0.6, transition:Equations.easeOutCirc } );
|
---|
| 228 | }
|
---|
| 229 |
|
---|
| 230 | public function mouseOut_alpha(event:Event):void {
|
---|
| 231 | Tweener.addTween(this, { alpha:this.original_alpha, time:0.8, transition:Equations.easeOutElastic } );
|
---|
| 232 | }
|
---|
| 233 |
|
---|
| 234 | public function getLabelTopY():Number
|
---|
| 235 | {
|
---|
| 236 | return this.pieLabel.y;
|
---|
| 237 | }
|
---|
| 238 |
|
---|
| 239 | public function getLabelBottomY():Number
|
---|
| 240 | {
|
---|
| 241 | return this.pieLabel.y + this.pieLabel.height;
|
---|
| 242 | }
|
---|
| 243 |
|
---|
| 244 | // Y value is from 0 to sc.Height from top to bottom
|
---|
| 245 | public function moveLabelDown( sc:ScreenCoordsBase, minY:Number ):Number
|
---|
| 246 | {
|
---|
| 247 | if (this.pieLabel.visible)
|
---|
| 248 | {
|
---|
| 249 | var bAdjustToBottom:Boolean = false;
|
---|
| 250 | var lblTop:Number = this.getLabelTopY();
|
---|
| 251 |
|
---|
| 252 | if (lblTop < minY)
|
---|
| 253 | {
|
---|
| 254 | // adjustment is positive
|
---|
| 255 | var adjust:Number = minY - lblTop;
|
---|
| 256 | if ((this.pieLabel.height + minY) > (sc.bottom - 1))
|
---|
| 257 | {
|
---|
| 258 | // calc adjust so label bottom is at bottom of screen
|
---|
| 259 | adjust = sc.bottom - this.pieLabel.height - lblTop;
|
---|
| 260 | bAdjustToBottom = true;
|
---|
| 261 | }
|
---|
| 262 | // Adjust the Y value
|
---|
| 263 | this.pieLabel.y += adjust;
|
---|
| 264 |
|
---|
| 265 | if (!bAdjustToBottom)
|
---|
| 266 | {
|
---|
| 267 | var lblRadius:Number = this.pieRadius + this.tick_size;
|
---|
| 268 | var calcSin:Number = ((this.pieLabel.y + this.pieLabel.height / 2) - this.pieSlice.y) / lblRadius;
|
---|
| 269 | calcSin = Math.max( -1, Math.min(1, calcSin));
|
---|
| 270 | var newAngle:Number = Math.asin(calcSin) / TO_RADIANS;
|
---|
| 271 |
|
---|
| 272 | if ((this.getTicAngle() > 90) && (this.getTicAngle() < 270))
|
---|
| 273 | {
|
---|
| 274 | newAngle = 180 - newAngle;
|
---|
| 275 | }
|
---|
| 276 | else if (this.getTicAngle() >= 270)
|
---|
| 277 | {
|
---|
| 278 | newAngle = 360 + newAngle;
|
---|
| 279 | }
|
---|
| 280 |
|
---|
| 281 | var newX:Number = this.pieSlice.x + lblRadius * Math.cos(newAngle * TO_RADIANS);
|
---|
| 282 | if (this.isRightSide())
|
---|
| 283 | {
|
---|
| 284 | this.pieLabel.x = newX + this.tick_extension_size + this.label_margin;
|
---|
| 285 | }
|
---|
| 286 | else
|
---|
| 287 | {
|
---|
| 288 | //if legend stands to the left side of the pie
|
---|
| 289 | this.pieLabel.x = newX - this.pieLabel.width -
|
---|
| 290 | this.tick_extension_size - this.label_margin - 4;
|
---|
| 291 | }
|
---|
| 292 | }
|
---|
| 293 | }
|
---|
| 294 | this.drawTicLines();
|
---|
| 295 |
|
---|
| 296 | return this.pieLabel.y + this.pieLabel.height;
|
---|
| 297 | }
|
---|
| 298 | else
|
---|
| 299 | {
|
---|
| 300 | return minY;
|
---|
| 301 | }
|
---|
| 302 | }
|
---|
| 303 |
|
---|
| 304 | // Y value is from 0 to sc.Height from top to bottom
|
---|
| 305 | public function moveLabelUp( sc:ScreenCoordsBase, maxY:Number ):Number
|
---|
| 306 | {
|
---|
| 307 | if (this.pieLabel.visible)
|
---|
| 308 | {
|
---|
| 309 | var sign:Number = 1;
|
---|
| 310 | var bAdjustToTop:Boolean = false;
|
---|
| 311 | var lblBottom:Number = this.getLabelBottomY();
|
---|
| 312 | if (lblBottom > maxY)
|
---|
| 313 | {
|
---|
| 314 | // adjustment is negative here
|
---|
| 315 | var adjust:Number = maxY - lblBottom;
|
---|
| 316 | if ((maxY - this.pieLabel.height) < (sc.top + 1))
|
---|
| 317 | {
|
---|
| 318 | // calc adjust so label top is at top of screen
|
---|
| 319 | adjust = sc.top - this.getLabelTopY();
|
---|
| 320 | bAdjustToTop = true;
|
---|
| 321 | }
|
---|
| 322 | // Adjust the Y value
|
---|
| 323 | this.pieLabel.y += adjust;
|
---|
| 324 |
|
---|
| 325 | if (!bAdjustToTop)
|
---|
| 326 | {
|
---|
| 327 | var lblRadius:Number = this.pieRadius + this.tick_size;
|
---|
| 328 | var calcSin:Number = ((this.pieLabel.y + this.pieLabel.height / 2) - this.pieSlice.y) / lblRadius;
|
---|
| 329 | calcSin = Math.max( -1, Math.min(1, calcSin));
|
---|
| 330 | var newAngle:Number = Math.asin(calcSin) / TO_RADIANS;
|
---|
| 331 |
|
---|
| 332 | if ((this.getTicAngle() > 90) && (this.getTicAngle() < 270))
|
---|
| 333 | {
|
---|
| 334 | newAngle = 180 - newAngle;
|
---|
| 335 | sign = -1;
|
---|
| 336 | }
|
---|
| 337 | else if (this.getTicAngle() >= 270)
|
---|
| 338 | {
|
---|
| 339 | newAngle = 360 + newAngle;
|
---|
| 340 | }
|
---|
| 341 |
|
---|
| 342 | var newX:Number = this.pieSlice.x + lblRadius * Math.cos(newAngle * TO_RADIANS);
|
---|
| 343 | if (this.isRightSide())
|
---|
| 344 | {
|
---|
| 345 | this.pieLabel.x = newX + this.tick_extension_size + this.label_margin;
|
---|
| 346 | }
|
---|
| 347 | else
|
---|
| 348 | {
|
---|
| 349 | //if legend stands to the left side of the pie
|
---|
| 350 | this.pieLabel.x = newX - this.pieLabel.width -
|
---|
| 351 | this.tick_extension_size - this.label_margin - 4;
|
---|
| 352 | }
|
---|
| 353 | }
|
---|
| 354 | }
|
---|
| 355 | this.drawTicLines();
|
---|
| 356 |
|
---|
| 357 | return this.pieLabel.y;
|
---|
| 358 | }
|
---|
| 359 | else
|
---|
| 360 | {
|
---|
| 361 | return maxY;
|
---|
| 362 | }
|
---|
| 363 | }
|
---|
| 364 |
|
---|
| 365 | public function get_radius_offsets() :Object {
|
---|
| 366 | // Update the label text here in case pie slices change dynamically
|
---|
| 367 | //var lblText:String = this.getText();
|
---|
| 368 | //this.myPieLabel.setText(lblText);
|
---|
| 369 |
|
---|
| 370 | var offset:Object = { top:animationOffset, right:animationOffset,
|
---|
| 371 | bottom:animationOffset, left:animationOffset };
|
---|
| 372 | if (this.pieLabel.visible)
|
---|
| 373 | {
|
---|
| 374 | var ticAngle:Number = this.getTicAngle();
|
---|
| 375 | var offset_threshold:Number = 20;
|
---|
| 376 | var ticLength:Number = this.tick_size;
|
---|
| 377 |
|
---|
| 378 | if ((ticAngle >= 0) && (ticAngle <= 90))
|
---|
| 379 | {
|
---|
| 380 | offset.bottom = (ticAngle / 90) * ticLength + this.pieLabel.height / 2 + 1;
|
---|
| 381 | offset.right = ((90 - ticAngle) / 90) * ticLength + this.tick_extension_size + this.label_margin + this.pieLabel.width;
|
---|
| 382 | }
|
---|
| 383 | else if ((ticAngle > 90) && (ticAngle <= 180))
|
---|
| 384 | {
|
---|
| 385 | offset.bottom = ((180 - ticAngle) / 90) * ticLength + this.pieLabel.height / 2 + 1;
|
---|
| 386 | offset.left = ((ticAngle - 90) / 90) * ticLength + this.tick_extension_size + this.label_margin + this.pieLabel.width + 4;
|
---|
| 387 | }
|
---|
| 388 | else if ((ticAngle > 180) && (ticAngle < 270))
|
---|
| 389 | {
|
---|
| 390 | offset.top = ((ticAngle - 180) / 90) * ticLength + this.pieLabel.height / 2 + 1;
|
---|
| 391 | offset.left = ((270 - ticAngle) / 90) * ticLength + this.tick_extension_size + this.label_margin + this.pieLabel.width + 4;
|
---|
| 392 | }
|
---|
| 393 | else // if ((ticAngle >= 270) && (ticAngle <= 360))
|
---|
| 394 | {
|
---|
| 395 | offset.top = ((360 - ticAngle) / 90) * ticLength + this.pieLabel.height / 2 + 1;
|
---|
| 396 | offset.right = ((ticAngle - 270) / 90) * ticLength + this.tick_extension_size + this.label_margin + this.pieLabel.width;
|
---|
| 397 | }
|
---|
| 398 | }
|
---|
| 399 | return offset;
|
---|
| 400 | }
|
---|
| 401 | protected function drawTicLines():void
|
---|
| 402 | {
|
---|
| 403 | if ((this.pieLabel.text != '') && (this.pieLabel.visible))
|
---|
| 404 | {
|
---|
| 405 | var ticAngle:Number = this.getTicAngle();
|
---|
| 406 |
|
---|
| 407 | var lblRadius:Number = this.pieRadius + this.tick_size;
|
---|
| 408 | var lblAngle:Number = ticAngle * TO_RADIANS;
|
---|
| 409 |
|
---|
| 410 | var ticLblX:Number;
|
---|
| 411 | var ticLblY:Number;
|
---|
| 412 | if (this.pieSlice.isRightSide())
|
---|
| 413 | {
|
---|
| 414 | ticLblX = this.pieLabel.x - this.label_margin;
|
---|
| 415 | }
|
---|
| 416 | else
|
---|
| 417 | {
|
---|
| 418 | //if legend stands to the left side of the pie
|
---|
| 419 | ticLblX = this.pieLabel.x + this.pieLabel.width + this.label_margin + 4;
|
---|
| 420 | }
|
---|
| 421 | ticLblY = this.pieLabel.y + this.pieLabel.height / 2;
|
---|
| 422 |
|
---|
| 423 | var ticArcX:Number = this.pieSlice.x + this.pieRadius * Math.cos(lblAngle);
|
---|
| 424 | var ticArcY:Number = this.pieSlice.y + this.pieRadius * Math.sin(lblAngle);
|
---|
| 425 |
|
---|
| 426 | // Draw the line from the slice to the label
|
---|
| 427 | this.graphics.clear();
|
---|
| 428 | this.graphics.lineStyle( 1, this.pieSlice.get_colour(), 1 );
|
---|
| 429 |
|
---|
| 430 | // move to the end of the tic closest to the label
|
---|
| 431 | this.graphics.moveTo(ticLblX, ticLblY);
|
---|
| 432 | // draw a line the length of the tic extender
|
---|
| 433 | if (this.pieSlice.isRightSide())
|
---|
| 434 | {
|
---|
| 435 | this.graphics.lineTo(ticLblX - this.tick_extension_size, ticLblY);
|
---|
| 436 | }
|
---|
| 437 | else
|
---|
| 438 | {
|
---|
| 439 | this.graphics.lineTo(ticLblX + this.tick_extension_size, ticLblY);
|
---|
| 440 | }
|
---|
| 441 | // Draw a line from the end of the tic extender to the arc
|
---|
| 442 | this.graphics.lineTo(ticArcX, ticArcY);
|
---|
| 443 | }
|
---|
| 444 | }
|
---|
| 445 |
|
---|
| 446 | public function getTicAngle():Number
|
---|
| 447 | {
|
---|
| 448 | return this.pieSlice.getTicAngle();
|
---|
| 449 | }
|
---|
| 450 |
|
---|
| 451 | public function isRightSide():Boolean
|
---|
| 452 | {
|
---|
| 453 | return this.pieSlice.isRightSide();
|
---|
| 454 | }
|
---|
| 455 | }
|
---|
| 456 | }
|
---|