990 lines
26 KiB
ActionScript
Executable File
990 lines
26 KiB
ActionScript
Executable File
/*
|
|
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
|
|
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
|
|
*/
|
|
package com.yahoo.astra.fl.charts.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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |