first commit
This commit is contained in:
770
com/yahoo/astra/fl/charts/series/PieSeries.as
Executable file
770
com/yahoo/astra/fl/charts/series/PieSeries.as
Executable file
@ -0,0 +1,770 @@
|
||||
/*
|
||||
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
|
||||
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
|
||||
*/
|
||||
package com.yahoo.astra.fl.charts.series
|
||||
{
|
||||
import com.yahoo.astra.animation.Animation;
|
||||
import com.yahoo.astra.animation.AnimationEvent;
|
||||
import com.yahoo.astra.fl.charts.PieChart;
|
||||
import com.yahoo.astra.fl.charts.legend.LegendItemData;
|
||||
import com.yahoo.astra.fl.charts.skins.RectangleSkin;
|
||||
import com.yahoo.astra.utils.GeomUtil;
|
||||
import com.yahoo.astra.utils.GraphicsUtil;
|
||||
import com.yahoo.astra.utils.NumberUtil;
|
||||
|
||||
import fl.core.InvalidationType;
|
||||
import fl.core.UIComponent;
|
||||
|
||||
import flash.display.Shape;
|
||||
import flash.display.Sprite;
|
||||
import flash.events.Event;
|
||||
import flash.geom.Point;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFieldAutoSize;
|
||||
import flash.text.TextFormat;
|
||||
import flash.text.TextFormatAlign;
|
||||
|
||||
//--------------------------------------
|
||||
// Styles
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* The colors of the markers in this series.
|
||||
*
|
||||
* @default [0x729fcf, 0xfcaf3e, 0x73d216, 0xfce94f, 0xad7fa8, 0x3465a4]
|
||||
*/
|
||||
[Style(name="fillColors", type="Array")]
|
||||
|
||||
/**
|
||||
* If true, a label is displayed on each marker. The label text is created
|
||||
* with the labelFunction property of the series. The default label function
|
||||
* sets the label to the percentage value of the item.
|
||||
*
|
||||
* @default false
|
||||
* @see PieSeries#labelFunction
|
||||
*/
|
||||
[Style(name="showLabels", type="Boolean")]
|
||||
|
||||
/**
|
||||
* If true, marker labels that overlap previously-created labels will be
|
||||
* hidden to improve readability.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
[Style(name="hideOverlappingLabels", type="Boolean")]
|
||||
|
||||
/**
|
||||
* Renders data points as a series of pie-like wedges.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public class PieSeries extends Series implements ICategorySeries
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Class Variables
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private static var defaultStyles:Object =
|
||||
{
|
||||
fillColors:
|
||||
[
|
||||
0x00b8bf, 0x8dd5e7, 0xc0fff6, 0xffa928, 0xedff9f, 0xd00050,
|
||||
0xc6c6c6, 0xc3eafb, 0xfcffad, 0xcfff83, 0x444444, 0x4d95dd,
|
||||
0xb8ebff, 0x60558f, 0x737d7e, 0xa64d9a, 0x8e9a9b, 0x803e77
|
||||
],
|
||||
borderColors:
|
||||
[
|
||||
0x00b8bf, 0x8dd5e7, 0xc0fff6, 0xffa928, 0xedff9f, 0xd00050,
|
||||
0xc6c6c6, 0xc3eafb, 0xfcffad, 0xcfff83, 0x444444, 0x4d95dd,
|
||||
0xb8ebff, 0x60558f, 0x737d7e, 0xa64d9a, 0x8e9a9b, 0x803e77
|
||||
],
|
||||
fillAlphas: [1.0],
|
||||
borderAlphas: [0.0],
|
||||
markerSkins: [RectangleSkin],
|
||||
showLabels: false,
|
||||
hideOverlappingLabels: true
|
||||
//see textFormat default style defined in constructor below
|
||||
//works around stylemanager global style bug!
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private static const RENDERER_STYLES:Object =
|
||||
{
|
||||
fillColor: "fillColors",
|
||||
fillAlpha: "fillAlphas",
|
||||
borderColor: "borderColors",
|
||||
borderAlpha: "borderAlphas",
|
||||
skin: "markerSkins"
|
||||
};
|
||||
|
||||
//--------------------------------------
|
||||
// Class Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @copy fl.core.UIComponent#getStyleDefinition()
|
||||
*/
|
||||
public static function getStyleDefinition():Object
|
||||
{
|
||||
return mergeStyles(defaultStyles, Series.getStyleDefinition());
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function PieSeries(data:Object = null)
|
||||
{
|
||||
super(data);
|
||||
//we have to set this as an instance style because textFormat is
|
||||
//defined as a global style in StyleManager, and that takes
|
||||
//precedence over shared/class styles
|
||||
this.setStyle("textFormat", new TextFormat("_sans", 11, 0x000000, true, false, false, "", "", TextFormatAlign.LEFT, 0, 0, 0, 0));
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The text fields used to display labels over each marker.
|
||||
*/
|
||||
protected var labels:Array = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Holds the labels created by the previous redraw so that they can
|
||||
* be reused.
|
||||
*/
|
||||
protected var labelsCache:Array;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the masks that define the shapes of the markers.
|
||||
*/
|
||||
protected var markerMasks:Array = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The Animation instance that controls animation in this series.
|
||||
*/
|
||||
private var _animation:Animation;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _previousData:Array = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the dataField property.
|
||||
*/
|
||||
private var _dataField:String;
|
||||
|
||||
/**
|
||||
* The field used to access data for this series.
|
||||
*/
|
||||
public function get dataField():String
|
||||
{
|
||||
return this._dataField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set dataField(value:String):void
|
||||
{
|
||||
if(this._dataField != value)
|
||||
{
|
||||
this._dataField = value;
|
||||
this.dispatchEvent(new Event("dataChange"));
|
||||
this.invalidate(InvalidationType.DATA);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the categoryField property.
|
||||
*/
|
||||
private var _categoryField:String;
|
||||
|
||||
/**
|
||||
* @copy com.yahoo.astra.fl.charts.series.ICategorySeries#categoryField
|
||||
*/
|
||||
public function get categoryField():String
|
||||
{
|
||||
return this._categoryField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set categoryField(value:String):void
|
||||
{
|
||||
if(this._categoryField != value)
|
||||
{
|
||||
this._categoryField = value;
|
||||
this.dispatchEvent(new Event("dataChange"));
|
||||
this.invalidate(InvalidationType.DATA);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the categoryNames property.
|
||||
*/
|
||||
private var _categoryNames:Array;
|
||||
|
||||
/**
|
||||
* @copy com.yahoo.astra.fl.charts.series.ICategorySeries#categoryNames
|
||||
*/
|
||||
public function get categoryNames():Array
|
||||
{
|
||||
return this._categoryNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set categoryNames(value:Array):void
|
||||
{
|
||||
this._categoryNames = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the labelFunction property.
|
||||
*/
|
||||
private var _labelFunction:Function = defaultLabelFunction;
|
||||
|
||||
/**
|
||||
* A function may be set to determine the text value of the labels.
|
||||
*
|
||||
* <pre>function labelFunction(item:Object):String</pre>
|
||||
*/
|
||||
public function get labelFunction():Function
|
||||
{
|
||||
return this._labelFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set labelFunction(value:Function):void
|
||||
{
|
||||
this._labelFunction = value;
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Public Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
override public function clone():ISeries
|
||||
{
|
||||
var series:PieSeries = new PieSeries();
|
||||
if(this.dataProvider is Array)
|
||||
{
|
||||
//copy the array rather than pass it by reference
|
||||
series.dataProvider = (this.dataProvider as Array).concat();
|
||||
}
|
||||
else if(this.dataProvider is XMLList)
|
||||
{
|
||||
series.dataProvider = (this.dataProvider as XMLList).copy();
|
||||
}
|
||||
series.displayName = this.displayName;
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an item to it's value.
|
||||
*/
|
||||
public function itemToData(item:Object):Number
|
||||
{
|
||||
var primaryDataField:String = PieChart(this.chart).seriesToDataField(this);
|
||||
if(primaryDataField)
|
||||
{
|
||||
return Number(item[primaryDataField]);
|
||||
}
|
||||
return Number(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an item to the category in which it is displayed.
|
||||
*/
|
||||
public function itemToCategory(item:Object, index:int):String
|
||||
{
|
||||
var primaryCategoryField:String = PieChart(this.chart).seriesToCategoryField(this);
|
||||
if(primaryCategoryField)
|
||||
{
|
||||
return item[primaryCategoryField];
|
||||
}
|
||||
|
||||
if(this._categoryNames && index >= 0 && index < this._categoryNames.length)
|
||||
{
|
||||
return this._categoryNames[index];
|
||||
}
|
||||
return index.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an item's value to its percentage equivilent.
|
||||
*/
|
||||
public function itemToPercentage(item:Object):Number
|
||||
{
|
||||
var totalValue:Number = this.calculateTotalValue();
|
||||
if(totalValue == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 100 * (this.itemToData(item) / totalValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function createLegendItemData():Array
|
||||
{
|
||||
var items:Array = [];
|
||||
var markerSkins:Array = this.getStyleValue("markerSkins") as Array;
|
||||
var fillColors:Array = this.getStyleValue("fillColors") as Array;
|
||||
var legendItemCount:int = this.length;
|
||||
for(var i:int = 0; i < legendItemCount; i++)
|
||||
{
|
||||
var item:Object = this.dataProvider[i];
|
||||
var categoryName:String = this.itemToCategory(item, i);
|
||||
var markerSkin:Object = markerSkins[i % markerSkins.length]
|
||||
var fillColor:uint = fillColors[i % fillColors.length];
|
||||
var data:LegendItemData = new LegendItemData(categoryName, markerSkin, fillColor, 1, fillColor, 1);
|
||||
items.push(data);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Protected Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override protected function draw():void
|
||||
{
|
||||
var stylesInvalid:Boolean = this.isInvalid(InvalidationType.STYLES);
|
||||
var sizeInvalid:Boolean = this.isInvalid(InvalidationType.SIZE);
|
||||
var dataInvalid:Boolean = this.isInvalid(InvalidationType.DATA);
|
||||
|
||||
super.draw();
|
||||
|
||||
this.drawMarkers(stylesInvalid, sizeInvalid);
|
||||
|
||||
var showLabels:Boolean = this.getStyleValue("showLabels") as Boolean;
|
||||
this.createLabelCache();
|
||||
if(showLabels)
|
||||
{
|
||||
this.drawLabels();
|
||||
}
|
||||
this.clearLabelCache();
|
||||
|
||||
this.beginAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* All markers are removed from the display list.
|
||||
*/
|
||||
override protected function removeAllMarkers():void
|
||||
{
|
||||
super.removeAllMarkers();
|
||||
var markerCount:int = this.markerMasks.length;
|
||||
for(var i:int = 0; i < markerCount; i++)
|
||||
{
|
||||
var mask:Shape = this.markerMasks.pop() as Shape;
|
||||
this.removeChild(mask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Add or remove markers as needed. current markers will be reused.
|
||||
*/
|
||||
override protected function refreshMarkers():void
|
||||
{
|
||||
super.refreshMarkers();
|
||||
|
||||
var itemCount:int = this.length;
|
||||
var difference:int = itemCount - this.markerMasks.length;
|
||||
if(difference > 0)
|
||||
{
|
||||
for(var i:int = 0; i < difference; i++)
|
||||
{
|
||||
var mask:Shape = new Shape();
|
||||
this.addChild(mask);
|
||||
this.markerMasks.push(mask);
|
||||
|
||||
var marker:Sprite = this.markers[i + (itemCount-difference)] as Sprite;
|
||||
marker.mask = mask;
|
||||
marker.width = this.width;
|
||||
marker.height = this.height;
|
||||
}
|
||||
}
|
||||
else if(difference < 0)
|
||||
{
|
||||
difference = Math.abs(difference);
|
||||
for(i = 0; i < difference; i++)
|
||||
{
|
||||
mask = this.markerMasks.pop() as Shape;
|
||||
this.removeChild(mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The default function called to initialize the text on the marker
|
||||
* labels.
|
||||
*/
|
||||
protected function defaultLabelFunction(item:Object):String
|
||||
{
|
||||
var percentage:Number = this.itemToPercentage(item);
|
||||
return (percentage < 0.01 ? "< 0.01" : NumberUtil.roundToNearest(percentage, 0.01)) + "%";
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Draws the markers in this series.
|
||||
*/
|
||||
protected function drawMarkers(stylesInvalid:Boolean, sizeInvalid:Boolean):void
|
||||
{
|
||||
var markerCount:int = this.markers.length;
|
||||
for(var i:int = 0; i < markerCount; i++)
|
||||
{
|
||||
var marker:UIComponent = UIComponent(this.markers[i]);
|
||||
|
||||
if(stylesInvalid)
|
||||
{
|
||||
this.copyStylesToRenderer(ISeriesItemRenderer(marker), RENDERER_STYLES);
|
||||
}
|
||||
|
||||
if(sizeInvalid)
|
||||
{
|
||||
marker.width = this.width;
|
||||
marker.height = this.height;
|
||||
}
|
||||
//not really required, but we should validate anyway.
|
||||
this.validateMarker(ISeriesItemRenderer(marker));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Either creates a new label TextField or retrieves one from the
|
||||
* cache.
|
||||
*/
|
||||
protected function getLabel():TextField
|
||||
{
|
||||
var label:TextField;
|
||||
if(this.labelsCache.length > 0)
|
||||
{
|
||||
label = TextField(this.labelsCache.shift());
|
||||
}
|
||||
else
|
||||
{
|
||||
label = new TextField();
|
||||
label.autoSize = TextFieldAutoSize.LEFT;
|
||||
label.selectable = false;
|
||||
label.mouseEnabled = false;
|
||||
this.addChild(label);
|
||||
}
|
||||
label.visible = true;
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Updates the label text and positions the labels.
|
||||
*/
|
||||
protected function drawLabels():void
|
||||
{
|
||||
var textFormat:TextFormat = this.getStyleValue("textFormat") as TextFormat;
|
||||
var embedFonts:Boolean = this.getStyleValue("embedFonts") as Boolean;
|
||||
var hideOverlappingLabels:Boolean = this.getStyleValue("hideOverlappingLabels") as Boolean;
|
||||
|
||||
var angle:Number = 0;
|
||||
var valueSum:Number = 0;
|
||||
var totalValue:Number = this.calculateTotalValue();
|
||||
var markerCount:int = this.markers.length;
|
||||
for(var i:int = 0; i < markerCount; i++)
|
||||
{
|
||||
var label:TextField = this.getLabel();
|
||||
this.labels.push(label);
|
||||
label.defaultTextFormat = textFormat;
|
||||
label.embedFonts = embedFonts;
|
||||
label.text = this.labelFunction(this.dataProvider[i]);
|
||||
|
||||
var value:Number = this.itemToData(this.dataProvider[i]);
|
||||
if(totalValue == 0)
|
||||
{
|
||||
angle = 360 / this.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
angle = 360 * ((valueSum + value / 2) / totalValue);
|
||||
}
|
||||
valueSum += value;
|
||||
var halfWidth:Number = this.width / 2;
|
||||
var halfHeight:Number = this.height / 2;
|
||||
var radius:Number = Math.min(halfWidth, halfHeight);
|
||||
var position:Point = Point.polar(2 * radius / 3, -GeomUtil.degreesToRadians(angle));
|
||||
label.x = halfWidth + position.x - label.width / 2;
|
||||
label.y = halfHeight + position.y - label.height / 2;
|
||||
|
||||
if(hideOverlappingLabels)
|
||||
{
|
||||
for(var j:int = 0; j < i; j++)
|
||||
{
|
||||
var previousLabel:TextField = TextField(this.labels[j]);
|
||||
if(previousLabel.hitTestObject(label))
|
||||
{
|
||||
label.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a styles from the series to a child through a style map.
|
||||
*
|
||||
* @see copyStylesToChild()
|
||||
*/
|
||||
protected function copyStylesToRenderer(child:ISeriesItemRenderer, styleMap:Object):void
|
||||
{
|
||||
var index:int = this.markers.indexOf(child);
|
||||
var childComponent:UIComponent = child as UIComponent;
|
||||
for(var n:String in styleMap)
|
||||
{
|
||||
var styleValues:Array = this.getStyleValue(styleMap[n]) as Array;
|
||||
//if it doesn't exist, ignore it and go with the defaults for this series
|
||||
if(!styleValues) continue;
|
||||
childComponent.setStyle(n, styleValues[index % styleValues.length])
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Private Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Sets up the animation for the markers.
|
||||
*/
|
||||
private function beginAnimation():void
|
||||
{
|
||||
var itemCount:int = this.length;
|
||||
if(!this._previousData || this._previousData.length != this.length)
|
||||
{
|
||||
this._previousData = [];
|
||||
for(var i:int = 0; i < itemCount; i++)
|
||||
{
|
||||
this._previousData.push(0);
|
||||
}
|
||||
}
|
||||
|
||||
//handle animating all the markers in one fell swoop.
|
||||
if(this._animation)
|
||||
{
|
||||
if(this._animation.active)
|
||||
{
|
||||
this._animation.pause();
|
||||
}
|
||||
this._animation.removeEventListener(AnimationEvent.UPDATE, tweenUpdateHandler);
|
||||
this._animation.removeEventListener(AnimationEvent.PAUSE, tweenPauseHandler);
|
||||
this._animation.removeEventListener(AnimationEvent.COMPLETE, tweenCompleteHandler);
|
||||
this._animation = null;
|
||||
}
|
||||
|
||||
var data:Array = this.dataProviderToArrayOfNumbers();
|
||||
|
||||
//don't animate on livepreview!
|
||||
if(this.isLivePreview || !this.getStyleValue("animationEnabled"))
|
||||
{
|
||||
this.renderMarkerMasks(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
var animationDuration:int = this.getStyleValue("animationDuration") as int;
|
||||
var animationEasingFunction:Function = this.getStyleValue("animationEasingFunction") as Function;
|
||||
|
||||
this._animation = new Animation(animationDuration, this._previousData, data);
|
||||
this._animation.addEventListener(AnimationEvent.UPDATE, tweenUpdateHandler);
|
||||
this._animation.addEventListener(AnimationEvent.PAUSE, tweenPauseHandler);
|
||||
this._animation.addEventListener(AnimationEvent.COMPLETE, tweenCompleteHandler);
|
||||
this._animation.easingFunction = animationEasingFunction;
|
||||
this.renderMarkerMasks(this._previousData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Determines the total sum of all values in the data provider.
|
||||
*/
|
||||
private function calculateTotalValue():Number
|
||||
{
|
||||
var totalValue:Number = 0;
|
||||
var itemCount:int = this.length;
|
||||
for(var i:int = 0; i < itemCount; i++)
|
||||
{
|
||||
var currentItem:Object = this.dataProvider[i];
|
||||
var value:Number = this.itemToData(currentItem);
|
||||
|
||||
if(!isNaN(value))
|
||||
{
|
||||
totalValue += value;
|
||||
}
|
||||
}
|
||||
return totalValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Retreives all the numeric values from the data provider
|
||||
* and places them into an Array so that they may be used
|
||||
* in an animation.
|
||||
*/
|
||||
private function dataProviderToArrayOfNumbers():Array
|
||||
{
|
||||
var output:Array = [];
|
||||
|
||||
var itemCount:int = this.length;
|
||||
for(var i:int = 0; i < itemCount; i++)
|
||||
{
|
||||
var item:Object = this.dataProvider[i];
|
||||
var value:Number = 0;
|
||||
if(item != null)
|
||||
{
|
||||
value = this.itemToData(item);
|
||||
}
|
||||
output.push(value);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function tweenUpdateHandler(event:AnimationEvent):void
|
||||
{
|
||||
this.renderMarkerMasks(event.parameters as Array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function tweenCompleteHandler(event:AnimationEvent):void
|
||||
{
|
||||
this.tweenUpdateHandler(event);
|
||||
this.tweenPauseHandler(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function tweenPauseHandler(event:AnimationEvent):void
|
||||
{
|
||||
this._previousData = (event.parameters as Array).concat();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private function renderMarkerMasks(data:Array):void
|
||||
{
|
||||
var values:Array = [];
|
||||
var totalValue:Number = 0;
|
||||
var itemCount:int = data.length;
|
||||
for(var i:int = 0; i < itemCount; i++)
|
||||
{
|
||||
var value:Number = Number(data[i]);
|
||||
|
||||
values.push(value);
|
||||
if(!isNaN(value))
|
||||
{
|
||||
totalValue += value;
|
||||
}
|
||||
}
|
||||
|
||||
var totalAngle:Number = 0;
|
||||
var halfWidth:Number = this.width / 2;
|
||||
var halfHeight:Number = this.height / 2;
|
||||
var radius:Number = Math.min(halfWidth, halfHeight);
|
||||
var fillColors:Array = this.getStyleValue("fillColors") as Array;
|
||||
|
||||
var angle:Number = 0;
|
||||
for(i = 0; i < itemCount; i++)
|
||||
{
|
||||
value = Number(data[i]);
|
||||
if(totalValue == 0)
|
||||
{
|
||||
angle = 360 / data.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
angle = 360 * (value / totalValue);
|
||||
}
|
||||
|
||||
var mask:Shape = this.markerMasks[i] as Shape;
|
||||
mask.graphics.clear();
|
||||
mask.graphics.beginFill(0xff0000);
|
||||
GraphicsUtil.drawWedge(mask.graphics, halfWidth, halfHeight, totalAngle, angle, radius);
|
||||
mask.graphics.endFill();
|
||||
totalAngle += angle;
|
||||
|
||||
var marker:UIComponent = UIComponent(this.markers[i]);
|
||||
marker.drawNow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Places all the existing labels in a cache so that they may be reused
|
||||
* when we redraw the series.
|
||||
*/
|
||||
private function createLabelCache():void
|
||||
{
|
||||
this.labelsCache = this.labels.concat();
|
||||
this.labels = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* If any labels are left in the cache after we've redrawn, they can be
|
||||
* removed from the display list.
|
||||
*/
|
||||
private function clearLabelCache():void
|
||||
{
|
||||
var cacheLength:int = this.labelsCache.length;
|
||||
for(var i:int = 0; i < cacheLength; i++)
|
||||
{
|
||||
var label:TextField = TextField(this.labelsCache.shift());
|
||||
this.removeChild(label);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user