PageRenderTime 27ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/flare/vis/axis/Axis.as

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