~halega/+junk/sharpdevelop

« back to all changes in this revision

Viewing changes to src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Drawing/GraphDrawer.cs

  • Committer: sk
  • Date: 2011-09-10 05:17:57 UTC
  • Revision ID: halega@halega.com-20110910051757-qfouz1llya9m6boy
4.1.0.7915 Release Candidate 1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
 
2
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
 
3
 
 
4
using System;
 
5
using System.Collections.Generic;
 
6
using System.Linq;
 
7
using System.Text;
 
8
using System.Windows;
 
9
using System.Windows.Controls;
 
10
using System.Windows.Input;
 
11
using System.Windows.Media;
 
12
using System.Windows.Media.Animation;
 
13
using System.Windows.Shapes;
 
14
 
 
15
using Debugger.AddIn.Visualizers.Graph;
 
16
using Debugger.AddIn.Visualizers.Graph.Drawing;
 
17
using Debugger.AddIn.Visualizers.Graph.Layout;
 
18
 
 
19
namespace Debugger.AddIn.Visualizers.Graph
 
20
{
 
21
        /// <summary>
 
22
        /// Draws <see cref="PositionedGraph" /> on Canvas.
 
23
        /// Keeps the last displayed graph and does a smooth transition into the new graph.
 
24
        /// </summary>
 
25
        public class GraphDrawer
 
26
        {
 
27
                Canvas canvas;
 
28
                TextBlock edgeTooltip = new TextBlock();
 
29
                static double animationDurationSeconds = 0.5;
 
30
                
 
31
                public GraphDrawer(Canvas canvas)
 
32
                {
 
33
                        this.canvas = canvas;
 
34
                }
 
35
                
 
36
                /// <summary>
 
37
                /// Starts animation from oldGraph to newGraph.
 
38
                /// </summary>
 
39
                /// <param name="oldGraph"></param>
 
40
                /// <param name="newGraph"></param>
 
41
                /// <param name="diff"></param>
 
42
                public void StartAnimation(PositionedGraph oldGraph, PositionedGraph newGraph, GraphDiff diff)
 
43
                {
 
44
                        // account for that the visual controls could have been reused (we are not reusing controls now - NodeControlCache does nothing)
 
45
                        
 
46
                        this.canvas.Width = newGraph.BoundingRect.Width;
 
47
                        this.canvas.Height = newGraph.BoundingRect.Height;
 
48
                        
 
49
                        if (oldGraph == null) {
 
50
                                Draw(newGraph);
 
51
                                return;
 
52
                        }
 
53
                        
 
54
                        var durationMove = new Duration(TimeSpan.FromSeconds(animationDurationSeconds));
 
55
                        var durationFade = durationMove;
 
56
                        
 
57
                        DoubleAnimation fadeOutAnim = new DoubleAnimation(1.0, 0.0, durationFade);
 
58
                        DoubleAnimation fadeInAnim = new DoubleAnimation(0.0, 1.0, durationFade);
 
59
                        
 
60
                        foreach (UIElement drawing in canvas.Children) {
 
61
                                var arrow = drawing as Path;
 
62
                                if (arrow != null) {
 
63
                                        arrow.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
 
64
                                }
 
65
                        }
 
66
                        
 
67
                        foreach (PositionedEdge edge in newGraph.Edges) {
 
68
                                AddEdgeToCanvas(edge).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
 
69
                        }
 
70
                        
 
71
                        foreach (PositionedNode removedNode in diff.RemovedNodes) {
 
72
                                removedNode.NodeVisualControl.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
 
73
                        }
 
74
                        
 
75
                        foreach (PositionedNode addedNode in diff.AddedNodes) {
 
76
                                AddNodeToCanvas(addedNode).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
 
77
                        }
 
78
                        
 
79
                        bool first = true;
 
80
                        foreach (PositionedNode node in diff.ChangedNodes) {
 
81
                                var newNode = diff.GetMatchingNewNode(node);
 
82
                                
 
83
                                PointAnimation anim = new PointAnimation();
 
84
                                if (first) {
 
85
                                        anim.Completed += (o, e) => {
 
86
                                                Draw(newGraph);
 
87
                                                if (oldGraph != null) {
 
88
                                                        foreach (var oldNode in oldGraph.Nodes) {
 
89
                                                                oldNode.ReleaseNodeVisualControl();
 
90
                                                        }
 
91
                                                }
 
92
                                        };
 
93
                                        first = false;
 
94
                                }
 
95
                                anim.From = node.LeftTop;
 
96
                                
 
97
                                anim.To = newNode.LeftTop;
 
98
                                anim.DecelerationRatio = 0.3;
 
99
                                anim.AccelerationRatio = 0.3;
 
100
                                anim.Duration = durationMove;
 
101
                                node.NodeVisualControl.BeginAnimation(CanvasLocationAdapter.LocationProperty, anim);
 
102
                        }
 
103
                }
 
104
 
 
105
                /// <summary>
 
106
                /// Draws <see cref="PositionedGraph"></see> on Canvas.
 
107
                /// </summary>
 
108
                /// <param name="posGraph">Graph to draw.</param>
 
109
                /// <param name="canvas">Destination Canvas.</param>
 
110
                public void Draw(PositionedGraph posGraph)
 
111
                {
 
112
                        canvas.Children.Clear();
 
113
                        
 
114
                        // draw nodes
 
115
                        foreach (PositionedNode node in posGraph.Nodes) {
 
116
                                AddNodeToCanvas(node);
 
117
                        }
 
118
                        
 
119
                        // draw edges
 
120
                        foreach (PositionedEdge edge in posGraph.Edges) {
 
121
                                AddEdgeToCanvas(edge);
 
122
                        }
 
123
                        
 
124
                        edgeTooltip.Visibility = Visibility.Hidden;
 
125
                        edgeTooltip.Background = Brushes.White;
 
126
                        canvas.Children.Add(edgeTooltip);
 
127
                }
 
128
                
 
129
                /// <summary>
 
130
                /// Clears the drawing Canvas.
 
131
                /// </summary>
 
132
                public void ClearCanvas()
 
133
                {
 
134
                        canvas.Children.Clear();
 
135
                }
 
136
                
 
137
                PositionedGraphNodeControl AddNodeToCanvas(PositionedNode node)
 
138
                {
 
139
                        canvas.Children.Add(node.NodeVisualControl);
 
140
                        Canvas.SetLeft(node.NodeVisualControl, node.Left);
 
141
                        Canvas.SetTop(node.NodeVisualControl, node.Top);
 
142
                        return node.NodeVisualControl;
 
143
                }
 
144
                
 
145
                Path AddEdgeToCanvas(PositionedEdge edge)
 
146
                {
 
147
                        var edgeSplineFigure = CreateEdgeSpline(edge);
 
148
                        PathGeometry geometryVisible = new PathGeometry();
 
149
                        geometryVisible.Figures.Add(edgeSplineFigure);
 
150
                        geometryVisible.Figures.Add(CreateEdgeArrow(edge));
 
151
                        
 
152
                        Path pathVisible = new Path();
 
153
                        pathVisible.Stroke = Brushes.Black;
 
154
                        pathVisible.Fill = Brushes.Black;
 
155
                        pathVisible.StrokeThickness = 1;
 
156
                        pathVisible.Data = geometryVisible;
 
157
                        
 
158
                        // remember this spline Path at PositionedEdge to be able to highlight edge from PositionedNodeProperty
 
159
                        edge.Spline = pathVisible;
 
160
                        // and remember the the edge for the spline, so that we can get edge name on spline mouse-over
 
161
                        pathVisible.Tag = edge;
 
162
                        
 
163
                        PathGeometry geometryInVisible = new PathGeometry();
 
164
                        geometryInVisible.Figures.Add(edgeSplineFigure);
 
165
                        
 
166
                        Path pathInVisible = new Path();
 
167
                        pathInVisible.Stroke = Brushes.Transparent;
 
168
                        pathInVisible.Fill = Brushes.Transparent;
 
169
                        pathInVisible.StrokeThickness = 16;
 
170
                        pathInVisible.Data = geometryInVisible;
 
171
                        
 
172
                        pathInVisible.MouseEnter += delegate(object sender, MouseEventArgs e)
 
173
                        {
 
174
                                pathVisible.StrokeThickness = 2;
 
175
                                this.edgeTooltip.Text = ((PositionedEdge)pathVisible.Tag).Name;
 
176
                                Point mousePos = e.GetPosition(this.canvas);
 
177
                                Canvas.SetLeft(this.edgeTooltip, mousePos.X - 5);
 
178
                                Canvas.SetTop(this.edgeTooltip, mousePos.Y - 20);
 
179
                                this.edgeTooltip.Visibility = Visibility.Visible;
 
180
                        };
 
181
                        pathInVisible.MouseLeave += delegate(object sender, MouseEventArgs e)
 
182
                        {
 
183
                                pathVisible.StrokeThickness = 1;
 
184
                                this.edgeTooltip.Visibility = Visibility.Hidden;
 
185
                        };
 
186
                        pathInVisible.MouseMove += delegate(object sender, MouseEventArgs e)
 
187
                        {
 
188
                                Point mousePos = e.GetPosition(this.canvas);
 
189
                                Canvas.SetLeft(this.edgeTooltip, mousePos.X - 5);
 
190
                                Canvas.SetTop(this.edgeTooltip, mousePos.Y - 20);
 
191
                        };
 
192
                        
 
193
                        canvas.Children.Add(pathVisible);
 
194
                        canvas.Children.Add(pathInVisible);
 
195
                        
 
196
                        return pathVisible;
 
197
                }
 
198
                
 
199
                PathFigure CreateEdgeSpline(PositionedEdge edge)
 
200
                {
 
201
                        PathFigure figure = new PathFigure();
 
202
                        figure.IsClosed = false;
 
203
                        figure.IsFilled = false;
 
204
                        
 
205
                        figure.StartPoint = edge.SplinePoints[0];
 
206
                        for (int i = 1; i < edge.SplinePoints.Count; i += 3)
 
207
                        {
 
208
                                figure.Segments.Add(new BezierSegment(edge.SplinePoints[i], edge.SplinePoints[i + 1], edge.SplinePoints[i + 2], true));
 
209
                        }
 
210
                        
 
211
                        return figure;
 
212
                }
 
213
                
 
214
                PathFigure CreateEdgeArrow(PositionedEdge edge)
 
215
                {
 
216
                        Point splineEndPoint = edge.SplinePoints[edge.SplinePoints.Count - 1];
 
217
                        Point splineEndHandlePoint = edge.SplinePoints[edge.SplinePoints.Count - 2];
 
218
                        
 
219
                        Vector tangent = splineEndPoint - splineEndHandlePoint;
 
220
                        tangent.Normalize();
 
221
                        tangent = tangent * 20;
 
222
                        Point basePoint = splineEndPoint - 0.4 * tangent;
 
223
                        
 
224
                        PathFigure arrowFigure = new PathFigure();
 
225
                        arrowFigure.IsClosed = true;
 
226
                        arrowFigure.IsFilled = true;
 
227
 
 
228
                        arrowFigure.StartPoint = splineEndPoint;        // arrow tip
 
229
                        Vector tangent2 = Rotate90(tangent);
 
230
                        arrowFigure.Segments.Add(new LineSegment(basePoint + tangent2 * 0.15, true));
 
231
                        arrowFigure.Segments.Add(new LineSegment(basePoint - tangent2 * 0.15, true));
 
232
                        
 
233
                        return arrowFigure;
 
234
                }
 
235
                
 
236
                static Vector Rotate90(Vector v)
 
237
                {
 
238
                        // (x, y) -> (y, -x)
 
239
                        double t = v.X;
 
240
                        v.X = v.Y;
 
241
                        v.Y = -t;
 
242
                        return v;
 
243
                }
 
244
        }
 
245
}