【Weiler-Atherton算法】 计算机图形学多边形裁剪算法

这篇具有很好参考价值的文章主要介绍了【Weiler-Atherton算法】 计算机图形学多边形裁剪算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


源代码: https://github.com/ricar0/Weiler-Atherton-Alogrithm/tree/master

什么是多边形裁剪

通常来说就是利用多边形来裁剪多边形的一种方法,一般情况下是利用矩形来裁剪凹凸多边形

  1. 凸多边形
    【Weiler-Atherton算法】 计算机图形学多边形裁剪算法
  2. 凹多边形
    【Weiler-Atherton算法】 计算机图形学多边形裁剪算法
    上面红色划线部分就是裁剪出的部分

前置知识

  1. OPENGL基础语法
    基本上就是一些画线和画多边形的操作,难度较低
  2. 求两直线交点
    较为基础的数学知识
  3. 求一个点是否落在多边形内/外
    计算几何知识
  4. Weiler-Atherton多边形裁剪算法

这里着重介绍Weiler-Atherton算法,其余不懂的可以先学会再看。

算法步骤

  1. 首先绘制两个相交的多边形
  2. 对于多边形1,我们从一个点出发,将所有经过的点(包含交点)存入新的数组中,对于多边形2也是同理
  3. 对两个新数组中的相同点进行点对映射
  4. 开始对裁剪多边形1进行遍历,从任意点出发,如果改点将从多边形2的内部穿越到外部,我们改变遍历点的对象,从多边形2开始遍历,依次类推…
  5. 直到当前点被遍历过,那么之前肯定形成了一个回路,我们将当前回路绘制出来就是裁剪出的多边形。
  6. 一直重复4和5操作,直到所有点都被遍历

接下来结合图片解释一下

【Weiler-Atherton算法】 计算机图形学多边形裁剪算法
对于如下这个图,我们利用矩形裁剪凹多边形。
首先从E点出发,判断E到J是否为出点,发现不是。遍历到J点,判断JF是否是出点,发现是,这时候改变遍历的对象,通过映射关系从K点开始。判断发现KC又是出点,因此再次改变遍历对象,遍历多边形到E,发现J已经被遍历过,这时直接绘制出JKE…

程序框图

【Weiler-Atherton算法】 计算机图形学多边形裁剪算法

代码实现

建立窗口以及自动调整大小

void reshape(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 0.1, 100000.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0, 0, 25, 0, 0, -1, 0, 1, 0);
}
int main(int argc,char** argv) {
    glutInit(&argc, const_cast<char**>(argv));
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    // 初始化窗口
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow(argv[0]);
    init();
    glutReshapeFunc(reshape);
    glutDisplayFunc(display);

    glutMainLoop();
}


建立点和线

struct Point2d {
    double x, y;
    bool operator < (const Point2d &rhs) const {
        if (x==rhs.x) return y < rhs.y;
        return x < rhs.x;
    }
};
struct Line{
    Point2d start;
    Point2d end;
};

求两条线段交点的模板,如果不存在返回-inf

inline Point2d Vector(Point2d a, Point2d b) {  //向量ab
    return{ b.x - a.x, b.y - a.y };
}
double dis2(Point2d a, Point2d b) {          //两点间的距离的平方
    return (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y);
}
double cross(Point2d A, Point2d B, Point2d P) {  //向量的外积
    Point2d AB = Vector(A,B);
    Point2d AP = Vector(A,P);
    return AB.x*AP.y - AB.y*AP.x;
}
double dot(Point2d A, Point2d B, Point2d P) {     //向量的内积
    Point2d AB = Vector(A,B);
    Point2d AP = Vector(A,P);
    return AB.x*AP.x + AB.y*AP.y;
}
int dir(Point2d A, Point2d B, Point2d P) {    //点与线段方位判定
    if (cross(A, B, P) > 0)  return -1;
    else if (cross(A, B, P)<0) return 1;
    else if (dot(A, B, P) < 0) return -2;
    else if (dot(A, B, P) >= 0)
    {
        if (dis2(A, B) < dis2(A, P)) return 2;
        else return 0;
    }
    return 0;
}
double disLine(Point2d A, Point2d B, Point2d P) {   //点P到直线AB的距离
    return fabs(cross(A, B, P)) / sqrt(dis2(A, B));
}
Point2d intersection(Line u, Line v) {
    Point2d A1 = u.start;
    Point2d A2 = u.end;
    Point2d B1 = v.start;
    Point2d B2 = v.end;
    if (dir(A1, A2, B1)*dir(A1, A2, B2) <= 0 && dir(B1, B2, A1)*dir(B1, B2, A2) <= 0) {//判断有无交点
        double t = disLine(A1, A2, B1) / (disLine(A1, A2, B1) + disLine(A1, A2, B2));
        Point2d B1B2 = Vector(B1, B2);
        Point2d inter = { B1.x + B1B2.x*t, B1.y + B1B2.y*t };
        return {inter.x, inter.y};
    } else {
        return {-inf, -inf};
    }
}

