first commit
This commit is contained in:
51
com/yahoo/astra/fl/charts/axes/AxisData.as
Executable file
51
com/yahoo/astra/fl/charts/axes/AxisData.as
Executable file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
/**
|
||||
* Positioning and other data used by an IAxisRenderer to draw
|
||||
* items like ticks. This data is created by an IAxis instance.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
* @see IAxis
|
||||
* @see IAxisRenderer
|
||||
*/
|
||||
public class AxisData
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function AxisData(position:Number, value:Object, label:String)
|
||||
{
|
||||
this.position = position;
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* The position of the item on the axis renderer.
|
||||
*/
|
||||
public var position:Number;
|
||||
|
||||
/**
|
||||
* The value of the item.
|
||||
*/
|
||||
public var value:Object;
|
||||
|
||||
/**
|
||||
* The label value of the item.
|
||||
*/
|
||||
public var label:String;
|
||||
}
|
||||
}
|
31
com/yahoo/astra/fl/charts/axes/AxisOrientation.as
Executable file
31
com/yahoo/astra/fl/charts/axes/AxisOrientation.as
Executable file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
/**
|
||||
* Orientation values available to <code>IAxis</code> objects.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public class AxisOrientation
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Constants
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* The AxisOrientation.VERTICAL constant specifies that a chart axis
|
||||
* should be displayed vertically.
|
||||
*/
|
||||
public static const VERTICAL:String = "vertical";
|
||||
|
||||
/**
|
||||
* The AxisOrientation.VERTICAL constant specifies that a chart axis
|
||||
* should be displayed horizontally.
|
||||
*/
|
||||
public static const HORIZONTAL:String = "horizontal";
|
||||
}
|
||||
}
|
272
com/yahoo/astra/fl/charts/axes/BaseAxis.as
Executable file
272
com/yahoo/astra/fl/charts/axes/BaseAxis.as
Executable file
@ -0,0 +1,272 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
import com.yahoo.astra.fl.charts.IChart;
|
||||
|
||||
/**
|
||||
* Implements some of the most common axis functionality
|
||||
* to prevent duplicate code in IAxis implementations.
|
||||
*
|
||||
* <p>This class is not meant to be instantiated directly! It is an abstract base class.</p>
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public class BaseAxis
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function BaseAxis()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the chart property.
|
||||
*/
|
||||
private var _chart:IChart;
|
||||
|
||||
/**
|
||||
* @copy com.yahoo.astra.fl.charts.axes.IAxis#chart
|
||||
*/
|
||||
public function get chart():IChart
|
||||
{
|
||||
return this._chart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set chart(value:IChart):void
|
||||
{
|
||||
this._chart = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the renderer property.
|
||||
*/
|
||||
private var _renderer:IAxisRenderer;
|
||||
|
||||
//TODO: Consider having the renderer know about the axis
|
||||
//rather than the axis knowing about the renderer. This
|
||||
//change will allow multiple views to this model.
|
||||
//if this is implemented, a separate controller will be
|
||||
//needed too.
|
||||
/**
|
||||
* The visual renderer applied to this axis.
|
||||
*/
|
||||
public function get renderer():IAxisRenderer
|
||||
{
|
||||
return this._renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set renderer(value:IAxisRenderer):void
|
||||
{
|
||||
this._renderer = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the labelFunction property.
|
||||
*/
|
||||
private var _labelFunction:Function;
|
||||
|
||||
/**
|
||||
* @copy com.yahoo.astra.fl.charts.axes.labelFunction
|
||||
*/
|
||||
public function get labelFunction():Function
|
||||
{
|
||||
return this._labelFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set labelFunction(value:Function):void
|
||||
{
|
||||
this._labelFunction = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the reverse property.
|
||||
*/
|
||||
private var _reverse:Boolean = false;
|
||||
|
||||
/**
|
||||
* @copy com.yahoo.astra.fl.charts.axes.IAxis#reverse
|
||||
*/
|
||||
public function get reverse():Boolean
|
||||
{
|
||||
return this._reverse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set reverse(value:Boolean):void
|
||||
{
|
||||
this._reverse = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the title property.
|
||||
*/
|
||||
private var _title:String = "";
|
||||
|
||||
/**
|
||||
* @copy com.yahoo.astra.fl.charts.axes.IAxis#title
|
||||
*/
|
||||
public function get title():String
|
||||
{
|
||||
return this._title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set title(value:String):void
|
||||
{
|
||||
this._title = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* placeholder for maximum label width
|
||||
*/
|
||||
protected var _maxLabelWidth:Number;
|
||||
|
||||
/**
|
||||
* Gets or sets the maximum width of a label
|
||||
*/
|
||||
public function get maxLabelWidth():Number
|
||||
{
|
||||
return _maxLabelWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
public function set maxLabelWidth(value:Number):void
|
||||
{
|
||||
_maxLabelWidth = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* placeholder for maximum label width
|
||||
*/
|
||||
protected var _maxLabelHeight:Number;
|
||||
|
||||
/**
|
||||
* Gets or sets the maximum height of a label
|
||||
*/
|
||||
public function get maxLabelHeight():Number
|
||||
{
|
||||
return _maxLabelHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
public function set maxLabelHeight(value:Number):void
|
||||
{
|
||||
_maxLabelHeight = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
protected var _dataProvider:Array;
|
||||
|
||||
/**
|
||||
* Data provider for the axis
|
||||
*/
|
||||
public function get dataProvider():Array
|
||||
{
|
||||
return _dataProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
public function set dataProvider(value:Array):void
|
||||
{
|
||||
_dataProvider = value;
|
||||
this.parseDataProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _labelSpacing:Number = 2;
|
||||
|
||||
/**
|
||||
* @copy com.yahoo.astra.fl.charts.axes.IAxis#labelSpacing
|
||||
*/
|
||||
public function get labelSpacing():Number
|
||||
{
|
||||
return _labelSpacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
public function set labelSpacing(value:Number):void
|
||||
{
|
||||
if(value != _labelSpacing) _labelSpacing = value;
|
||||
}
|
||||
//--------------------------------------
|
||||
// Public Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @copy com.yahoo.astra.fl.charts.axes.IAxis#valueToLabel()
|
||||
*/
|
||||
public function valueToLabel(value:Object):String
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
var text:String = value.toString();
|
||||
if(this._labelFunction != null)
|
||||
{
|
||||
text = this._labelFunction(value);
|
||||
}
|
||||
|
||||
if(text == null)
|
||||
{
|
||||
text = "";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Protected Methods
|
||||
//--------------------------------------
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
protected function parseDataProvider():void{}
|
||||
|
||||
}
|
||||
}
|
378
com/yahoo/astra/fl/charts/axes/CategoryAxis.as
Executable file
378
com/yahoo/astra/fl/charts/axes/CategoryAxis.as
Executable file
@ -0,0 +1,378 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
import com.yahoo.astra.fl.charts.series.ISeries;
|
||||
import com.yahoo.astra.fl.charts.CartesianChart;
|
||||
import fl.core.UIComponent;
|
||||
import flash.text.TextFormat;
|
||||
/**
|
||||
* An axis type representing a set of categories.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public class CategoryAxis extends BaseAxis implements IAxis, IClusteringAxis
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function CategoryAxis()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Used to determine the position of an item based on a value.
|
||||
*/
|
||||
protected var categorySize:Number = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the categoryNames property.
|
||||
*/
|
||||
private var _categoryNames:Array = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Indicates whether the category labels are user-defined or generated by the axis.
|
||||
*/
|
||||
private var _categoryNamesSetByUser:Boolean = false;
|
||||
|
||||
/**
|
||||
* The category labels to display along the axis.
|
||||
*/
|
||||
public function get categoryNames():Array
|
||||
{
|
||||
return this._categoryNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set categoryNames(value:Array):void
|
||||
{
|
||||
this._categoryNamesSetByUser = value != null && value.length > 0;
|
||||
if(!this._categoryNamesSetByUser)
|
||||
{
|
||||
this._categoryNames = [];
|
||||
}
|
||||
else
|
||||
{
|
||||
//ensure that all category names are strings
|
||||
this._categoryNames = getCategoryNames(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get clusterCount():int
|
||||
{
|
||||
return this.categoryNames.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _numLabels:Number;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _numLabelsSetByUser:Boolean = false;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get numLabels():Number
|
||||
{
|
||||
return _numLabels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
public function set numLabels(value:Number):void
|
||||
{
|
||||
if(_numLabelsSetByUser) return;
|
||||
_numLabels = value;
|
||||
_numLabelsSetByUser = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _majorUnit:Number = 1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Holds value for calculateCategoryCount
|
||||
*/
|
||||
private var _calculateCategoryCount:Boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates whether or not to calculate the number of categories (ticks and labels)
|
||||
* when there is not enough room to display all labels on the axis. If set to true, the axis
|
||||
* will determine the number of categories to plot. If not, all categories will be plotted.
|
||||
*/
|
||||
public function get calculateCategoryCount():Boolean
|
||||
{
|
||||
return _calculateCategoryCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
public function set calculateCategoryCount(value:Boolean):void
|
||||
{
|
||||
_calculateCategoryCount = value;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Public Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function valueToLocal(value:Object):Number
|
||||
{
|
||||
if(value === null)
|
||||
{
|
||||
return NaN;
|
||||
}
|
||||
|
||||
var index:int = this.categoryNames.indexOf(value.toString());
|
||||
if(index >= 0)
|
||||
{
|
||||
var position:int = this.categorySize * index + (this.categorySize / 2);
|
||||
if(this.reverse)
|
||||
{
|
||||
position = this.renderer.length - position;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
return NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function updateScale():void
|
||||
{
|
||||
if(!this._categoryNamesSetByUser)
|
||||
{
|
||||
this.autoDetectCategories(this.dataProvider);
|
||||
}
|
||||
this.calculateCategorySize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getMaxLabel():String
|
||||
{
|
||||
var categoryCount:int = this.categoryNames.length;
|
||||
var maxLength:Number = 0;
|
||||
var currentLength:Number;
|
||||
var maxString:String = "x";
|
||||
|
||||
for(var i:int = 0; i < categoryCount; i++)
|
||||
{
|
||||
currentLength = (this.categoryNames[i].toString()).length;
|
||||
|
||||
if(currentLength > maxLength)
|
||||
{
|
||||
maxLength = currentLength;
|
||||
maxString = this.categoryNames[i];
|
||||
}
|
||||
}
|
||||
|
||||
return this.valueToLabel(maxString) as String;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Private Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Update the labels by adding or removing some, setting the text, etc.
|
||||
*/
|
||||
private function autoDetectCategories(data:Array):void
|
||||
{
|
||||
var uniqueCategoryNames:Array = [];
|
||||
var seriesCount:int = data.length;
|
||||
for(var i:int = 0; i < seriesCount; i++)
|
||||
{
|
||||
var series:ISeries = data[i] as ISeries;
|
||||
if(!series)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var seriesLength:int = series.length;
|
||||
for(var j:int = 0; j < seriesLength; j++)
|
||||
{
|
||||
var category:Object = this.chart.itemToAxisValue(series, j, this);
|
||||
|
||||
//names must be unique
|
||||
if(uniqueCategoryNames.indexOf(category) < 0)
|
||||
{
|
||||
uniqueCategoryNames.push(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
this._categoryNames = getCategoryNames(uniqueCategoryNames.concat());
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Determines the amount of space provided to each category.
|
||||
*/
|
||||
private function calculateCategorySize():void
|
||||
{
|
||||
var categoryCount:int = this.categoryNames.length;
|
||||
this.categorySize = this.renderer.length;
|
||||
if(categoryCount > 0)
|
||||
{
|
||||
this.categorySize /= categoryCount;
|
||||
}
|
||||
|
||||
//If the number of labels will not fit on the axis or the user has specified the number of labels to
|
||||
//display, calculate the major unit.
|
||||
var maxLabelSize:Number = (this.chart as CartesianChart).horizontalAxis == this ? this.maxLabelWidth : this.maxLabelHeight;
|
||||
if((this.categorySize < maxLabelSize && this.calculateCategoryCount) || (this._numLabelsSetByUser && this.numLabels != categoryCount))
|
||||
{
|
||||
this.calculateMajorUnit();
|
||||
(this.renderer as ICartesianAxisRenderer).majorUnitSetByUser = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
(this.renderer as ICartesianAxisRenderer).majorUnitSetByUser = true;
|
||||
}
|
||||
this.updateAxisRenderer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Calculates which labels to skip if they will not all fit on the axis.
|
||||
*/
|
||||
private function calculateMajorUnit():void
|
||||
{
|
||||
var overflow:Number = 0;
|
||||
var rotation:Number = 0;
|
||||
var chart:CartesianChart = this.chart as CartesianChart;
|
||||
var maxLabelSize:Number;
|
||||
if(chart.horizontalAxis == this)
|
||||
{
|
||||
maxLabelSize = this.maxLabelWidth;
|
||||
rotation = chart.getHorizontalAxisStyle("rotation") as Number;
|
||||
if(rotation >= 0)
|
||||
{
|
||||
if(!isNaN(chart.horizontalAxisLabelData.rightLabelOffset)) overflow += chart.horizontalAxisLabelData.rightLabelOffset as Number;
|
||||
}
|
||||
if(rotation <= 0)
|
||||
{
|
||||
if(!isNaN(chart.horizontalAxisLabelData.leftLabelOffset)) overflow += chart.horizontalAxisLabelData.leftLabelOffset as Number;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
maxLabelSize = this.maxLabelHeight;
|
||||
rotation = chart.getVerticalAxisStyle("rotation") as Number;
|
||||
if(!isNaN(chart.verticalAxisLabelData.topLabelOffset)) overflow = chart.verticalAxisLabelData.topLabelOffset as Number;
|
||||
}
|
||||
var labelSpacing:Number = this.labelSpacing;
|
||||
maxLabelSize += (labelSpacing*2);
|
||||
var categoryCount:int = this.categoryNames.length;
|
||||
|
||||
|
||||
var maxNumLabels:Number = this.renderer.length/maxLabelSize;
|
||||
|
||||
//If the user specified number of labels to display, attempt to show the correct number.
|
||||
if(this._numLabelsSetByUser)
|
||||
{
|
||||
maxNumLabels = Math.min(maxNumLabels, this.numLabels);
|
||||
}
|
||||
|
||||
var tempMajorUnit:Number = Math.ceil(this.categoryNames.length/maxNumLabels);
|
||||
this._majorUnit = tempMajorUnit;
|
||||
if(this.renderer.length%tempMajorUnit != 0 && !this._numLabelsSetByUser)
|
||||
{
|
||||
var len:Number = Math.min(tempMajorUnit, ((this.renderer.length/2)-tempMajorUnit));
|
||||
for(var i:int = 0;i < len; i++)
|
||||
{
|
||||
tempMajorUnit++;
|
||||
if(this.renderer.length%tempMajorUnit == 0)
|
||||
{
|
||||
this._majorUnit = tempMajorUnit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Ensures all values in an array are string values
|
||||
*/
|
||||
private function getCategoryNames(value:Array):Array
|
||||
{
|
||||
var names:Array = [];
|
||||
if(value != null && value.length > 0)
|
||||
{
|
||||
for(var i:int = 0; i < value.length; i++)
|
||||
{
|
||||
names.push(value[i].toString());
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
protected function updateAxisRenderer():void
|
||||
{
|
||||
var ticks:Array = [];
|
||||
var categoryCount:int = this.categoryNames.length;
|
||||
var currentCat:int = 0;
|
||||
while(currentCat < categoryCount && !isNaN(categoryCount))
|
||||
{
|
||||
var category:String = this.categoryNames[currentCat];
|
||||
var position:Number = this.valueToLocal(category);
|
||||
var label:String = this.valueToLabel(category);
|
||||
var axisData:AxisData = new AxisData(position, category, label);
|
||||
ticks.push(axisData);
|
||||
currentCat += this._majorUnit;
|
||||
}
|
||||
|
||||
//If a major unit has been calculated, we are not plotting all categories.
|
||||
//Adjust the postion of each tick.
|
||||
if(this._majorUnit > 1)
|
||||
{
|
||||
categoryCount = ticks.length;
|
||||
var categorySize:Number = this.renderer.length / categoryCount;
|
||||
for(var i:int = 0; i < categoryCount; i++)
|
||||
{
|
||||
(ticks[i] as AxisData).position = categorySize * i + (categorySize/2);
|
||||
}
|
||||
}
|
||||
|
||||
this.renderer.ticks = ticks;
|
||||
this.renderer.minorTicks = [];
|
||||
}
|
||||
}
|
||||
}
|
990
com/yahoo/astra/fl/charts/axes/DefaultAxisRenderer.as
Executable file
990
com/yahoo/astra/fl/charts/axes/DefaultAxisRenderer.as
Executable file
@ -0,0 +1,990 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
import com.yahoo.astra.utils.GeomUtil;
|
||||
import com.yahoo.astra.utils.NumberUtil;
|
||||
import com.yahoo.astra.display.BitmapText;
|
||||
|
||||
import com.yahoo.astra.utils.DynamicRegistration;
|
||||
|
||||
import fl.core.InvalidationType;
|
||||
import fl.core.UIComponent;
|
||||
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Rectangle;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFieldAutoSize;
|
||||
import flash.text.TextFormat;
|
||||
import flash.text.TextFormatAlign;
|
||||
|
||||
//--------------------------------------
|
||||
// Styles
|
||||
//--------------------------------------
|
||||
|
||||
//-- Axis
|
||||
|
||||
/**
|
||||
* If false, the axis is not drawn. Titles, labels, ticks, and grid
|
||||
* lines may still be drawn, however, so you must specifically hide each
|
||||
* item if nothing should be drawn.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
[Style(name="showAxis", type="Boolean")]
|
||||
|
||||
/**
|
||||
* The line weight, in pixels, for the axis.
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
[Style(name="axisWeight", type="int")]
|
||||
|
||||
/**
|
||||
* The line color for the axis.
|
||||
*
|
||||
* @default #888a85
|
||||
*/
|
||||
[Style(name="axisColor", type="uint")]
|
||||
|
||||
//-- Labels
|
||||
|
||||
/**
|
||||
* If true, labels will be displayed on the axis.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
[Style(name="showLabels", type="Boolean")]
|
||||
|
||||
/**
|
||||
* The distance, in pixels, between a label and the axis.
|
||||
*
|
||||
* @default 2
|
||||
*/
|
||||
[Style(name="labelDistance", type="Number")]
|
||||
|
||||
/**
|
||||
* The distance, in pixels, between a title and the axis labels.
|
||||
*
|
||||
* @default 2
|
||||
*/
|
||||
[Style(name="titleDistance", type="Number")]
|
||||
|
||||
/**
|
||||
* If true, labels that overlap previously drawn labels on the axis will be
|
||||
* hidden. The first and last labels on the axis will always be drawn.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
[Style(name="hideOverlappingLabels", type="Boolean")]
|
||||
|
||||
/**
|
||||
* The angle, in degrees, of the labels on the axis. May be a value
|
||||
* between <code>-90</code> and <code>90</code>.
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
[Style(name="labelRotation", type="Number")]
|
||||
|
||||
/**
|
||||
* The angle, in degrees, of the title on the axis. May be a value
|
||||
* between <code>-90</code> and <code>90</code>.
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
[Style(name="titleRotation", type="Number")]
|
||||
|
||||
//-- Ticks
|
||||
|
||||
/**
|
||||
* If true, ticks will be displayed on the axis.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
[Style(name="showTicks", type="Boolean")]
|
||||
|
||||
/**
|
||||
* The line weight, in pixels, for the ticks on the axis.
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
[Style(name="tickWeight", type="int")]
|
||||
|
||||
/**
|
||||
* The line color for the ticks on the axis.
|
||||
*
|
||||
* @default #888a85
|
||||
*/
|
||||
[Style(name="tickColor", type="uint")]
|
||||
|
||||
/**
|
||||
* The length, in pixels, of the ticks on the axis.
|
||||
*
|
||||
* @default 4
|
||||
*/
|
||||
[Style(name="tickLength", type="Number")]
|
||||
|
||||
/**
|
||||
* The position of the ticks on the axis.
|
||||
*
|
||||
* @default "cross"
|
||||
* @see TickPosition
|
||||
*/
|
||||
[Style(name="tickPosition", type="String")]
|
||||
|
||||
//-- Minor ticks
|
||||
|
||||
/**
|
||||
* If true, ticks will be displayed on the axis at minor positions.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
[Style(name="showMinorTicks", type="Boolean")]
|
||||
|
||||
/**
|
||||
* The line weight, in pixels, for the minor ticks on the axis.
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
[Style(name="minorTickWeight", type="int")]
|
||||
|
||||
/**
|
||||
* The line color for the minor ticks on the axis.
|
||||
*
|
||||
* @default #888a85
|
||||
*/
|
||||
[Style(name="minorTickColor", type="uint")]
|
||||
|
||||
/**
|
||||
* The length of the minor ticks on the axis.
|
||||
*
|
||||
* @default 3
|
||||
*/
|
||||
[Style(name="minorTickLength", type="Number")]
|
||||
|
||||
/**
|
||||
* The position of the minor ticks on the axis.
|
||||
*
|
||||
* @default "outside"
|
||||
* @see com.yahoo.astra.fl.charts.TickPosition
|
||||
*/
|
||||
[Style(name="minorTickPosition", type="String")]
|
||||
|
||||
//-- Title
|
||||
|
||||
/**
|
||||
* If true, the axis title will be displayed.
|
||||
*
|
||||
* @default 2
|
||||
*/
|
||||
[Style(name="showTitle", type="Boolean")]
|
||||
|
||||
/**
|
||||
* The TextFormat object to use to render the axis title label.
|
||||
*
|
||||
* @default TextFormat("_sans", 11, 0x000000, false, false, false, '', '', TextFormatAlign.LEFT, 0, 0, 0, 0)
|
||||
*/
|
||||
[Style(name="titleTextFormat", type="TextFormat")]
|
||||
|
||||
/**
|
||||
* The default axis renderer for a cartesian chart.
|
||||
*
|
||||
* @see com.yahoo.astra.fl.charts.CartesianChart
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public class DefaultAxisRenderer extends UIComponent implements ICartesianAxisRenderer
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Class Variables
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private static var defaultStyles:Object =
|
||||
{
|
||||
//axis
|
||||
showAxis: true,
|
||||
axisWeight: 1,
|
||||
axisColor: 0x888a85,
|
||||
|
||||
//labels
|
||||
showLabels: true,
|
||||
labelDistance: 2,
|
||||
embedFonts: false,
|
||||
hideOverlappingLabels: true,
|
||||
labelRotation: 0,
|
||||
titleRotation: 0,
|
||||
titleDistance: 2,
|
||||
|
||||
//ticks
|
||||
showTicks: true,
|
||||
tickWeight: 1,
|
||||
tickColor: 0x888a85,
|
||||
tickLength: 4,
|
||||
tickPosition: TickPosition.CROSS,
|
||||
|
||||
//minor ticks
|
||||
showMinorTicks: true,
|
||||
minorTickWeight: 1,
|
||||
minorTickColor: 0x888a85,
|
||||
minorTickLength: 3,
|
||||
minorTickPosition: TickPosition.OUTSIDE,
|
||||
|
||||
//title
|
||||
showTitle: true,
|
||||
titleTextFormat: new TextFormat("_sans", 11, 0x000000, false, false, false, "", "", TextFormatAlign.LEFT, 0, 0, 0, 0)
|
||||
};
|
||||
|
||||
//--------------------------------------
|
||||
// Class Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @copy fl.core.UIComponent#getStyleDefinition()
|
||||
*/
|
||||
public static function getStyleDefinition():Object
|
||||
{
|
||||
return mergeStyles(defaultStyles, UIComponent.getStyleDefinition());
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function DefaultAxisRenderer(orientation:String)
|
||||
{
|
||||
super();
|
||||
this.orientation = orientation;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the TextFields used for labels on this axis.
|
||||
*/
|
||||
protected var labelTextFields:Array = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* A cache to allow the reuse of TextFields when redrawing the renderer.
|
||||
*/
|
||||
private var _labelCache:Array;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The TextField used to display the axis title.
|
||||
*/
|
||||
protected var titleTextField:BitmapText;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get length():Number
|
||||
{
|
||||
if(this.orientation == AxisOrientation.VERTICAL)
|
||||
{
|
||||
return this.contentBounds.height;
|
||||
}
|
||||
return this.contentBounds.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the orientation property.
|
||||
*/
|
||||
private var _orientation:String = AxisOrientation.VERTICAL;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get orientation():String
|
||||
{
|
||||
return this._orientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set orientation(value:String):void
|
||||
{
|
||||
if(this._orientation != value)
|
||||
{
|
||||
this._orientation = value;
|
||||
this.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the contentBounds property.
|
||||
*/
|
||||
protected var _contentBounds:Rectangle = new Rectangle();
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get contentBounds():Rectangle
|
||||
{
|
||||
return this._contentBounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the ticks property.
|
||||
*/
|
||||
private var _ticks:Array = [];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get ticks():Array
|
||||
{
|
||||
return this._ticks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set ticks(value:Array):void
|
||||
{
|
||||
this._ticks = value;
|
||||
this.invalidate(InvalidationType.DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the minorTicks property.
|
||||
*/
|
||||
private var _minorTicks:Array = [];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get minorTicks():Array
|
||||
{
|
||||
return this._minorTicks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set minorTicks(value:Array):void
|
||||
{
|
||||
this._minorTicks = value;
|
||||
this.invalidate(InvalidationType.DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the title property.
|
||||
*/
|
||||
private var _title:String = "";
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get title():String
|
||||
{
|
||||
return this._title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set title(value:String):void
|
||||
{
|
||||
if(this._title != value)
|
||||
{
|
||||
this._title = value ? value : "";
|
||||
this.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private var _outerTickOffset:Number = 0;
|
||||
|
||||
public function get outerTickOffset():Number
|
||||
{
|
||||
return _outerTickOffset;
|
||||
}
|
||||
|
||||
public function set outerTickOffset(value:Number):void
|
||||
{
|
||||
_outerTickOffset = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the majorUnitSetByUser
|
||||
*/
|
||||
private var _majorUnitSetByUser:Boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates whether the major unit is user-defined or generated by the axis.
|
||||
*/
|
||||
public function get majorUnitSetByUser():Boolean
|
||||
{
|
||||
return this._majorUnitSetByUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
public function set majorUnitSetByUser(value:Boolean):void
|
||||
{
|
||||
this._majorUnitSetByUser = value;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Public Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function updateAxis():void
|
||||
{
|
||||
var showLabels:Boolean = this.getStyleValue("showLabels") as Boolean;
|
||||
var labelDistance:Number = this.getStyleValue("labelDistance") as Number;
|
||||
var textFormat:TextFormat = this.getStyleValue("textFormat") as TextFormat;
|
||||
var labelRotation:Number = this.getStyleValue("labelRotation") as Number;
|
||||
var embedFonts:Boolean = this.getStyleValue("embedFonts") as Boolean;
|
||||
labelRotation = Math.max(-90, Math.min(labelRotation, 90));
|
||||
|
||||
this.createCache();
|
||||
this.updateLabels(this.ticks, showLabels, textFormat, labelDistance, labelRotation, embedFonts);
|
||||
this.clearCache();
|
||||
|
||||
this.updateTitle();
|
||||
this.draw();
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Protected Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override protected function configUI():void
|
||||
{
|
||||
super.configUI();
|
||||
|
||||
if(!this.titleTextField)
|
||||
{
|
||||
this.titleTextField = new BitmapText();
|
||||
this.titleTextField.autoSize = TextFieldAutoSize.LEFT;
|
||||
this.addChild(this.titleTextField);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override protected function draw():void
|
||||
{
|
||||
this.graphics.clear();
|
||||
|
||||
this.positionTitle();
|
||||
|
||||
var showTicks:Boolean = this.getStyleValue("showTicks") as Boolean;
|
||||
var showMinorTicks:Boolean = this.getStyleValue("showMinorTicks") as Boolean;
|
||||
var filteredMinorTicks:Array = this.minorTicks.concat();
|
||||
if(showMinorTicks && showTicks)
|
||||
{
|
||||
//filter out minor ticks that appear at the same position
|
||||
//as major ticks.
|
||||
filteredMinorTicks = filteredMinorTicks.filter(function(item:AxisData, index:int, source:Array):Boolean
|
||||
{
|
||||
return !this.ticks.some(function(item2:AxisData, index2:int, source2:Array):Boolean
|
||||
{
|
||||
//using fuzzyEquals because we may encounter rounding errors
|
||||
return NumberUtil.fuzzyEquals(item.position, item2.position, 10);
|
||||
});
|
||||
}, this);
|
||||
}
|
||||
|
||||
this.drawAxis();
|
||||
|
||||
var showLabels:Boolean = this.getStyleValue("showLabels") as Boolean;
|
||||
var labelDistance:Number = this.getStyleValue("labelDistance") as Number;
|
||||
var textFormat:TextFormat = this.getStyleValue("textFormat") as TextFormat;
|
||||
var labelRotation:Number = this.getStyleValue("labelRotation") as Number;
|
||||
var embedFonts:Boolean = this.getStyleValue("embedFonts") as Boolean;
|
||||
labelRotation = Math.max(-90, Math.min(labelRotation, 90));
|
||||
this.positionLabels(this.ticks, showLabels, labelDistance, labelRotation, embedFonts);
|
||||
|
||||
var tickPosition:String = this.getStyleValue("tickPosition") as String;
|
||||
var tickLength:Number = this.getStyleValue("tickLength") as Number;
|
||||
var tickWeight:int = this.getStyleValue("tickWeight") as int;
|
||||
var tickColor:uint = this.getStyleValue("tickColor") as uint;
|
||||
this.drawTicks(this.ticks, showTicks, tickPosition, tickLength, tickWeight, tickColor);
|
||||
|
||||
var minorTickPosition:String = this.getStyleValue("minorTickPosition") as String;
|
||||
var minorTickLength:Number = this.getStyleValue("minorTickLength") as Number;
|
||||
var minorTickWeight:int = this.getStyleValue("minorTickWeight") as int;
|
||||
var minorTickColor:uint = this.getStyleValue("minorTickColor") as uint;
|
||||
this.drawTicks(filteredMinorTicks, showMinorTicks, minorTickPosition, minorTickLength, minorTickWeight, minorTickColor);
|
||||
|
||||
super.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Updates the title text and styles.
|
||||
*/
|
||||
protected function updateTitle():void
|
||||
{
|
||||
var showTitle:Boolean = this.getStyleValue("showTitle") as Boolean;
|
||||
if(!showTitle)
|
||||
{
|
||||
this.titleTextField.text = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
var textFormat:TextFormat = this.getStyleValue("titleTextFormat") as TextFormat;
|
||||
var embedFonts:Boolean = this.getStyleValue("embedFonts") as Boolean;
|
||||
this.titleTextField.defaultTextFormat = textFormat;
|
||||
this.titleTextField.embedFonts = embedFonts;
|
||||
this.titleTextField.text = this.title;
|
||||
|
||||
var titleRotation:Number = this.getStyleValue("titleRotation") as Number;
|
||||
this.titleTextField.rotation = Math.max(-90, Math.min(titleRotation, 90));;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Positions the title along the axis.
|
||||
*/
|
||||
protected function positionTitle():void
|
||||
{
|
||||
var showTitle:Boolean = this.getStyleValue("showTitle") as Boolean;
|
||||
this.titleTextField.visible = showTitle;
|
||||
if(showTitle)
|
||||
{
|
||||
var titleRotation:Number = this.titleTextField.rotation;
|
||||
if(this.orientation == AxisOrientation.VERTICAL)
|
||||
{
|
||||
this.titleTextField.y = this.contentBounds.y + (this.contentBounds.height) / 2;
|
||||
this.titleTextField.x = 0;
|
||||
if(titleRotation > 0)
|
||||
{
|
||||
this.titleTextField.x += this.titleTextField.contentHeight * (titleRotation/90);
|
||||
this.titleTextField.y -= this.titleTextField.height/2;
|
||||
}
|
||||
else if(titleRotation < 0)
|
||||
{
|
||||
this.titleTextField.y += this.titleTextField.height/2;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.titleTextField.y -= this.titleTextField.height /2;
|
||||
}
|
||||
}
|
||||
else //horizontal
|
||||
{
|
||||
this.titleTextField.x = this.contentBounds.x + (this.contentBounds.width/2);
|
||||
this.titleTextField.y = this.y + this.height - this.titleTextField.height;
|
||||
if(titleRotation > 0)
|
||||
{
|
||||
this.titleTextField.x += (-.5 + titleRotation/90) * this.titleTextField.width;
|
||||
|
||||
}
|
||||
else if(titleRotation < 0)
|
||||
{
|
||||
this.titleTextField.x -= (this.titleTextField.width * (1 - Math.abs(titleRotation/180)))/2;
|
||||
this.titleTextField.y += this.titleTextField.height - (Math.sin((90 - titleRotation) * Math.PI/180) * this.titleTextField.contentHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.titleTextField.x -= this.titleTextField.width/2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Draws the axis origin line.
|
||||
*/
|
||||
protected function drawAxis():void
|
||||
{
|
||||
var showAxis:Boolean = this.getStyleValue("showAxis") as Boolean;
|
||||
if(!showAxis)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var axisWeight:int = this.getStyleValue("axisWeight") as int;
|
||||
var axisColor:uint = this.getStyleValue("axisColor") as uint;
|
||||
this.graphics.lineStyle(axisWeight, axisColor);
|
||||
if(this.orientation == AxisOrientation.VERTICAL)
|
||||
{
|
||||
//we round these values because that's what the Flash CS3 components do
|
||||
//with positions
|
||||
var verticalX:Number = this.contentBounds.x;
|
||||
var verticalStart:Number = this.contentBounds.y;
|
||||
var verticalEnd:Number = this.contentBounds.y + this.contentBounds.height;
|
||||
this.graphics.moveTo(verticalX, verticalStart);
|
||||
this.graphics.lineTo(verticalX, verticalEnd);
|
||||
}
|
||||
else //horizontal
|
||||
{
|
||||
var horizontalY:Number = this.contentBounds.y + this.contentBounds.height;
|
||||
var horizontalStart:Number = this.contentBounds.x;
|
||||
var horizontalEnd:Number = this.contentBounds.x + this.contentBounds.width;
|
||||
this.graphics.moveTo(horizontalStart, horizontalY);
|
||||
this.graphics.lineTo(horizontalEnd, horizontalY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Draws a set of ticks on the axis.
|
||||
*/
|
||||
protected function drawTicks(data:Array, showTicks:Boolean, tickPosition:String,
|
||||
tickLength:Number, tickWeight:Number, tickColor:uint):void
|
||||
{
|
||||
if(!showTicks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.graphics.lineStyle(tickWeight, tickColor);
|
||||
|
||||
var dataCount:int = data.length;
|
||||
for(var i:int = 0; i < dataCount; i++)
|
||||
{
|
||||
var axisData:AxisData = AxisData(data[i]);
|
||||
if(isNaN(axisData.position))
|
||||
{
|
||||
//skip bad positions
|
||||
continue;
|
||||
}
|
||||
|
||||
var position:Number = axisData.position;
|
||||
if(this.orientation == AxisOrientation.VERTICAL)
|
||||
{
|
||||
position += this.contentBounds.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
position += this.contentBounds.x;
|
||||
}
|
||||
position = position;
|
||||
switch(tickPosition)
|
||||
{
|
||||
case TickPosition.OUTSIDE:
|
||||
{
|
||||
if(this.orientation == AxisOrientation.VERTICAL)
|
||||
{
|
||||
this.graphics.moveTo(this.contentBounds.x - tickLength, position);
|
||||
this.graphics.lineTo(this.contentBounds.x, position);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.graphics.moveTo(position, this.contentBounds.y + this.contentBounds.height);
|
||||
this.graphics.lineTo(position, this.contentBounds.y + this.contentBounds.height + tickLength);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TickPosition.INSIDE:
|
||||
{
|
||||
if(this.orientation == AxisOrientation.VERTICAL)
|
||||
{
|
||||
this.graphics.moveTo(this.contentBounds.x, position);
|
||||
this.graphics.lineTo(this.contentBounds.x + tickLength, position);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.graphics.moveTo(position, this.contentBounds.y + this.contentBounds.height - tickLength);
|
||||
this.graphics.lineTo(position, this.contentBounds.y + this.contentBounds.height);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: //CROSS
|
||||
{
|
||||
if(this.orientation == AxisOrientation.VERTICAL)
|
||||
{
|
||||
this.graphics.moveTo(this.contentBounds.x - tickLength / 2, position);
|
||||
this.graphics.lineTo(this.contentBounds.x + tickLength / 2, position);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.graphics.moveTo(position, this.contentBounds.y + this.contentBounds.height - tickLength / 2);
|
||||
this.graphics.lineTo(position, this.contentBounds.y + this.contentBounds.height + tickLength / 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Saves the label TextFields so that they may be reused.
|
||||
*/
|
||||
protected function createCache():void
|
||||
{
|
||||
this._labelCache = this.labelTextFields.concat();
|
||||
this.labelTextFields = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Removes unused label TextFields.
|
||||
*/
|
||||
protected function clearCache():void
|
||||
{
|
||||
var cacheLength:int = this._labelCache.length;
|
||||
for(var i:int = 0; i < cacheLength; i++)
|
||||
{
|
||||
var label:BitmapText = BitmapText(this._labelCache.shift());
|
||||
this.removeChild(label);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Creates the labels, sets their text and styles them. Positions the labels too.
|
||||
*/
|
||||
protected function updateLabels(data:Array, showLabels:Boolean, textFormat:TextFormat, labelDistance:Number, labelRotation:Number, embedFonts:Boolean):void
|
||||
{
|
||||
if(!showLabels)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dataCount:int = data.length;
|
||||
for(var i:int = 0; i < dataCount; i++)
|
||||
{
|
||||
var axisData:AxisData = AxisData(data[i]);
|
||||
var position:Number = axisData.position;
|
||||
if(isNaN(position))
|
||||
{
|
||||
//skip bad positions
|
||||
continue;
|
||||
}
|
||||
|
||||
var label:BitmapText = this.getLabel();
|
||||
label.defaultTextFormat = textFormat;
|
||||
label.embedFonts = embedFonts;
|
||||
label.rotation = 0;
|
||||
label.text = axisData.label;
|
||||
this.labelTextFields.push(label);
|
||||
}
|
||||
this.positionLabels(data, showLabels, labelDistance, labelRotation, embedFonts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Positions a set of labels on the axis.
|
||||
*/
|
||||
protected function positionLabels(labels:Array, showLabels:Boolean, labelDistance:Number, labelRotation:Number, embedFonts:Boolean):void
|
||||
{
|
||||
var labelCount:int = this.labelTextFields.length;
|
||||
for(var i:int = 0; i < labelCount; i++)
|
||||
{
|
||||
var label:BitmapText = BitmapText(this.labelTextFields[i]);
|
||||
label.rotation = 0;
|
||||
var axisData:AxisData = AxisData(this.ticks[i]);
|
||||
var position:Number = axisData.position;
|
||||
if(this.orientation == AxisOrientation.VERTICAL)
|
||||
{
|
||||
position += this.contentBounds.y;
|
||||
if(showLabels)
|
||||
{
|
||||
label.x = this.contentBounds.x - labelDistance - this.outerTickOffset - label.width;
|
||||
label.y = position - label.height/2;
|
||||
}
|
||||
|
||||
if(labelRotation == 0)
|
||||
{
|
||||
//do nothing. already ideally positioned
|
||||
}
|
||||
else if(labelRotation < 90 && labelRotation > 0)
|
||||
{
|
||||
DynamicRegistration.rotate(label, new Point(label.width, label.height / 2), labelRotation);
|
||||
}
|
||||
else if(labelRotation > -90 && labelRotation < 0)
|
||||
{
|
||||
DynamicRegistration.rotate(label, new Point(label.width, label.height / 2), labelRotation);
|
||||
}
|
||||
else if(labelRotation == -90)
|
||||
{
|
||||
label.y -= label.width / 2;
|
||||
DynamicRegistration.rotate(label, new Point(label.width, label.height / 2), labelRotation);
|
||||
}
|
||||
else //90
|
||||
{
|
||||
label.y += label.width / 2;
|
||||
DynamicRegistration.rotate(label, new Point(label.width, label.height / 2), labelRotation);
|
||||
}
|
||||
}
|
||||
else //horizontal
|
||||
{
|
||||
position += this.contentBounds.x;
|
||||
if(showLabels)
|
||||
{
|
||||
label.y = this.contentBounds.y + this.contentBounds.height + labelDistance + this.outerTickOffset;
|
||||
}
|
||||
|
||||
if(labelRotation > 0)
|
||||
{
|
||||
label.x = position;
|
||||
label.y -= (label.height * labelRotation / 180);
|
||||
DynamicRegistration.rotate(label, new Point(0, label.height / 2), labelRotation);
|
||||
}
|
||||
else if(labelRotation < 0)
|
||||
{
|
||||
label.x = position - label.width;
|
||||
label.y -= (label.height * Math.abs(labelRotation) / 180);
|
||||
DynamicRegistration.rotate(label, new Point(label.width, label.height / 2), labelRotation);
|
||||
}
|
||||
else //labelRotation == 0
|
||||
{
|
||||
label.x = position - label.width / 2;
|
||||
}
|
||||
}
|
||||
|
||||
this.handleOverlappingLabels();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Either creates a new label TextField or retrieves one from the cache.
|
||||
*/
|
||||
protected function getLabel():BitmapText
|
||||
{
|
||||
if(this._labelCache.length > 0)
|
||||
{
|
||||
return BitmapText(this._labelCache.shift());
|
||||
}
|
||||
var labelRotation:Number = this.getStyleValue("labelRotation") as Number;
|
||||
var label:BitmapText = new BitmapText();
|
||||
label.selectable = false;
|
||||
label.autoSize = TextFieldAutoSize.LEFT;
|
||||
this.addChild(label);
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* If labels overlap, some may need to be hidden.
|
||||
*/
|
||||
protected function handleOverlappingLabels():void
|
||||
{
|
||||
var showLabels:Boolean = this.getStyleValue("showLabels");
|
||||
var hideOverlappingLabels:Boolean = this.getStyleValue("hideOverlappingLabels");
|
||||
if(!showLabels || !hideOverlappingLabels)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var labelRotation:Number = this.getStyleValue("labelRotation") as Number;
|
||||
var lastVisibleLabel:BitmapText;
|
||||
var labelCount:int = this.labelTextFields.length;
|
||||
for(var i:int = 0; i < labelCount; i++)
|
||||
{
|
||||
var index:int = labelRotation >= 0 ? i : (labelCount - i - 1);
|
||||
var label:BitmapText = BitmapText(this.labelTextFields[index]);
|
||||
label.visible = true;
|
||||
if(lastVisibleLabel)
|
||||
{
|
||||
if(this.orientation == AxisOrientation.HORIZONTAL)
|
||||
{
|
||||
if(labelRotation >= 0)
|
||||
{
|
||||
var xDifference:Number = label.x - lastVisibleLabel.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
xDifference = (lastVisibleLabel.x + lastVisibleLabel.textWidth) - (label.x + label.textWidth);
|
||||
}
|
||||
if(lastVisibleLabel.textWidth > xDifference)
|
||||
{
|
||||
var offset:Point = Point.polar(xDifference, GeomUtil.degreesToRadians(labelRotation));
|
||||
if(Math.abs(offset.y) <= label.textHeight)
|
||||
{
|
||||
label.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
var xDifference:Number;
|
||||
var maxWidth:Number;
|
||||
if(labelRotation > 0)
|
||||
{
|
||||
xDifference = Math.abs(label.x - lastVisibleLabel.x);
|
||||
maxWidth = lastVisibleLabel.rotationWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
xDifference = Math.abs((lastVisibleLabel.x + lastVisibleLabel.width) - (label.x + label.rotationWidth));
|
||||
maxWidth = label.rotationWidth;
|
||||
}
|
||||
|
||||
if(maxWidth > xDifference)
|
||||
{
|
||||
label.visible = false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
else //vertical
|
||||
{
|
||||
/*
|
||||
var yDifference:Number = Math.abs(lastVisibleLabel.y - label.y);
|
||||
|
||||
var maxHeight:Number;
|
||||
if(lastVisibleLabel.y > label.y)
|
||||
{
|
||||
maxHeight = label.rotationHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxHeight = lastVisibleLabel.rotationHeight;
|
||||
}
|
||||
if(maxHeight > yDifference)
|
||||
{
|
||||
label.visible = false;
|
||||
}
|
||||
*/
|
||||
if(labelRotation >= 0)
|
||||
{
|
||||
var yDifference:Number = lastVisibleLabel.y - label.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
yDifference = (lastVisibleLabel.y + lastVisibleLabel.textHeight) - (label.y + label.textHeight);
|
||||
}
|
||||
yDifference = Math.abs(yDifference);
|
||||
if(lastVisibleLabel.textHeight > yDifference)
|
||||
{
|
||||
offset = Point.polar(yDifference, GeomUtil.degreesToRadians(labelRotation));
|
||||
if(offset.x <= label.textWidth)
|
||||
{
|
||||
label.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(label.visible)
|
||||
{
|
||||
lastVisibleLabel = label;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
191
com/yahoo/astra/fl/charts/axes/DefaultGridLinesRenderer.as
Executable file
191
com/yahoo/astra/fl/charts/axes/DefaultGridLinesRenderer.as
Executable file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
import com.yahoo.astra.utils.NumberUtil;
|
||||
|
||||
import fl.core.UIComponent;
|
||||
|
||||
/**
|
||||
* Renders grid lines associated with a cartesian axis.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public class DefaultGridLinesRenderer extends UIComponent implements IGridLinesRenderer
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function DefaultGridLinesRenderer()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the axisRenderer property.
|
||||
*/
|
||||
private var _axisRenderer:IAxisRenderer;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get axisRenderer():IAxisRenderer
|
||||
{
|
||||
return this._axisRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set axisRenderer(value:IAxisRenderer):void
|
||||
{
|
||||
if(this._axisRenderer != value)
|
||||
{
|
||||
this._axisRenderer = value;
|
||||
this.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Protected Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override protected function draw():void
|
||||
{
|
||||
this.graphics.clear();
|
||||
|
||||
if(!this.axisRenderer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var showLines:Boolean = this.getStyleValue("showLines") as Boolean;
|
||||
var showMinorLines:Boolean = this.getStyleValue("showMinorLines") as Boolean;
|
||||
|
||||
//grab the line and minor line data from the axis renderer
|
||||
var lines:Array = this.axisRenderer.ticks.concat();
|
||||
var minorLines:Array = this.axisRenderer.minorTicks.concat();
|
||||
if(showMinorLines && showLines)
|
||||
{
|
||||
//filter out minor ticks that appear at the same position
|
||||
//as major ticks.
|
||||
minorLines = minorLines.filter(function(item:AxisData, index:int, source:Array):Boolean
|
||||
{
|
||||
return !lines.some(function(item2:AxisData, index2:int, source2:Array):Boolean
|
||||
{
|
||||
//using fuzzyEquals because we may encounter rounding errors
|
||||
return NumberUtil.fuzzyEquals(item.position, item2.position, 10);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var lineWeight:int = this.getStyleValue("lineWeight") as int;
|
||||
var lineColor:uint = this.getStyleValue("lineColor") as uint;
|
||||
var fillColors:Array = this.getStyleValue("fillColors") as Array;
|
||||
var fillAlphas:Array = this.getStyleValue("fillAlphas") as Array;
|
||||
this.drawLines(lines, showLines, lineWeight, lineColor, fillColors, fillAlphas);
|
||||
|
||||
var minorLineWeight:int = this.getStyleValue("minorLineWeight") as int;
|
||||
var minorLineColor:uint = this.getStyleValue("minorLineColor") as uint;
|
||||
this.drawLines(minorLines, showMinorLines, minorLineWeight, minorLineColor);
|
||||
|
||||
super.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a set of lines based on AxisData positioning.
|
||||
*/
|
||||
protected function drawLines(data:Array, showLines:Boolean, lineWeight:Number, lineColor:uint, fillColors:Array = null, fillAlphas:Array = null):void
|
||||
{
|
||||
var lastPosition:Number;
|
||||
var renderer:ICartesianAxisRenderer = ICartesianAxisRenderer(this.axisRenderer);
|
||||
var dataCount:int = data.length;
|
||||
|
||||
var showZeroGridLine:Boolean = this.getStyleValue("showZeroGridLine") as Boolean;
|
||||
var zeroGridLineWeight:Number = this.getStyleValue("zeroGridLineWeight") as Number;
|
||||
var zeroGridLineColor:uint = this.getStyleValue("zeroGridLineColor") as uint;
|
||||
for(var i:int = 0; i < dataCount; i++)
|
||||
{
|
||||
var axisData:AxisData = AxisData(data[i]);
|
||||
if(isNaN(axisData.position))
|
||||
{
|
||||
//skip bad positions
|
||||
continue;
|
||||
}
|
||||
|
||||
var position:Number = axisData.position;
|
||||
var nonOriginZero:Boolean =(i > 0 && axisData.value == 0 && showZeroGridLine);
|
||||
if(renderer.orientation == AxisOrientation.VERTICAL)
|
||||
{
|
||||
if(!isNaN(lastPosition) && fillColors && fillColors.length > 0)
|
||||
{
|
||||
var color:uint = fillColors[(i - 1) % fillColors.length];
|
||||
var alpha:Number = (fillAlphas && fillAlphas.length > 0) ? fillAlphas[(i - 1) % fillAlphas.length] : 1;
|
||||
this.graphics.lineStyle(0, 0, 0);
|
||||
this.graphics.beginFill(color, alpha);
|
||||
this.graphics.drawRect(0, lastPosition, renderer.contentBounds.width, position - lastPosition);
|
||||
this.graphics.endFill();
|
||||
}
|
||||
|
||||
if(showLines)
|
||||
{
|
||||
if(nonOriginZero)
|
||||
{
|
||||
this.graphics.lineStyle(zeroGridLineWeight, zeroGridLineColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.graphics.lineStyle(lineWeight, lineColor);
|
||||
}
|
||||
this.graphics.moveTo(0, position);
|
||||
this.graphics.lineTo(renderer.contentBounds.width, position);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!isNaN(lastPosition) && fillColors && fillColors.length > 0)
|
||||
{
|
||||
color = fillColors[(i - 1) % fillColors.length];
|
||||
alpha = (fillAlphas && fillAlphas.length > 0) ? fillAlphas[(i - 1) % fillAlphas.length] : 1;
|
||||
this.graphics.lineStyle(0, 0, 0);
|
||||
this.graphics.beginFill(color, alpha);
|
||||
this.graphics.drawRect(lastPosition, 0, position - lastPosition, renderer.contentBounds.height);
|
||||
this.graphics.endFill();
|
||||
}
|
||||
|
||||
if(showLines)
|
||||
{
|
||||
if(nonOriginZero)
|
||||
{
|
||||
this.graphics.lineStyle(zeroGridLineWeight, zeroGridLineColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.graphics.lineStyle(lineWeight, lineColor);
|
||||
}
|
||||
this.graphics.moveTo(position, 0);
|
||||
this.graphics.lineTo(position, renderer.contentBounds.height);
|
||||
}
|
||||
}
|
||||
|
||||
lastPosition = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
151
com/yahoo/astra/fl/charts/axes/IAxis.as
Executable file
151
com/yahoo/astra/fl/charts/axes/IAxis.as
Executable file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
import com.yahoo.astra.fl.charts.IChart;
|
||||
|
||||
/**
|
||||
* Data-only representation of a chart's axis.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public interface IAxis
|
||||
{
|
||||
/**
|
||||
* The chart in which this axis appears.
|
||||
*/
|
||||
function get chart():IChart;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set chart(value:IChart):void;
|
||||
|
||||
/**
|
||||
* The visual renderer associated with this axis.
|
||||
*/
|
||||
function get renderer():IAxisRenderer;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set renderer(value:IAxisRenderer):void;
|
||||
|
||||
/**
|
||||
* Sets the direction of the labels and other visual objects along the axis.
|
||||
* By default, vertical axes draw objects from bottom to top, and horizontal
|
||||
* axes draw objects from left to right.
|
||||
*/
|
||||
function get reverse():Boolean;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set reverse(value:Boolean):void;
|
||||
|
||||
/**
|
||||
* The text that will appear next to the axis to indicate information
|
||||
* about the data that it displays.
|
||||
*/
|
||||
function get title():String;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set title(value:String):void;
|
||||
|
||||
/**
|
||||
* A function may be set to determine the text value of the labels.
|
||||
*
|
||||
* <pre>function labelFunction(value:Object):String</pre>
|
||||
*/
|
||||
function get labelFunction():Function;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set labelFunction(value:Function):void
|
||||
|
||||
/**
|
||||
* Gets or sets the maximum width of a label
|
||||
*/
|
||||
function get maxLabelWidth():Number;
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
function set maxLabelWidth(value:Number):void;
|
||||
/**
|
||||
* Gets or sets the maximum width of a label
|
||||
*/
|
||||
function get maxLabelHeight():Number;
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
function set maxLabelHeight(value:Number):void;
|
||||
|
||||
/**
|
||||
* Data used in determining the axis scale
|
||||
*/
|
||||
function get dataProvider():Array;
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
function set dataProvider(value:Array):void;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function get numLabels():Number;
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
function set numLabels(value:Number):void;
|
||||
|
||||
/**
|
||||
* The space, in pixels, between labels on an axis.
|
||||
*/
|
||||
function get labelSpacing():Number;
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
function set labelSpacing(value:Number):void;
|
||||
|
||||
/**
|
||||
* Determines the axis scale based on the input data set.
|
||||
* Seperating this function from the draw method optimizes processing time,
|
||||
* and it allows the chart to synchronize its axes.
|
||||
*
|
||||
*/
|
||||
function updateScale():void;
|
||||
|
||||
/**
|
||||
* Calculates the position of a data point along the axis.
|
||||
*
|
||||
* @param value The data used to determine the position
|
||||
* @return The display position in pixels on the axis
|
||||
*/
|
||||
function valueToLocal(value:Object):Number;
|
||||
|
||||
/**
|
||||
* Converts a value on the axis to formatted label text.
|
||||
*
|
||||
* @param value the value of the item for which a label is needed
|
||||
* @return the formatted label text
|
||||
*/
|
||||
function valueToLabel(value:Object):String;
|
||||
|
||||
/**
|
||||
* Returns the maximum string length of a label on the axis.
|
||||
*
|
||||
* @return the formatted label
|
||||
*/
|
||||
function getMaxLabel():String;
|
||||
}
|
||||
}
|
50
com/yahoo/astra/fl/charts/axes/IAxisRenderer.as
Executable file
50
com/yahoo/astra/fl/charts/axes/IAxisRenderer.as
Executable file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
/**
|
||||
* A visual representation of an IAxis instance.
|
||||
*
|
||||
* Should be a subclass of UIComponent.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public interface IAxisRenderer
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* The total length of the axis renderer, in pixels.
|
||||
*/
|
||||
function get length():Number;
|
||||
|
||||
/**
|
||||
* An Array of AxisData objects specifying the positions of the ticks.
|
||||
*
|
||||
* @see AxisData
|
||||
*/
|
||||
function get ticks():Array;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set ticks(value:Array):void;
|
||||
|
||||
/**
|
||||
* An Array of AxisData objects specifying the positions of the minor ticks.
|
||||
*
|
||||
* @see AxisData
|
||||
*/
|
||||
function get minorTicks():Array
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set minorTicks(value:Array):void;
|
||||
}
|
||||
}
|
80
com/yahoo/astra/fl/charts/axes/ICartesianAxisRenderer.as
Executable file
80
com/yahoo/astra/fl/charts/axes/ICartesianAxisRenderer.as
Executable file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
import flash.geom.Rectangle;
|
||||
|
||||
/**
|
||||
* Interface for a cartesian chart's axis renderers.
|
||||
*
|
||||
* @see com.yahoo.astra.fl.charts.CartesianChart
|
||||
*/
|
||||
public interface ICartesianAxisRenderer extends IAxisRenderer
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the axis is displayed vertically or horizontally.
|
||||
*
|
||||
* @see com.yahoo.astra.fl.charts.axes.AxisOrientation
|
||||
*/
|
||||
function get orientation():String;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set orientation(value:String):void;
|
||||
|
||||
/**
|
||||
* The title text to display on the axis.
|
||||
*/
|
||||
function get title():String;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set title(value:String):void;
|
||||
|
||||
/**
|
||||
* Represents the area where content should be drawn within the axis.
|
||||
* This value is used to determine the containing chart's own
|
||||
* <code>contentBounds</code> property.
|
||||
*/
|
||||
function get contentBounds():Rectangle;
|
||||
|
||||
/**
|
||||
* Indicates the number of pixels of an outer tick.
|
||||
*/
|
||||
function get outerTickOffset():Number
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
function set outerTickOffset(value:Number):void
|
||||
|
||||
/**
|
||||
* Indicates whether the user explicitly set a major unit for the axis of this renderer.
|
||||
*/
|
||||
function get majorUnitSetByUser():Boolean;
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
function set majorUnitSetByUser(value:Boolean):void;
|
||||
//--------------------------------------
|
||||
// Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Calculates the <code>contentBounds</code> value for the axis renderer.
|
||||
* Seperating this function from the draw method optimizes processing time,
|
||||
* and it allows the chart to synchronize its axes.
|
||||
*/
|
||||
function updateAxis():void;
|
||||
}
|
||||
}
|
30
com/yahoo/astra/fl/charts/axes/IClusteringAxis.as
Executable file
30
com/yahoo/astra/fl/charts/axes/IClusteringAxis.as
Executable file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
/**
|
||||
* An axis that supports clustering. When combined with a series that
|
||||
* supports clustering, the number of clusters will allow the series to
|
||||
* determine the optimal positioning of markers.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public interface IClusteringAxis extends IAxis
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* The number of clusters available on the axis. In the case of the
|
||||
* CategoryAxis, this is the number of category names displayed on the
|
||||
* axis.
|
||||
*
|
||||
* @see CategoryAxis
|
||||
*/
|
||||
function get clusterCount():int;
|
||||
}
|
||||
}
|
30
com/yahoo/astra/fl/charts/axes/IGridLinesRenderer.as
Executable file
30
com/yahoo/astra/fl/charts/axes/IGridLinesRenderer.as
Executable file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
/**
|
||||
* A renderer for grid lines appearing on a chart's axis.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public interface IGridLinesRenderer
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* The axis renderer from which the grid lines receive their
|
||||
* major and minor unit data.
|
||||
*/
|
||||
function get axisRenderer():IAxisRenderer;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set axisRenderer(value:IAxisRenderer):void;
|
||||
}
|
||||
}
|
26
com/yahoo/astra/fl/charts/axes/IOriginAxis.as
Executable file
26
com/yahoo/astra/fl/charts/axes/IOriginAxis.as
Executable file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
/**
|
||||
* An axis with an origin.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public interface IOriginAxis extends IAxis
|
||||
{
|
||||
/**
|
||||
* Returns the value of the origin. This is not the position of the
|
||||
* origin. To get the origin's position, pass the origin value to
|
||||
* valueToLocal().
|
||||
*
|
||||
* Note: This value may not be the true origin value. It may be a
|
||||
* minimum or maximum value if the actual origin is not visible.
|
||||
*
|
||||
* @see IAxis#valueToLocal()
|
||||
*/
|
||||
function get origin():Object;
|
||||
}
|
||||
}
|
16
com/yahoo/astra/fl/charts/axes/IRadialAxisRenderer.as
Executable file
16
com/yahoo/astra/fl/charts/axes/IRadialAxisRenderer.as
Executable file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
/**
|
||||
* Interface for a chart's axis renderers.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public interface IRadialAxisRenderer extends IAxisRenderer
|
||||
{
|
||||
//nothing yet!
|
||||
}
|
||||
}
|
49
com/yahoo/astra/fl/charts/axes/IStackingAxis.as
Executable file
49
com/yahoo/astra/fl/charts/axes/IStackingAxis.as
Executable file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
import com.yahoo.astra.fl.charts.series.IStackedSeries;
|
||||
|
||||
/**
|
||||
* A type of axis that allows values to be stacked.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public interface IStackingAxis extends IAxis
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* If true, the axis will allow the stacking of series that implement
|
||||
* the interface IStackedSeries.
|
||||
*
|
||||
* <p>Must be explicitly enabled.
|
||||
*
|
||||
* @see com.yahoo.astra.fl.charts.series.IStackedSeries
|
||||
*/
|
||||
function get stackingEnabled():Boolean;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set stackingEnabled(value:Boolean):void;
|
||||
|
||||
//--------------------------------------
|
||||
// Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Calculates the sum of values if they were stacked on the axis.
|
||||
* The first value is important because some axis types, such as
|
||||
* NumericAxis, may differentiate between positive and negative values.
|
||||
*
|
||||
* @see NumericAxis
|
||||
*/
|
||||
function stack(top:Object, ...rest:Array):Object;
|
||||
}
|
||||
}
|
948
com/yahoo/astra/fl/charts/axes/NumericAxis.as
Executable file
948
com/yahoo/astra/fl/charts/axes/NumericAxis.as
Executable file
@ -0,0 +1,948 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
import com.yahoo.astra.fl.charts.series.ISeries;
|
||||
import com.yahoo.astra.fl.utils.UIComponentUtil;
|
||||
import com.yahoo.astra.utils.NumberUtil;
|
||||
import com.yahoo.astra.fl.charts.CartesianChart;
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
import fl.core.UIComponent;
|
||||
|
||||
/**
|
||||
* An axis type representing a numeric range from minimum to maximum
|
||||
* with major and minor divisions.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public class NumericAxis extends BaseAxis implements IAxis, IOriginAxis, IStackingAxis
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function NumericAxis()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* The multiplier used to calculate the position on the renderer from an
|
||||
* axis value.
|
||||
*/
|
||||
protected var positionMultiplier:Number = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the minimum value.
|
||||
*/
|
||||
private var _minimum:Number = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Indicates whether the minimum bound is user-defined or generated by the axis.
|
||||
*/
|
||||
private var _minimumSetByUser:Boolean = false;
|
||||
|
||||
/**
|
||||
* The minimum value displayed on the axis. By default, this value is generated
|
||||
* by the axis itself. If the user defines this value, the axis will skip this
|
||||
* automatic generation. To enable this behavior again, set this property to NaN.
|
||||
*/
|
||||
public function get minimum():Number
|
||||
{
|
||||
return this._minimum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set minimum(value:Number):void
|
||||
{
|
||||
this._minimum = value;
|
||||
this._minimumSetByUser = !isNaN(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the maximum value.
|
||||
*/
|
||||
private var _maximum:Number = 100;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Indicates whether the maximum bound is user-defined or generated by the axis.
|
||||
*/
|
||||
private var _maximumSetByUser:Boolean = false;
|
||||
|
||||
/**
|
||||
* The maximum value displayed on the axis. By default, this value is generated
|
||||
* by the axis itself. If the user defines this value, the axis will skip this
|
||||
* automatic generation. To enable this behavior again, set this property to NaN.
|
||||
*/
|
||||
public function get maximum():Number
|
||||
{
|
||||
return this._maximum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set maximum(value:Number):void
|
||||
{
|
||||
this._maximum = value;
|
||||
this._maximumSetByUser = !isNaN(value);
|
||||
}
|
||||
|
||||
//-- Units
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the major unit.
|
||||
*/
|
||||
private var _majorUnit:Number = 10;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Indicates whether the major unit is user-defined or generated by the axis.
|
||||
*/
|
||||
private var _majorUnitSetByUser:Boolean = false;
|
||||
|
||||
/**
|
||||
* The major unit at which new ticks and labels are drawn. By default, this value
|
||||
* is generated by the axis itself. If the user defines this value, the axis will
|
||||
* skip the automatic generation. To enable this behavior again, set this property
|
||||
* to NaN.
|
||||
*/
|
||||
public function get majorUnit():Number
|
||||
{
|
||||
return this._majorUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set majorUnit(value:Number):void
|
||||
{
|
||||
this._majorUnit = value;
|
||||
this._majorUnitSetByUser = !isNaN(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the minor unit.
|
||||
*/
|
||||
private var _minorUnit:Number = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Indicates whether the minor unit is user-defined or generated by the axis.
|
||||
*/
|
||||
private var _minorUnitSetByUser:Boolean = false;
|
||||
|
||||
/**
|
||||
* The minor unit at which new ticks are drawn. By default, this value
|
||||
* is generated by the axis itself. If the user defines this value, the axis will
|
||||
* skip the automatic generation. To enable this behavior again, set this property
|
||||
* to NaN.
|
||||
*/
|
||||
public function get minorUnit():Number
|
||||
{
|
||||
return this._minorUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set minorUnit(value:Number):void
|
||||
{
|
||||
this._minorUnit = value;
|
||||
this._minorUnitSetByUser = !isNaN(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get origin():Object
|
||||
{
|
||||
var origin:Number = 0;
|
||||
if(this.scale == ScaleType.LOGARITHMIC)
|
||||
{
|
||||
origin = 1;
|
||||
}
|
||||
|
||||
origin = Math.max(origin, this.minimum);
|
||||
origin = Math.min(origin, this.maximum);
|
||||
return origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the stackingEnabled property.
|
||||
*/
|
||||
private var _stackingEnabled:Boolean = false;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get stackingEnabled():Boolean
|
||||
{
|
||||
return this._stackingEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set stackingEnabled(value:Boolean):void
|
||||
{
|
||||
this._stackingEnabled = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the alwaysShowZero property.
|
||||
*/
|
||||
private var _alwaysShowZero:Boolean = true;
|
||||
|
||||
/**
|
||||
* If true, the axis will attempt to keep zero visible at all times.
|
||||
* If both the minimum and maximum values displayed on the axis are
|
||||
* above zero, the minimum will be reset to zero. If both minimum and
|
||||
* maximum appear below zero, the maximum will be reset to zero. If
|
||||
* the minimum and maximum appear at positive and negative values
|
||||
* respectively, zero is already visible and the axis scale does not
|
||||
* change.
|
||||
*
|
||||
* <p>This property has no affect if you manually set the minimum and
|
||||
* maximum values of the axis.</p>
|
||||
*/
|
||||
public function get alwaysShowZero():Boolean
|
||||
{
|
||||
return this._alwaysShowZero;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set alwaysShowZero(value:Boolean):void
|
||||
{
|
||||
this._alwaysShowZero = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the snapToUnits property.
|
||||
*/
|
||||
private var _snapToUnits:Boolean = true;
|
||||
|
||||
/**
|
||||
* If true, the labels, ticks, gridlines, and other objects will snap to
|
||||
* the nearest major or minor unit. If false, their position will be based
|
||||
* on the minimum value.
|
||||
*/
|
||||
public function get snapToUnits():Boolean
|
||||
{
|
||||
return this._snapToUnits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set snapToUnits(value:Boolean):void
|
||||
{
|
||||
this._snapToUnits = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the scale property.
|
||||
*/
|
||||
private var _scale:String = ScaleType.LINEAR;
|
||||
|
||||
/**
|
||||
* The type of scaling used to display items on the axis.
|
||||
*
|
||||
* @see com.yahoo.astra.fl.charts.ScaleType
|
||||
*/
|
||||
public function get scale():String
|
||||
{
|
||||
return this._scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set scale(value:String):void
|
||||
{
|
||||
this._scale = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _dataMinimum:Number = NaN;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _dataMaximum:Number = NaN;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _numLabels:Number;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _numLabelsSetByUser:Boolean = false;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get numLabels():Number
|
||||
{
|
||||
return _numLabels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
public function set numLabels(value:Number):void
|
||||
{
|
||||
if(_numLabelsSetByUser) return;
|
||||
_numLabels = value;
|
||||
_numLabelsSetByUser = true;
|
||||
_majorUnitSetByUser = false;
|
||||
_minorUnitSetByUser = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _roundMajorUnit:Boolean = true;
|
||||
|
||||
/**
|
||||
* Indicates whether to round the major unit
|
||||
*/
|
||||
public function get roundMajorUnit():Boolean
|
||||
{
|
||||
return _roundMajorUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
public function set roundMajorUnit(value:Boolean):void
|
||||
{
|
||||
_roundMajorUnit = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Holds value for idealPixels
|
||||
*/
|
||||
private var _idealPixels:Number = 70;
|
||||
|
||||
/**
|
||||
* Desired distance between majorUnits. Used to calculate the major unit
|
||||
* when unspecified and <code>calculateByLabelSize</code> is set to false.
|
||||
*/
|
||||
public function get idealPixels():Number
|
||||
{
|
||||
return _idealPixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
public function set idealPixels(value:Number):void
|
||||
{
|
||||
_idealPixels = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Holds value for calculateByLabelSize
|
||||
*/
|
||||
private var _calculateByLabelSize:Boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates whether to use the maximum size of an axis label
|
||||
* when calculating the majorUnit.
|
||||
*/
|
||||
public function get calculateByLabelSize():Boolean
|
||||
{
|
||||
return _calculateByLabelSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private (setter)
|
||||
*/
|
||||
public function set calculateByLabelSize(value:Boolean):void
|
||||
{
|
||||
_calculateByLabelSize = value;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Public Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function valueToLocal(data:Object):Number
|
||||
{
|
||||
if(data == null)
|
||||
{
|
||||
//bad data. a properly-designed renderer will not draw this.
|
||||
return NaN;
|
||||
}
|
||||
|
||||
var position:Number = 0;
|
||||
|
||||
if(this.scale == ScaleType.LINEAR)
|
||||
{
|
||||
position = (Number(data) - this.minimum) * this.positionMultiplier;
|
||||
}
|
||||
else
|
||||
{
|
||||
var logOfData:Number = Math.log(Number(data));
|
||||
var logOfMinimum:Number = Math.log(this.minimum);
|
||||
position = (logOfData - logOfMinimum) * this.positionMultiplier;
|
||||
}
|
||||
|
||||
if(this.reverse)
|
||||
{
|
||||
position = this.renderer.length - position;
|
||||
}
|
||||
|
||||
//the vertical axis has its origin on the bottom
|
||||
if(this.renderer is ICartesianAxisRenderer && ICartesianAxisRenderer(this.renderer).orientation == AxisOrientation.VERTICAL)
|
||||
{
|
||||
position = this.renderer.length - position;
|
||||
}
|
||||
|
||||
return Math.round(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function stack(top:Object, ...rest:Array):Object
|
||||
{
|
||||
var numericValue:Number = Number(top);
|
||||
var negative:Boolean = false;
|
||||
if(numericValue < 0)
|
||||
{
|
||||
negative = true;
|
||||
}
|
||||
|
||||
var restCount:int = rest.length;
|
||||
for(var i:int = 0; i < restCount; i++)
|
||||
{
|
||||
var currentValue:Number = Number(rest[i]);
|
||||
if(negative && currentValue < 0)
|
||||
{
|
||||
numericValue += currentValue;
|
||||
}
|
||||
else if(!negative && currentValue > 0)
|
||||
{
|
||||
numericValue += currentValue;
|
||||
}
|
||||
}
|
||||
return numericValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function updateScale():void
|
||||
{
|
||||
this.resetScale();
|
||||
this.calculatePositionMultiplier();
|
||||
|
||||
(this.renderer as ICartesianAxisRenderer).majorUnitSetByUser = this._majorUnitSetByUser;
|
||||
this.renderer.ticks = this.createAxisData(this.majorUnit);
|
||||
this.renderer.minorTicks = this.createAxisData(this.minorUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getMaxLabel():String
|
||||
{
|
||||
var difference:Number = Math.round(this.maximum - this.minimum);
|
||||
var maxString:String = this.valueToLabel(this.maximum);
|
||||
var minString:String = this.valueToLabel(this.minimum);
|
||||
var halfString:String = this.valueToLabel(Math.round(difference/2));
|
||||
var thirdString:String = this.valueToLabel(Math.round(difference/3));
|
||||
if(maxString.length < minString.length) maxString = minString;
|
||||
if(halfString.length > maxString.length) maxString = halfString;
|
||||
if(thirdString.length > maxString.length) maxString = thirdString;
|
||||
return maxString as String;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Protected Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* If the minimum, maximum, major unit or minor unit have not been set by the user,
|
||||
* these values must be generated by the axis. May be overridden to use custom
|
||||
* scaling algorithms.
|
||||
*/
|
||||
protected function resetScale():void
|
||||
{
|
||||
//use the discovered min and max from the data
|
||||
//if the developer didn't specify anything
|
||||
if(!this._minimumSetByUser)
|
||||
{
|
||||
this._minimum = this._dataMinimum;
|
||||
}
|
||||
if(!this._maximumSetByUser)
|
||||
{
|
||||
this._maximum = this._dataMaximum;
|
||||
}
|
||||
|
||||
this.checkMinLessThanMax();
|
||||
|
||||
this.pinToOrigin();
|
||||
|
||||
this.calculateMajorUnit();
|
||||
this.adjustMinAndMaxFromMajorUnit();
|
||||
|
||||
this.correctLogScaleMinimum();
|
||||
|
||||
//ensure that min != max
|
||||
if(!this._maximumSetByUser && this._minimum == this._maximum)
|
||||
{
|
||||
this._maximum = this._minimum + 1;
|
||||
if(!this._majorUnitSetByUser)
|
||||
{
|
||||
//rarely happens, so I'll hardcode a nice major unit
|
||||
//for our difference of one
|
||||
this._majorUnit = 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
this.calculateMinorUnit();
|
||||
|
||||
//even if they are manually set by the user, check all values for possible floating point errors.
|
||||
//we don't want extra labels or anything like that!
|
||||
this._minimum = NumberUtil.roundToPrecision(this._minimum, 10);
|
||||
this._maximum = NumberUtil.roundToPrecision(this._maximum, 10);
|
||||
this._majorUnit = NumberUtil.roundToPrecision(this._majorUnit, 10);
|
||||
this._minorUnit = NumberUtil.roundToPrecision(this._minorUnit, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Determines the best major unit.
|
||||
*/
|
||||
protected function calculateMajorUnit():void
|
||||
{
|
||||
if(this._majorUnitSetByUser)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var chart:CartesianChart = this.chart as CartesianChart;
|
||||
var labelSpacing:Number = 0;
|
||||
var approxLabelDistance:Number = this.idealPixels;
|
||||
var overflow:Number = 0;
|
||||
if(this.calculateByLabelSize)
|
||||
{
|
||||
var rotation:Number;
|
||||
//Check to see if this axis is horizontal. Since the width of labels will be variable, we will need to apply a different alogrithm to determine the majorUnit.
|
||||
if(chart.horizontalAxis == this)
|
||||
{
|
||||
//extract the approximate width of the labels by getting the textWidth of the maximum date when rendered by the label function with the textFormat of the renderer.
|
||||
approxLabelDistance = this.maxLabelWidth;
|
||||
rotation = chart.getHorizontalAxisStyle("rotation") as Number;
|
||||
if(rotation >= 0)
|
||||
{
|
||||
if(!isNaN(chart.horizontalAxisLabelData.rightLabelOffset)) overflow += chart.horizontalAxisLabelData.rightLabelOffset as Number;
|
||||
}
|
||||
if(rotation <= 0)
|
||||
{
|
||||
if(!isNaN(chart.horizontalAxisLabelData.leftLabelOffset)) overflow += chart.horizontalAxisLabelData.leftLabelOffset as Number;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
approxLabelDistance = this.maxLabelHeight;
|
||||
rotation = chart.getVerticalAxisStyle("rotation") as Number;
|
||||
if(!isNaN(chart.verticalAxisLabelData.topLabelOffset)) overflow = chart.verticalAxisLabelData.topLabelOffset as Number;
|
||||
}
|
||||
labelSpacing = this.labelSpacing;
|
||||
approxLabelDistance += (labelSpacing*2);
|
||||
}
|
||||
|
||||
var difference:Number = this.maximum - this.minimum;
|
||||
var tempMajorUnit:Number = 0;
|
||||
|
||||
var maxLabels:Number = ((this.renderer.length + overflow) - labelSpacing)/approxLabelDistance;
|
||||
|
||||
if(this.calculateByLabelSize)
|
||||
{
|
||||
maxLabels = Math.floor(maxLabels);
|
||||
//Adjust the max labels to account for potential maximum and minimum adjustments that may occur.
|
||||
if(!this._maximumSetByUser && !this._minimumSetByUser && !(this.alwaysShowZero && this._minimum == 0)) maxLabels -= 1;
|
||||
}
|
||||
|
||||
//If set by user, use specified number of labels unless its too many
|
||||
if(this._numLabelsSetByUser)
|
||||
{
|
||||
maxLabels = Math.min(maxLabels, this.numLabels);
|
||||
}
|
||||
|
||||
tempMajorUnit = difference/maxLabels;
|
||||
|
||||
if(!this.calculateByLabelSize)
|
||||
{
|
||||
tempMajorUnit = this.niceNumber(tempMajorUnit);
|
||||
}
|
||||
else if(this.roundMajorUnit)
|
||||
{
|
||||
var order:Number = Math.ceil(Math.log(tempMajorUnit) * Math.LOG10E);
|
||||
var roundedMajorUnit:Number = Math.pow(10, order);
|
||||
|
||||
if (roundedMajorUnit / 2 >= tempMajorUnit)
|
||||
{
|
||||
var roundedDiff:Number = Math.floor((roundedMajorUnit / 2 - tempMajorUnit) / (Math.pow(10,order-1)/2));
|
||||
tempMajorUnit = roundedMajorUnit/2 - roundedDiff*Math.pow(10,order-1)/2;
|
||||
}
|
||||
else
|
||||
{
|
||||
tempMajorUnit = roundedMajorUnit;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isNaN(tempMajorUnit)) this._majorUnit = tempMajorUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Determines the best minor unit.
|
||||
*/
|
||||
protected function calculateMinorUnit():void
|
||||
{
|
||||
if(this._minorUnitSetByUser)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var range:Number = this.maximum - this.minimum;
|
||||
var majorUnitSpacing:Number = this.renderer.length * (this.majorUnit / range);
|
||||
|
||||
if(this._majorUnit != 1)
|
||||
{
|
||||
if(this._majorUnit % 2 == 0)
|
||||
{
|
||||
this._minorUnit = this._majorUnit / 2;
|
||||
}
|
||||
else if(this._majorUnit % 3 == 0)
|
||||
{
|
||||
this._minorUnit = this._majorUnit / 3;
|
||||
}
|
||||
else this._minorUnit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Creates the AxisData objects for the axis renderer.
|
||||
*/
|
||||
protected function createAxisData(unit:Number):Array
|
||||
{
|
||||
if(unit <= 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var data:Array = [];
|
||||
var displayedMaximum:Boolean = false;
|
||||
var value:Number = this.minimum;
|
||||
while(value < this.maximum || NumberUtil.fuzzyEquals(value, this.maximum))
|
||||
{
|
||||
if(value % 1 != 0) value = NumberUtil.roundToPrecision(value, 10);
|
||||
|
||||
//because Flash UIComponents round the position to the nearest pixel, we need to do the same.
|
||||
var position:Number = Math.round(this.valueToLocal(value));
|
||||
var label:String = this.valueToLabel(value);
|
||||
var axisData:AxisData = new AxisData(position, value, label);
|
||||
data.push(axisData);
|
||||
|
||||
//if the maximum has been displayed, we're done!
|
||||
if(displayedMaximum) break;
|
||||
|
||||
//a bad unit will get us stuck in an infinite loop
|
||||
if(unit <= 0)
|
||||
{
|
||||
value = this.maximum;
|
||||
}
|
||||
else
|
||||
{
|
||||
value += unit;
|
||||
if(this.snapToUnits && !this._minimumSetByUser && this.alwaysShowZero)
|
||||
{
|
||||
value = NumberUtil.roundDownToNearest(value, unit);
|
||||
}
|
||||
if(this._majorUnitSetByUser) value = Math.min(value, this.maximum);
|
||||
}
|
||||
displayedMaximum = NumberUtil.fuzzyEquals(value, this.maximum);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Private Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* If we want to always show zero, corrects the min or max as needed.
|
||||
*/
|
||||
private function pinToOrigin():void
|
||||
{
|
||||
//if we're pinned to zero, and min or max is supposed to be generated,
|
||||
//make sure zero is somewhere in the range
|
||||
if(this.alwaysShowZero)
|
||||
{
|
||||
if(!this._minimumSetByUser && this._minimum > 0 && this._maximum > 0)
|
||||
{
|
||||
this._minimum = 0;
|
||||
}
|
||||
else if(!this._maximumSetByUser && this._minimum < 0 && this._maximum < 0)
|
||||
{
|
||||
this._maximum = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Increases the maximum and decreases the minimum based on the major unit.
|
||||
*/
|
||||
private function adjustMinAndMaxFromMajorUnit():void
|
||||
{
|
||||
//adjust the maximum so that it appears on a major unit
|
||||
//but don't change the maximum if the user set it or it is pinned to zero
|
||||
if(!this._maximumSetByUser && !(this.alwaysShowZero && this._maximum == 0))
|
||||
{
|
||||
var oldMaximum:Number = this._maximum;
|
||||
if(this._minimumSetByUser)
|
||||
{
|
||||
//if the user sets the minimum, we need to ensure that the maximum is an increment of the major unit starting from
|
||||
//the minimum instead of zero
|
||||
this._maximum = NumberUtil.roundUpToNearest(this._maximum - this._minimum, this._majorUnit);
|
||||
this._maximum += this._minimum;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._maximum = NumberUtil.roundUpToNearest(this._maximum, this._majorUnit);
|
||||
}
|
||||
|
||||
//uncomment to include an additional major unit in this adjustment
|
||||
if(this._maximum == oldMaximum /*|| this._maximum - oldMaximum < this._majorUnit */)
|
||||
{
|
||||
this._maximum += this._majorUnit;
|
||||
}
|
||||
}
|
||||
|
||||
//adjust the minimum so that it appears on a major unit
|
||||
//but don't change the minimum if the user set it or it is pinned to zero
|
||||
if(!this._minimumSetByUser && !(this.alwaysShowZero && this._minimum == 0))
|
||||
{
|
||||
var oldMinimum:Number = this._minimum;
|
||||
this._minimum = NumberUtil.roundDownToNearest(this._minimum, this._majorUnit);
|
||||
|
||||
//uncomment to include an additional major unit in this adjustment
|
||||
if(this._minimum == oldMinimum /*|| oldMinimum - this._minimum < this._majorUnit*/)
|
||||
{
|
||||
this._minimum -= this._majorUnit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* If we're using logarithmic scale, corrects the minimum if it gets set
|
||||
* to a value <= 0.
|
||||
*/
|
||||
private function correctLogScaleMinimum():void
|
||||
{
|
||||
//logarithmic scale can't have a minimum value <= 0. If that's the case, push it up to 1.0
|
||||
//TODO: Determine if there's a better way to handle this...
|
||||
if(!this._minimumSetByUser && this.scale == ScaleType.LOGARITHMIC && this._minimum <= 0)
|
||||
{
|
||||
//use the dataMinimum if it's between 0 and 1
|
||||
//otherwise, just use 1
|
||||
if(this._dataMinimum > 0 && this._dataMinimum < 1)
|
||||
{
|
||||
this._minimum = this._dataMinimum;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._minimum = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Calculates a "nice" number for use with major or minor units
|
||||
* on the axis. Only returns numbers similar to 10, 20, 25, and 50.
|
||||
*/
|
||||
private function niceNumber(value:Number):Number
|
||||
{
|
||||
if(value == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var count:int = 0;
|
||||
while(value > 10.0e-8)
|
||||
{
|
||||
value /= 10;
|
||||
count++;
|
||||
}
|
||||
|
||||
//all that division in the while loop up there
|
||||
//could cause rounding errors. Don't you hate that?
|
||||
value = NumberUtil.roundToPrecision(value, 10);
|
||||
|
||||
if(value > 4.0e-8)
|
||||
{
|
||||
value = 5.0e-8;
|
||||
}
|
||||
else if(value > 2.0e-8)
|
||||
{
|
||||
value = 2.5e-8;
|
||||
}
|
||||
else if(value > 1.0e-8)
|
||||
{
|
||||
value = 2.0e-8;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = 1.0e-8;
|
||||
}
|
||||
|
||||
for(var i:int = count; i > 0; i--)
|
||||
{
|
||||
value *= 10;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Swaps the minimum and maximum values, if needed.
|
||||
*/
|
||||
private function checkMinLessThanMax():void
|
||||
{
|
||||
if(this._minimum > this._maximum)
|
||||
{
|
||||
var temp:Number = this._minimum;
|
||||
this._minimum = this._maximum;
|
||||
this._maximum = temp;
|
||||
|
||||
//be sure to swap these flags too!
|
||||
var temp2:Boolean = this._minimumSetByUser;
|
||||
this._minimumSetByUser = this._maximumSetByUser;
|
||||
this._maximumSetByUser = temp2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Calculates the multiplier used to convert a data point to an actual position
|
||||
* on the axis.
|
||||
*/
|
||||
private function calculatePositionMultiplier():void
|
||||
{
|
||||
var range:Number = this.maximum - this.minimum;
|
||||
if(this.scale == ScaleType.LOGARITHMIC)
|
||||
{
|
||||
range = Math.log(this.maximum) - Math.log(this.minimum);
|
||||
}
|
||||
|
||||
if(range == 0)
|
||||
{
|
||||
this.positionMultiplier = 0;
|
||||
return;
|
||||
}
|
||||
this.positionMultiplier = this.renderer.length / range;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override protected function parseDataProvider():void
|
||||
{
|
||||
var seriesCount:int = this.dataProvider.length;
|
||||
var dataMinimum:Number = NaN;
|
||||
var dataMaximum:Number = NaN;
|
||||
for(var i:int = 0; i < seriesCount; i++)
|
||||
{
|
||||
var series:ISeries = this.dataProvider[i] as ISeries;
|
||||
var seriesLength:int = series.length;
|
||||
for(var j:int = 0; j < seriesLength; j++)
|
||||
{
|
||||
var item:Object = series.dataProvider[j];
|
||||
if(item === null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//automatically calculates stacked values
|
||||
var value:Number = Number(this.chart.itemToAxisValue(series, j, this));
|
||||
if(isNaN(value))
|
||||
{
|
||||
continue; //skip bad data
|
||||
}
|
||||
|
||||
//don't let bad data propogate
|
||||
//Math.min()/Math.max() with a NaN argument will choose NaN. Ya Rly.
|
||||
dataMinimum = isNaN(dataMinimum) ? value : Math.min(dataMinimum, value);
|
||||
dataMaximum = isNaN(dataMaximum) ? value : Math.max(dataMaximum, value);
|
||||
}
|
||||
}
|
||||
|
||||
if(!isNaN(dataMinimum) && !isNaN(dataMaximum))
|
||||
{
|
||||
this._dataMinimum = dataMinimum;
|
||||
this._dataMaximum = dataMaximum;
|
||||
}
|
||||
else
|
||||
{
|
||||
//some sensible defaults
|
||||
this._dataMinimum = 0;
|
||||
this._dataMaximum = 1;
|
||||
}
|
||||
|
||||
if(!this._minimumSetByUser)
|
||||
{
|
||||
this._minimum = this._dataMinimum;
|
||||
}
|
||||
if(!this._maximumSetByUser)
|
||||
{
|
||||
this._maximum = this._dataMaximum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
273
com/yahoo/astra/fl/charts/axes/RadialAxisRenderer.as
Executable file
273
com/yahoo/astra/fl/charts/axes/RadialAxisRenderer.as
Executable file
@ -0,0 +1,273 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
import com.yahoo.astra.utils.GeomUtil;
|
||||
import com.yahoo.astra.utils.NumberUtil;
|
||||
|
||||
import fl.core.UIComponent;
|
||||
|
||||
import flash.geom.Point;
|
||||
|
||||
//TODO: Add support for labels.
|
||||
/**
|
||||
* The default axis renderer for radial axes.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public class RadialAxisRenderer extends UIComponent implements IRadialAxisRenderer
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Class Variables
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private static var defaultStyles:Object =
|
||||
{
|
||||
//axis
|
||||
showAxis: true,
|
||||
axisWeight: 1,
|
||||
axisColor: 0x888a85,
|
||||
|
||||
//ticks
|
||||
showTicks: true,
|
||||
tickWeight: 1,
|
||||
tickColor: 0x888a85,
|
||||
tickLength: 4,
|
||||
tickPosition: TickPosition.INSIDE,
|
||||
|
||||
//minor ticks
|
||||
showMinorTicks: true,
|
||||
minorTickWeight: 1,
|
||||
minorTickColor: 0x888a85,
|
||||
minorTickLength: 3,
|
||||
minorTickPosition: TickPosition.INSIDE
|
||||
};
|
||||
|
||||
//--------------------------------------
|
||||
// Class Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @copy fl.core.UIComponent#getStyleDefinition()
|
||||
*/
|
||||
public static function getStyleDefinition():Object
|
||||
{
|
||||
return mergeStyles(defaultStyles, UIComponent.getStyleDefinition());
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function RadialAxisRenderer()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Properties
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get length():Number
|
||||
{
|
||||
return Math.min(this.width, this.height) * Math.PI;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the ticks property.
|
||||
*/
|
||||
private var _ticks:Array = [];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get ticks():Array
|
||||
{
|
||||
return this._ticks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set ticks(value:Array):void
|
||||
{
|
||||
this._ticks = value;
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Storage for the minorTicks property.
|
||||
*/
|
||||
private var _minorTicks:Array = [];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get minorTicks():Array
|
||||
{
|
||||
return this._minorTicks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set minorTicks(value:Array):void
|
||||
{
|
||||
this._minorTicks = value;
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Public Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function updateBounds():void
|
||||
{
|
||||
//no labels are created at this time, so this function is pretty useless
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Protected Methods
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
override protected function draw():void
|
||||
{
|
||||
var showTicks:Boolean = this.getStyleValue("showTicks") as Boolean;
|
||||
var showMinorTicks:Boolean = this.getStyleValue("showMinorTicks") as Boolean;
|
||||
var ticks:Array = this.ticks.concat();
|
||||
var minorTicks:Array = this.minorTicks.concat();
|
||||
if(showMinorTicks && showTicks)
|
||||
{
|
||||
//filter out minor ticks that appear at the same position
|
||||
//as major ticks.
|
||||
minorTicks = minorTicks.filter(function(item:AxisData, index:int, source:Array):Boolean
|
||||
{
|
||||
return !ticks.some(function(item2:AxisData, index2:int, source2:Array):Boolean
|
||||
{
|
||||
//using fuzzyEquals because we may encounter rounding errors
|
||||
return NumberUtil.fuzzyEquals(item.position, item2.position, 10);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.graphics.clear();
|
||||
|
||||
this.drawAxis();
|
||||
|
||||
var tickPosition:String = this.getStyleValue("tickPosition") as String;
|
||||
var tickLength:Number = this.getStyleValue("tickLength") as Number;
|
||||
var tickWeight:int = this.getStyleValue("tickWeight") as int;
|
||||
var tickColor:uint = this.getStyleValue("tickColor") as uint;
|
||||
this.drawTicks(ticks, showTicks, tickPosition, tickLength, tickWeight, tickColor);
|
||||
|
||||
var minorTickPosition:String = this.getStyleValue("minorTickPosition") as String;
|
||||
var minorTickLength:Number = this.getStyleValue("minorTickLength") as Number;
|
||||
var minorTickWeight:int = this.getStyleValue("minorTickWeight") as int;
|
||||
var minorTickColor:uint = this.getStyleValue("minorTickColor") as uint;
|
||||
this.drawTicks(minorTicks, showMinorTicks, minorTickPosition, minorTickLength, minorTickWeight, minorTickColor);
|
||||
|
||||
super.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Draws the main axis line.
|
||||
*/
|
||||
protected function drawAxis():void
|
||||
{
|
||||
var showAxis:Boolean = this.getStyleValue("showAxis") as Boolean;
|
||||
if(!showAxis)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var axisWeight:int = this.getStyleValue("axisWeight") as int;
|
||||
var axisColor:uint = this.getStyleValue("axisColor") as uint;
|
||||
this.graphics.lineStyle(axisWeight, axisColor);
|
||||
|
||||
var center:Point = new Point(this.width / 2, this.height / 2);
|
||||
var radius:Number = Math.min(center.x, center.y);
|
||||
this.graphics.drawCircle(center.x, center.y, radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Draws a set of ticks along the main axis line. This function is shared
|
||||
* by major and minor ticks.
|
||||
*/
|
||||
protected function drawTicks(data:Array, showTicks:Boolean, tickPosition:String,
|
||||
tickLength:Number, tickWeight:Number, tickColor:uint):void
|
||||
{
|
||||
if(!showTicks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.graphics.lineStyle(tickWeight, tickColor);
|
||||
|
||||
var center:Point = new Point(this.width / 2, this.height / 2);
|
||||
var radius:Number = Math.min(center.x, center.y);
|
||||
|
||||
var dataCount:int = data.length;
|
||||
for(var i:int = 0; i < dataCount; i++)
|
||||
{
|
||||
var axisData:AxisData = AxisData(data[i]);
|
||||
if(isNaN(axisData.position))
|
||||
{
|
||||
//skip bad positions
|
||||
continue;
|
||||
}
|
||||
|
||||
var position:Number = axisData.position;
|
||||
var angle:Number = GeomUtil.degreesToRadians(position * 360 / this.length);
|
||||
var tickCenter:Point = Point.polar(radius, angle);
|
||||
tickCenter = tickCenter.add(center);
|
||||
switch(tickPosition)
|
||||
{
|
||||
case TickPosition.OUTSIDE:
|
||||
var outsideEnd:Point = Point.polar(tickLength, angle);
|
||||
outsideEnd = outsideEnd.add(tickCenter);
|
||||
this.graphics.moveTo(tickCenter.x, tickCenter.y);
|
||||
this.graphics.lineTo(outsideEnd.x, outsideEnd.y);
|
||||
break;
|
||||
case TickPosition.INSIDE:
|
||||
var insideEnd:Point = Point.polar(tickLength, GeomUtil.degreesToRadians(180 + GeomUtil.radiansToDegrees(angle)));
|
||||
insideEnd = insideEnd.add(tickCenter);
|
||||
this.graphics.moveTo(tickCenter.x, tickCenter.y);
|
||||
this.graphics.lineTo(insideEnd.x, insideEnd.y);
|
||||
break;
|
||||
default: //CROSS
|
||||
outsideEnd = Point.polar(tickLength / 2, angle);
|
||||
outsideEnd = outsideEnd.add(tickCenter);
|
||||
insideEnd = Point.polar(tickLength / 2, GeomUtil.degreesToRadians(180 + GeomUtil.radiansToDegrees(angle)));
|
||||
insideEnd = insideEnd.add(tickCenter);
|
||||
this.graphics.moveTo(outsideEnd.x, outsideEnd.y);
|
||||
this.graphics.lineTo(insideEnd.x, insideEnd.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
31
com/yahoo/astra/fl/charts/axes/ScaleType.as
Executable file
31
com/yahoo/astra/fl/charts/axes/ScaleType.as
Executable file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
/**
|
||||
* Scale types available to <code>IAxis</code> objects.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public class ScaleType
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Constants
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* The ScaleType.LINEAR constant specifies that chart axis objects
|
||||
* should be displayed on a linear scale.
|
||||
*/
|
||||
public static const LINEAR:String = "linear";
|
||||
|
||||
/**
|
||||
* The ScaleType.LOGARITHMIC constant specifies that chart axis objects
|
||||
* should be displayed on a logarithmic scale.
|
||||
*/
|
||||
public static const LOGARITHMIC:String = "logarithmic";
|
||||
}
|
||||
}
|
37
com/yahoo/astra/fl/charts/axes/TickPosition.as
Executable file
37
com/yahoo/astra/fl/charts/axes/TickPosition.as
Executable file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
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.axes
|
||||
{
|
||||
/**
|
||||
* Position values available to axis ticks.
|
||||
*
|
||||
* @author Josh Tynjala
|
||||
*/
|
||||
public class TickPosition
|
||||
{
|
||||
|
||||
//--------------------------------------
|
||||
// Constants
|
||||
//--------------------------------------
|
||||
|
||||
/**
|
||||
* The TickPosition.OUTSIDE constant specifies that chart axis ticks
|
||||
* should be displayed on the outside of the axis.
|
||||
*/
|
||||
public static const OUTSIDE:String = "outside";
|
||||
|
||||
/**
|
||||
* The TickPosition.INSIDE constant specifies display of chart axis
|
||||
* ticks should be displayed on the inside of the axis.
|
||||
*/
|
||||
public static const INSIDE:String = "inside";
|
||||
|
||||
/**
|
||||
* The TickPosition.CROSS constant specifies display of chart axis ticks
|
||||
* should be displayed crossing the axis.
|
||||
*/
|
||||
public static const CROSS:String = "cross";
|
||||
}
|
||||
}
|
1005
com/yahoo/astra/fl/charts/axes/TimeAxis.as
Executable file
1005
com/yahoo/astra/fl/charts/axes/TimeAxis.as
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user