source: code/Website/open-flash-chart/charts/series/pies/PieSliceContainer.as@ 7849

Last change on this file since 7849 was 7849, checked in by dennisw, 15 years ago
File size: 13.6 KB
Line 
1package 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}
Note: See TracBrowser for help on using the repository browser.