求两点距离,用于排序

double dis(Point2d point1, Point2d point2) {
    return sqrt((point1.x-point2.x)*(point1.x-point2.x) + (point1.y-point2.y)*(point1.y-point2.y));
}

判断点是否落在多边形内,这里加了个误差0.001

bool isPointInsidePoly(Point2d P,const vector<Point2d>& polyVertices) {
    std::size_t vertCount = polyVertices.size();
    if (vertCount < 2)
        return false;
    Point2d tmp = P;
    for (int l = 0; l < 2; l++) {
        for (int r = 0; r < 2; r++) {
            P = tmp;
            if (l % 2) P.x += 0.001;
            else P.x -= 0.001;
            if (r % 2) P.y += 0.001;
            else P.y -= 0.001;
            bool inside = false;
            for (unsigned i = 1; i <= vertCount; ++i) {
                const Point2d &A = polyVertices[i - 1];
                const Point2d &B = polyVertices[i % vertCount];
                if ((B.y <= P.y && P.y < A.y) || (A.y <= P.y && P.y < B.y)) {
                    double t = (P.x - B.x) * (A.y - B.y) - (A.x - B.x) * (P.y - B.y);
                    if (A.y < B.y)
                        t = -t;
                    if (t < 0)
                        inside = !inside;
                }
            }
            if (inside) return inside;
        }
    }
    return false;
}

求交点以及重新放入数组

void getIntersections() {//求出所有交点以及按照顺序存放在新数组中
    int len1 = poly1.size();//求出new1
    for (int i = 0; i < len1; i++) {
        new1.push_back(poly1[i]);
        vector<Point2d> tmp;
        for (auto it2 : p2) {
            Point2d p = intersection({{poly1[i].x, poly1[i].y},{poly1[(i+1)%len1].x, poly1[(i+1)%len1].y}}, it2);
            if (p.x != -inf && p.y != -inf) tmp.push_back({p.x, p.y});
        }
        sort(tmp.begin(), tmp.end(), [&](Point2d p1, Point2d p2){
            return dis(p1, poly1[i]) < dis(p2, poly1[i]);
        });
        for (auto it : tmp) new1.push_back(it);
    }

    int len2 = poly2.size();//求出new2
    for (int i = 0; i < len2; i++) {
        new2.push_back(poly2[i]);
        vector<Point2d> tmp;
        for (auto it2 : p1) {
            Point2d p = intersection({{poly2[i].x, poly2[i].y},{poly2[(i+1)%len2].x, poly2[(i+1)%len2].y}}, it2);
            if (p.x != -inf && p.y != -inf) tmp.push_back({p.x, p.y});
        }
        sort(tmp.begin(), tmp.end(), [&](Point2d p1, Point2d p2){
            return dis(p1, poly2[i]) < dis(p2, poly2[i]);
        });
        for (auto it : tmp) new2.push_back(it);
    }
    for (int i = 0; i < new1.size(); i++) {//映射关系,给定eps为误差范围
        for (int j = 0; j < new2.size(); j++) {
            if (fabs(new1[i].x-new2[j].x)<eps&&fabs(new1[i].y-new2[j].y)<eps) {
                pos1[i] = j;
                pos2[j] = i;
            }
        }
    }
    work();
}

绘制两个多边形以及初始化操作

void prework() {
    p1.clear();
    p2.clear();
    new1.clear();
    new2.clear();
    vis1.clear();
    vis2.clear();
    pos1.clear();
    pos2.clear();
}
void display() {
    prework();//初始化
    glClear(GL_COLOR_BUFFER_BIT);
    glBegin(GL_LINES);
    glColor3f(1.0, 1.0, 1.0);
    int len1 = poly1.size();//绘制多边形
    for (int i = 0; i < len1; i++) {
        glVertex2f(poly1[i].x, poly1[i].y);
        glVertex2f(poly1[(i+1)%len1].x, poly1[(i+1)%len1].y);
        p1.push_back({{poly1[i].x, poly1[i].y}, {poly1[(i+1)%len1].x, poly1[(i+1)%len1].y}});
    }
    int len2 = poly2.size();
    for (int i = 0; i < len2; i++) {
        glVertex2f(poly2[i].x, poly2[i].y);
        glVertex2f(poly2[(i+1)%len2].x, poly2[(i+1)%len2].y);
        p2.push_back({{poly2[i].x, poly2[i].y}, {poly2[(i+1)%len2].x, poly2[(i+1)%len2].y}});
    }
    getIntersections();
    glEnd();
    glFlush();
}

