【Weiler-Atherton算法】 计算机图形学多边形裁剪算法
目录
- 什么是多边形裁剪
- 前置知识
- 算法步骤
- 程序框图
- 代码实现
源代码: https://github.com/ricar0/Weiler-Atherton-Alogrithm/tree/master
什么是多边形裁剪
通常来说就是利用多边形来裁剪多边形的一种方法,一般情况下是利用矩形来裁剪凹凸多边形
- 凸多边形
- 凹多边形
上面红色划线部分就是裁剪出的部分
前置知识
- OPENGL基础语法
基本上就是一些画线和画多边形的操作,难度较低 - 求两直线交点
较为基础的数学知识 - 求一个点是否落在多边形内/外
计算几何知识 - Weiler-Atherton多边形裁剪算法
这里着重介绍Weiler-Atherton算法,其余不懂的可以先学会再看。
算法步骤
- 首先绘制两个相交的多边形
- 对于多边形1,我们从一个点出发,将所有经过的点(包含交点)存入新的数组中,对于多边形2也是同理
- 对两个新数组中的相同点进行点对映射
- 开始对裁剪多边形1进行遍历,从任意点出发,如果改点将从多边形2的内部穿越到外部,我们改变遍历点的对象,从多边形2开始遍历,依次类推…
- 直到当前点被遍历过,那么之前肯定形成了一个回路,我们将当前回路绘制出来就是裁剪出的多边形。
- 一直重复4和5操作,直到所有点都被遍历
接下来结合图片解释一下
对于如下这个图,我们利用矩形裁剪凹多边形。
首先从E点出发,判断E到J是否为出点,发现不是。遍历到J点,判断JF是否是出点,发现是,这时候改变遍历的对象,通过映射关系从K点开始。判断发现KC又是出点,因此再次改变遍历对象,遍历多边形到E,发现J已经被遍历过,这时直接绘制出JKE…
程序框图
代码实现
建立窗口以及自动调整大小
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) { //向量abreturn{ 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();//求出new1for (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();//求出new2for (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;}}}}
}
这里存入需要绘制的两个多边形,按顺序存
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算法】 计算机图形学多边形裁剪算法相关推荐
- 七巧板复原算法——计算机图形学基本算法之一, 点在多边形内部的判断
注:此时我已经完成了一个演示版本,但是为了文章的渐进性,我将把开发过程一步步的写出来,用来记录. 本实验代码用到的图形学关系和算法列举如下: 基本计算机图形学关系和算法 1.点在多边形内部的 点在多边 ...
- 七巧板复原算法——计算机图形学基本算法之二,线段相交判断
判断线段相交,朴素的方法(初中直线方程的判断方法),就是先计算两条直线的交点,然后再判断交点是否在其中一条的线段上.这也是笔者能唯一想到的方法,后来抱着试试看有没有更好方法的想法,搜了一下网络.哦, ...
- 计算机图形学算法详解,计算机图形学裁剪算法详解
<计算机图形学裁剪算法详解>由会员分享,可在线阅读,更多相关<计算机图形学裁剪算法详解(10页珍藏版)>请在人人文库网上搜索. 1.裁剪算法详解在使用计算机处理图形信息时,计算 ...
- GIS地图界面和计算机图形学填充算法
一 GIS地图界面 GIS程序最常见的界面就是,不同区域被边界隔开,填充为不同的颜色: 如下三个:是超图iServer自带: 京津地区土地利用现状,京津地区人口分布,京津地区地貌分布:用于学习是很好资 ...
- 计算机图形学常见算法原理,计算机图形学常用算法及代码大全
<计算机图形学常用算法及代码大全>由会员分享,可在线阅读,更多相关<计算机图形学常用算法及代码大全(41页珍藏版)>请在人人文库网上搜索. 1.2.1.1 生成直线的DDA算法 ...
- 计算机图形学要学什么语言,计算机图形学:算法与实现
计算机图形学:算法与实现 语音 编辑 锁定 讨论 上传视频 <计算机图形学:算法与实现>一书的出版社是清华大学出版社,出版时间是第1版 (2012年1月1日). 书 名 计算机图形学 ...
- 计算机图形学直线算法论文,《计算机图形学》中直线生成算法的教学心得
摘要:<计算机图形学>是计算机科学与技术专业一门重要的专业课,其中直线生成算法是教学重点之一.该文通过分析几种直线生成算法的特点,阐述了理论教学和实践教学的重点和难点,总结了教学的体会和心 ...
- 计算机图形学 裁剪算法源代码,OpenGL计算机图形学梁友栋裁剪算法实验代码及运行结果.doc...
OpenGL计算机图形学梁友栋裁剪算法实验代码及运行结果.doc (10页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 14.9 积分 .<计算 ...
- 计算机图形学椭圆_椭圆算法| 计算机图形学
计算机图形学椭圆 椭圆的性质 (Properties of ellipse) Ellipse is defined as the locus of a point in a plane which m ...
最新文章
- 在像Angular2这样的SPA应用中使用Google Analytics的方法
- 16. 3Sum Closest
- MPLS 具有哪些特点?——Vecloud
- php重置密码,linux密码忘记重置密码的方法
- 用VS Code打造最佳Markdown编辑器
- python大数据运维库_大数据集群运维(10)Pycharm下安装模块
- 前端学习(2955):项目中组件的全局注册
- 关于python2到python3更新的一些书写规则的更改
- 【杭州云栖】飞天技术汇CDN与边缘计算专场:让内容离消费者更进一步
- android air flash,在Adobe Flash上​​触摸滚动Android上的Air
- bzoj4820 [Sdoi2017]硬币游戏 高斯消元+概率+kmp
- 50兆 svg 文件超过_如何让 Flutter 应用更好地使用 SVG?
- Latch free等待事件
- 若干小球碰撞的一种暴力解题法
- 树莓派专用msata硬盘转接板
- 计算机中级应用,计算机办公软件应用: 中级
- 大数据告诉你,上海二手房到底难不难卖?
- c++入门 简单语句 空语句 作用域和块 复合语句
- 中台详解(上)-什么是中台
- 详解交换机端口级联连接方式
热门文章
- zabbix报警-邮件-钉钉
- P1240 诸侯安置 [dp]
- 天涯明月刀@小虫@有你真好@下载
- 3词法分析 - 有穷自动机
- linux mkdir命令用法,linux里面的mkdir命令
- mysql 权限管理 针对表的字段 级别 授权 columns_priv表
- -bash: mysql: command not found 解决办法
- Linux多进程编程
- k8s探针检测php,k8s实践11:kubernetes监测探针简单测试
- 王者服务器维护公告2月,王者荣耀2月22日体验服停机更新公告 英雄调整