blob: 55aa6420f67ec333870eb5fcee22c642397eccaa [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.charts.chartClasses
{
import flash.events.Event;
import mx.charts.AxisLabel;
import mx.core.mx_internal;
use namespace mx_internal;
/**
* The NumericAxis class acts as a common base class
* for axis types representing a continuous range of values
* between a defined minimum and maximum.
* The built-in LinearAxis, LogAxis, and DateTimeAxis
* classes all extend this base class.
*
* @see mx.charts.DateTimeAxis
* @see mx.charts.LinearAxis
* @see mx.charts.LogAxis
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class NumericAxis extends AxisBase implements IAxis
{
include "../../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function NumericAxis()
{
super();
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var _labelSet:AxisLabelSet;
/**
* @private
*/
private var _cachedDataDescriptions:Array /* of DataDescription */;
/**
* @private
*/
private var _cachedValuesHaveBounds:Boolean;
/**
* @private
*/
private var _regenerateAutoValues:Boolean = true;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// assignedMaximum
//----------------------------------
/**
* The explicitly assigned maximum value.
* If no value has been assigned, this will be <code>NaN</code>.
* Typically, calculations should be performed
* with the <code>computedMaximum</code> field.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var assignedMaximum:Number;
//----------------------------------
// assignedMinimum
//----------------------------------
/**
* The explicitly assigned minimum value.
* If no value has been assigned, this will be <code>NaN</code>.
* Typically calculations should be performed
* with the <code>computedMinimum</code> field.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var assignedMinimum:Number;
//----------------------------------
// autoAdjust
//----------------------------------
/**
* @private
* Storage for the autoAdjust property.
*/
private var _autoAdjust:Boolean = true;
[Inspectable(category="General", defaultValue="true")]
/**
* Specifies whether Flex rounds values.
* If <code>false</code>, Flex does not round the values
* set by the <code>minimum</code> and <code>maximum</code> properties,
* or modify the default <code>minimum</code> and
* <code>maximum</code> values.
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get autoAdjust():Boolean
{
return _autoAdjust;
}
/**
* @private
*/
public function set autoAdjust(value:Boolean):void
{
_autoAdjust = value;
dataChanged();
}
//----------------------------------
// baseAtZero
//----------------------------------
/**
* Storage for baseAtZero property
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
private var _baseAtZero:Boolean = true;
[Inspectable(category="General", defaultValue="true")]
/**
* Specifies whether Flex tries to keep the <code>minimum</code>
* and <code>maximum</code> values rooted at zero.
* If all axis values are positive, the minimum axis value is zero.
* If all axis values are negative, the maximum axis value is zero.
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get baseAtZero():Boolean
{
return _baseAtZero;
}
/**
* @private
*/
public function set baseAtZero(value:Boolean):void
{
_baseAtZero = value;
dataChanged();
}
//----------------------------------
// computedInterval
//----------------------------------
/**
* The computed interval represented by this axis.
* The <code>computedInterval</code> is used
* by the AxisRenderer and Gridlines classes
* to determine where to render tick marks and grid lines.
* The NumericAxis base class watches this field for changes
* to determine if the chart needs to be re-rendered.
* Derived classes are responsible for computing the value
* of this field.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var computedInterval:Number;
//----------------------------------
// computedMaximum
//----------------------------------
/**
* The computed maximum value represented by this axis.
* If the user has explicitly assigned a maximum value,
* the <code>computedMaximum</code> and
* <code>assignedMaximum</code> properties
* are usually the same.
* Otherwise, the <code>computedMaximum</code> is generated
* from the values represented in the chart.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var computedMaximum:Number;
//----------------------------------
// computedMinimum
//----------------------------------
/**
* The computed minimum value represented by this axis.
* If the user has explicitly assigned a minimum value,
* the <code>computedMinimum</code> and
* <code>assignedMinimum</code> properties
* are usually be the same.
* Otherwise, the <code>computedMinimum</code> is generated
* from the values represented in the chart.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var computedMinimum:Number;
//----------------------------------
// direction
//----------------------------------
/**
* @private
*/
private var _direction:String = "normal";
[Inspectable(category="General", enumeration="normal,inverted", defaultValue="normal")]
/**
* Determines the direction in which the axis is rendered.
* Possible values are <code>normal</code>,
* and <code>inverted</code>.
*
* All derived classes should take care of the way min and max
* are set depending on <code>direction</code>.
*
* All series should take care of the way it is rendered
* depending on the <code>direction</code> of its underlying axis.
*
* @default "normal"
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 4
*/
public function get direction():String
{
return _direction;
}
/**
* @private
*/
public function set direction(value:String):void
{
if(_direction != value)
{
_direction = value;
invalidateCache();
dataChanged();
}
}
//----------------------------------
// labelCache
//----------------------------------
/**
* The most recent set of AxisLabel objects
* generated to represent this axis.
* This property is <code>null</code> if the axis
* has been modified and requires new labels.
* To guarantee that the value of the <code>labelCache</code> property
* is correct, call the <code>buildLabelCache()</code> method
* before accessing the <code>labelCache</code> property.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var labelCache:Array /* of AxisLabel */;
//----------------------------------
// labelFunction
//----------------------------------
/**
* @private
* Storage for the labelFunction property.
*/
private var _labelFunction:Function = null;
[Inspectable(category="General")]
/**
* Called to format axis values for display as labels.
* A <code>labelFunction</code> has the following signature:
* <pre>
* function <i>function_name</i>(<i>labelValue</i>:Object, <i>previousValue</i>:Object, <i>axis</i>:IAxis):String { ... }
* </pre>
*
* <p>If you know the types of the data your function will be formatting,
* you can specify an explicit type for the <code>labelValue</code>
* and <code>previousValue</code> parameters.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get labelFunction():Function
{
return _labelFunction;
}
/**
* @private
*/
public function set labelFunction(value:Function):void
{
_labelFunction = value;
invalidateCache();
dispatchEvent(new Event("mappingChange"));
dispatchEvent(new Event("axisChange"));
}
//----------------------------------
// labelMaximum
//----------------------------------
/**
* The maximum value where a label should be placed.
* After computing an adjusted minimum value,
* many axis types expand the range of the axis further
* to make room for additional rendering artifacts in the chart,
* such as labels and borders.
* This value represents the maximum value in the chart
* <em>before</em> it is adjusted for these artifacts.
* Typically axes generate labels to make sure
* this value is labeled, rather than the adjusted maximum of the axis.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var labelMaximum:Number;
//----------------------------------
// labelMinimum
//----------------------------------
/**
* The minimum value where a label should be placed.
* After computing an adjusted minimum value,
* many axis types expand the range of the axis further
* to make room for additional rendering artifacts in the chart,
* such as labels and borders.
* This value represents the minimum value in the chart
* <em>before</em> it is adjusted for these artifacts.
* Typically axes will generate labels to make sure
* this value is labeled, rather than the adjusted minimum of the axis.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var labelMinimum:Number;
//----------------------------------
// mappedMaximum
//----------------------------------
[Inspectable(environment="none")]
/**
* The computed minimum value for the axis
* as long as this value is greater than 0.
* If the maximum value is less than or equal to 0,
* then the <code>baseline</code> property is the computed maximum.
* If neither value is greater than 0,
* then the <code>baseline</code> property is 0.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get baseline():Number
{
var baseVal:Number;
if (computedMinimum >= 0)
baseVal = computedMinimum;
else if (computedMaximum <= 0)
baseVal = computedMaximum;
else
baseVal = 0;
return baseVal;
}
//----------------------------------
// minorTickCache
//----------------------------------
/**
* The most recent set of minor tick marks generated to represent this axis.
* This property may be <code>null</code> if the axis
* has been modified and requires new labels and tick marks.
* Use the public accessor <code>minorTicks</code>
* to build the minor tick marks on demand.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var minorTickCache:Array /* of Number */;
//----------------------------------
// minorTicks
//----------------------------------
[Inspectable(environment="none")]
/**
* An Array of minor tick marks generated to represent this axis.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get minorTicks():Array /* of Number */
{
if (!minorTickCache)
minorTickCache = buildMinorTickCache();
return minorTickCache;
}
//----------------------------------
// padding
//----------------------------------
/**
* @private
* Storage for the padding property.
*/
private var _padding:Number;
[Inspectable(category="General")]
/**
* Specifies padding that Flex adds to the calculated minimum and maximum
* values for the axis when rendering the values on the screen.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get padding():Number
{
return _padding;
}
/**
* @private
*/
public function set padding(value:Number):void
{
_padding = value;
invalidateCache();
dispatchEvent(new Event("axisChange"));
}
//----------------------------------
// parseFunction
//----------------------------------
/**
* @private
* Storage for the parseFunction property.
*/
private var _parseFunction:Function = null;
[Inspectable(category="Other")]
/**
* Specify a <code>parseFunction</code> to customize how
* the values rendered by your chart are converted into numeric values.
* A custom <code>parseFunction</code> is passed a data value
* and should return a corresponding number representing the same value.
* By default, this axis uses the ECMA function <code>parseFloat()</code>.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get parseFunction():Function
{
return _parseFunction;
}
/**
* @private
*/
public function set parseFunction(value:Function):void
{
_parseFunction = value;
invalidateCache();
_cachedDataDescriptions = null;
dispatchEvent(new Event("mappingChange"));
dispatchEvent(new Event("axisChange"));
}
//----------------------------------
// requiredDescribedFields
//----------------------------------
/**
* The fields of the DescribeData structure that this axis is interested in.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function get requiredDescribedFields():uint
{
return DataDescription.REQUIRED_MIN_MAX |
DataDescription.REQUIRED_BOUNDED_VALUES;
}
//----------------------------------
// ticks
//----------------------------------
/**
* An Array of tick marks for this axis.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function get ticks():Array /* of Number */
{
var result:Array /* of Number */ = [];
var n:int = labelCache.length;
for (var i:int = 0; i < n; i++)
{
result.push(labelCache[i].position);
}
return result;
}
//----------------------------------
// zeroValue
//----------------------------------
/**
* @private
*/
mx_internal function get zeroValue():Number
{
return 0;
}
//--------------------------------------------------------------------------
//
// Overridden methods: AxisBase
//
//--------------------------------------------------------------------------
/**
* @private
*/
override public function dataChanged():void
{
// We don't call invalidateCache() here, because invalidateCache()
// forces a rebuild of the labels.
// But just changing the data doesn't guarantee the labels have changed.
// Instead, we need to recalc our autogenerated values,
// and then see if the values have changed.
minorTickCache = null;
_cachedDataDescriptions = null;
_regenerateAutoValues = true;
dispatchEvent(new Event("mappingChange"));
if (isNaN(assignedMinimum) || isNaN(assignedMaximum))
{
dispatchEvent(new Event("axisChange"));
}
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @copy mx.charts.chartClasses.IAxis#mapCache()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function mapCache(cache:Array /* of Object */, field:String,
convertedField:String,
indexValues:Boolean = false):void
{
var n:int = cache.length;
var i:int;
var v:Object;
if (_parseFunction != null)
{
for (i = 0; i < n; i++)
{
v = cache[i];
v[convertedField] = _parseFunction(v[field]);
}
}
else
{
for (i = 0; i < n && cache[i][field] == null; i++)
{
}
if (i == n)
return;
if ((cache[i][field] is String))
{
for (; i < n; i++)
{
v = cache[i];
v[convertedField] = Number(v[field]);
}
}
else if (cache[i][field] is XML ||
cache[i][field] is XMLList)
{
for (; i < n; i++)
{
v = cache[i];
v[convertedField] = parseFloat(v[field].toString());
}
}
else if (cache[i][field] is Number ||
cache[i][field] is int ||
cache[i][field] is uint)
{
for (; i < n; i++)
{
v = cache[i];
if (v[field] == null || v[field].toString() == "")
v[convertedField] = NaN;
else
v[convertedField] = v[field];
}
}
else
{
for (; i < n; i++)
{
v = cache[i];
v[convertedField] = parseFloat(v[field]);
}
}
}
}
/**
* @copy mx.charts.chartClasses.IAxis#filterCache()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function filterCache(cache:Array /* of Object */, field:String,
filteredField:String):void
{
update();
// Avoid roundoff errors.
var max:Number = computedMaximum + 0.00001;
var min:Number = computedMinimum - 0.00001;
var n:int = cache.length;
for (var i:int = 0; i < n; i++)
{
var v:Object = cache[i][field];
cache[i][filteredField] = v >= min && v <= max ? v : NaN;
}
}
/**
* @copy mx.charts.chartClasses.IAxis#transformCache()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function transformCache(cache:Array /* of Object */, field:String,
convertedField:String):void
{
update();
var r:Number = computedMaximum - computedMinimum;
var n:int = cache.length;
for (var i:int = 0; i < n; i++)
{
cache[i][convertedField] = (cache[i][field] - computedMinimum) / r;
}
}
/**
* @copy mx.charts.chartClasses.IAxis#invertTransform()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function invertTransform(value:Number):Object
{
update();
return value * (computedMaximum - computedMinimum) + computedMinimum;
}
/**
* @copy mx.charts.chartClasses.IAxis#formatForScreen()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function formatForScreen(value:Object):String
{
if(direction == "inverted")
value = -(Number(value)) as Object;
else
value = Number(value) as Object;
return value.toString();
}
/**
* @copy mx.charts.chartClasses.IAxis#getLabelEstimate()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function getLabelEstimate():AxisLabelSet
{
update();
var updated:Boolean = buildLabelCache();
if (updated)
{
_labelSet = new AxisLabelSet();
_labelSet.labels = labelCache;
_labelSet.accurate = _cachedValuesHaveBounds == false;
_labelSet.minorTicks = minorTicks;
_labelSet.ticks = ticks;
}
return _labelSet;
}
/**
* @copy mx.charts.chartClasses.IAxis#preferDropLabels()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function preferDropLabels():Boolean
{
return true;
}
/**
* @copy mx.charts.chartClasses.IAxis#getLabels()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function getLabels(minimumAxisLength:Number):AxisLabelSet
{
var updated:Boolean;
if (_cachedValuesHaveBounds || !labelCache)
{
_regenerateAutoValues = true;
updateCache(true,minimumAxisLength);
updated = buildLabelCache();
}
else
{
updated = false;
}
if (updated)
{
_labelSet = new AxisLabelSet();
_labelSet.labels = labelCache;
_labelSet.minorTicks = minorTicks;
_labelSet.ticks = ticks;
}
return _labelSet;
}
/**
* @copy mx.charts.chartClasses.IAxis#reduceLabels()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function reduceLabels(intervalStart:AxisLabel,
intervalEnd:AxisLabel):AxisLabelSet
{
return _labelSet;
}
/**
* Populates the <code>labelCache</code> property with labels representing the current
* values of the axis. Subclasses must implement this function. This function is called
* many times, so you should check to see if the <code>labelCache</code> property
* is <code>null</code> before performing any calculations.
*
* @return true if the labels were regenerated.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function buildLabelCache():Boolean
{
return false;
}
/**
* Builds an Array of positions for the minor tick marks Array that is generated by this axis.
* Subclasses must implement this function. This function is called automatically
* by the NumericAxis. You should access the <code>minorTicks</code> property
* instead of calling this function directly.
*
* @return An Array of positions from 0 to 1 that represent points between the axis
* minimum and maximum values where minor tick marks are rendered.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function buildMinorTickCache():Array /* of Number */
{
return [];
}
/**
* @private
*/
private function updateCache(checkForMargins:Boolean,
minimumAxisLength:Number):void
{
if (_regenerateAutoValues)
{
// We're going to remember these.
// As an optimization, if after going through all of this
// the values that generate our labels haven't changed,
// we won't bother regenerating the labels.
var oldMin:Number = computedMinimum;
var oldMax:Number = computedMaximum;
var oldInterval:Number = computedInterval;
// First we check and see if the author has provided
// any set min/max values.
if (!isNaN(assignedMinimum))
computedMinimum = assignedMinimum;
if (!isNaN(assignedMaximum))
computedMaximum = assignedMaximum;
// If either min or max is unset, we need to generate it.
var autoGen:Boolean = isNaN(assignedMinimum) ||
isNaN(assignedMaximum);
// If we still need either a min or a max, we'll autogenerate here.
if (autoGen)
autoGenerate(checkForMargins, minimumAxisLength);
// At this point we know we've got a valid min/max pair,
// either specified by the author or generated from the data.
// Next, we go into our adjustment routine.
// This is where we push those min/max values out to be nice,
// clean values that look good when you make labels out of them.
// The same algorithm is also how we generate a nice usable interval
// from the min/max values we have.so we're going to execute
// this section if either the user has asked us to autoadjust
// the min/max to nice clean values, or if the interval hasn't
// been set by the user.
// Once we've invoked the routine, we'll decide what we want
// to do with the numbers it generated based on why the routine
// got called in the first place.
adjustMinMax(computedMinimum,computedMaximum);
// OK, we've now calculated the ideal min/max values
// to show on the chart.
// However, there may be reasons we want to make the actual range
// of the chart spread a little bit further.
// Specifically, if there are series that need a little extra space
// around their data points, either for labels, or just pretty
// drawing,or other reasons (like BubbleSeries which render more
// than one axis in each dimension).
// But whatever the final range ends up being, we'll use
// the min/max values we have now as the outer labels of the chart.
labelMinimum = computedMinimum;
labelMaximum = computedMaximum;
// When do we adjust for margins?
// In general, it's if we autogenerated our min/max values
// and the user has asked us to make nice clean numbers around them.
// We also have two optimizations in here.
// First of all, if we auto generated our min/max from data
// and found that none of our data needed any space around
// their values, then this would be wasted effort.
// Secondly, there are times this function gets called
// when we're looking for an estimate...so we don't want to bother
// with margins at that point.
if ((autoAdjust || autoGen) &&
checkForMargins &&
_cachedValuesHaveBounds)
{
adjustForMargins(minimumAxisLength);
}
// If the author asked for additional padding outside
// the min/max values, give it to them here.
// However, it's possible that our margin calculations
// have already generated more padding than was requested.
// In which case we don't add more.
var us:Number = unitSize;
if (!isNaN(_padding))
{
if (labelMinimum - computedMinimum < _padding * us)
computedMinimum = labelMinimum - _padding * us;
if (computedMaximum - labelMaximum < _padding * us)
computedMaximum = labelMaximum + _padding * us;
}
// Authors don't get all the fun...
// series can ask for padding too.
// Check and see if any series have asked for padding,
// and do the same calculation for them.
var cachedDataDescriptions:Array /* of DataDescription */ = dataDescriptions;
var n:int = cachedDataDescriptions.length;
for (var i:int = 0; i < n; i++)
{
var desc:DataDescription = cachedDataDescriptions[i]
if (!isNaN(desc.padding))
{
if (isNaN(assignedMinimum) &&
desc.min - computedMinimum < desc.padding * us)
{
computedMinimum = desc.min - desc.padding * us;
}
if (isNaN(assignedMaximum) &&
computedMaximum - desc.max < desc.padding * us)
{
computedMaximum = desc.max + desc.padding * us;
}
}
}
if (computedMinimum == computedMaximum)
{
var us2:Number = unitSize/2;
computedMinimum -= us2;
computedMaximum += us2;
}
// Finally, if the min/max/interval has changed, invalidate it here.
if (oldMin != computedMinimum ||
oldMax != computedMaximum ||
oldInterval != computedInterval)
{
labelCache = null;
minorTickCache = null;
}
_regenerateAutoValues = false;
}
}
/**
* Invalidates the cached labels and tick marks that represent this axis's values.
* Derived classes should call this function whenever values used in the calculation
* of labels and tick marks change.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function invalidateCache():void
{
minorTickCache = null;
_regenerateAutoValues = true;
labelCache = null;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function update():void
{
updateCache(true, 0);
}
/**
* @private
*/
private function adjustForMargins(minimumAxisLength:Number):void
{
// Make sure we have room for bounded values.
// These are values that specify required padding in screen space
// (i.e., a bar chart will specify space for its label,
// in screen space).
// This is imperfect, because by necessity at this point we don't know
// how much space we'll really have on the axis.
// But if we calc now, and the axis gets smaller (it won't get bigger)
// we'll be conservative but safe.
var min:Number = computedMinimum;
var max:Number = computedMaximum;
var n:int;
var i:int;
var boundedValues:Array /* of BoundedValue */ = [];
var cachedDataDescriptions:Array /* of DataDescription */ = dataDescriptions;
var boundsWithinRegion:Boolean = true;
n = cachedDataDescriptions.length;
for (i = 0; i < n; i++)
{
if (cachedDataDescriptions[i].boundedValues)
{
boundedValues = boundedValues.concat(
cachedDataDescriptions[i].boundedValues);
}
}
n = boundedValues.length;
for (i = 0; i < n; i++)
{
if (!(boundedValues[i].lowerMargin < minimumAxisLength && boundedValues[i].upperMargin < minimumAxisLength))
boundsWithinRegion = false;
}
if (boundedValues.length > 0 && minimumAxisLength > 0 && boundsWithinRegion)
{
if (isNaN(min))
min = boundedValues[0].value;
if (isNaN(max))
max = boundedValues[0].value;
var axisLength:Number = minimumAxisLength;
var currentRange:Number = max - min;
var itrCount:int = 0;
n = boundedValues.length;
var verify:Boolean = true;
while (verify)
{
var lowestOverlap:Number = minimumAxisLength;
var lowestValue:BoundedValue;
var highestOverlap:Number = 0;
var highestValue:BoundedValue;
var v1:Number = max;
var m1:Number = 0;
var v2:Number = min;
var m2:Number = 0;
for (i = 0; i < n; i++)
{
var v:BoundedValue = boundedValues[i];
var vpos:Number =
(v.value - min) / currentRange * minimumAxisLength;
if ((!isNaN(v.lowerMargin)) &&
vpos - v.lowerMargin < lowestOverlap)
{
lowestOverlap = vpos - v.lowerMargin;
lowestValue = v;
}
if ((!isNaN(v.upperMargin)) &&
vpos + v.upperMargin> highestOverlap)
{
highestOverlap = vpos + v.upperMargin;
highestValue = v;
}
}
if (lowestOverlap > -0.0001 &&
highestOverlap < minimumAxisLength + 0.0001)
{
break;
}
if (highestOverlap > minimumAxisLength)
{
v1 = highestValue.value;
m1 = highestValue.upperMargin;
}
else
{
// If we're only adjusting one side of the equation,
// we know we're 100% accurate.
verify = false;
}
if (lowestOverlap < 0)
{
v2 = lowestValue.value;
m2 = lowestValue.lowerMargin;
}
else
{
// If we're only adjusting one side of the equation,
// we know we're 100% accurate.
verify = false;
}
var p1:Number = minimumAxisLength - m1;
min = (p1 * v2 - m2 * v1) / Math.abs(p1 - m2);
max = minimumAxisLength * (v1 - min) /
(minimumAxisLength - m1) + min;
currentRange = max - min;
if (itrCount++ == 3)
break;
}
}
var adjusted:Array /* of Number */ = guardMinMax(min, max);
if (adjusted)
{
min = adjusted[0];
max = adjusted[1];
}
if (isNaN(assignedMinimum))
computedMinimum = min;
if (isNaN(assignedMaximum))
computedMaximum = max;
}
/**
* An Array of DataDescription structures describing the data being represented by the chart.
* An axis can use this property to generate values for properties, such as its range.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function get dataDescriptions():Array /* of DataDescription */
{
if (!_cachedDataDescriptions)
{
_cachedDataDescriptions = describeData(requiredDescribedFields);
_cachedValuesHaveBounds = false;
}
return _cachedDataDescriptions;
}
/**
* @private
*/
private function autoGenerate(checkForMargins:Boolean,
minimumAxisLength:Number):void
{
var min:Number;
var max:Number;
var v:Object;
var cachedDataDescriptions:Array /* of DataDescription */ = this.dataDescriptions;
if (autoAdjust && baseAtZero)
{
if(direction == "inverted")
max = zeroValue;
else
min = zeroValue;
}
var n:int = cachedDataDescriptions.length;
var val:Number;
if (n > 0)
{
var temp:int;
if(direction == "inverted")
{
min = (cachedDataDescriptions[0].min);
if(!isNaN(max) && min > max) //if min > max, swap them
{
temp = max;
max = min;
min = temp;
}
if (isNaN(max))
max = (cachedDataDescriptions[0].max);
else if (!isNaN(cachedDataDescriptions[0].max))
max = Math.max(max, (cachedDataDescriptions[0].max));
}
else
{
max = (cachedDataDescriptions[0].max);
if(!isNaN(min) && min > max) //if min > max, swap them
{
temp = max;
max = min;
min = temp;
}
if (isNaN(min))
min = (cachedDataDescriptions[0].min);
else if (!isNaN(cachedDataDescriptions[0].min))
min = Math.min(min, (cachedDataDescriptions[0].min));
}
for (var i:int = 0; i < n; i++)
{
var desc:DataDescription = cachedDataDescriptions[i];
if (isNaN(min))
min = desc.min;
else if (!isNaN(desc.min))
min = Math.min(min,desc.min);
if (isNaN(max))
max= desc.max;
else if (!isNaN(desc.max))
max = Math.max(max,desc.max);
_cachedValuesHaveBounds = _cachedValuesHaveBounds ||
(desc.boundedValues &&
desc.boundedValues.length > 0);
}
}
var adjusted:Array /* of Number */ = guardMinMax(min,max);
if (adjusted)
{
min = adjusted[0];
max = adjusted[1];
}
if (isNaN(assignedMinimum))
computedMinimum = min;
if (isNaN(assignedMaximum))
computedMaximum = max;
}
/**
* Adjusts the generated or assigned range of the axis's labels.
* This method is called during the update cycle of the axis. Subclasses can override this method
* to do special processing on the values. By default, no adjustments are made to the range.
*
* @param minValue The computed minimum value.
* @param maxValue The computed maximum value.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function adjustMinMax(minValue:Number,
maxValue:Number):void
{
}
/**
* Protects the range against invalid values for this axis type.
* This function is called during the update cycle of the axis to guarantee that invalid
* ranges are not generated. Subclasses can override this class and define logic that
* is appropriate to their axis type.
*
* @param min The computed minimum value.
* @param max The computed maximum value.
*
* @return null if no adjustment is necessary, or an Array containing the adjusted
* values of the form <code>[min,max]</code>.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function guardMinMax(min:Number, max:Number):Array /* of Number */
{
if (isNaN(min) || !isFinite(min))
return [ 0, 100 ];
else if (isNaN(max) || !isFinite(max) || min == max)
return [ min, min + 100 ];
return null;
}
/**
* @private
*/
mx_internal function getSortOrder():Boolean
{
return computedMinimum < computedMaximum
}
}
}