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

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

介绍

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

  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中即为凸包点。加以绘制结果如下:
在这里插入图片描述
参考教材《测绘程序设计(下册)》 李英冰

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是C语言的凸包算法的实现代码,使用的是Graham扫描: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define PI 3.14159265358979323846 typedef struct Point { double x; double y; } Point; typedef struct Stack { Point* arr; int top; } Stack; int cmp(const void* a, const void* b) { Point* p1 = (Point*)a; Point* p2 = (Point*)b; if (p1->x != p2->x) { return p1->x < p2->x ? -1 : 1; } else { return p1->y < p2->y ? -1 : 1; } } double cross_product(Point p1, Point p2, Point p3) { return (p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y); } double distance(Point p1, Point p2) { return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2)); } void push(Stack* s, Point p) { s->top++; s->arr[s->top] = p; } Point pop(Stack* s) { Point p = s->arr[s->top]; s->top--; return p; } void print_points(Point* points, int n) { for (int i = 0; i < n; i++) { printf("(%f, %f)\n", points[i].x, points[i].y); } } void print_stack(Stack s) { for (int i = 0; i <= s.top; i++) { printf("(%f, %f)\n", s.arr[i].x, s.arr[i].y); } } void graham_scan(Point* points, int n) { qsort(points, n, sizeof(Point), cmp); Stack s; s.arr = (Point*)malloc(n * sizeof(Point)); s.top = -1; push(&s, points[0]); push(&s, points[1]); for (int i = 2; i < n; i++) { while (s.top >= 1 && cross_product(s.arr[s.top - 1], s.arr[s.top], points[i]) <= 0) { pop(&s); } push(&s, points[i]); } int lower_hull_size = s.top + 1; push(&s, points[n - 2]); for (int i = n - 3; i >= 0; i--) { while (s.top >= lower_hull_size && cross_product(s.arr[s.top - 1], s.arr[s.top], points[i]) <= 0) { pop(&s); } push(&s, points[i]); } printf("Convex Hull Points (in clockwise order):\n"); print_stack(s); free(s.arr); } int main() { double minx = -10.0; double miny = -10.0; double maxx = 10.0; double maxy = 10.0; int n = 100; Point* points = (Point*)malloc(n * sizeof(Point)); for (int i = 0; i < n; i++) { points[i].x = (double)rand() / RAND_MAX * (maxx - minx) + minx; points[i].y = (double)rand() / RAND_MAX * (maxy - miny) + miny; } printf("Randomly Generated Points:\n"); print_points(points, n); graham_scan(points, n); free(points); return 0; } ``` 上述代码中,首先定义了一个`Point`结构体和一个`Stack`结构体,分别表示平面上的点和栈。然后实现了一个用于比较两个点大小的函数`cmp`,用于在排序时确定点的顺序。接着实现了计算叉积和计算两点之间距离的函数。然后实现了栈的`push`和`pop`操作,以及用于打印点集和栈的函数。 最后,实现了Graham扫描的主要部分`graham_scan`函数。该函数首先对点集进行排序,然后依次遍历每个点,将其加入凸壳栈中。每次加入点后,检查凸壳栈中前两个点与新加入的点的叉积是否为正,如果不是则将栈顶元素弹出,直到满足条件为止。遍历完所有点后,再从后往前遍历一次点集,重复上述步骤,得到完整的凸包。最后按顺时针方向输出凸包多边形的坐标。 在`main`函数中,首先指定了点集的范围和点的个数,然后随机生成点集,并打印出来。最后调用`graham_scan`函数求解凸包

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值