凸包多边形生成算法---快速凸包法

快速凸包法生成凸包多边形

介绍

快速凸包法,用于寻找一群点集中的凸包点
主要步骤分为两步

  1. 求上下左右四个顶点
  2. 迭代求出凸包点
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中即为凸包点。加以绘制结果如下:
在这里插入图片描述
参考教材《测绘程序设计(下册)》 李英冰

猜你喜欢

转载自blog.csdn.net/weixin_51205206/article/details/124411961