PageRenderTime 4083ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/vis/genvis/src/genvis/vis/axis/Axis.as

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