342 lines
10 KiB
ActionScript
Executable File
342 lines
10 KiB
ActionScript
Executable File
/*
|
|
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.*;
|
|
import com.yahoo.astra.fl.charts.axes.IAxis;
|
|
import com.yahoo.astra.fl.charts.axes.IClusteringAxis;
|
|
import com.yahoo.astra.fl.charts.axes.IOriginAxis;
|
|
import com.yahoo.astra.fl.charts.skins.RectangleSkin;
|
|
import com.yahoo.astra.fl.utils.UIComponentUtil;
|
|
|
|
import fl.core.UIComponent;
|
|
|
|
import flash.display.DisplayObject;
|
|
import flash.geom.Point;
|
|
import flash.utils.Dictionary;
|
|
|
|
/**
|
|
* Renders data points as a series of vertical columns.
|
|
*
|
|
* @author Josh Tynjala
|
|
*/
|
|
public class ColumnSeries extends CartesianSeries
|
|
{
|
|
|
|
//--------------------------------------
|
|
// Static Variables
|
|
//--------------------------------------
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
private static var defaultStyles:Object =
|
|
{
|
|
markerSkin: RectangleSkin,
|
|
markerSize: 18
|
|
};
|
|
|
|
//--------------------------------------
|
|
// Static Methods
|
|
//--------------------------------------
|
|
|
|
/**
|
|
* @copy fl.core.UIComponent#getStyleDefinition()
|
|
*/
|
|
public static function getStyleDefinition():Object
|
|
{
|
|
return mergeStyles(defaultStyles, Series.getStyleDefinition());
|
|
}
|
|
|
|
//--------------------------------------
|
|
// Constructor
|
|
//--------------------------------------
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public function ColumnSeries(data:Object = null)
|
|
{
|
|
super(data);
|
|
}
|
|
|
|
//--------------------------------------
|
|
// Properties
|
|
//--------------------------------------
|
|
|
|
/**
|
|
* @private
|
|
* The Animation instance that controls animation in this series.
|
|
*/
|
|
private var _animation:Animation;
|
|
|
|
//--------------------------------------
|
|
// Public Methods
|
|
//--------------------------------------
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
override public function clone():ISeries
|
|
{
|
|
var series:ColumnSeries = new ColumnSeries();
|
|
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;
|
|
series.horizontalField = this.horizontalField;
|
|
series.verticalField = this.verticalField;
|
|
|
|
return series;
|
|
}
|
|
|
|
//--------------------------------------
|
|
// Protected Methods
|
|
//--------------------------------------
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
override protected function draw():void
|
|
{
|
|
super.draw();
|
|
|
|
//if we don't have data, let's get out of here
|
|
if(!this.dataProvider)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//grab the axes
|
|
var cartesianChart:CartesianChart = this.chart as CartesianChart;
|
|
var valueAxis:IOriginAxis = cartesianChart.verticalAxis as IOriginAxis;
|
|
var otherAxis:IAxis = cartesianChart.horizontalAxis;
|
|
if(!valueAxis)
|
|
{
|
|
throw new Error("To use a ColumnSeries object, the vertical axis of the chart it appears within must be an IOriginAxis.");
|
|
return;
|
|
}
|
|
|
|
var allSeriesOfType:Array = ChartUtil.findSeriesOfType(this, cartesianChart);
|
|
var markerSizes:Array = [];
|
|
var totalMarkerSize:Number = this.calculateTotalMarkerSize(otherAxis, markerSizes);
|
|
var seriesIndex:int = allSeriesOfType.indexOf(this);
|
|
var markerSize:Number = markerSizes[seriesIndex] as Number;
|
|
var xOffset:Number = this.calculateXOffset(valueAxis, otherAxis, markerSizes, totalMarkerSize, allSeriesOfType);
|
|
|
|
var startValues:Array = [];
|
|
var endValues:Array = [];
|
|
var itemCount:int = this.length;
|
|
for(var i:int = 0; i < itemCount; i++)
|
|
{
|
|
var originValue:Object = this.calculateOriginValue(i, valueAxis, allSeriesOfType);
|
|
var originPosition:Number = valueAxis.valueToLocal(originValue);
|
|
|
|
var position:Point = IChart(this.chart).itemToPosition(this, i);
|
|
var marker:DisplayObject = this.markers[i] as DisplayObject;
|
|
|
|
marker.x = position.x + xOffset;
|
|
|
|
marker.width = markerSize;
|
|
|
|
//if we have a bad position, don't display the marker
|
|
if(isNaN(position.x) || isNaN(position.y))
|
|
{
|
|
this.invalidateMarker(ISeriesItemRenderer(marker));
|
|
}
|
|
else if(this.isMarkerInvalid(ISeriesItemRenderer(marker)))
|
|
{
|
|
//initialize the marker to the origin
|
|
marker.y = originPosition;
|
|
marker.height = 0;
|
|
|
|
if(marker is UIComponent)
|
|
{
|
|
(marker as UIComponent).drawNow();
|
|
}
|
|
this.validateMarker(ISeriesItemRenderer(marker));
|
|
}
|
|
|
|
//stupid Flash UIComponent rounding!
|
|
position.y = Math.round(position.y);
|
|
originPosition = Math.round(originPosition);
|
|
|
|
var calculatedHeight:Number = originPosition - position.y;
|
|
if(calculatedHeight < 0)
|
|
{
|
|
calculatedHeight = Math.abs(calculatedHeight);
|
|
position.y = Math.round(originPosition);
|
|
//always put the marker on the origin
|
|
marker.y = position.y;
|
|
}
|
|
|
|
startValues.push(marker.y, marker.height);
|
|
endValues.push(position.y, calculatedHeight);
|
|
}
|
|
|
|
//handle animating all the markers in one fell swoop.
|
|
if(this._animation)
|
|
{
|
|
this._animation.removeEventListener(AnimationEvent.UPDATE, tweenUpdateHandler);
|
|
this._animation.removeEventListener(AnimationEvent.COMPLETE, tweenUpdateHandler);
|
|
this._animation = null;
|
|
}
|
|
|
|
//don't animate on livepreview!
|
|
if(this.isLivePreview || !this.getStyleValue("animationEnabled"))
|
|
{
|
|
this.drawMarkers(endValues);
|
|
}
|
|
else
|
|
{
|
|
var animationDuration:int = this.getStyleValue("animationDuration") as int;
|
|
var animationEasingFunction:Function = this.getStyleValue("animationEasingFunction") as Function;
|
|
|
|
this._animation = new Animation(animationDuration, startValues, endValues);
|
|
this._animation.addEventListener(AnimationEvent.UPDATE, tweenUpdateHandler);
|
|
this._animation.addEventListener(AnimationEvent.COMPLETE, tweenUpdateHandler);
|
|
this._animation.easingFunction = animationEasingFunction;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Calculates the x offset caused by clustering.
|
|
*/
|
|
protected function calculateXOffset(valueAxis:IOriginAxis, otherAxis:IAxis, markerSizes:Array, totalMarkerSize:Number, allSeriesOfType:Array):Number
|
|
{
|
|
var seriesIndex:int = allSeriesOfType.indexOf(this);
|
|
|
|
//special case for axes that allow clustering
|
|
if(otherAxis is IClusteringAxis)
|
|
{
|
|
var xOffset:Number = 0;
|
|
for(var i:int = 0; i < seriesIndex; i++)
|
|
{
|
|
xOffset += markerSizes[i] as Number;
|
|
}
|
|
//center based on the sum of all marker sizes
|
|
return -(totalMarkerSize / 2) + xOffset;
|
|
}
|
|
|
|
//center based on the marker size of this series
|
|
return -(markerSizes[seriesIndex] as Number) / 2;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Calculates the sum of the chart's series marker sizes.
|
|
*/
|
|
protected function calculateTotalMarkerSize(axis:IAxis, sizes:Array):Number
|
|
{
|
|
var totalMarkerSize:Number = 0;
|
|
var allSeriesOfType:Array = ChartUtil.findSeriesOfType(this, this.chart as IChart);
|
|
var seriesCount:int = allSeriesOfType.length;
|
|
for(var i:int = 0; i < seriesCount; i++)
|
|
{
|
|
var series:ColumnSeries = ColumnSeries(allSeriesOfType[i]);
|
|
var markerSize:Number = this.calculateMarkerSize(series, axis);
|
|
sizes.push(markerSize);
|
|
if(axis is IClusteringAxis)
|
|
{
|
|
totalMarkerSize += markerSize;
|
|
}
|
|
else
|
|
{
|
|
totalMarkerSize = Math.max(totalMarkerSize, markerSize);
|
|
}
|
|
}
|
|
return totalMarkerSize;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Determines the marker size for a series.
|
|
*/
|
|
protected function calculateMarkerSize(series:ISeries, axis:IAxis):Number
|
|
{
|
|
var markerSize:Number = UIComponentUtil.getStyleValue(UIComponent(series), "markerSize") as Number;
|
|
var maximumAllowedMarkerSize:Number = this.calculateMaximumAllowedMarkerSize(axis);
|
|
|
|
markerSize = Math.min(maximumAllowedMarkerSize, markerSize);
|
|
//we need to use floor because CS3 UIComponents round the position
|
|
markerSize = Math.floor(markerSize);
|
|
return markerSize;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Determines the maximum possible marker size for the containing chart.
|
|
*/
|
|
protected function calculateMaximumAllowedMarkerSize(axis:IAxis):Number
|
|
{
|
|
if(axis is IClusteringAxis)
|
|
{
|
|
var allSeriesOfType:Array = ChartUtil.findSeriesOfType(this, this.chart as IChart);
|
|
return (this.width / IClusteringAxis(axis).clusterCount) / allSeriesOfType.length;
|
|
}
|
|
return Number.POSITIVE_INFINITY;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Determines the origin of the column. Either the axis origin or the
|
|
* stacked value.
|
|
*/
|
|
protected function calculateOriginValue(index:int, axis:IOriginAxis, allSeriesOfType:Array):Object
|
|
{
|
|
return axis.origin;
|
|
}
|
|
|
|
//--------------------------------------
|
|
// Private Methods
|
|
//--------------------------------------
|
|
|
|
/**
|
|
* @private
|
|
* Draws the markers. Used with animation.
|
|
*/
|
|
private function drawMarkers(data:Array):void
|
|
{
|
|
var itemCount:int = this.length;
|
|
for(var i:int = 0; i < itemCount; i++)
|
|
{
|
|
var marker:DisplayObject = this.markers[i] as DisplayObject;
|
|
var markerY:Number = data[i * 2];
|
|
var markerHeight:Number = data[i * 2 + 1];
|
|
marker.y = markerY;
|
|
marker.height = markerHeight;
|
|
|
|
if(marker is UIComponent)
|
|
{
|
|
UIComponent(marker).drawNow();
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------
|
|
// Private Event Handlers
|
|
//--------------------------------------
|
|
|
|
/**
|
|
* @private
|
|
* Draws the markers every time the tween updates.
|
|
*/
|
|
private function tweenUpdateHandler(event:AnimationEvent):void
|
|
{
|
|
var data:Array = event.parameters as Array;
|
|
this.drawMarkers(data);
|
|
}
|
|
|
|
}
|
|
} |