快速凸包法生成凸包多边形
介绍
快速凸包法,用于寻找一群点集中的凸包点
主要步骤分为两步
- 求上下左右四个顶点
- 迭代求出凸包点
Grid grid = new Grid();
生成grid对象,all_points存放所有散点。
第一步较为简单,对所有点进行排序即可获得。存放在peak_points中。
第二步需要多种进行多种情况的判断,以及迭代求解。
可拆分为三个功能函数,及一个主题函数
- 函数1—已知三点求三角形面积—用三角形的面积大小判断点距线距离远近
- 函数2—判断点是否在线的左侧
- 函数3—迭代函数。对(左侧点集,head,tail)组合运算,求出F点,直到左侧点集为空,此时的F即为凸包点,加入栈CH
- 函数4—主函数。迭代入口
求解步骤:
1、栈CH加入P1
2、连接P1,P2,求解LEFT点集。组成第一个(LEFT,head,tail)组合
3、对第一个(LEFT,head,tail)组合迭代。迭代中LEFT为空时,CH加入此时的F。否则形成新的组合(LEFT1,head,F),(LEFT2,F,tail)。重复迭代
4、(LEFT,P1,P2)大组合计算完后,计算P2-P3,P3-P4,P4-P1各组合。CH中即为凸包点
数据结构
- point类
- Grid类
class point
{
public string pointname;
public double x;
public double y;
public double h;
public point(string name, double x, double y, double h)
{
pointname = name;
this.x = x;
this.y = y;
this.h = h;
}
public point()
{
}
}
class Grid
{
//使用快速凸包法
public double BaseHeight;//参考高程
public List<point> all_points = new List<point>();//所有点
public List<point> points = new List<point>();//移除四个顶点的点集
public List<point> peak_points = new List<point>();//四个顶点
public List<point> CH = new List<point>();//栈CH
}
步骤一 查找四个顶点
顶点 P1 P2 P3 P4
P1: x 最小
P2: y 最大
P3: x 最大
p4:y 最小
即散点集中最上、下、左、右的四个点。
利用List的排序方法
//1.查找四个顶点
public void FindPeak()
{
points.AddRange(all_points);
List<point> ps = new List<point>();
int n = all_points.Count;
ps.AddRange(all_points);
ps.Sort((point p_1, point p_2) => p_1.x.CompareTo(p_2.x));//按照x升序
point p1 = new point(ps[0].pointname, ps[0].x, ps[0].y, ps[0].h);
point p3 = new point(ps[n - 1].pointname, ps[n - 1].x, ps[n - 1].y, ps[n - 1].h);
ps.Sort((point p_1, point p_2) => p_1.y.CompareTo(p_2.y));//按照y升序
point p4 = new point(ps[0].pointname, ps[0].x, ps[0].y, ps[0].h);
point p2 = new point(ps[n - 1].pointname, ps[n - 1].x, ps[n - 1].y, ps[n - 1].h);
//遍历移除四个顶点
for (int j = 0; j < all_points.Count; j++)
{
if (all_points[j].pointname == p1.pointname ||
all_points[j].pointname == p2.pointname ||
all_points[j].pointname == p3.pointname ||
all_points[j].pointname == p4.pointname )
{
points.Remove(all_points[j]);
}
}
peak_points.Add(p1);
peak_points.Add(p2);
peak_points.Add(p3);
peak_points.Add(p4);
}
这部目的是求出peak_points 的四个顶点
步骤二 利用迭代求出凸包点
2.1函数—三点计算面积—用于判断点到线的距离
//函数---计算面积
double arc(point p1,point p2,point p3)
{
return 1/2.0*Math.Abs(p1.x*(p2.y-p3.y)+p2.x*(p3.y-p1.y)+p3.x*(p1.y-p2.y));
}
2.2函数—判断点在线的左边
线p1-p2,判断p3是否在线p1-p2的左侧
public int judge_left(point p1,point p2,point p3)
{
if (p1.x * p2.y - p2.x * p1.y + p3.x * (p1.y - p2.y) + p3.y * (p2.x - p1.x) > 0)
{
return 1;
}
return -1;
}
2.3迭代函数
(左侧点集 head tail)组合
即不断迭代产生新的F,形成(左侧点集 head tail)组合 ,直到左侧点集为空,则该F即为凸包点,加入CH栈。
public void fun(List<point> left_point,point head,point tail)
{
int flag = -1;//记录位置
double areamax = 0;//记录最大面积
for (int i = 0; i < left_point.Count; i++)
{
if (arc(head, tail, left_point[i]) > areamax)
{
areamax = arc(head, tail, left_point[i]);
flag = i;
}
}
//以上为寻找F
if (flag == -1) //left_point为空
{
CH.Add(tail);
}
//找到F
else
{
point F = left_point[flag];//F点
left_point.RemoveAt(flag);//移除F
List<point> left_point1 = new List<point>();
List<point> left_point2 = new List<point>();
for (int j = 0; j < left_point.Count; j++)
{
if (judge_left(head, F, left_point[j]) == 1)
{
left_point1.Add(left_point[j]);
}
}
fun(left_point1,head,F);
for (int k = 0; k < left_point.Count; k++)
{
if (judge_left(F, tail, left_point[k]) == 1)
{
left_point2.Add(left_point[k]);
}
}
fun(left_point2,F,tail);
}
}
求凸包点
public void Converx()
{
CH.Add(peak_points[0]);//第一步加入第一点
for (int i = 0; i < peak_points.Count; i++)//大遍历,四个顶点
{
List<point> Left = new List<point>();//LP
for (int j = 0; j < points.Count; j++)
{
if (judge_left(peak_points[i%4], peak_points[(i + 1)%4], points[j]) == 1)
{
Left.Add(points[j]); //LP加入,同时points移除
points.Remove(points[j]);
// j--;//
}
}
//求出Left
fun(Left,peak_points[i%4],peak_points[(i+1)%4]);
}
}
最后CH中即为凸包点。加以绘制结果如下:
参考教材《测绘程序设计(下册)》 李英冰