summaryrefslogtreecommitdiff
path: root/src/js/util/mxMorphing.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/js/util/mxMorphing.js')
-rw-r--r--src/js/util/mxMorphing.js239
1 files changed, 239 insertions, 0 deletions
diff --git a/src/js/util/mxMorphing.js b/src/js/util/mxMorphing.js
new file mode 100644
index 0000000..442143d
--- /dev/null
+++ b/src/js/util/mxMorphing.js
@@ -0,0 +1,239 @@
+/**
+ * $Id: mxMorphing.js,v 1.4 2010-06-03 13:37:07 gaudenz Exp $
+ * Copyright (c) 2006-2010, JGraph Ltd
+ */
+/**
+ *
+ * Class: mxMorphing
+ *
+ * Implements animation for morphing cells. Here is an example of
+ * using this class for animating the result of a layout algorithm:
+ *
+ * (code)
+ * graph.getModel().beginUpdate();
+ * try
+ * {
+ * var circleLayout = new mxCircleLayout(graph);
+ * circleLayout.execute(graph.getDefaultParent());
+ * }
+ * finally
+ * {
+ * var morph = new mxMorphing(graph);
+ * morph.addListener(mxEvent.DONE, function()
+ * {
+ * graph.getModel().endUpdate();
+ * });
+ *
+ * morph.startAnimation();
+ * }
+ * (end)
+ *
+ * Constructor: mxMorphing
+ *
+ * Constructs an animation.
+ *
+ * Parameters:
+ *
+ * graph - Reference to the enclosing <mxGraph>.
+ * steps - Optional number of steps in the morphing animation. Default is 6.
+ * ease - Optional easing constant for the animation. Default is 1.5.
+ * delay - Optional delay between the animation steps. Passed to <mxAnimation>.
+ */
+function mxMorphing(graph, steps, ease, delay)
+{
+ mxAnimation.call(this, delay);
+ this.graph = graph;
+ this.steps = (steps != null) ? steps : 6;
+ this.ease = (ease != null) ? ease : 1.5;
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxMorphing.prototype = new mxAnimation();
+mxMorphing.prototype.constructor = mxMorphing;
+
+/**
+ * Variable: graph
+ *
+ * Specifies the delay between the animation steps. Defaul is 30ms.
+ */
+mxMorphing.prototype.graph = null;
+
+/**
+ * Variable: steps
+ *
+ * Specifies the maximum number of steps for the morphing.
+ */
+mxMorphing.prototype.steps = null;
+
+/**
+ * Variable: step
+ *
+ * Contains the current step.
+ */
+mxMorphing.prototype.step = 0;
+
+/**
+ * Variable: ease
+ *
+ * Ease-off for movement towards the given vector. Larger values are
+ * slower and smoother. Default is 4.
+ */
+mxMorphing.prototype.ease = null;
+
+/**
+ * Variable: cells
+ *
+ * Optional array of cells to be animated. If this is not specified
+ * then all cells are checked and animated if they have been moved
+ * in the current transaction.
+ */
+mxMorphing.prototype.cells = null;
+
+/**
+ * Function: updateAnimation
+ *
+ * Animation step.
+ */
+mxMorphing.prototype.updateAnimation = function()
+{
+ var move = new mxCellStatePreview(this.graph);
+
+ if (this.cells != null)
+ {
+ // Animates the given cells individually without recursion
+ for (var i = 0; i < this.cells.length; i++)
+ {
+ this.animateCell(cells[i], move, false);
+ }
+ }
+ else
+ {
+ // Animates all changed cells by using recursion to find
+ // the changed cells but not for the animation itself
+ this.animateCell(this.graph.getModel().getRoot(), move, true);
+ }
+
+ this.show(move);
+
+ if (move.isEmpty() ||
+ this.step++ >= this.steps)
+ {
+ this.stopAnimation();
+ }
+};
+
+/**
+ * Function: show
+ *
+ * Shows the changes in the given <mxCellStatePreview>.
+ */
+mxMorphing.prototype.show = function(move)
+{
+ move.show();
+};
+
+/**
+ * Function: animateCell
+ *
+ * Animates the given cell state using <mxCellStatePreview.moveState>.
+ */
+mxMorphing.prototype.animateCell = function(cell, move, recurse)
+{
+ var state = this.graph.getView().getState(cell);
+ var delta = null;
+
+ if (state != null)
+ {
+ // Moves the animated state from where it will be after the model
+ // change by subtracting the given delta vector from that location
+ delta = this.getDelta(state);
+
+ if (this.graph.getModel().isVertex(cell) &&
+ (delta.x != 0 || delta.y != 0))
+ {
+ var translate = this.graph.view.getTranslate();
+ var scale = this.graph.view.getScale();
+
+ delta.x += translate.x * scale;
+ delta.y += translate.y * scale;
+
+ move.moveState(state, -delta.x / this.ease, -delta.y / this.ease);
+ }
+ }
+
+ if (recurse && !this.stopRecursion(state, delta))
+ {
+ var childCount = this.graph.getModel().getChildCount(cell);
+
+ for (var i = 0; i < childCount; i++)
+ {
+ this.animateCell(this.graph.getModel().getChildAt(cell, i), move, recurse);
+ }
+ }
+};
+
+/**
+ * Function: stopRecursion
+ *
+ * Returns true if the animation should not recursively find more
+ * deltas for children if the given parent state has been animated.
+ */
+mxMorphing.prototype.stopRecursion = function(state, delta)
+{
+ return delta != null && (delta.x != 0 || delta.y != 0);
+};
+
+/**
+ * Function: getDelta
+ *
+ * Returns the vector between the current rendered state and the future
+ * location of the state after the display will be updated.
+ */
+mxMorphing.prototype.getDelta = function(state)
+{
+ var origin = this.getOriginForCell(state.cell);
+ var translate = this.graph.getView().getTranslate();
+ var scale = this.graph.getView().getScale();
+ var current = new mxPoint(
+ state.x / scale - translate.x,
+ state.y / scale - translate.y);
+
+ return new mxPoint(
+ (origin.x - current.x) * scale,
+ (origin.y - current.y) * scale);
+};
+
+/**
+ * Function: getOriginForCell
+ *
+ * Returns the top, left corner of the given cell. TODO: Improve performance
+ * by using caching inside this method as the result per cell never changes
+ * during the lifecycle of this object.
+ */
+mxMorphing.prototype.getOriginForCell = function(cell)
+{
+ var result = null;
+
+ if (cell != null)
+ {
+ result = this.getOriginForCell(this.graph.getModel().getParent(cell));
+ var geo = this.graph.getCellGeometry(cell);
+
+ // TODO: Handle offset, relative geometries etc
+ if (geo != null)
+ {
+ result.x += geo.x;
+ result.y += geo.y;
+ }
+ }
+
+ if (result == null)
+ {
+ var t = this.graph.view.getTranslate();
+ result = new mxPoint(-t.x, -t.y);
+ }
+
+ return result;
+};