PageRenderTime 53ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/flare/src/flare/vis/axis/Axis.as

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