最核心的代码,遍历两个多边形

void work() {
    vector<Point2d> now;//当前选择到的点
    int len1 = new1.size();
    int len2 = new2.size();
    for (int i = 0; i < new1.size(); i++) {//new1 第一个新多边形 new2第一个新多边形
        if (vis1[i]) continue;
        int ch = 1, nowpos = i;
        while (1) {
            if (ch == 1) {//遍历第一个多边形
                if (isPointInsidePoly(new1[nowpos], poly2)) now.push_back(new1[nowpos]);
                if (vis1[nowpos]) {//如果该点遍历过
                    glBegin(GL_LINES);
                    glColor3f(1, 0, 0);
                    for (int j = 0; j < now.size(); j++) {//绘制交多边形
                        glVertex2f(now[j].x, now[j].y);
                        glVertex2f(now[(j+1)%now.size()].x, now[(j+1)%now.size()].y);
                    }
                    now.clear();
                    glEnd();
                    glFlush();
                    break;
                }
                vis1[nowpos] = true;//给当前经历点打上标记
                if (isPointInsidePoly(new1[nowpos], poly2) && !isPointInsidePoly(new1[(nowpos+1)%len1], poly2)) {//判断是否为出点
                    ch = 2;
                    nowpos = pos1[nowpos];
                    nowpos = (nowpos + 1) % len2;
                } else {
                    nowpos = (nowpos + 1) % len1;
                }
            } else {//遍历第二个多边形
                if (isPointInsidePoly(new2[nowpos], poly1)) now.push_back(new2[nowpos]);
                if (vis2[nowpos]) {//如果该点遍历过
                    glBegin(GL_LINES);
                    glColor3f(1, 0, 0);
                    for (int j = 0; j < now.size(); j++) {//绘制交多边形
                        glVertex2f(now[j].x, now[j].y);
                        glVertex2f(now[(j+1)%now.size()].x, now[(j+1)%now.size()].y);
                    }
                    now.clear();
                    glEnd();
                    glFlush();
                    break;
                }
                vis2[nowpos] = true;//给当前点打上标记
                if (isPointInsidePoly(new2[nowpos], poly1) && !isPointInsidePoly(new2[(nowpos+1)%len2], poly1)) {//判断是否为出点
                    ch = 1;
                    nowpos = pos2[nowpos];
                    nowpos = (nowpos + 1) % len1;
                } else {
                    nowpos = (nowpos + 1) % len2;
                }
            }
        }
    }
}

这里存入需要绘制的两个多边形,按顺序存文章来源地址https://www.toymoban.com/news/detail-405774.html

void init() {
    poly1.clear();
    poly2.clear();
//    poly1.push_back({-5, -5});
//    poly1.push_back({-5, 5});
//    poly1.push_back({5, 5});
//    poly1.push_back({5, -5});
//
//    poly2.push_back({-7, 0});
//    poly2.push_back({0, 7});
//    poly2.push_back({7, 0});
//    poly2.push_back({0, -7});

//    poly1.push_back({0, -6});
//    poly1.push_back({-3, -3});
//    poly1.push_back({0, 3});
//    poly1.push_back({3, 0});
//
//    poly2.push_back({0, -3});
//    poly2.push_back({-3, 3});
//    poly2.push_back({0, 6});
//    poly2.push_back({3, 3});

//    poly1.push_back({-8, -6});
//    poly1.push_back({-8,  6});
//    poly1.push_back({8, 6});
//    poly1.push_back({8, -6});
//
//    poly2.push_back({-2, 10});
//    poly2.push_back({12, -6});
//    poly2.push_back({-2, 2});
//    poly2.push_back({-12, -6});

    poly2.push_back({-6, -3});
    poly2.push_back({-6,  3});
    poly2.push_back({6, 3});
    poly2.push_back({6, -3});

    poly1.push_back({-1.98, 0.91});
    poly1.push_back({4, 6});
    poly1.push_back({12, 6});
    poly1.push_back({4, -2});
    poly1.push_back({8, 4.7});
    glClearColor(0.0, 0.3, 0.7, 0.0);
    glShadeModel(GL_SMOOTH);
}

