PageRenderTime 4766ms CodeModel.GetById 27ms RepoModel.GetById 2ms app.codeStats 1ms

/datadust/trunk/lib-src/flare/src/flare/vis/axis/Axis.as

http://simile-widgets.googlecode.com/
ActionScript | 778 lines | 454 code | 93 blank | 231 comment | 68 complexity | 755903fa9a04f46622ab46ee0ea344eb MD5 | raw file
Possible License(s): BSD-3-Clause
  1. package flare.vis.axis
  2. {
  3. import flare.animate.Transitioner;
  4. import flare.display.TextSprite;
  5. import flare.scale.IScaleMap;
  6. import flare.scale.LinearScale;
  7. import flare.scale.Scale;
  8. import flare.scale.ScaleType;
  9. import flare.util.Arrays;
  10. import flare.util.Stats;
  11. import flare.util.Strings;
  12. import flash.display.DisplayObject;
  13. import flash.display.Sprite;
  14. import flash.geom.Point;
  15. import flash.text.TextFormat;
  16. import flash.utils.Dictionary;
  17. /**
  18. * A metric data axis consisting of axis labels and gridlines.
  19. *
  20. * <p>Axis labels can be configured both in terms of text formatting,
  21. * orientation, and position. Use the <code>labelOffsetX</code> or
  22. * <code>labelOffsetY</code> property to adjust label positioning. For
  23. * example, <code>labelOffsetX = -10;</code> places the anchor point for
  24. * the label ten pixels to the left of the data bounds, whereas
  25. * <code>labelOffsetX = 10;</code> will place the point 10 pixels to the
  26. * right of the data bounds. One could simultaneously adjust the
  27. * <code>horizontalAnchor</code> property to align the labels as desired.
  28. * </p>
  29. *
  30. * <p>Similarly, axis gridlines can also be configured. The
  31. * <code>lineCapX1</code>, <code>lineCapX2</code>, <code>lineCapY1</code>,
  32. * and <code>lineCapY2</code> properties determine by how much the
  33. * grid lines should exceed the data bounds. For example,
  34. * <code>lineCapX1 = 5</code> causes the grid line to extend an extra
  35. * 5 pixels to the left. Each of these values should be greater than or
  36. * equal to zero.</p>
  37. */
  38. public class Axis extends Sprite implements IScaleMap
  39. {
  40. // children indices
  41. private static const LABELS:uint = 1;
  42. private static const GRIDLINES:uint = 0;
  43. private static const DIMENSION_LABEL:uint = 2;
  44. // axis scale
  45. private var _prevScale:Scale;
  46. // axis settings
  47. private var _xa:Number=0, _ya:Number=0; // start of the axis
  48. private var _xb:Number=0, _yb:Number=0; // end of the axis
  49. private var _xaP:Number=0, _yaP:Number=0; // previous start of the axis
  50. private var _xbP:Number=0, _ybP:Number=0; // previous end of the axis
  51. private var _xd:int, _yd:int; // axis directions (1 or -1)
  52. private var _xlo:Number, _ylo:Number; // label offsets
  53. // gridline settings
  54. private var _lineColor:uint = 0xd8d8d8;
  55. private var _lineWidth:Number = 0;
  56. // label settings
  57. private var _numLabels:int = -1;
  58. private var _anchorH:int = TextSprite.LEFT;
  59. private var _anchorV:int = TextSprite.TOP;
  60. private var _labelAngle:Number = 0;
  61. private var _labelColor:uint = 0;
  62. private var _labelFormat:String = null;
  63. private var _labelTextMode:int = TextSprite.BITMAP;
  64. private var _labelTextFormat:TextFormat = new TextFormat("Arial",12,0);
  65. // dimension label settings
  66. private var _dimensionAnchorH:int = TextSprite.LEFT;
  67. private var _dimensionAnchorV:int = TextSprite.TOP;
  68. private var _dimensionLabelAngle:Number = 0;
  69. private var _dimensionLabelText:String = null;
  70. private var _dimensionLabelColor:uint = 0;
  71. private var _dimensionLabelTextFormat:TextFormat = new TextFormat("Arial",12,0);
  72. // temporary variables
  73. private var _point:Point = new Point();
  74. // -- Properties ------------------------------------------------------
  75. /** Sprite containing the axis labels. */
  76. public function get labels():Sprite { return getChildAt(LABELS) as Sprite; }
  77. /** Sprite containing the axis grid lines. */
  78. public function get gridLines():Sprite { return getChildAt(GRIDLINES) as Sprite; }
  79. /** @inheritDoc */
  80. public function get x1():Number { return _xa; }
  81. public function set x1(x:Number):void { _xa = x; }
  82. /** @inheritDoc */
  83. public function get y1():Number { return _ya; }
  84. public function set y1(y:Number):void { _ya = y; }
  85. /** @inheritDoc */
  86. public function get x2():Number { return _xb; }
  87. public function set x2(x:Number):void { _xb = x; }
  88. /** @inheritDoc */
  89. public function get y2():Number { return _yb; }
  90. public function set y2(y:Number):void { _yb = y; }
  91. /** The Scale used to map values to this axis. */
  92. public var axisScale:Scale;
  93. /** Flag indicating if axis labels should be shown. */
  94. public var showLabels:Boolean = true;
  95. /** Flag indicating if labels should be removed in case of overlap. */
  96. public var fixLabelOverlap:Boolean = true;
  97. /** Flag indicating if axis grid lines should be shown. */
  98. public var showLines:Boolean = true;
  99. /** X length of axis gridlines. */
  100. public var lineLengthX:Number = 0;
  101. /** Y length of axis gridlines. */
  102. public var lineLengthY:Number = 0;
  103. /** X offset for axis gridlines at the lower end of the axis. */
  104. public var lineCapX1:Number = 0;
  105. /** X offset for axis gridlines at the upper end of the axis. */
  106. public var lineCapX2:Number = 0;
  107. /** Y offset for axis gridlines at the lower end of the axis. */
  108. public var lineCapY1:Number = 0;
  109. /** Y offset for axis gridlines at the upper end of the axis. */
  110. public var lineCapY2:Number = 0;
  111. /** X-dimension offset value for axis labels. If negative or zero, this
  112. * value indicates how much to offset to the left of the data bounds.
  113. * If positive, the offset is made to the right of the data bounds. */
  114. public var labelOffsetX:Number = 0;
  115. /** Y-dimension offset value for axis labels. If negative or zero, this
  116. * value indicates how much to offset above the data bounds.
  117. * If positive, the offset is made beneath the data bounds.*/
  118. public var labelOffsetY:Number = 0;
  119. /** The line color of axis grid lines. */
  120. public function get lineColor():uint { return _lineColor; }
  121. public function set lineColor(c:uint):void { _lineColor = c; updateGridLines(); }
  122. /** The line width of axis grid lines. */
  123. public function get lineWidth():Number { return _lineWidth; }
  124. public function set lineWidth(w:Number):void { _lineWidth = w; updateGridLines(); }
  125. /** The color of axis label text. */
  126. public function get labelColor():uint { return _labelColor; }
  127. public function set labelColor(c:uint):void { _labelColor = c; updateLabels(); }
  128. /** The angle (orientation) of axis label text. */
  129. public function get labelAngle():Number { return _labelAngle; }
  130. public function set labelAngle(a:Number):void { _labelAngle = a; updateLabels(); }
  131. /** TextFormat (font, size, style) for axis label text. */
  132. public function get labelTextFormat():TextFormat { return _labelTextFormat; }
  133. public function set labelTextFormat(f:TextFormat):void { _labelTextFormat = f; updateLabels(); }
  134. /** The text rendering mode to use for label TextSprites.
  135. * @see flare.display.TextSprite. */
  136. public function get labelTextMode():int { return _labelTextMode; }
  137. public function set labelTextMode(m:int):void { _labelTextMode = m; updateLabels(); }
  138. /** String formatting pattern used for axis labels, overwrites any
  139. * formatting pattern used by the <code>axisScale</code>. If null,
  140. * the formatting pattern for the <code>axisScale</code> is used. */
  141. public function get labelFormat():String {
  142. return _labelFormat==null ? null
  143. : _labelFormat.substring(3, _labelFormat.length-1);
  144. }
  145. public function set labelFormat(fmt:String):void {
  146. _labelFormat = "{0:"+fmt+"}"; updateLabels();
  147. }
  148. /** The number of labels and gridlines to generate by default. If this
  149. * number is zero or less (default -1), the number of labels will be
  150. * automatically determined from the current scale and size. */
  151. public function get numLabels():int {
  152. // if set positive, return number
  153. if (_numLabels > 0) return _numLabels;
  154. // if ordinal return all labels
  155. if (ScaleType.isOrdinal(axisScale.scaleType)) return -1;
  156. // otherwise determine based on axis size (random hack...)
  157. var lx:Number = _xb-_xa; if (lx<0) lx = -lx;
  158. var ly:Number = _yb-_ya; if (ly<0) ly = -ly;
  159. lx = (lx > ly ? lx : ly);
  160. return lx > 200 ? 10 : lx < 20 ? 1 : int(lx/20);
  161. }
  162. public function set numLabels(n:int):void { _numLabels = n; }
  163. /** The horizontal anchor point for axis labels.
  164. * @see flare.display.TextSprite. */
  165. public function get horizontalAnchor():int { return _anchorH; }
  166. public function set horizontalAnchor(a:int):void { _anchorH = a; updateLabels(); }
  167. /** The vertical anchor point for axis labels.
  168. * @see flare.display.TextSprite. */
  169. public function get verticalAnchor():int { return _anchorV; }
  170. public function set verticalAnchor(a:int):void { _anchorV = a; updateLabels(); }
  171. /** The x-coordinate of the axis origin. */
  172. public function get originX():Number {
  173. return (ScaleType.isQuantitative(axisScale.scaleType) ? X(0) : x1);
  174. }
  175. /** The y-coordinate of the axis origin. */
  176. public function get originY():Number {
  177. return (ScaleType.isQuantitative(axisScale.scaleType) ? Y(0) : y1);
  178. }
  179. /** Sprite containing the axis dimension label. */
  180. public function get dimensionLabel():Sprite { return getChildAt(DIMENSION_LABEL) as Sprite; }
  181. public var dimensionLabelOffsetX:Number = 0;
  182. public var dimensionLabelOffsetY:Number = 0;
  183. /** The horizontal anchor point for axis dimension label.
  184. * @see flare.display.TextSprite. */
  185. public function get dimensionHorizontalAnchor():int { return _dimensionAnchorH; }
  186. public function set dimensionHorizontalAnchor(a:int):void { _dimensionAnchorH = a; updateDimensionLabel(); }
  187. /** The vertical anchor point for axis dimension label.
  188. * @see flare.display.TextSprite. */
  189. public function get dimensionVerticalAnchor():int { return _dimensionAnchorV; }
  190. public function set dimensionVerticalAnchor(a:int):void { _dimensionAnchorV = a; updateDimensionLabel(); }
  191. /** The angle (orientation) of axis label text. */
  192. public function get dimensionLabelAngle():Number { return _dimensionLabelAngle; }
  193. public function set dimensionLabelAngle(a:Number):void { _dimensionLabelAngle = a; updateDimensionLabel(); }
  194. /** The axis dimension label text. */
  195. public function get dimensionLabelText():String { return _dimensionLabelText; }
  196. public function set dimensionLabelText(s:String):void { _dimensionLabelText = s; updateDimensionLabel(); }
  197. /** The color of axis dimension label text. */
  198. public function get dimensionLabelColor():uint { return _dimensionLabelColor; }
  199. public function set dimensionLabelColor(c:uint):void { _dimensionLabelColor = c; updateDimensionLabel(); }
  200. /** TextFormat (font, size, style) for axis label text. */
  201. public function get dimensionLabelTextFormat():TextFormat { return _dimensionLabelTextFormat; }
  202. public function set dimensionLabelTextFormat(f:TextFormat):void { _dimensionLabelTextFormat = f; updateDimensionLabel(); }
  203. // -- Initialization --------------------------------------------------
  204. /**
  205. * Creates a new Axis.
  206. * @param axisScale the axis scale to use. If null, a linear scale
  207. * is assumed.
  208. */
  209. public function Axis(axisScale:Scale=null)
  210. {
  211. this.axisScale = axisScale ? axisScale : new LinearScale();
  212. _prevScale = this.axisScale;
  213. initializeChildren();
  214. }
  215. /**
  216. * Initializes the child container sprites for labels and grid lines.
  217. */
  218. protected function initializeChildren():void
  219. {
  220. addChild(new Sprite()); // add gridlines
  221. addChild(new Sprite()); // add labels
  222. addChild(new Sprite()); // add dimension label container
  223. }
  224. // -- Updates ---------------------------------------------------------
  225. /**
  226. * Updates this axis, performing filtering and layout as needed.
  227. * @param trans a Transitioner for collecting value updates
  228. * @return the input transitioner.
  229. */
  230. public function update(trans:Transitioner):Transitioner
  231. {
  232. var t:Transitioner = (trans!=null ? trans : Transitioner.DEFAULT);
  233. // compute directions and offsets
  234. _xd = lineLengthX < 0 ? -1 : 1;
  235. _yd = lineLengthY < 0 ? -1 : 1;
  236. _xlo = _xd*labelOffsetX + (labelOffsetX>0 ? lineLengthX : 0);
  237. _ylo = -_yd*labelOffsetY + (labelOffsetY<0 ? lineLengthY : 0);
  238. // run updates
  239. filter(t);
  240. layout(t);
  241. updateLabels(); // TODO run through transitioner?
  242. updateGridLines(); // TODO run through transitioner?
  243. updateDimensionLabel();
  244. return trans;
  245. }
  246. // -- Lookups ---------------------------------------------------------
  247. /**
  248. * Returns the horizontal offset along the axis for the input value.
  249. * @param value an input data value
  250. * @return the horizontal offset along the axis corresponding to the
  251. * input value. This is the x-position minus <code>x1</code>.
  252. */
  253. public function offsetX(value:Object):Number
  254. {
  255. return axisScale.interpolate(value) * (_xb - _xa);
  256. }
  257. /**
  258. * Returns the vertical offset along the axis for the input value.
  259. * @param value an input data value
  260. * @return the vertical offset along the axis corresponding to the
  261. * input value. This is the y-position minus <code>y1</code>.
  262. */
  263. public function offsetY(value:Object):Number
  264. {
  265. return axisScale.interpolate(value) * (_yb - _ya);
  266. }
  267. /** @inheritDoc */
  268. public function X(value:Object):Number
  269. {
  270. return _xa + offsetX(value);
  271. }
  272. /** @inheritDoc */
  273. public function Y(value:Object):Number
  274. {
  275. return _ya + offsetY(value);
  276. }
  277. /** @inheritDoc */
  278. public function value(x:Number, y:Number, stayInBounds:Boolean=true):Object
  279. {
  280. // project the input point onto the axis line
  281. // (P-A).(B-A) / |B-A|^2 == fractional projection onto axis line
  282. var dx:Number = (_xb-_xa);
  283. var dy:Number = (_yb-_ya);
  284. var f:Number = ((x-_xa)*dx + (y-_ya)*dy) / (dx*dx + dy*dy);
  285. // correct bounds, if desired
  286. if (stayInBounds) {
  287. if (f < 0) f = 0;
  288. if (f > 1) f = 1;
  289. }
  290. // lookup and return value
  291. return axisScale.lookup(f);
  292. }
  293. /**
  294. * Clears the previous axis scale used, if cached.
  295. */
  296. public function clearPreviousScale():void
  297. {
  298. _prevScale = axisScale;
  299. }
  300. // -- Filter ----------------------------------------------------------
  301. /**
  302. * Performs filtering, determining which axis labels and grid lines
  303. * should be visible.
  304. * @param trans a Transitioner for collecting value updates.
  305. */
  306. protected function filter(trans:Transitioner) : void
  307. {
  308. var ordinal:uint = 0, i:uint, idx:int = -1, val:Object;
  309. var label:AxisLabel = null;
  310. var gline:AxisGridLine = null;
  311. var nl:uint = labels.numChildren;
  312. var ng:uint = gridLines.numChildren;
  313. var keepLabels:Array = new Array(nl);
  314. var keepLines:Array = new Array(ng);
  315. var values:Array = axisScale.values(numLabels);
  316. if (showLabels) { // process labels
  317. for (i=0, ordinal=0; i<values.length; ++i) {
  318. val = values[i];
  319. if ((idx = findLabel(val, nl)) < 0) {
  320. label = createLabel(val);
  321. } else {
  322. label = labels.getChildAt(idx) as AxisLabel;
  323. keepLabels[idx] = true;
  324. }
  325. label.ordinal = ordinal++;
  326. }
  327. }
  328. if (showLines) { // process gridlines
  329. for (i=0, ordinal=0; i<values.length; ++i) {
  330. val = values[i];
  331. if ((idx = findGridLine(val, ng)) < 0) {
  332. gline = createGridLine(val);
  333. } else {
  334. gline = gridLines.getChildAt(idx) as AxisGridLine;
  335. keepLines[idx] = true;
  336. }
  337. gline.ordinal = ordinal++;
  338. }
  339. }
  340. markRemovals(trans, keepLabels, labels);
  341. markRemovals(trans, keepLines, gridLines);
  342. }
  343. /**
  344. * Marks all items slated for removal from this axis.
  345. * @param trans a Transitioner for collecting value updates.
  346. * @param keep a Boolean array indicating which items to keep
  347. * @param con a container Sprite whose contents should be marked
  348. * for removal
  349. */
  350. protected function markRemovals(trans:Transitioner, keep:Array, con:Sprite) : void
  351. {
  352. for (var i:uint = keep.length; --i >= 0; ) {
  353. if (!keep[i]) trans.removeChild(con.getChildAt(i));
  354. }
  355. }
  356. // -- Layout ----------------------------------------------------------
  357. /**
  358. * Performs layout, setting the position of labels and grid lines.
  359. * @param trans a Transitioner for collecting value updates.
  360. */
  361. protected function layout(trans:Transitioner) : void
  362. {
  363. var i:uint, label:AxisLabel, gline:AxisGridLine, p:Point;
  364. var _lab:Sprite = this.labels;
  365. var _gls:Sprite = this.gridLines;
  366. var o:Object;
  367. // layout labels
  368. for (i=0; i<_lab.numChildren; ++i) {
  369. label = _lab.getChildAt(i) as AxisLabel;
  370. p = positionLabel(label, axisScale);
  371. o = trans.$(label);
  372. o.x = p.x;
  373. o.y = p.y;
  374. o.alpha = trans.willRemove(label) ? 0 : 1;
  375. }
  376. // fix label overlap
  377. if (fixLabelOverlap) fixOverlap(trans);
  378. // layout gridlines
  379. for (i=0; i<_gls.numChildren; ++i) {
  380. gline = _gls.getChildAt(i) as AxisGridLine;
  381. p = positionGridLine(gline, axisScale);
  382. o = trans.$(gline);
  383. o.x1 = p.x;
  384. o.y1 = p.y;
  385. o.x2 = p.x + lineLengthX + _xd*(lineCapX1+lineCapX2);
  386. o.y2 = p.y + lineLengthY + _yd*(lineCapY1+lineCapY2);
  387. o.alpha = trans.willRemove(gline) ? 0 : 1;
  388. }
  389. // update previous scale
  390. _prevScale = axisScale.clone(); // clone scale
  391. _xaP = _xa; _yaP = _ya; _xbP = _xb; _ybP = _yb;
  392. }
  393. // -- Label Overlap ---------------------------------------------------
  394. /**
  395. * Eliminates overlap between labels along an axis.
  396. * @param trans a transitioner, potentially storing label positions
  397. */
  398. protected function fixOverlap(trans:Transitioner):void
  399. {
  400. var labs:Array = [], d:DisplayObject, i:int;
  401. // collect and sort labels
  402. for (i=0; i<labels.numChildren; ++i) {
  403. var s:AxisLabel = AxisLabel(labels.getChildAt(i));
  404. if (!trans.willRemove(s)) labs.push(s);
  405. }
  406. if (labs.length == 0) return;
  407. labs.sortOn("ordinal", Array.NUMERIC);
  408. // stores the labels to remove
  409. var rem:Dictionary = new Dictionary();
  410. if (axisScale.scaleType == ScaleType.LOG) {
  411. fixLogOverlap(labs, rem, trans, axisScale);
  412. }
  413. // maintain min and max if we get down to two
  414. i = labs.length;
  415. var min:Object = labs[0];
  416. var max:Object = labs[i-1];
  417. var mid:Object = (i&1) ? labs[(i>>1)] : null;
  418. // fix overlap with an iterative optimization
  419. // remove every other label with each iteration
  420. while (hasOverlap(labs, trans)) {
  421. // reduce labels
  422. i = labs.length;
  423. if (mid && i>3 && i<8) { // use min, med, max if we can
  424. for each (d in labs) rem[d] = d;
  425. if (rem[min]) delete rem[min];
  426. if (rem[max]) delete rem[max];
  427. if (rem[mid]) delete rem[mid];
  428. labs = [min, mid, max];
  429. }
  430. else if (i < 4) { // use min and max if we're down to two
  431. if (rem[min]) delete rem[min];
  432. if (rem[max]) delete rem[max];
  433. for each (d in labs) {
  434. if (d != min && d != max) rem[d] = d;
  435. }
  436. break;
  437. }
  438. else { // else remove every odd element
  439. i = i - (i&1 ? 2 : 1);
  440. for (; i>0; i-=2) {
  441. rem[labs[i]] = labs[i];
  442. labs.splice(i, 1); // remove from array
  443. }
  444. }
  445. }
  446. // remove the deleted labels
  447. for each (d in rem) {
  448. trans.$(d).alpha = 0;
  449. trans.removeChild(d, true);
  450. }
  451. }
  452. private static function fixLogOverlap(labs:Array, rem:Dictionary,
  453. trans:Transitioner, scale:Scale):void
  454. {
  455. var base:int = int(Object(scale).base), i:int, j:int, zidx:int;
  456. if (!hasOverlap(labs, trans)) return;
  457. // find zero
  458. zidx = Arrays.binarySearch(labs, 0, "value");
  459. var neg:Boolean = Number(scale.min) < 0;
  460. var pos:Boolean = Number(scale.max) > 0;
  461. // if includes negative, traverse backwards from zero/end
  462. if (neg) {
  463. i = (zidx<0 ? labs.length : zidx) - (pos ? 1 : 2);
  464. for (j=pos?1:2; i>=0; ++j, --i) {
  465. if (j == base) {
  466. j = 1;
  467. } else {
  468. rem[labs[i]] = labs[i];
  469. labs.splice(i, 1); --zidx;
  470. }
  471. }
  472. }
  473. // if includes positive, traverse forwards from zero/start
  474. if (pos) {
  475. i = (zidx<0 ? 0 : zidx+1) + (neg ? 0 : 1);
  476. for (j=neg?1:2; i<labs.length; ++j) {
  477. if (j == base) {
  478. j = 1; ++i;
  479. } else {
  480. rem[labs[i]] = labs[i];
  481. labs.splice(i, 1);
  482. }
  483. }
  484. }
  485. }
  486. private static function hasOverlap(labs:Array, trans:Transitioner):Boolean
  487. {
  488. var d:DisplayObject = labs[0], e:DisplayObject;
  489. for (var i:int=1; i<labs.length; ++i) {
  490. if (overlaps(trans, d, (e=labs[i]))) return true;
  491. d = e;
  492. }
  493. return false;
  494. }
  495. /**
  496. * Indicates if two display objects overlap, sensitive to any target
  497. * values stored in a transitioner.
  498. * @param trans a Transitioner, potentially with target values
  499. * @param l1 a display object
  500. * @param l2 a display object
  501. * @return true if the objects overlap (considering values in the
  502. * transitioner, if appropriate), false otherwise
  503. */
  504. private static function overlaps(trans:Transitioner,
  505. l1:DisplayObject, l2:DisplayObject):Boolean
  506. {
  507. if (trans.immediate) return l1.hitTestObject(l2);
  508. // get original co-ordinates
  509. var xa:Number = l1.x, ya:Number = l1.y;
  510. var xb:Number = l2.x, yb:Number = l2.y;
  511. var o:Object;
  512. // set to target co-ordinates
  513. o = trans.$(l1); l1.x = o.x; l1.y = o.y;
  514. o = trans.$(l2); l2.x = o.x; l2.y = o.y;
  515. // compute overlap
  516. var b:Boolean = l1.hitTestObject(l2);
  517. // reset to original coordinates
  518. l1.x = xa; l1.y = ya; l2.x = xb; l2.y = yb;
  519. return b;
  520. }
  521. // -- Axis Label Helpers ----------------------------------------------
  522. /**
  523. * Creates a new axis label.
  524. * @param val the value to create the label for
  525. * @return an AxisLabel
  526. */
  527. protected function createLabel(val:Object) : AxisLabel
  528. {
  529. var label:AxisLabel = new AxisLabel();
  530. var f:Number = _prevScale.interpolate(val);
  531. label.alpha = 0;
  532. label.value = val;
  533. label.x = _xlo + _xaP + f*(_xbP - _xaP);
  534. label.y = _ylo + _yaP + f*(_ybP - _yaP);
  535. updateLabel(label);
  536. labels.addChild(label);
  537. return label;
  538. }
  539. /**
  540. * Computes the position of an axis label.
  541. * @param label the axis label to layout
  542. * @param scale the scale used to map values to the axis
  543. * @return a Point with x,y coordinates for the axis label
  544. */
  545. protected function positionLabel(label:AxisLabel, scale:Scale) : Point
  546. {
  547. var f:Number = scale.interpolate(label.value);
  548. _point.x = _xlo + _xa + f*(_xb-_xa);
  549. _point.y = _ylo + _ya + f*(_yb-_ya);
  550. return _point;
  551. }
  552. /**
  553. * Updates an axis label's settings
  554. * @param label the label to update
  555. */
  556. protected function updateLabel(label:AxisLabel) : void
  557. {
  558. label.textFormat = _labelTextFormat;
  559. label.horizontalAnchor = _anchorH;
  560. label.verticalAnchor = _anchorV;
  561. label.rotation = (180/Math.PI) * _labelAngle;
  562. label.textMode = _labelTextMode;
  563. label.text = _labelFormat==null ? axisScale.label(label.value)
  564. : Strings.format(_labelFormat, label.value);
  565. }
  566. /**
  567. * Updates all axis labels.
  568. */
  569. protected function updateLabels() : void
  570. {
  571. var _labels:Sprite = this.labels;
  572. for (var i:uint = 0; i<_labels.numChildren; ++i) {
  573. updateLabel(_labels.getChildAt(i) as AxisLabel);
  574. }
  575. }
  576. /**
  577. * Returns the index of a label in the label's container sprite for a
  578. * given data value.
  579. * @param val the data value to find
  580. * @param len the number of labels to check
  581. * @return the index of a label with matching value, or -1 if no label
  582. * was found
  583. */
  584. protected function findLabel(val:Object, len:uint) : int
  585. {
  586. var _labels:Sprite = this.labels;
  587. for (var i:uint = 0; i < len; ++i) {
  588. // TODO: make this robust to repeated values
  589. if (Stats.equal((_labels.getChildAt(i) as AxisLabel).value, val)) {
  590. return i;
  591. }
  592. }
  593. return -1;
  594. }
  595. /**
  596. * Updates all axis labels.
  597. */
  598. protected function updateDimensionLabel() : void
  599. {
  600. var container:Sprite = this.dimensionLabel;
  601. var label:AxisLabel = container.numChildren > 0 ? (container.getChildAt(0) as AxisLabel) : null;
  602. if (label == null) {
  603. label = new AxisLabel();
  604. container.addChild(label);
  605. }
  606. label.x = this.dimensionLabelOffsetX + (this.x1 + this.x2) / 2;
  607. label.y = this.dimensionLabelOffsetY + (this.y1 + this.y2) / 2;
  608. label.horizontalAnchor = _dimensionAnchorH;
  609. label.verticalAnchor = _dimensionAnchorV;
  610. label.rotation = _dimensionLabelAngle;
  611. label.text = _dimensionLabelText;
  612. }
  613. // -- Axis GridLine Helpers -------------------------------------------
  614. /**
  615. * Creates a new axis grid line.
  616. * @param val the value to create the grid lines for
  617. * @return an AxisGridLine
  618. */
  619. protected function createGridLine(val:Object) : AxisGridLine
  620. {
  621. var gline:AxisGridLine = new AxisGridLine();
  622. var f:Number = _prevScale.interpolate(val);
  623. gline.alpha = 0;
  624. gline.value = val;
  625. gline.x1 = _xaP + f*(_xbP-_xaP) - _xd*lineCapX1;
  626. gline.y1 = _yaP + f*(_ybP-_yaP) - _yd*lineCapY1;
  627. gline.x2 = gline.x1 + lineLengthX + _xd*(lineCapX1 + lineCapX2)
  628. gline.y2 = gline.y1 + lineLengthY + _yd*(lineCapY1 + lineCapY2);
  629. updateGridLine(gline);
  630. gridLines.addChild(gline);
  631. return gline;
  632. }
  633. /**
  634. * Computes the position of an axis grid line.
  635. * @param gline the axis grid line to layout
  636. * @param scale the scale used to map values to the axis
  637. * @return a Point with x,y coordinates for the axis grid line
  638. */
  639. protected function positionGridLine(gline:AxisGridLine, scale:Scale) : Point
  640. {
  641. var f:Number = scale.interpolate(gline.value);
  642. _point.x = _xa + f*(_xb-_xa) - _xd*lineCapX1;
  643. _point.y = _ya + f*(_yb-_ya) - _yd*lineCapY1;
  644. return _point;
  645. }
  646. /**
  647. * Updates an axis grid line's settings
  648. * @param gline the grid line to update
  649. */
  650. protected function updateGridLine(gline:AxisGridLine) : void
  651. {
  652. gline.lineColor = _lineColor;
  653. gline.lineWidth = _lineWidth;
  654. }
  655. /**
  656. * Updates all grid lines.
  657. */
  658. protected function updateGridLines() : void
  659. {
  660. var _glines:Sprite = this.gridLines;
  661. for (var i:uint = 0; i<_glines.numChildren; ++i) {
  662. updateGridLine(_glines.getChildAt(i) as AxisGridLine);
  663. }
  664. }
  665. /**
  666. * Returns the index of a grid lines in the line's container sprite
  667. * for a given data value.
  668. * @param val the data value to find
  669. * @param len the number of grid lines to check
  670. * @return the index of a grid line with matching value, or -1 if no
  671. * grid line was found
  672. */
  673. protected function findGridLine(val:Object, len:uint) : int
  674. {
  675. var _glines:Sprite = this.gridLines;
  676. for (var i:uint = 0; i<len; ++i) {
  677. // TODO: make this robust to repeated values
  678. if (Stats.equal((_glines.getChildAt(i) as AxisGridLine).value, val)) {
  679. return i;
  680. }
  681. }
  682. return -1;
  683. }
  684. } // end of class Axis
  685. }