C# Delaunay三角剖分
- 格式:doc
- 大小:318.50 KB
- 文档页数:15
Delaunay三角剖分
在实际中运用的最多的三角剖分是Delaunay三角剖分。首先,我们来了解一下Delaunay边。Delaunay边的定义为:假设E中的一条边e(其端点为a,b),若e满足条件:存在一个圆经过a,b两点,圆内不含点集中任何其他的点,这一特性又称空圆特性,则称之为Delaunay边:
Delaunay三角剖分的定义为:如果点集的一个三角剖分只包含Delaunay边,那么该三角剖分称为Delaunay三角剖分。
要满足Delaunay三角剖分的定义,必须符合下面两个重要的准则:
1)空圆特性:Delaunay三角网是唯一的,在Delaunay三角形网中任一三角形的外接圆范围内不会有其它点存在;
2)最大化最小角特性:在散点集可能形成的三角剖分中,Delaunay三角剖分所形成的三角形的最小角最大。从这个意义上讲,Delaunay 三角网是“最接近于规则化的”的三角网。具体来说是指在两个相邻的三角形构成凸四边形的对角线,在相互交换后,六个内角的最小角不再增大。
经典的Delaunay剖分算法主要有两类[1]:
1)增量算法:又称为Delaunay空洞算法或加点法,其思路为从一个三角形开始,每次增加一个点,保证每一步得到的当前三角形是局部优化的三角形。
2)局部变换法:又称为换边或换面法,其思路为构造非优化的三角网,然后对两个共边三角形形成的凸四边形迭代换边优化。
迄今为止关于Delaunay剖分已经出现了很多算法,主要有分治算法、逐步插入法、三角网生长法等。其中三角网生长算法由于效率较低,目前较少采用; 分治算法最为高效,但算法相对比较复杂;逐点插入法实现简单,但它的时间复杂度差[2]。特别是近些年,随着计算机水平的不断提升,又出现了各种各样的改进算法。
本节将主要根据逐步插入法的原理,通过对给予的数据高程点进行Delaunay三角剖分。其基本步骤为:1)获取点集坐标数组;2)获取点集外围边界;3)根据边界及内部点生成三角网。
现新建一个项目,并命名为“Delaunay三角剖分”,同时添加相关的引用及定义“启动CAD()”函数。
1、获取点集坐标数组
添加一个按钮,设置其Name和Text属性都为“获取点集坐标数组”,为其Click事件添加代码如下:
private void 获取点集坐标数组_Click(object sender, EventArgs e)
{
Microsoft.VisualBasic.Interaction.AppActivate(AcadApp.Caption); AcadSelectionSet mySelectionSet;
mySelectionSet =
AcadDoc.SelectionSets.Add("NewSelectionSet001");
Int16[] FilterType = new Int16[1];
object[] FilterData = new object[1];
FilterType[0] = 0;
FilterData[0] = "POINT";
mySelectionSet.SelectOnScreen(FilterType, FilterData);
double[] arrPoints = new double[3 * mySelectionSet.Count];
int count = 0;
foreach (AcadObject acadObj in mySelectionSet)
{
if (acadObj.ObjectName == "AcDbPoint")
{
count++;
double[] PointCoord;
PointCoord =
(Double[])(((AcadPoint)acadObj).Coordinates);
arrPoints[3*count - 3] = PointCoord[0];
arrPoints[3 * count - 2] = PointCoord[1];
arrPoints[3 * count - 1] = PointCoord[2];
}
}
MessageBox.Show("共选择点的个数为:" + count.ToString());
AcadDoc.SelectionSets.Item("NewSelectionSet001").Delete();
}
其中,arrPoints数组保存各点的坐标值。
2、获取点集外围边界
根据前面获取的外围点集来获取外围边界,其主要思路为:1)搜寻X坐标最小的点,在此称为startPoint,从该点开始搜寻下一点,本例中以逆时针方向来搜寻边界;2)现定义一个虚拟点(该点并不存在)位置为startPoint正北方向,该点与startPoint形成一个向量Vector1,下一点(判断是否为边界点的点,或称选取的点)与startPoint也形成一个向量Vector2,则判断该选取的点是外围边界点所要满足的条件为向量Vector2与向量Vector1之间的夹角最大;3)按同样的方式,新形成的向量Vector1为新选取的外围边界点与上一个外围边界点形成的向量,向量Vector2为选取的点与新选取的外围边界点形成的向量,判断该选取的点是外围边界点所要满足的条件仍然为向量Vector2与向量Vector1之间的夹角最大;4)按此思路循环进行,直到找到一个边界点与startPoint相同。
为了数据查询、调用方便以及数据结构的管理,在调用任何一个点、边或三角形时只需要通过索引即可调取。故在全局变量中定义如下类用于管理边和三角形:
public class Edge
{
public int Start;//边的起点
public int End;//边的终点
public int LeftTri = -1;//左边三角形索引
public int RightTri = -1;//右边三角形索引
}
public class Tri
{
public int NodeA;//第一个节点的索引
public int NodeB;//第二个节点的索引
public int NodeC;//第三个节点的索引
public int AdjTriA = -1;//第一个邻接三角形索引
public int AdjTriB = -1;//第二个邻接三角形索引
public int AdjTriC = -1;//第三个邻接三角形索引
}
此外,在全局变量中定义边和三角形数组(由于在此用到了ArrayList,所以需要添加引用“using System.Collections;”),其代码如下:
private ArrayList arrayEdges = new ArrayList();
private ArrayList arrayTris = new ArrayList();
下面从startPoint开始通过寻找最大夹角来获取外围边界点,其代码如下:
//获取点集外围边界
int i, startIndex = 0, tempIndex, lastIndex, pointCount =
arrPoints.Length / 3;
for (i = 1; i < pointCount; i++) //寻找X值最小的点号
{
if (arrPoints[3 * i] < arrPoints[3 * startIndex])
{ startIndex = i;
}
}
Edge edge = new Edge();
edge.Start = startIndex;
lastIndex = startIndex - 1;
tempIndex = startIndex;
double[] vector1 = new double[2], vector2 = new double[2];
vector1[0] = 0; vector1[1] = 100;
double vector1Length,
vector2Length,angleTemp,angleMax,lengthMin;
angleMax = 0;
while (lastIndex != startIndex)
{
vector1Length = Math.Sqrt(vector1[0] * vector1[0] + vector1[1] *
vector1[1]);
lengthMin = 300;
for (i = 0; i < pointCount; i++)//找边界
{
if (i != edge.Start)
{
vector2[0] = arrPoints[3 * i] - arrPoints[3 * tempIndex];
vector2[1] = arrPoints[3 * i+1] - arrPoints[3 *
tempIndex+1];