到了这里,关于【Weiler-Atherton算法】 计算机图形学多边形裁剪算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【期末复习】计算机算法设计与分析

    小编相信大家都很急切,要如何短时间学会算法通过考试呢?下面就让楼主带大家一起了解吧。 算法期末考试,其实就是算法期末考试了。那么小编为什么会算法期末考试,相信大家都很好奇是怎么回事。大家可能会感到很惊讶,小编怎么会算法期末考试呢?但事实就是这样

    2024年02月03日
    浏览(39)
  • 图像处理与计算机视觉算法

    图像处理与计算机视觉算法是实现对图像和视频内容分析、理解和操作的一系列技术。这些算法可以分为多个类别,包括但不限于以下几个主要方面: 预处理 : 像素操作:灰度化、二值化、直方图均衡化等,用于改善图像的对比度和亮度分布。 去噪:高斯滤波、中值滤波、

    2024年02月22日
    浏览(49)
  • 计算机博弈算法(Adversarial Search)

    人机博弈是人工智能的重要分支,人们在这一领域探索的过程中产生了大量的研究成果,而 极小化极大算法(minimax) 是其中最基础的算法,它由Shannon在1950年正式提出。 Alpha-beta剪枝 的本质就是一种基于极小化极大算法的改进方法。Knuth等人在1975年优化了算法,提出了 负极大

    2024年02月04日
    浏览(41)
  • 计算机算法设计与分析期末复习

    以下是我的部分算法笔记,希望可以给复习的小伙伴们参考一下: 题目: 一切合法的输入数据都能得出满足要求的结果,包括典型的、苛刻的输入数据也能够得出满足要求的结果。这个含义对应算法的(正确性) 算法要对异常情况进行适当的处理,就是算法的(健壮性)

    2024年02月13日
    浏览(38)
  • 计算机毕业分享(含算法) opencv图像增强算法系统

    今天学长向大家分享一个毕业设计项目 毕业设计 opencv图像增强算法系统 项目运行效果: 毕业设计 基于机器视觉的图像增强 项目获取: https://gitee.com/sinonfin/algorithm-sharing 直方图均衡化是通过调整图像的灰阶分布,使得在0~255灰阶上的分布更加均衡,提高了图像的对比度,达

    2024年01月18日
    浏览(43)
  • 计算机导论07-算法和数据结构

    算法是 为使用计算机解决问题而制定的运算序列,是解决实际问题的方法及步骤 ;在计算机科学中,算法研究应用计算机程序处理问题的方法及其实现流程,是计算机问题解决方案的完整描述, 它是计算机科学的核心研究对象之一。 算法的概念 一般认为,算法(algorithm)

    2024年01月20日
    浏览(48)
  • 计算机视觉&多模态算法实习面试记录

    一面(12.20) 自我介绍:第一次面有点瓢嘴 介绍科研项目 如何使用的CLIP Open-vocab和zero-shot 介绍比赛项目——多模态行车数据视频 介绍任务是什么 自定义数据集? Yolo v8 介绍CLIP: 对比学习训练:一个batch的N张图片和文本进行对比;首先分别进行编码-再投影到相同特征维度

    2024年03月25日
    浏览(50)
  • 计算机视觉--距离变换算法的实战应用

    前言: Hello大家好,我是Dream。 计算机视觉CV是人工智能一个非常重要的领域 。 在本次的距离变换任务中,我们将使用 D4距离度量方法 来对图像进行处理。通过这次实验,我们可以更好地理解距离度量在计算机视觉中的应用。希望大家对计算机视觉和图像处理有了更深入的

    2024年02月15日
    浏览(49)
  • 计算机操作系统实验-进程调度模拟算法

    进程调度是处理机管理的核心内容。本实验要求用高级语言编写模拟进程调度程序,以 便加深理解有关进程控制快、进程队列等概念,并体会和了解优先数算法和时间片轮转算法 的具体实施办法。 1.设计进程控制块 PCB 的结构,通常应包括如下信息: 进程名、进程优先数(

    2024年02月05日
    浏览(70)
  • 计算机竞赛 - 基于机器视觉的图像拼接算法

    图像拼接在实际的应用场景很广,比如无人机航拍,遥感图像等等,图像拼接是进一步做图像理解基础步骤,拼接效果的好坏直接影响接下来的工作,所以一个好的图像拼接算法非常重要。 再举一个身边的例子吧,你用你的手机对某一场景拍照,但是你没有办法一次将所有你

    2024年02月13日
    浏览(66)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包