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