1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2009 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package mx.charts.chartClasses
13{
14
15import flash.events.Event;
16
17import mx.charts.AxisLabel;
18import mx.core.mx_internal;
19
20use namespace mx_internal;
21
22/**
23 *  The NumericAxis class acts as a common base class
24 *  for axis types representing a continuous range of values
25 *  between a defined minimum and maximum.
26 *  The built-in LinearAxis, LogAxis, and DateTimeAxis
27 *  classes all extend this base class.
28 *
29 *  @see mx.charts.DateTimeAxis
30 *  @see mx.charts.LinearAxis
31 *  @see mx.charts.LogAxis
32 *
33 *  @langversion 3.0
34 *  @playerversion Flash 9
35 *  @playerversion AIR 1.1
36 *  @productversion Flex 3
37 */
38public class NumericAxis extends AxisBase implements IAxis
39{
40    include "../../core/Version.as";
41
42    //--------------------------------------------------------------------------
43    //
44    //  Constructor
45    //
46    //--------------------------------------------------------------------------
47
48    /**
49     *  Constructor.
50     *
51     *  @langversion 3.0
52     *  @playerversion Flash 9
53     *  @playerversion AIR 1.1
54     *  @productversion Flex 3
55     */
56    public function NumericAxis()
57    {
58        super();
59    }
60
61    //--------------------------------------------------------------------------
62    //
63    //  Variables
64    //
65    //--------------------------------------------------------------------------
66
67    /**
68     *  @private
69     */
70    private var _labelSet:AxisLabelSet;
71
72    /**
73     *  @private
74     */
75    private var _cachedDataDescriptions:Array /* of DataDescription */;
76
77    /**
78     *  @private
79     */
80    private var _cachedValuesHaveBounds:Boolean;
81
82    /**
83     *  @private
84     */
85    private var _regenerateAutoValues:Boolean = true;
86
87    //--------------------------------------------------------------------------
88    //
89    //  Properties
90    //
91    //--------------------------------------------------------------------------
92
93    //----------------------------------
94    //  assignedMaximum
95    //----------------------------------
96
97    /**
98     *  The explicitly assigned maximum value.
99     *  If no value has been assigned, this will be <code>NaN</code>.
100     *  Typically, calculations should be performed
101     *  with the <code>computedMaximum</code> field.
102     *
103     *  @langversion 3.0
104     *  @playerversion Flash 9
105     *  @playerversion AIR 1.1
106     *  @productversion Flex 3
107     */
108    protected var assignedMaximum:Number;
109
110    //----------------------------------
111    //  assignedMinimum
112    //----------------------------------
113
114    /**
115     *  The explicitly assigned minimum value.
116     *  If no value has been assigned, this will be <code>NaN</code>.
117     *  Typically calculations should be performed
118     *  with the <code>computedMinimum</code> field.
119     *
120     *  @langversion 3.0
121     *  @playerversion Flash 9
122     *  @playerversion AIR 1.1
123     *  @productversion Flex 3
124     */
125    protected var assignedMinimum:Number;
126
127    //----------------------------------
128    //  autoAdjust
129    //----------------------------------
130
131    /**
132     *  @private
133     *  Storage for the autoAdjust property.
134     */
135    private var _autoAdjust:Boolean = true;
136
137    [Inspectable(category="General", defaultValue="true")]
138
139    /**
140     *  Specifies whether Flex rounds values.
141     *  If <code>false</code>, Flex does not round the values
142     *  set by the <code>minimum</code> and <code>maximum</code> properties,
143     *  or modify the default <code>minimum</code> and
144     *  <code>maximum</code> values.
145     *
146     *  @default true
147     *
148     *  @langversion 3.0
149     *  @playerversion Flash 9
150     *  @playerversion AIR 1.1
151     *  @productversion Flex 3
152     */
153    public function get autoAdjust():Boolean
154    {
155        return _autoAdjust;
156    }
157
158    /**
159     *  @private
160     */
161    public function set autoAdjust(value:Boolean):void
162    {
163        _autoAdjust = value;
164
165        dataChanged();
166    }
167
168    //----------------------------------
169    //  baseAtZero
170    //----------------------------------
171    /**
172     *  Storage for baseAtZero property
173     *
174     *  @langversion 3.0
175     *  @playerversion Flash 9
176     *  @playerversion AIR 1.1
177     *  @productversion Flex 3
178     */
179    private var _baseAtZero:Boolean = true;
180
181    [Inspectable(category="General", defaultValue="true")]
182
183    /**
184     *  Specifies whether Flex tries to keep the <code>minimum</code>
185     *  and <code>maximum</code> values rooted at zero.
186     *  If all axis values are positive, the minimum axis value is zero.
187     *  If all axis values are negative, the maximum axis value is zero.
188     *
189     *  @default true
190     *
191     *  @langversion 3.0
192     *  @playerversion Flash 9
193     *  @playerversion AIR 1.1
194     *  @productversion Flex 3
195     */
196    public function get baseAtZero():Boolean
197    {
198        return _baseAtZero;
199    }
200
201    /**
202     * @private
203     */
204    public function set baseAtZero(value:Boolean):void
205    {
206        _baseAtZero = value;
207
208        dataChanged();
209    }
210
211    //----------------------------------
212    //  computedInterval
213    //----------------------------------
214
215    /**
216     *  The computed interval represented by this axis.
217     *  The <code>computedInterval</code> is used
218     *  by the AxisRenderer and Gridlines classes
219     *  to determine where to render tick marks and grid lines.
220     *  The NumericAxis base class watches this field for changes
221     *  to determine if the chart needs to be re-rendered.
222     *  Derived classes are responsible for computing the value
223     *  of this field.
224     *
225     *  @langversion 3.0
226     *  @playerversion Flash 9
227     *  @playerversion AIR 1.1
228     *  @productversion Flex 3
229     */
230    protected var computedInterval:Number;
231
232    //----------------------------------
233    //  computedMaximum
234    //----------------------------------
235
236    /**
237     *  The computed maximum value represented by this axis.
238     *  If the user has explicitly assigned a maximum value,
239     *  the <code>computedMaximum</code> and
240     *  <code>assignedMaximum</code> properties
241     *  are usually the same.
242     *  Otherwise, the <code>computedMaximum</code> is generated
243     *  from the values represented in the chart.
244     *
245     *  @langversion 3.0
246     *  @playerversion Flash 9
247     *  @playerversion AIR 1.1
248     *  @productversion Flex 3
249     */
250    public var computedMaximum:Number;
251
252    //----------------------------------
253    //  computedMinimum
254    //----------------------------------
255
256    /**
257     *  The computed minimum value represented by this axis.
258     *  If the user has explicitly assigned a minimum value,
259     *  the <code>computedMinimum</code> and
260     *  <code>assignedMinimum</code> properties
261     *  are usually be the same.
262     *  Otherwise, the <code>computedMinimum</code> is generated
263     *  from the values represented in the chart.
264     *
265     *  @langversion 3.0
266     *  @playerversion Flash 9
267     *  @playerversion AIR 1.1
268     *  @productversion Flex 3
269     */
270    public var computedMinimum:Number;
271
272	//----------------------------------
273	//  direction
274	//----------------------------------
275
276	/**
277	 *  @private
278	 */
279	private var _direction:String = "normal";
280
281	[Inspectable(category="General", enumeration="normal,inverted", defaultValue="normal")]
282
283	/**
284 	 *  Determines the direction in which the axis is rendered.
285     *  Possible values are <code>normal</code>,
286     *  and <code>inverted</code>.
287     *
288     *  All derived classes should take care of the way min and max
289     *  are set depending on <code>direction</code>.
290     *
291     *  All series should take care of the way it is rendered
292     *  depending on the <code>direction</code> of its underlying axis.
293     *
294     *  @default "normal"
295	 *
296	 *  @langversion 3.0
297	 *  @playerversion Flash 9
298	 *  @playerversion AIR 1.1
299	 *  @productversion Flex 4
300	 */
301	public function get direction():String
302	{
303		return _direction;
304	}
305
306	/**
307	 *  @private
308	 */
309	public function set direction(value:String):void
310	{
311		if(_direction != value)
312		{
313			_direction = value;
314			invalidateCache();
315			dataChanged();
316		}
317	}
318
319    //----------------------------------
320    //  labelCache
321    //----------------------------------
322
323    /**
324     *  The most recent set of AxisLabel objects
325     *  generated to represent this axis.
326     *  This property is <code>null</code> if the axis
327     *  has been modified and requires new labels.
328     *  To guarantee that the value of the <code>labelCache</code> property
329     *  is correct, call the <code>buildLabelCache()</code> method
330     *  before accessing the <code>labelCache</code> property.
331     *
332     *  @langversion 3.0
333     *  @playerversion Flash 9
334     *  @playerversion AIR 1.1
335     *  @productversion Flex 3
336     */
337    protected var labelCache:Array /* of AxisLabel */;
338
339    //----------------------------------
340    //  labelFunction
341    //----------------------------------
342
343    /**
344     *  @private
345     *  Storage for the labelFunction property.
346     */
347    private var _labelFunction:Function = null;
348
349    [Inspectable(category="General")]
350
351    /**
352     *  Called to format axis values for display as labels.
353     *  A <code>labelFunction</code> has the following signature:
354     *  <pre>
355     *  function <i>function_name</i>(<i>labelValue</i>:Object, <i>previousValue</i>:Object, <i>axis</i>:IAxis):String { ... }
356     *  </pre>
357     *
358     *  <p>If you know the types of the data your function will be formatting,
359     *  you can specify an explicit type for the <code>labelValue</code>
360     *  and <code>previousValue</code> parameters.</p>
361     *
362     *  @langversion 3.0
363     *  @playerversion Flash 9
364     *  @playerversion AIR 1.1
365     *  @productversion Flex 3
366     */
367    public function get labelFunction():Function
368    {
369        return _labelFunction;
370    }
371
372    /**
373     *  @private
374     */
375    public function set labelFunction(value:Function):void
376    {
377        _labelFunction = value;
378
379        invalidateCache();
380
381        dispatchEvent(new Event("mappingChange"));
382        dispatchEvent(new Event("axisChange"));
383    }
384
385    //----------------------------------
386    //  labelMaximum
387    //----------------------------------
388
389    /**
390     *  The maximum value where a label should be placed.
391     *  After computing an adjusted minimum value,
392     *  many axis types expand the range of the axis further
393     *  to make room for additional rendering artifacts in the chart,
394     *  such as labels and borders.
395     *  This value represents the maximum value in the chart
396     *  <em>before</em> it is adjusted for these artifacts.
397     *  Typically axes generate labels to make sure
398     *  this value is labeled, rather than the adjusted maximum of the axis.
399     *
400     *  @langversion 3.0
401     *  @playerversion Flash 9
402     *  @playerversion AIR 1.1
403     *  @productversion Flex 3
404     */
405    protected var labelMaximum:Number;
406
407    //----------------------------------
408    //  labelMinimum
409    //----------------------------------
410
411    /**
412     *  The minimum value where a label should be placed.
413     *  After computing an adjusted minimum value,
414     *  many axis types expand the range of the axis further
415     *  to make room for additional rendering artifacts in the chart,
416     *  such as labels and borders.
417     *  This value represents the minimum value in the chart
418     *  <em>before</em> it is adjusted for these artifacts.
419     *  Typically axes will generate labels to make sure
420     *  this value is labeled, rather than the adjusted minimum of the axis.
421     *
422     *  @langversion 3.0
423     *  @playerversion Flash 9
424     *  @playerversion AIR 1.1
425     *  @productversion Flex 3
426     */
427    protected var labelMinimum:Number;
428
429    //----------------------------------
430    //  mappedMaximum
431    //----------------------------------
432
433    [Inspectable(environment="none")]
434
435    /**
436     *  The computed minimum value for the axis
437     *  as long as this value is greater than 0.
438     *  If the maximum value is less than or equal to 0,
439     *  then the <code>baseline</code> property is the computed maximum.
440     *  If neither value is greater than 0,
441     *  then the <code>baseline</code> property is 0.
442     *
443     *  @langversion 3.0
444     *  @playerversion Flash 9
445     *  @playerversion AIR 1.1
446     *  @productversion Flex 3
447     */
448    public function get baseline():Number
449    {
450        var baseVal:Number;
451
452        if (computedMinimum >= 0)
453            baseVal = computedMinimum;
454        else if (computedMaximum <= 0)
455            baseVal = computedMaximum;
456        else
457            baseVal = 0;
458
459        return baseVal;
460    }
461
462    //----------------------------------
463    //  minorTickCache
464    //----------------------------------
465
466    /**
467     *  The most recent set of minor tick marks generated to represent this axis.
468     *  This property may be <code>null</code> if the axis
469     *  has been modified and requires new labels and tick marks.
470     *  Use the public accessor <code>minorTicks</code>
471     *  to build the minor tick marks on demand.
472     *
473     *  @langversion 3.0
474     *  @playerversion Flash 9
475     *  @playerversion AIR 1.1
476     *  @productversion Flex 3
477     */
478    protected var minorTickCache:Array /* of Number */;
479
480    //----------------------------------
481    //  minorTicks
482    //----------------------------------
483
484    [Inspectable(environment="none")]
485
486    /**
487     *  An Array of minor tick marks generated to represent this axis.
488     *
489     *  @langversion 3.0
490     *  @playerversion Flash 9
491     *  @playerversion AIR 1.1
492     *  @productversion Flex 3
493     */
494    public function get minorTicks():Array /* of Number */
495    {
496        if (!minorTickCache)
497            minorTickCache = buildMinorTickCache();
498
499        return minorTickCache;
500    }
501
502    //----------------------------------
503    //  padding
504    //----------------------------------
505
506    /**
507     *  @private
508     *  Storage for the padding property.
509     */
510    private var _padding:Number;
511
512    [Inspectable(category="General")]
513
514    /**
515     *  Specifies padding that Flex adds to the calculated minimum and maximum
516     *  values for the axis when rendering the values on the screen.
517     *
518     *  @langversion 3.0
519     *  @playerversion Flash 9
520     *  @playerversion AIR 1.1
521     *  @productversion Flex 3
522     */
523    public function get padding():Number
524    {
525        return _padding;
526    }
527
528    /**
529     *  @private
530     */
531    public function set padding(value:Number):void
532    {
533        _padding = value;
534
535        invalidateCache();
536
537        dispatchEvent(new Event("axisChange"));
538    }
539
540    //----------------------------------
541    //  parseFunction
542    //----------------------------------
543
544    /**
545     *  @private
546     *  Storage for the parseFunction property.
547     */
548    private var _parseFunction:Function = null;
549
550    [Inspectable(category="Other")]
551
552    /**
553     *  Specify a <code>parseFunction</code> to customize how
554     *  the values rendered by your chart are converted into numeric values.
555     *  A custom <code>parseFunction</code> is passed a data value
556     *  and should return a corresponding number representing the same value.
557     *  By default, this axis uses the ECMA function <code>parseFloat()</code>.
558     *
559     *  @langversion 3.0
560     *  @playerversion Flash 9
561     *  @playerversion AIR 1.1
562     *  @productversion Flex 3
563     */
564    public function get parseFunction():Function
565    {
566        return _parseFunction;
567    }
568
569    /**
570     *  @private
571     */
572    public function set parseFunction(value:Function):void
573    {
574        _parseFunction = value;
575
576        invalidateCache();
577        _cachedDataDescriptions = null;
578
579        dispatchEvent(new Event("mappingChange"));
580        dispatchEvent(new Event("axisChange"));
581    }
582
583    //----------------------------------
584    //  requiredDescribedFields
585    //----------------------------------
586
587    /**
588     *  The fields of the DescribeData structure that this axis is interested in.
589     *
590     *  @langversion 3.0
591     *  @playerversion Flash 9
592     *  @playerversion AIR 1.1
593     *  @productversion Flex 3
594     */
595    protected function get requiredDescribedFields():uint
596    {
597        return DataDescription.REQUIRED_MIN_MAX |
598               DataDescription.REQUIRED_BOUNDED_VALUES;
599    }
600
601    //----------------------------------
602    //  ticks
603    //----------------------------------
604
605    /**
606     *  An Array of tick marks for this axis.
607     *
608     *  @langversion 3.0
609     *  @playerversion Flash 9
610     *  @playerversion AIR 1.1
611     *  @productversion Flex 3
612     */
613    protected function get ticks():Array /* of Number */
614    {
615        var result:Array /* of Number */ = [];
616
617        var n:int = labelCache.length;
618        for (var i:int = 0; i < n; i++)
619        {
620            result.push(labelCache[i].position);
621        }
622
623        return result;
624    }
625
626    //----------------------------------
627    //  zeroValue
628    //----------------------------------
629
630    /**
631     *  @private
632     */
633    mx_internal function get zeroValue():Number
634    {
635        return 0;
636    }
637
638    //--------------------------------------------------------------------------
639    //
640    //  Overridden methods: AxisBase
641    //
642    //--------------------------------------------------------------------------
643
644    /**
645     *  @private
646     */
647    override public function dataChanged():void
648    {
649        // We don't call invalidateCache() here, because invalidateCache()
650        // forces a rebuild of the labels.
651        // But just changing the data doesn't guarantee the labels have changed.
652        // Instead, we need to recalc our autogenerated values,
653        // and then see if the values have changed.
654        minorTickCache = null;
655        _cachedDataDescriptions = null;
656        _regenerateAutoValues = true;
657
658		dispatchEvent(new Event("mappingChange"));
659
660        if (isNaN(assignedMinimum) || isNaN(assignedMaximum))
661        {
662            dispatchEvent(new Event("axisChange"));
663        }
664    }
665
666    //--------------------------------------------------------------------------
667    //
668    //  Methods
669    //
670    //--------------------------------------------------------------------------
671
672    /**
673     *  @copy mx.charts.chartClasses.IAxis#mapCache()
674     *
675     *  @langversion 3.0
676     *  @playerversion Flash 9
677     *  @playerversion AIR 1.1
678     *  @productversion Flex 3
679     */
680    public function mapCache(cache:Array /* of Object */, field:String,
681                             convertedField:String,
682                             indexValues:Boolean = false):void
683    {
684        var n:int = cache.length;
685        var i:int;
686
687        var v:Object;
688
689        if (_parseFunction != null)
690        {
691            for (i = 0; i < n; i++)
692            {
693                v = cache[i];
694                v[convertedField] = _parseFunction(v[field]);
695            }
696        }
697        else
698        {
699            for (i = 0; i < n && cache[i][field] == null; i++)
700            {
701            }
702            if (i == n)
703                return;
704
705            if ((cache[i][field] is String))
706            {
707                for (; i < n; i++)
708                {
709                    v = cache[i];
710                    v[convertedField] = Number(v[field]);
711                }
712            }
713            else if (cache[i][field] is XML ||
714                     cache[i][field] is XMLList)
715            {
716                for (; i < n; i++)
717                {
718                    v = cache[i];
719                     v[convertedField] =   parseFloat(v[field].toString());
720                }
721            }
722            else if (cache[i][field] is Number ||
723                     cache[i][field] is int ||
724                     cache[i][field] is uint)
725            {
726                for (; i < n; i++)
727                {
728                    v = cache[i];
729                    if (v[field] == null || v[field].toString() == "")
730                    	v[convertedField] = NaN;
731                    else
732                        v[convertedField] = v[field];
733                }
734            }
735            else
736            {
737                for (; i < n; i++)
738                {
739                    v = cache[i];
740                    v[convertedField] = parseFloat(v[field]);
741                }
742            }
743        }
744    }
745
746    /**
747     *  @copy mx.charts.chartClasses.IAxis#filterCache()
748     *
749     *  @langversion 3.0
750     *  @playerversion Flash 9
751     *  @playerversion AIR 1.1
752     *  @productversion Flex 3
753     */
754    public function filterCache(cache:Array /* of Object */, field:String,
755                                filteredField:String):void
756    {
757        update();
758
759        // Avoid roundoff errors.
760        var max:Number = computedMaximum + 0.00001;
761        var min:Number = computedMinimum - 0.00001;
762
763        var n:int = cache.length;
764        for (var i:int = 0; i < n; i++)
765        {
766            var v:Object = cache[i][field];
767            cache[i][filteredField] = v >= min && v <= max ? v : NaN;
768        }
769    }
770
771    /**
772     *  @copy mx.charts.chartClasses.IAxis#transformCache()
773     *
774     *  @langversion 3.0
775     *  @playerversion Flash 9
776     *  @playerversion AIR 1.1
777     *  @productversion Flex 3
778     */
779    public function transformCache(cache:Array /* of Object */, field:String,
780                                   convertedField:String):void
781    {
782        update();
783
784        var r:Number = computedMaximum - computedMinimum;
785
786        var n:int = cache.length;
787        for (var i:int = 0; i < n; i++)
788        {
789            cache[i][convertedField] = (cache[i][field] - computedMinimum) / r;
790        }
791    }
792
793    /**
794     *  @copy mx.charts.chartClasses.IAxis#invertTransform()
795     *
796     *  @langversion 3.0
797     *  @playerversion Flash 9
798     *  @playerversion AIR 1.1
799     *  @productversion Flex 3
800     */
801    public function invertTransform(value:Number):Object
802    {
803        update();
804        return value * (computedMaximum - computedMinimum) + computedMinimum;
805    }
806
807    /**
808     *  @copy mx.charts.chartClasses.IAxis#formatForScreen()
809     *
810     *  @langversion 3.0
811     *  @playerversion Flash 9
812     *  @playerversion AIR 1.1
813     *  @productversion Flex 3
814     */
815    public function formatForScreen(value:Object):String
816    {
817    	if(direction == "inverted")
818    		value = -(Number(value)) as Object;
819    	else
820    		value = Number(value) as Object;
821        return value.toString();
822    }
823
824    /**
825     *  @copy mx.charts.chartClasses.IAxis#getLabelEstimate()
826     *
827     *  @langversion 3.0
828     *  @playerversion Flash 9
829     *  @playerversion AIR 1.1
830     *  @productversion Flex 3
831     */
832    public function getLabelEstimate():AxisLabelSet
833    {
834        update();
835
836        var updated:Boolean = buildLabelCache();
837        if (updated)
838        {
839            _labelSet = new AxisLabelSet();
840            _labelSet.labels = labelCache;
841            _labelSet.accurate = _cachedValuesHaveBounds == false;
842            _labelSet.minorTicks = minorTicks;
843            _labelSet.ticks = ticks;
844        }
845
846        return _labelSet;
847    }
848
849    /**
850     *  @copy mx.charts.chartClasses.IAxis#preferDropLabels()
851     *
852     *  @langversion 3.0
853     *  @playerversion Flash 9
854     *  @playerversion AIR 1.1
855     *  @productversion Flex 3
856     */
857    public function preferDropLabels():Boolean
858    {
859        return true;
860    }
861
862    /**
863     *  @copy mx.charts.chartClasses.IAxis#getLabels()
864     *
865     *  @langversion 3.0
866     *  @playerversion Flash 9
867     *  @playerversion AIR 1.1
868     *  @productversion Flex 3
869     */
870    public function getLabels(minimumAxisLength:Number):AxisLabelSet
871    {
872        var updated:Boolean;
873        if (_cachedValuesHaveBounds || !labelCache)
874        {
875            _regenerateAutoValues = true;
876            updateCache(true,minimumAxisLength);
877            updated = buildLabelCache();
878        }
879        else
880        {
881            updated = false;
882        }
883
884        if (updated)
885        {
886            _labelSet = new AxisLabelSet();
887            _labelSet.labels =  labelCache;
888            _labelSet.minorTicks = minorTicks;
889            _labelSet.ticks = ticks;
890        }
891
892        return _labelSet;
893    }
894
895    /**
896     *  @copy mx.charts.chartClasses.IAxis#reduceLabels()
897     *
898     *  @langversion 3.0
899     *  @playerversion Flash 9
900     *  @playerversion AIR 1.1
901     *  @productversion Flex 3
902     */
903    public function reduceLabels(intervalStart:AxisLabel,
904                                 intervalEnd:AxisLabel):AxisLabelSet
905    {
906        return _labelSet;
907    }
908
909    /**
910     *  Populates the <code>labelCache</code> property with labels representing the current
911     *  values of the axis. Subclasses must implement this function. This function is called
912     *  many times, so you should check to see if the <code>labelCache</code> property
913     *  is <code>null</code> before performing any calculations.
914     *
915     *  @return true if the labels were regenerated.
916     *
917     *  @langversion 3.0
918     *  @playerversion Flash 9
919     *  @playerversion AIR 1.1
920     *  @productversion Flex 3
921     */
922    protected function buildLabelCache():Boolean
923    {
924        return false;
925    }
926
927    /**
928     *  Builds an Array of positions for the minor tick marks Array that is generated by this axis.
929     *  Subclasses must implement this function. This function is  called automatically
930     *  by the NumericAxis. You should access the <code>minorTicks</code> property
931     *  instead of calling this function directly.
932     *
933     *  @return An Array of positions from 0 to 1 that represent points between the axis
934     *  minimum and maximum values where minor tick marks are rendered.
935     *
936     *  @langversion 3.0
937     *  @playerversion Flash 9
938     *  @playerversion AIR 1.1
939     *  @productversion Flex 3
940     */
941    protected function buildMinorTickCache():Array /* of Number */
942    {
943        return [];
944    }
945
946    /**
947    * @private
948     */
949    private function updateCache(checkForMargins:Boolean,
950                                   minimumAxisLength:Number):void
951    {
952        if (_regenerateAutoValues)
953        {
954            // We're going to remember these.
955            // As an optimization, if after going through all of this
956            // the values that generate our labels haven't changed,
957            // we won't bother regenerating the labels.
958            var oldMin:Number = computedMinimum;
959            var oldMax:Number = computedMaximum;
960            var oldInterval:Number = computedInterval;
961
962            // First we check and see if the author has provided
963            // any set min/max values.
964            if (!isNaN(assignedMinimum))
965                computedMinimum = assignedMinimum;
966            if (!isNaN(assignedMaximum))
967                computedMaximum = assignedMaximum;
968
969            // If either min or max is unset, we need to generate it.
970            var autoGen:Boolean = isNaN(assignedMinimum) ||
971                                  isNaN(assignedMaximum);
972
973            // If we still need either a min or a max, we'll autogenerate here.
974            if (autoGen)
975                autoGenerate(checkForMargins, minimumAxisLength);
976
977            // At this point we know we've got a valid min/max pair,
978            // either specified by the author or generated from the data.
979            // Next, we go into our adjustment routine.
980            // This is where we push those min/max values out to be nice,
981            // clean values that look good when you make labels out of them.
982            // The same algorithm is also how we generate a nice usable interval
983            // from the min/max values we have.so we're going to execute
984            // this section if either the user has asked us to autoadjust
985            // the min/max to nice clean values, or if the interval hasn't
986            // been set by the user.
987            // Once we've invoked the routine, we'll decide what we want
988            // to do with the numbers it generated based on why the routine
989            // got called in the first place.
990            adjustMinMax(computedMinimum,computedMaximum);
991
992            // OK, we've now calculated the ideal min/max values
993            // to show on the chart.
994            // However, there may be reasons we want to make the actual range
995            // of the chart spread a little bit further.
996            // Specifically, if there are series that need a little extra space
997            // around their data points, either for labels, or just pretty
998            // drawing,or other reasons (like BubbleSeries which render more
999            // than one axis in each dimension).
1000            // But whatever the final range ends up being, we'll use
1001            // the min/max values we have now as the outer labels of the chart.
1002            labelMinimum = computedMinimum;
1003            labelMaximum = computedMaximum;
1004
1005            // When do we adjust for margins?
1006            // In general, it's if we autogenerated our min/max values
1007            // and the user has asked us to make nice clean numbers around them.
1008            // We also have two optimizations in here.
1009            // First of all, if we auto generated our min/max from data
1010            // and found that none of our data needed any space around
1011            // their values, then this would be wasted effort.
1012            // Secondly, there are times this function gets called
1013            // when we're looking for an estimate...so we don't want to bother
1014            // with margins at that point.
1015            if ((autoAdjust || autoGen) &&
1016                checkForMargins &&
1017                _cachedValuesHaveBounds)
1018            {
1019                adjustForMargins(minimumAxisLength);
1020            }
1021
1022            // If the author asked for additional padding outside
1023            // the min/max values, give it to them here.
1024            // However, it's possible that our margin calculations
1025            // have already generated more padding than was requested.
1026            // In which case we don't add more.
1027            var us:Number = unitSize;
1028            if (!isNaN(_padding))
1029            {
1030                if (labelMinimum - computedMinimum < _padding * us)
1031                    computedMinimum = labelMinimum - _padding * us;
1032
1033                if (computedMaximum - labelMaximum < _padding * us)
1034                    computedMaximum = labelMaximum + _padding * us;
1035            }
1036
1037            // Authors don't get all the fun...
1038            // series can ask for padding too.
1039            // Check and see if any series have asked for padding,
1040            // and do the same calculation for them.
1041            var cachedDataDescriptions:Array /* of DataDescription */ = dataDescriptions;
1042            var n:int = cachedDataDescriptions.length;
1043            for (var i:int = 0; i < n; i++)
1044            {
1045                var desc:DataDescription =  cachedDataDescriptions[i]
1046                if (!isNaN(desc.padding))
1047                {
1048                    if (isNaN(assignedMinimum) &&
1049                        desc.min - computedMinimum < desc.padding * us)
1050                    {
1051                        computedMinimum = desc.min - desc.padding * us;
1052                    }
1053
1054                    if (isNaN(assignedMaximum) &&
1055                        computedMaximum - desc.max < desc.padding * us)
1056                    {
1057                        computedMaximum = desc.max + desc.padding * us;
1058                    }
1059                }
1060            }
1061
1062            if (computedMinimum == computedMaximum)
1063            {
1064                var us2:Number = unitSize/2;
1065                computedMinimum -= us2;
1066                computedMaximum += us2;
1067            }
1068
1069            // Finally, if the min/max/interval has changed, invalidate it here.
1070            if (oldMin != computedMinimum ||
1071                oldMax != computedMaximum ||
1072                oldInterval != computedInterval)
1073            {
1074                labelCache = null;
1075                minorTickCache = null;
1076            }
1077
1078            _regenerateAutoValues = false;
1079        }
1080    }
1081
1082    /**
1083     *  Invalidates the cached labels and tick marks that represent this axis's values.
1084     *  Derived classes should call this function whenever values used in the calculation
1085     *  of labels and tick marks change.
1086     *
1087     *  @langversion 3.0
1088     *  @playerversion Flash 9
1089     *  @playerversion AIR 1.1
1090     *  @productversion Flex 3
1091     */
1092    protected function invalidateCache():void
1093    {
1094        minorTickCache = null;
1095        _regenerateAutoValues = true;
1096        labelCache = null;
1097    }
1098
1099    /**
1100     *  @inheritDoc
1101     *
1102     *  @langversion 3.0
1103     *  @playerversion Flash 9
1104     *  @playerversion AIR 1.1
1105     *  @productversion Flex 3
1106     */
1107    public function update():void
1108    {
1109        updateCache(true, 0);
1110    }
1111
1112    /**
1113     *  @private
1114     */
1115    private function adjustForMargins(minimumAxisLength:Number):void
1116    {
1117        // Make sure we have room for bounded values.
1118        // These are values that specify required padding in screen space
1119        // (i.e., a bar chart will specify space for its label,
1120        // in screen space).
1121        // This is imperfect, because by necessity at this point we don't know
1122        // how much space we'll really have on the axis.
1123        // But if we calc now, and the axis gets smaller (it won't get bigger)
1124        // we'll be conservative but safe.
1125
1126        var min:Number = computedMinimum;
1127        var max:Number = computedMaximum;
1128
1129        var n:int;
1130        var i:int;
1131
1132        var boundedValues:Array /* of BoundedValue */ = [];
1133        var cachedDataDescriptions:Array /* of DataDescription */ = dataDescriptions;
1134        var boundsWithinRegion:Boolean = true;
1135        n = cachedDataDescriptions.length;
1136        for (i = 0; i < n; i++)
1137        {
1138            if (cachedDataDescriptions[i].boundedValues)
1139            {
1140                boundedValues = boundedValues.concat(
1141                    cachedDataDescriptions[i].boundedValues);
1142            }
1143        }
1144        n = boundedValues.length;
1145        for (i = 0; i < n; i++)
1146        {
1147            if (!(boundedValues[i].lowerMargin < minimumAxisLength && boundedValues[i].upperMargin < minimumAxisLength))
1148                    boundsWithinRegion = false;
1149        }
1150
1151        if (boundedValues.length > 0 && minimumAxisLength > 0 && boundsWithinRegion)
1152        {
1153            if (isNaN(min))
1154                min = boundedValues[0].value;
1155            if (isNaN(max))
1156                max = boundedValues[0].value;
1157
1158            var axisLength:Number = minimumAxisLength;
1159            var currentRange:Number = max - min;
1160            var itrCount:int = 0;
1161            n = boundedValues.length;
1162
1163            var verify:Boolean = true;
1164            while (verify)
1165            {
1166                var lowestOverlap:Number = minimumAxisLength;
1167                var lowestValue:BoundedValue;
1168                var highestOverlap:Number = 0;
1169                var highestValue:BoundedValue;
1170
1171                var v1:Number = max;
1172                var m1:Number = 0;
1173                var v2:Number = min;
1174                var m2:Number = 0;
1175
1176                for (i = 0; i < n; i++)
1177                {
1178                    var v:BoundedValue = boundedValues[i];
1179
1180                    var vpos:Number =
1181                        (v.value - min) / currentRange * minimumAxisLength;
1182
1183                    if ((!isNaN(v.lowerMargin)) &&
1184                        vpos - v.lowerMargin < lowestOverlap)
1185                    {
1186                        lowestOverlap = vpos - v.lowerMargin;
1187                        lowestValue = v;
1188                    }
1189
1190                    if ((!isNaN(v.upperMargin)) &&
1191                        vpos + v.upperMargin> highestOverlap)
1192                    {
1193                        highestOverlap = vpos + v.upperMargin;
1194                        highestValue = v;
1195                    }
1196                }
1197
1198                if (lowestOverlap > -0.0001 &&
1199                    highestOverlap < minimumAxisLength + 0.0001)
1200                {
1201                    break;
1202                }
1203
1204                if (highestOverlap > minimumAxisLength)
1205                {
1206                    v1 = highestValue.value;
1207                    m1 = highestValue.upperMargin;
1208                }
1209                else
1210                {
1211                    // If we're only adjusting one side of the equation,
1212                    // we know we're 100% accurate.
1213                    verify = false;
1214                }
1215
1216                if (lowestOverlap < 0)
1217                {
1218                    v2 = lowestValue.value;
1219                    m2 = lowestValue.lowerMargin;
1220                }
1221                else
1222                {
1223                    // If we're only adjusting one side of the equation,
1224                    // we know we're 100% accurate.
1225                    verify = false;
1226                }
1227
1228                var p1:Number = minimumAxisLength - m1;
1229                min = (p1 * v2 - m2 * v1) / Math.abs(p1 - m2);
1230                max = minimumAxisLength * (v1 - min) /
1231                      (minimumAxisLength - m1) + min;
1232                currentRange = max - min;
1233
1234                if (itrCount++ == 3)
1235                    break;
1236            }
1237        }
1238
1239        var adjusted:Array /* of Number */ = guardMinMax(min, max);
1240        if (adjusted)
1241        {
1242            min = adjusted[0];
1243            max = adjusted[1];
1244        }
1245
1246        if (isNaN(assignedMinimum))
1247            computedMinimum = min;
1248        if (isNaN(assignedMaximum))
1249            computedMaximum = max;
1250    }
1251
1252    /**
1253     *  An Array of DataDescription structures describing the data being represented by the chart.
1254     *  An axis can use this property to generate values for properties, such as its range.
1255     *
1256     *  @langversion 3.0
1257     *  @playerversion Flash 9
1258     *  @playerversion AIR 1.1
1259     *  @productversion Flex 3
1260     */
1261    protected function get dataDescriptions():Array /* of DataDescription */
1262    {
1263        if (!_cachedDataDescriptions)
1264        {
1265            _cachedDataDescriptions = describeData(requiredDescribedFields);
1266            _cachedValuesHaveBounds = false;
1267        }
1268        return _cachedDataDescriptions;
1269    }
1270
1271    /**
1272     *  @private
1273     */
1274    private function autoGenerate(checkForMargins:Boolean,
1275                                    minimumAxisLength:Number):void
1276    {
1277
1278        var min:Number;
1279        var max:Number;
1280        var v:Object;
1281
1282        var cachedDataDescriptions:Array /* of DataDescription */ = this.dataDescriptions;
1283
1284        if (autoAdjust && baseAtZero)
1285        {
1286        	if(direction == "inverted")
1287        		max = zeroValue;
1288        	else
1289            	min = zeroValue;
1290        }
1291
1292        var n:int = cachedDataDescriptions.length;
1293        var val:Number;
1294
1295        if (n > 0)
1296        {
1297			var temp:int;
1298        	if(direction == "inverted")
1299        	{
1300           		min = (cachedDataDescriptions[0].min);
1301				if(!isNaN(max) && min > max)	//if min > max, swap them
1302				{
1303					temp = max;
1304					max = min;
1305					min = temp;
1306				}
1307            	if (isNaN(max))
1308            		max = (cachedDataDescriptions[0].max);
1309            	else if (!isNaN(cachedDataDescriptions[0].max))
1310            		max = Math.max(max, (cachedDataDescriptions[0].max));
1311         	}
1312         	else
1313         	{
1314         		max = (cachedDataDescriptions[0].max);
1315				if(!isNaN(min) && min > max)	//if min > max, swap them
1316				{
1317					temp = max;
1318					max = min;
1319					min = temp;
1320				}
1321             	if (isNaN(min))
1322            		min = (cachedDataDescriptions[0].min);
1323            	else if (!isNaN(cachedDataDescriptions[0].min))
1324            		min = Math.min(min, (cachedDataDescriptions[0].min));
1325         	}
1326            for (var i:int = 0; i < n; i++)
1327            {
1328                var desc:DataDescription = cachedDataDescriptions[i];
1329                if (isNaN(min))
1330                    min = desc.min;
1331                else if (!isNaN(desc.min))
1332                    min = Math.min(min,desc.min);
1333                if (isNaN(max))
1334                    max= desc.max;
1335                else if (!isNaN(desc.max))
1336                    max = Math.max(max,desc.max);
1337
1338                _cachedValuesHaveBounds = _cachedValuesHaveBounds ||
1339                    (desc.boundedValues &&
1340                     desc.boundedValues.length > 0);
1341            }
1342
1343        }
1344
1345		var adjusted:Array /* of Number */ = guardMinMax(min,max);
1346
1347        if (adjusted)
1348        {
1349            min = adjusted[0];
1350            max = adjusted[1];
1351        }
1352
1353        if (isNaN(assignedMinimum))
1354            computedMinimum = min;
1355        if (isNaN(assignedMaximum))
1356            computedMaximum = max;
1357    }
1358
1359    /**
1360     *  Adjusts the generated or assigned range of the axis's labels.
1361     *  This method is called during the update cycle of the axis. Subclasses can override this method
1362     *  to do special processing on the values. By default, no adjustments are made to the range.
1363     *
1364     *  @param minValue The computed minimum value.
1365     *  @param maxValue The computed maximum value.
1366     *
1367     *  @langversion 3.0
1368     *  @playerversion Flash 9
1369     *  @playerversion AIR 1.1
1370     *  @productversion Flex 3
1371     */
1372    protected function adjustMinMax(minValue:Number,
1373                                    maxValue:Number):void
1374    {
1375    }
1376
1377
1378    /**
1379     *  Protects the range against invalid values for this axis type.
1380     *  This function is called during the update cycle of the axis to guarantee that invalid
1381     *  ranges are not generated. Subclasses can override this class and define logic that
1382     *  is appropriate to their axis type.
1383     *
1384     *  @param min The computed minimum value.
1385     *  @param max The computed maximum value.
1386     *
1387     *  @return null if no adjustment is necessary, or an Array containing the adjusted
1388     *  values of the form <code>[min,max]</code>.
1389     *
1390     *  @langversion 3.0
1391     *  @playerversion Flash 9
1392     *  @playerversion AIR 1.1
1393     *  @productversion Flex 3
1394     */
1395    protected function guardMinMax(min:Number, max:Number):Array /* of Number */
1396    {
1397        if (isNaN(min) || !isFinite(min))
1398            return [ 0, 100 ];
1399
1400        else if (isNaN(max) || !isFinite(max) || min == max)
1401            return [ min, min + 100 ];
1402
1403        return null;
1404    }
1405
1406    /**
1407     *  @private
1408     */
1409     mx_internal function getSortOrder():Boolean
1410     {
1411        return computedMinimum < computedMaximum
1412     }
1413}
1414
1415}
1416