Guest User

Untitled

a guest
Oct 19th, 2017
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.15 KB | None | 0 0
  1. package flare.vis
  2. {
  3. import flare.animate.ISchedulable;
  4. import flare.animate.Scheduler;
  5. import flare.animate.Transitioner;
  6. import flare.util.Displays;
  7. import flare.vis.axis.Axes;
  8. import flare.vis.axis.CartesianAxes;
  9. import flare.vis.controls.ControlList;
  10. import flare.vis.data.Data;
  11. import flare.vis.data.Tree;
  12. import flare.vis.events.DataEvent;
  13. import flare.vis.events.VisualizationEvent;
  14. import flare.vis.operator.IOperator;
  15. import flare.vis.operator.OperatorList;
  16.  
  17. import flash.display.DisplayObject;
  18. import flash.display.Sprite;
  19. import flash.events.Event;
  20. import flash.geom.Rectangle;
  21.  
  22. [Event(name="update", type="flare.vis.events.VisualizationEvent")]
  23.  
  24. /**
  25. * The Visualization class represents an interactive data visualization.
  26. * A visualization instance consists of
  27. * <ul>
  28. * <li>A <code>Data</code> instance containing <code>DataSprite</code>
  29. * objects that visually represent individual data elements</li>
  30. * <li>An <code>OperatorList</code> of visualization operators that
  31. * determine visual encodings for position, color, size and other
  32. * properties.</li>
  33. * <li>A <code>ControlList</code> of interactive controls that enable
  34. * interaction with the visualized data.</li>
  35. * <li>An <code>Axes</code> instance for presenting axes for metric
  36. * data visualizations. Axes are often configuring automatically by
  37. * the visualization's operators.</li>
  38. * </ul>
  39. *
  40. * <p>Visual objects are added to the display list within the
  41. * <code>marks</code> property of the visualization, as the
  42. * <code>Data</code> object is not a <code>DisplayObjectContainer</code>.
  43. * </p>
  44. *
  45. * <p>All visual elements are contained within <code>layers</code> Sprite.
  46. * This includes the <code>axes</code>, <code>marks</code>, and
  47. * (optionally) <code>labels</code> layers. Clients who wish to add
  48. * additional layers to a visualization should add them directly to the
  49. * <code>layers</code> sprite. Just take care to maintain the desired order
  50. * of elements to avoid occlusion.</p>
  51. *
  52. * <p>To create a new Visualization, load in a data set, construct
  53. * a <code>Data</code> instance, and instantiate a new
  54. * <code>Visualization</code> with the input data. Then add the series
  55. * of desired operators to the <code>operators</code> property to
  56. * define the visual encodings.</p>
  57. *
  58. * @see flare.vis.operator
  59. */
  60. public class Visualization extends Sprite
  61. {
  62. // -- Properties ------------------------------------------------------
  63.  
  64. private var _bounds:Rectangle = new Rectangle(0,0,500,500);
  65.  
  66. private var _layers:Sprite; // sprite for all layers in visualization
  67. private var _marks:Sprite; // sprite for all visualized data items
  68. private var _labels:Sprite; // (optional) sprite for labels
  69. private var _axes:Axes; // (optional) axes, lines, and axis labels
  70.  
  71. private var _data:Data; // data structure holding visualized data
  72.  
  73. private var _ops:Object; // map of all named operators
  74. private var _operators:OperatorList; // the "main" operator list
  75. private var _controls:ControlList; // interactive controls
  76. private var _rec:ISchedulable; // for running continuous updates
  77.  
  78. /** An object storing extra properties for the visualziation. */
  79. public var props:Object = {};
  80.  
  81. /** The layout bounds of the visualization. This determines the layout
  82. * region for data elements. For example, with an axis layout, the
  83. * bounds determined the data layout region--this does not include
  84. * space used by axis labels. */
  85. public function get bounds():Rectangle { return _bounds; }
  86. public function set bounds(r:Rectangle):void {
  87. _bounds = r;
  88. if (stage) stage.invalidate();
  89. }
  90.  
  91. /** Container sprite holding each layer in the visualization. */
  92. public function get layers():Sprite { return _layers; }
  93.  
  94. /** Sprite containing the <code>DataSprite</code> instances. */
  95. public function get marks():Sprite { return _marks; }
  96.  
  97. /** Sprite containing a separate layer for labels. Null by default. */
  98. public function get labels():Sprite { return _labels; }
  99. public function set labels(l:Sprite):void {
  100. if (_labels != null)
  101. _layers.removeChild(_labels);
  102. _labels = l;
  103. if (_labels != null) {
  104. _labels.name = "_labels";
  105. _layers.addChildAt(_labels, _layers.getChildIndex(_marks)+1);
  106. }
  107. }
  108.  
  109. /**
  110. * The axes for this visualization. May be null if no axes are needed.
  111. */
  112. public function get axes():Axes { return _axes; }
  113. public function set axes(a:Axes):void {
  114. if (_axes != null)
  115. _layers.removeChild(_axes);
  116. _axes = a;
  117. if (_axes != null) {
  118. _axes.visualization = this;
  119. _axes.name = "_axes";
  120. _layers.addChildAt(_axes, 0);
  121. }
  122. }
  123. /** The axes as an x-y <code>CartesianAxes</code> instance. Returns
  124. * null if <code>axes</code> is null or not a cartesian axes instance.
  125. */
  126. public function get xyAxes():CartesianAxes { return _axes as CartesianAxes; }
  127.  
  128. /** The visual data elements in this visualization. */
  129. public function get data():Data { return _data; }
  130.  
  131. /** Tree structure of visual data elements in this visualization.
  132. * Generates a spanning tree over a graph structure, if necessary. */
  133. public function get tree():Tree { return _data.tree; }
  134. public function set data(d:Data):void
  135. {
  136. if (_data != null) {
  137. _data.visit(_marks.removeChild);
  138. _data.removeEventListener(DataEvent.ADD, dataAdded);
  139. _data.removeEventListener(DataEvent.REMOVE, dataRemoved);
  140. }
  141. _data = d;
  142. if (_data != null) {
  143. _data.visit(_marks.addChild);
  144. _data.addEventListener(DataEvent.ADD, dataAdded);
  145. _data.addEventListener(DataEvent.REMOVE, dataRemoved);
  146. }
  147. }
  148.  
  149. /** The operator list for defining the visual encodings. */
  150. public function get operators():OperatorList { return _operators; }
  151.  
  152. /** The control list containing interactive controls. */
  153. public function get controls():ControlList { return _controls; }
  154.  
  155. /** Flag indicating if the visualization should update with every
  156. * frame. False by default. */
  157. public function get continuousUpdates():Boolean { return _rec != null; }
  158. public function set continuousUpdates(b:Boolean):void
  159. {
  160. if (b && _rec==null) {
  161. _rec = new Recurrence(this);
  162. Scheduler.instance.add(_rec);
  163. }
  164. else if (!b && _rec!=null) {
  165. Scheduler.instance.remove(_rec);
  166. _rec = null;
  167. }
  168. }
  169.  
  170. // -- Methods ---------------------------------------------------------
  171.  
  172. /**
  173. * Creates a new Visualization with the given data and axes.
  174. * @param data the <code>Data</code> instance containing the
  175. * <code>DataSprite</code> elements in this visualization.
  176. * @param axes the <code>Axes</code> to use with this visualization.
  177. * Null by default; layout operators may re-configure the axes.
  178. */
  179. public function Visualization(data:Data=null, axes:Axes=null) {
  180. addChild(_layers = new Sprite());
  181. _layers.name = "_layers";
  182.  
  183. _layers.addChild(_marks = new Sprite());
  184. _marks.name = "_marks";
  185.  
  186. if (data != null) this.data = data;
  187. if (axes != null) this.axes = axes;
  188.  
  189. _operators = new OperatorList();
  190. _operators.visualization = this;
  191. _ops = { main:_operators };
  192.  
  193. _controls = new ControlList();
  194. _controls.visualization = this;
  195.  
  196. Displays.addStageListener(this, Event.RENDER,
  197. setHitArea, false, int.MIN_VALUE+1);
  198. }
  199.  
  200. /**
  201. * Update this visualization, re-calculating axis layout and running
  202. * the operator chain. The input transitioner is used to actually
  203. * perform value updates, enabling animated transitions. This method
  204. * also issues a <code>VisualizationEvent.UPDATE</code> event to any
  205. * registered listeners.
  206. * @param t a transitioner or time span for updating object values. If
  207. * the input is a transitioner, it will be used to store the updated
  208. * values. If the input is a number, a new Transitioner with duration
  209. * set to the input value will be used. The input is null by default,
  210. * in which case object values are updated immediately.
  211. * @param operators an optional list of named operators to run in the
  212. * update.
  213. * @return the transitioner used to store updated values.
  214. */
  215. public function update(t:*=null, ...operators):Transitioner
  216. {
  217. if (operators) {
  218. if (operators.length == 0) {
  219. operators = null;
  220. } else if (operators[0] is Array) {
  221. operators = operators[0].length > 0 ? operators[0] : null;
  222. }
  223. }
  224. var trans:Transitioner = Transitioner.instance(t);
  225. if (_axes != null) _axes.update(trans);
  226. if (operators) {
  227. for each (var name:String in operators) {
  228. if (_ops.hasOwnProperty(name))
  229. _ops[name].operate(trans);
  230. else
  231. throw new Error("Unknown operator: " + name);
  232. }
  233. } else {
  234. _operators.operate(trans);
  235. }
  236. if (_axes != null) _axes.update(trans);
  237. fireEvent(VisualizationEvent.UPDATE, trans, operators);
  238. return trans;
  239. }
  240.  
  241. /**
  242. * A function generator that can be used to invoke a visualization
  243. * update at a later time. This method returns a function that
  244. * accepts a <code>Transitioner</code> as its sole argument and then
  245. * executes a visualization update using the specified named
  246. * operators.
  247. * @param operators an optional array of named operators to run
  248. * @return a function that takes a <code>Transitioner</code> argument
  249. * and invokes an update.
  250. */
  251. public function updateLater(...operators):Function
  252. {
  253. return function(t:Transitioner):Transitioner {
  254. return update(t, operators);
  255. }
  256. }
  257.  
  258. /**
  259. * Updates the data display bounds for a visualization based on a
  260. * given aspect ratio and provided width and height values. If both
  261. * width and height values are provided, they will be treated as the
  262. * maximum bounds. If only one of the width or height is provided, then
  263. * the width or height will match that value, and the other will be
  264. * determined by the aspect ratio. Finally, if neither width nor height
  265. * is provided, then the current width and height of the display bounds
  266. * will be used as the maximum bounds. After calling this method, a
  267. * call to <code>update</code> is necessary to reflect the change.
  268. * @param ar the desired aspect ratio for the data display
  269. * @param width the desired width. If a height value is also provided,
  270. * this width value will be treated as the maximum possible width
  271. * (the actual width may be lower).
  272. * @param height the desired height. If a width value is also provided,
  273. * this height value will be treated as the maximum possible height
  274. * (the actual height may be lower).
  275. */
  276. public function setAspectRatio(ar:Number, width:Number=-1,
  277. height:Number=-1):void
  278. {
  279. // compute new bounds
  280. if (width > 0 && height < 0) {
  281. height = width / ar;
  282. } else if (width < 0 && height > 0) {
  283. width = ar * height;
  284. } else {
  285. if (width < 0 && height < 0) {
  286. width = bounds.width;
  287. height = bounds.height;
  288. }
  289. if (ar > 1) { // width > height
  290. height = width / ar;
  291. } else if (ar < 1) { // height > width
  292. width = ar * height;
  293. }
  294. }
  295. // update bounds
  296. bounds.width = width;
  297. bounds.height = height;
  298. }
  299.  
  300. // -- Named Operators -------------------------------------------------
  301.  
  302. /**
  303. * Sets a new named operator. This method can be used to add extra
  304. * operators to a visualization, in addition to those in the main
  305. * <code>operators</code> property. These operators can be invoked by
  306. * passing the operator name as an additional parameter of the
  307. * <code>update</code> method. If an operator of the same name
  308. * already exists, it will be replaced. Note that the name "main"
  309. * refers to the same operator list as the <code>operators</code>
  310. * property and can not be replaced.
  311. * @param name the name of the operator to add
  312. * @param op the operator to add
  313. * @return the added operator
  314. */
  315. public function setOperator(name:String, op:IOperator):IOperator
  316. {
  317. if (name=="main") {
  318. throw new ArgumentError("Illegal group name: " +
  319. "\"main\" is a reserved name.");
  320. }
  321. _ops[name] = op;
  322. op.visualization = this;
  323. return op;
  324. }
  325.  
  326. /**
  327. * Removes a named operator. An error will be thrown if the caller
  328. * attempts to remove the operator "main".
  329. * @param name the name of the operator to remove
  330. * @return the removed operator
  331. */
  332. public function removeOperator(name:String):IOperator
  333. {
  334. if (name=="main") {
  335. throw new ArgumentError("Illegal group name: " +
  336. "\"main\" is a reserved name.");
  337. }
  338. var op:IOperator = _ops[name];
  339. if (op) delete _ops[name];
  340. return op;
  341. }
  342.  
  343. /**
  344. * Retrieves the operator with the given name. The name "main" will
  345. * return the operator list stored in the <code>operators</code>
  346. * property.
  347. * @param name the name of the operator
  348. * @return the operator
  349. */
  350. public function operator(name:String):IOperator
  351. {
  352. return _ops[name];
  353. }
  354.  
  355. // -- Event Handling --------------------------------------------------
  356.  
  357. /**
  358. * Creates a sprite covering the bounds for this visualization and
  359. * sets it to be this visualization's hit area. Typically, this
  360. * method is triggered in response to a <code>RENDER</code> event.
  361. * <p>To disable automatic hit area calculation, use
  362. * <code>stage.removeEventListener(Event.RENDER, vis.setHitArea)</code>
  363. * <em>after</em> the visualization has been added to the stage.</p>
  364. * @param evt an event that triggered the hit area update
  365. */
  366. public function setHitArea(evt:Event=null):void
  367. {
  368. // get the union of the specified and actual bounds
  369. var rb:Rectangle = getBounds(this);
  370. var x1:Number = rb.left, x2:Number = rb.right;
  371. var y1:Number = rb.top, y2:Number = rb.bottom;
  372. if (bounds) {
  373. x1 = Math.min(x1, bounds.left);
  374. y1 = Math.min(y1, bounds.top);
  375. x2 = Math.max(x2, bounds.right);
  376. y2 = Math.max(y1, bounds.bottom);
  377. }
  378.  
  379. // create the hit area sprite
  380. var hit:Sprite = getChildByName("_hitArea") as Sprite;
  381. if (hit == null) {
  382. hit = new Sprite();
  383. hit.name = "_hitArea";
  384. addChildAt(hit, 0);
  385. }
  386. hit.visible = false;
  387. hit.mouseEnabled = false;
  388. hit.graphics.clear();
  389. hit.graphics.beginFill(0xffffff, 1);
  390. hit.graphics.drawRect(x1, y1, x2-x1, y2-y1);
  391. hitArea = hit;
  392. }
  393.  
  394. /**
  395. * Fires a visualization event of the given type.
  396. * @param type the type of the event
  397. * @param t a transitioner that listeners should use for any value
  398. * updates performed in response to this event
  399. */
  400. protected function fireEvent(type:String, t:Transitioner,
  401. params:Array):void
  402. {
  403. // fire event, if anyone is listening
  404. if (hasEventListener(type)) {
  405. dispatchEvent(new VisualizationEvent(type, t, params));
  406. }
  407. }
  408.  
  409. /**
  410. * Data listener invoked when new items are added to this
  411. * Visualization's <code>data</code> instance.
  412. * @param evt the data event
  413. */
  414. protected function dataAdded(evt:DataEvent):void
  415. {
  416. if (evt.node) {
  417. for each (var d:DisplayObject in evt.items)
  418. _marks.addChild(d);
  419. } else {
  420. for each (d in evt.items)
  421. _marks.addChildAt(d, 0);
  422. }
  423. }
  424.  
  425. /**
  426. * Data listener invoked when new items are removed from this
  427. * Visualization's <code>data</code> instance.
  428. * @param evt the data event
  429. */
  430. protected function dataRemoved(evt:DataEvent):void
  431. {
  432. for each (var d:DisplayObject in evt.items)
  433. _marks.removeChild(d);
  434. }
  435.  
  436. } // end of class Visualization
  437. }
  438.  
  439. import flare.animate.ISchedulable;
  440. import flare.vis.Visualization;
  441.  
  442. /**
  443. * Simple ISchedulable instance that repeatedly calls a Visualization's
  444. * <code>update</code> method.
  445. */
  446. class Recurrence implements ISchedulable {
  447. private var _vis:Visualization;
  448. public function get id():String { return null; }
  449. public function set id(s:String):void { /* do nothing */ }
  450. public function cancelled():void { /* do nothing */ }
  451. public function Recurrence(vis:Visualization) {
  452. _vis = vis;
  453. }
  454. public function evaluate(t:Number):Boolean {
  455. _vis.update(); return false;
  456. }
  457. }
Add Comment
Please, Sign In to add comment