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)
5
using System.Collections.Generic;
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;
15
using Debugger.AddIn.Visualizers.Graph;
16
using Debugger.AddIn.Visualizers.Graph.Drawing;
17
using Debugger.AddIn.Visualizers.Graph.Layout;
19
namespace Debugger.AddIn.Visualizers.Graph
22
/// Draws <see cref="PositionedGraph" /> on Canvas.
23
/// Keeps the last displayed graph and does a smooth transition into the new graph.
25
public class GraphDrawer
28
TextBlock edgeTooltip = new TextBlock();
29
static double animationDurationSeconds = 0.5;
31
public GraphDrawer(Canvas canvas)
37
/// Starts animation from oldGraph to newGraph.
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)
44
// account for that the visual controls could have been reused (we are not reusing controls now - NodeControlCache does nothing)
46
this.canvas.Width = newGraph.BoundingRect.Width;
47
this.canvas.Height = newGraph.BoundingRect.Height;
49
if (oldGraph == null) {
54
var durationMove = new Duration(TimeSpan.FromSeconds(animationDurationSeconds));
55
var durationFade = durationMove;
57
DoubleAnimation fadeOutAnim = new DoubleAnimation(1.0, 0.0, durationFade);
58
DoubleAnimation fadeInAnim = new DoubleAnimation(0.0, 1.0, durationFade);
60
foreach (UIElement drawing in canvas.Children) {
61
var arrow = drawing as Path;
63
arrow.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
67
foreach (PositionedEdge edge in newGraph.Edges) {
68
AddEdgeToCanvas(edge).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
71
foreach (PositionedNode removedNode in diff.RemovedNodes) {
72
removedNode.NodeVisualControl.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
75
foreach (PositionedNode addedNode in diff.AddedNodes) {
76
AddNodeToCanvas(addedNode).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
80
foreach (PositionedNode node in diff.ChangedNodes) {
81
var newNode = diff.GetMatchingNewNode(node);
83
PointAnimation anim = new PointAnimation();
85
anim.Completed += (o, e) => {
87
if (oldGraph != null) {
88
foreach (var oldNode in oldGraph.Nodes) {
89
oldNode.ReleaseNodeVisualControl();
95
anim.From = node.LeftTop;
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);
106
/// Draws <see cref="PositionedGraph"></see> on Canvas.
108
/// <param name="posGraph">Graph to draw.</param>
109
/// <param name="canvas">Destination Canvas.</param>
110
public void Draw(PositionedGraph posGraph)
112
canvas.Children.Clear();
115
foreach (PositionedNode node in posGraph.Nodes) {
116
AddNodeToCanvas(node);
120
foreach (PositionedEdge edge in posGraph.Edges) {
121
AddEdgeToCanvas(edge);
124
edgeTooltip.Visibility = Visibility.Hidden;
125
edgeTooltip.Background = Brushes.White;
126
canvas.Children.Add(edgeTooltip);
130
/// Clears the drawing Canvas.
132
public void ClearCanvas()
134
canvas.Children.Clear();
137
PositionedGraphNodeControl AddNodeToCanvas(PositionedNode node)
139
canvas.Children.Add(node.NodeVisualControl);
140
Canvas.SetLeft(node.NodeVisualControl, node.Left);
141
Canvas.SetTop(node.NodeVisualControl, node.Top);
142
return node.NodeVisualControl;
145
Path AddEdgeToCanvas(PositionedEdge edge)
147
var edgeSplineFigure = CreateEdgeSpline(edge);
148
PathGeometry geometryVisible = new PathGeometry();
149
geometryVisible.Figures.Add(edgeSplineFigure);
150
geometryVisible.Figures.Add(CreateEdgeArrow(edge));
152
Path pathVisible = new Path();
153
pathVisible.Stroke = Brushes.Black;
154
pathVisible.Fill = Brushes.Black;
155
pathVisible.StrokeThickness = 1;
156
pathVisible.Data = geometryVisible;
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;
163
PathGeometry geometryInVisible = new PathGeometry();
164
geometryInVisible.Figures.Add(edgeSplineFigure);
166
Path pathInVisible = new Path();
167
pathInVisible.Stroke = Brushes.Transparent;
168
pathInVisible.Fill = Brushes.Transparent;
169
pathInVisible.StrokeThickness = 16;
170
pathInVisible.Data = geometryInVisible;
172
pathInVisible.MouseEnter += delegate(object sender, MouseEventArgs e)
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;
181
pathInVisible.MouseLeave += delegate(object sender, MouseEventArgs e)
183
pathVisible.StrokeThickness = 1;
184
this.edgeTooltip.Visibility = Visibility.Hidden;
186
pathInVisible.MouseMove += delegate(object sender, MouseEventArgs e)
188
Point mousePos = e.GetPosition(this.canvas);
189
Canvas.SetLeft(this.edgeTooltip, mousePos.X - 5);
190
Canvas.SetTop(this.edgeTooltip, mousePos.Y - 20);
193
canvas.Children.Add(pathVisible);
194
canvas.Children.Add(pathInVisible);
199
PathFigure CreateEdgeSpline(PositionedEdge edge)
201
PathFigure figure = new PathFigure();
202
figure.IsClosed = false;
203
figure.IsFilled = false;
205
figure.StartPoint = edge.SplinePoints[0];
206
for (int i = 1; i < edge.SplinePoints.Count; i += 3)
208
figure.Segments.Add(new BezierSegment(edge.SplinePoints[i], edge.SplinePoints[i + 1], edge.SplinePoints[i + 2], true));
214
PathFigure CreateEdgeArrow(PositionedEdge edge)
216
Point splineEndPoint = edge.SplinePoints[edge.SplinePoints.Count - 1];
217
Point splineEndHandlePoint = edge.SplinePoints[edge.SplinePoints.Count - 2];
219
Vector tangent = splineEndPoint - splineEndHandlePoint;
221
tangent = tangent * 20;
222
Point basePoint = splineEndPoint - 0.4 * tangent;
224
PathFigure arrowFigure = new PathFigure();
225
arrowFigure.IsClosed = true;
226
arrowFigure.IsFilled = true;
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));
236
static Vector Rotate90(Vector v)