/// <summary>
        /// Calculates diff between 2 <see cref="PositionedGraph"/>s.
        /// The <see cref="GraphDiff"/> describes a matching between nodes in the graphs, added and removed nodes.
        /// </summary>
        public GraphDiff MatchGraphs(PositionedGraph oldGraph, PositionedGraph newGraph)
        {
            if (oldGraph == null)
            {
                if (newGraph == null)
                {
                    return(new GraphDiff());
                }
                else
                {
                    GraphDiff addAllDiff = new GraphDiff();
                    foreach (PositionedNode newNode in newGraph.Nodes)
                    {
                        addAllDiff.SetAdded(newNode);
                    }
                    return(addAllDiff);
                }
            }
            else if (newGraph == null)
            {
                GraphDiff removeAllDiff = new GraphDiff();
                foreach (PositionedNode oldNode in oldGraph.Nodes)
                {
                    removeAllDiff.SetRemoved(oldNode);
                }
                return(removeAllDiff);
            }
            // none of the graphs is null

            GraphDiff diff = new GraphDiff();

            Dictionary <int, PositionedNode>  newNodeForHashCode = BuildHashToNodeMap(newGraph);
            Dictionary <PositionedNode, bool> newNodeMatched     = new Dictionary <PositionedNode, bool>();

            foreach (PositionedNode oldNode in oldGraph.Nodes)
            {
                PositionedNode matchingNode = MatchNode(oldNode, newNodeForHashCode);

                if (matchingNode != null)
                {
                    diff.SetMatching(oldNode, matchingNode);
                    newNodeMatched[matchingNode] = true;
                }
                else
                {
                    diff.SetRemoved(oldNode);
                }
            }
            foreach (PositionedNode newNode in newGraph.Nodes)
            {
                if (!newNodeMatched.ContainsKey(newNode))
                {
                    diff.SetAdded(newNode);
                }
            }
            return(diff);
        }
		public GraphDiff MatchGraphs(PositionedGraph oldGraph, PositionedGraph newGraph)
		{
			if (oldGraph == null)
			{
				if (newGraph == null)
				{
					return new GraphDiff();
				}
				else
				{
					GraphDiff addAllDiff = new GraphDiff();
					foreach	(PositionedGraphNode newNode in newGraph.Nodes)
						addAllDiff.SetAdded(newNode);
					return addAllDiff;
				}
			}
			else if (newGraph == null)
			{
				GraphDiff removeAllDiff = new GraphDiff();
				foreach	(PositionedGraphNode oldNode in oldGraph.Nodes)
					removeAllDiff.SetRemoved(oldNode);
				return removeAllDiff;
			}
			
			// none of the graphs is null
			GraphDiff diff = new GraphDiff();
			
			Dictionary<int, PositionedGraphNode> newNodeForHashCode = buildHashToNodeMap(newGraph);
			Dictionary<PositionedGraphNode, bool> newNodeMatched = new Dictionary<PositionedGraphNode, bool>();
			
			foreach	(PositionedGraphNode oldNode in oldGraph.Nodes)
			{
				PositionedGraphNode matchingNode = matchNode(oldNode, newNodeForHashCode);
				if (matchingNode != null)
				{
					diff.SetMatching(oldNode, matchingNode);
					newNodeMatched[matchingNode] = true;
				}
				else
				{
					diff.SetRemoved(oldNode);
				}
			}
			foreach	(PositionedGraphNode newNode in newGraph.Nodes)
			{
				if (!newNodeMatched.ContainsKey(newNode))
				{
					diff.SetAdded(newNode);
				}
			}
			
			return diff;
		}
		/// <summary>
		/// Starts animation from oldGraph to newGraph.
		/// </summary>
		/// <param name="oldGraph"></param>
		/// <param name="newGraph"></param>
		/// <param name="diff"></param>
		public void StartAnimation(PositionedGraph oldGraph, PositionedGraph newGraph, GraphDiff diff)
		{
			// account for that the visual controls could have been reused (we are not reusing controls now - NodeControlCache does nothing)
			
			this.canvas.Width = newGraph.BoundingRect.Width;
			this.canvas.Height = newGraph.BoundingRect.Height;
			
			if (oldGraph == null) {
				Draw(newGraph);
				return;
			}
			
			var durationMove = new Duration(TimeSpan.FromSeconds(animationDurationSeconds));
			var durationFade = durationMove;
			
			DoubleAnimation fadeOutAnim = new DoubleAnimation(1.0, 0.0, durationFade);
			DoubleAnimation fadeInAnim = new DoubleAnimation(0.0, 1.0, durationFade);
			
			foreach	(UIElement drawing in canvas.Children) {
				var arrow = drawing as Path;
				if (arrow != null) {
					arrow.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
				}
			}
			
			foreach	(PositionedEdge edge in newGraph.Edges) {
				AddEdgeToCanvas(edge).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
			}
			
			foreach	(PositionedNode removedNode in diff.RemovedNodes) {
				removedNode.NodeVisualControl.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
			}
			
			foreach	(PositionedNode addedNode in diff.AddedNodes) {
				AddNodeToCanvas(addedNode).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
			}
			
			bool first = true;
			foreach	(PositionedNode node in diff.ChangedNodes) {
				var newNode = diff.GetMatchingNewNode(node);
				
				PointAnimation anim = new PointAnimation();
				if (first) {
					anim.Completed += (o, e) => {
						Draw(newGraph);
						if (oldGraph != null) {
							foreach (var oldNode in oldGraph.Nodes) {
								oldNode.ReleaseNodeVisualControl();
							}
						}
					};
					first = false;
				}
				anim.From = node.LeftTop;
				
				anim.To = newNode.LeftTop;
				anim.DecelerationRatio = 0.3;
				anim.AccelerationRatio = 0.3;
				anim.Duration = durationMove;
				node.NodeVisualControl.BeginAnimation(CanvasLocationAdapter.LocationProperty, anim);
			}
		}
		/// <summary>
		/// Starts animation from oldGraph to newGraph.
		/// </summary>
		/// <param name="oldGraph"></param>
		/// <param name="newGraph"></param>
		/// <param name="diff"></param>
		public void StartAnimation(PositionedGraph oldGraph, PositionedGraph newGraph, GraphDiff diff)
		{
			if (oldGraph != null)
			{
				foreach	(var oldNode in oldGraph.Nodes)
				{
					foreach	(var newNode in newGraph.Nodes)
					{
						if (oldNode.NodeVisualControl == newNode.NodeVisualControl)
						{
							ClearCanvas();
						}
					}
				}
			}
			
			this.canvas.Width = newGraph.BoundingRect.Width;
			this.canvas.Height = newGraph.BoundingRect.Height;
			
			if (oldGraph == null)
			{
				Draw(newGraph);
				return;
			}
			
			double seconds = 0.5;
			var durationMove = new Duration(TimeSpan.FromSeconds(seconds));
			var durationFade = durationMove;
			
			DoubleAnimation fadeOutAnim = new DoubleAnimation(1.0, 0.0, durationFade);
			DoubleAnimation fadeInAnim = new DoubleAnimation(0.0, 1.0, durationFade);
			
			foreach	(UIElement drawing in canvas.Children)
			{
				var arrow = drawing as Path;
				if (arrow != null)
				{
					arrow.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
				}
			}
			
			foreach	(PositionedEdge edge in newGraph.Edges)
			{
				addEdgeToCanvas(edge).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
			}
			
			foreach	(PositionedGraphNode removedNode in diff.RemovedNodes)
			{
				removedNode.NodeVisualControl.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
			}
			
			foreach	(PositionedGraphNode addedNode in diff.AddedNodes)
			{
				addNodeToCanvas(addedNode).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
			}
			
			bool first = true;
			foreach	(PositionedGraphNode node in diff.ChangedNodes)
			{
				var newNode = diff.GetMatchingNewNode(node);
				
				PointAnimation anim = new PointAnimation();
				if (first)
				{
					anim.Completed += new EventHandler((o, e) => { Draw(newGraph); });
					first = false;
				}
				anim.From = node.LeftTop;
				
				anim.To = newNode.LeftTop;
				anim.DecelerationRatio = 0.3;
				anim.AccelerationRatio = 0.3;
				anim.Duration = durationMove;
				node.NodeVisualControl.BeginAnimation(CanvasLocationAdapter.LocationProperty, anim);
			}
		}