使用 WPF 3D 动画化图形算法






4.82/5 (13投票s)
数据结构和动画概述。
引言
在本文中,我将描述如何使用 WPF 在 .NET 中实现一个用于创建图动画的工具。首先,将讨论底层数据结构。第二部分将重点介绍 3D 内容和动画。本文仅提供一个粗略的概述。我不会深入解释所有内容,但您可以在文章末尾找到一些可能有助于您深入了解基础知识的资源。
本文包含以下内容
目标
- 独立于 UI 的面向对象图数据结构
- 基于 WPF 的 3D 图形和动画
- 高级 API,用于创建动画而无需了解 WPF 的详细信息
最终应用程序
视频
在开始实现之前,我想展示一个使用最终工具创建的简单动画
用法
- 通过单击空白区域,将创建一个新节点
- 通过在 1 秒内单击两个节点(不一定是不同的节点),将创建一个新边
- 通过单击节点或边,可以在右侧面板中编辑其属性
- 所有图算法都可以通过在菜单中选择相应条目来执行
图的数据结构
图由节点和边组成。图可以是定向的或无向的,并且通常您会将一些数据附加到节点或边(例如,距离)。由于图可以通过多种方式实现(例如,关联列表、邻接列表或关联矩阵),因此我决定使用以下实现
所有图都必须实现 IGraph
接口,该接口基本上允许向图中添加/删除节点和边,并允许您检索节点的邻居节点和边。
当您将节点或边添加到图中时,我的 Node
或 Edge
实现会保留对其被添加到的 IGraph
的引用。如果您想获取节点的邻居,请求将被转发到 IGraph
接口的当前实现。这使得创建自己的 IGraph
实现变得很简单。我只提供一个名为 Graph
的实现,它使用两个 HashSet 来存储节点和边。
在以下示例中,创建了由一条边连接的两个节点
IGraph<string, int> graph = new Graph<string, int>();
var node1 = new Node<string, int>("Munich");
var node2 = new Node<string, int>("Berlin");
var edge = new Edge<string, int>(node1, node2, 586);
graph.Add(node1);
graph.Add(node2);
graph.Add(edge);
Console.WriteLine("Distance from {0} to {1}: {2}",
node1.Data, node2.Data, edge.Data);
现在,我们可以以简单而灵活的方式创建强类型图,并访问邻居和数据。您可以将数据附加到节点和边。基于给定的实现,我们已经能够实现任意图算法。
用户界面
UI 应该能够通过几次鼠标单击来创建由节点和边组成的图。此外,应该可以轻松地执行先前创建的图算法并显示它们的动画。
应用程序的大部分内容都可以通过 MVVM 模式直接实现,但有一个例外:ViewPort3D
中的 3D 元素无法通过数据绑定创建,必须手动将其添加到容器中。任意数据都可以附加到节点和边。应用程序使用 IGraph<NodeData,EdgeData>
作为图。NodeData
和 EdgeData
包含 UI 特定的信息,如位置和颜色。
3D 元素
由于节点和边元素需要响应单击事件,因此我们的 3D 元素必须派生自 UIElement3D
。WPF3D 团队已发表关于如何 子类化 UIElement3D
的博文。节点应表示为球体,边应表示为圆柱体/圆环体;因此,我创建了以下层次结构
每次将新节点或边添加到图中时,VisualFactory
都会实例化相应的 GraphUIElement
。GraphUIElement
拥有一个 GeometryModel3D
,它是一个 MeshGeometry3D
和一个 Material
。
每当底层数据发生更改(例如,新位置或颜色)时,GraphUIElement
都会自行更新。当节点应该移动时,会将新动画添加到相应的 依赖项属性。所有这些都在后台发生;如果您想创建自己的图算法(请参阅下文),则无需关心内部细节。
最复杂的 GraphUIElement
是连接两个节点的 RegularEdgeUIElement
;困难在于正确旋转和缩放圆柱体以到达正确的位置。
图算法
该应用程序已包含几种图算法,例如 Dijkstra 或 Kruskal。要创建自己的算法,请向解决方案添加一个新类并实现 IGraphAlgorithm
接口。重新编译解决方案后,您的算法将列在菜单中。
创建动画非常简单,一些扩展方法使其更加容易。在以下示例中,我们将两个节点添加到图中,然后让这两个节点闪烁 3 秒。闪烁完成后,这两个节点将移动到另一个位置。
public void Execute(IGraph<NodeData, EdgeData> graph)
{
graph.Clear();
this.node1 = graph.AddNode(new NodeData(new Point3D(0, 25, 0)));
this.node2 = graph.AddNode(new NodeData(new Point3D(0, -25, 0)));
this.graph.AddEdge(this.node1, this.node2);
this.node1.Blink(3000);
this.node2.Blink(3000, this.Callback);
}
private void Callback()
{
this.node1.Move(new Point3D(25, 0, 0), 4000);
this.node2.Move(new Point3D(-25, 0, 0), 4000);
}
动画
WPF 中的动画是异步和并行运行的,这很好,只要动画应该并行运行(例如,两个节点的闪烁)。但是,节点不应该移动,直到闪烁动画完成,因此会注册一个回调,并在动画结束时执行。
结论
我希望本文能帮助您了解该应用程序的工作原理。您可以下载代码并按需修改或扩展它。如果您有任何问题,请随时提问。
资源
- http://blogs.msdn.com/wpf3d/archive/2007/09/05/subclassing-uielement3d.aspx
- WPF 3D:第 1 部分(共 n 部分)
- http://www.palmmedia.de/Blog/2009/12/29/wpf-animation-of-graph-algorithms-part-1
- http://www.palmmedia.de/Blog/2009/12/29/wpf-animation-of-graph-algorithms-part-2
历史
- 2010 年 1 月 21 日 - 初始发布。