欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。

线段剪裁作用

所谓线段剪裁,就是在二维平面上有一堆线段,和一个矩形窗口。求出现在窗口里线段部分是哪些。


上图中绿色为线段,红色为窗口。

剪裁后效果如下。


在这里我们规定矩形的边是平行于x轴和y轴的。

朴素思想及优化

朴素做法就是对于某一条段,用4个边界分别与线段求交。再判断交点的关系,决定哪段在剪裁窗口内。这种方法计算量大,情况也非常复杂。

优化

之前有基于解方程CohenSutherland算法
本文是基于参数化线段方程求交的Liang-barsky算法。

窗口左右下上分别为 xWmin, xWmax, yWmin, yWmax。

设一条线段的两个端点为P0(x0,y0),Pend(xend,yend)设一条线段的两个端点为P_0(x_0, y_0), P_{end}(x_{end}, y_{end})设一条线段的两个端点为P0​(x0​,y0​),Pend​(xend​,yend​)

∆x=xend−x0,∆y=yend−y0∆x = x_{end}-x_0, ∆y = y_{end}-y_0∆x=xend​−x0​,∆y=yend​−y0​

xWmin<=x0+u∗∆x<=xWmax(0<=u<=1)xWmin<= x_0 + u*∆x <= xWmax (0<=u<=1)xWmin<=x0​+u∗∆x<=xWmax(0<=u<=1)

yWmin<=y0+u∗∆y<=yWmax(0<=u<=1)yWmin<= y_0 + u*∆y <= yWmax (0<=u<=1)yWmin<=y0​+u∗∆y<=yWmax(0<=u<=1)

上述不等式拆成4个不等式。

u∗−∆x<=x0−xWminu*-∆x <= x_0-xWminu∗−∆x<=x0​−xWmin

u∗∆x<=xWmax−x0u*∆x <= xWmax-x_0u∗∆x<=xWmax−x0​

u∗−∆y<=y0−yWminu*-∆y<= y_0-yWminu∗−∆y<=y0​−yWmin

u∗∆y<=yWmax−y0u*∆y <= yWmax-y_0u∗∆y<=yWmax−y0​

从上式中可以看出,当∆x 或 ∆y=0 且 不等式后边的结果是负数时,不等式是无解的,也就是u没有效范围,反映到线段上就是完全被丢弃。如下图,绿色的线将被丢弃。


通过上述4个式子可以解出u的范围,再与[0,1]取交集,就是有效的区间。

一开始,有效区间为[u1, u2], u1=0, u2=1

将上述4个式子整理一下

u∗pk<=qk,k=1,2,3,4u*p_k<=q_k,\ k=1,2,3,4u∗pk​<=qk​, k=1,2,3,4

对于一个p, q 如果 p>0 就会解出u<=q/p, 则更新u2为min(u, u2)。
如果p<0 , 就会解出u>=q/p, 则更新u1为max(u, u1)。
如果p==0, 判断是否在区域外。


上图展示了左右两边对直线进行剪裁,会确定一个最终区间。
x0>xend时,也有类似的结论。
对于下上边的剪裁也是同理。

线段求交点击前往

实现

#include "glew/2.2.0_1/include/GL/glew.h"
#include "glfw/3.3.4/include/GLFW/glfw3.h"
#include <iostream>
using namespace std;void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{//如果按下ESC,把windowShouldClose设置为True,外面的循环会关闭应用if(key==GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);std::cout<<"ESC"<<mode;
}const double esp = 1e-6;int doubleCmp(double a, double b) {if (fabs(a-b)< esp)return 0;if (a<b)return -1;return 1;
}class Point {public:double x, y;Point(int xx, int yy):x(xx), y(yy){}
};void setPixel(Point p) {// cout<<p.x<<","<<p.y<<endl;glPointSize(4);glBegin(GL_POINTS);glVertex2d(p.x, p.y);glEnd();glFlush();
}/** 简单Bresenham 算法* 只处理|m|>1*/
void LineBres_2(Point p1, Point p2) {if(p2.y<p1.y) {Point t = p2;p2=p1;p1=t;}int deltaX = abs(p2.x-p1.x), deltaY = abs(p2.y-p1.y);int p0 = 2*deltaX-deltaY;int twDeltaX = 2*deltaX, twDeltaXmTwoDeltaY = 2*deltaX-2*deltaY;int step = 0;if(deltaX>0) step = (p2.x-p1.x) / deltaX;setPixel(p1);for (; p1.y < p2.y;) {p1.y++;if(p0<0) {p0+=twDeltaX;} else {p0+=twDeltaXmTwoDeltaY;p1.x+=step;}setPixel(p1);// cout<<p1.x<<","<<p1.y<<endl;}}/** 简单Bresenham 算法* 只处理|m|<=1*/
void LineBres_1(Point p1, Point p2) {if(p2.x<p1.x) {Point t = p2;p2=p1;p1=t;}int deltaX = abs(p2.x-p1.x), deltaY = abs(p2.y-p1.y);if(deltaX<deltaY) {LineBres_2(p1, p2);return;}int p0 = 2*deltaY-deltaX;int twDeltaY = 2*deltaY, twDeltaYmTwoDeltaX = 2*deltaY-2*deltaX;int step = 0;if(deltaY>0) step = (p2.y-p1.y) / deltaY;setPixel(p1);for (; p1.x < p2.x;) {p1.x++;if(p0<0) {p0+=twDeltaY;} else {p0+=twDeltaYmTwoDeltaX;p1.y+=step;}setPixel(p1);// cout<<p1.x<<","<<p1.y<<endl;}}/*
P0(X0, Y0), Pend(Xend, Yend)
∆x = Xend-X0, ∆y = Yend-Y0.
xWmin<= X0 + u*∆x <= xWmax (0<=u<=1),
yWmin<= Y0 + u*∆y <= yWmax (0<=u<=1),拆解上述不等可以得到*/
const int xWmin = -100, xWmax = 100, yWmin = -100, yWmax = 100;bool clipTest(double p, double q, double &u1, double &u2){int d = doubleCmp(p, 0);if(0==d) {return doubleCmp(q,0)>=0;}double r = q/p;if (d<0) {if(doubleCmp(r, u2)>0)return false;u1 = max(r, u1);} else {if(doubleCmp(r, u1)<0)return false;u2 = min(r, u2);}return true;
}void LiangBarsky(Point p1, Point p2) {double u1 = 0.0, u2 = 1.0;int dx = p2.x-p1.x;int dy = p2.y-p1.y;if(!clipTest(-dx, p1.x-xWmin, u1, u2))return;if(!clipTest(dx, xWmax-p1.x, u1, u2))return;if(!clipTest(-dy, p1.y-yWmin, u1, u2))return;if(!clipTest(dy, yWmax-p1.y, u1, u2))return;if (u2<1) {p2.x = p1.x + u2*dx;p2.y = p1.y + u2*dy;}if (u1>0) {p1.x += u1*dx;p1.y += u1*dy;}LineBres_1(p1, p2);
}int main(void) {//初始化GLFW库if (!glfwInit())return -1;//创建窗口以及上下文GLFWwindow *window = glfwCreateWindow(400, 400, "hello world", NULL, NULL);if (!window) {//创建失败会返回NULLglfwTerminate();}//建立当前窗口的上下文glfwMakeContextCurrent(window);glfwSetKeyCallback(window, key_callback); //注册回调函数//glViewport(0, 0, 400, 400);gluOrtho2D(-200, 200.0, -200, 200.0);//循环,直到用户关闭窗口cout<<123<<endl;while (!glfwWindowShouldClose(window)) {/*******轮询事件*******/glfwPollEvents();// cout<<456<<endl;//选择清空的颜色RGBAglClearColor(0, 0, 0, 1);glClear(GL_COLOR_BUFFER_BIT);// glColor3f(0,0, 0);glMatrixMode(GL_PROJECTION);/*//开始画一个三角形glBegin(GL_TRIANGLES);glColor3f(1, 0, 0); //RedglVertex3f(0.5, 0, 0);glVertex3f(-1, -1, 0);glVertex3f(1, -1, 0);//结束一个画图步骤glEnd();
*/
/*int point1[] = {50, 100};glLineWidth(4.0f);glBegin(GL_LINE_STRIP);glColor3f(100,100, 100);glVertex2i(10, 10);glColor3f(100,0, 0);glVertex2i(20, 20);glEnd();glBegin(GL_POINTS);glVertex2i(100,100);glEnd();
*///LineDDA(Point(0,0),Point(60,100));//LineDDA(Point(0,0),Point(100,60));// 画边框glColor3f(1, 0, 0); //RedLineBres_1(Point(xWmin,yWmin),Point(xWmin,yWmax));LineBres_1(Point(xWmin,yWmin),Point(xWmax,yWmin));LineBres_1(Point(xWmax,yWmax),Point(xWmax,yWmin));LineBres_1(Point(xWmin,yWmax),Point(xWmax,yWmax));glColor3f(0, 1, 0); //green// 剪裁前线段/*LineBres_1(Point(150,-100),Point(150,0));LineBres_1(Point(145,-100),Point(-150,100));LineBres_1(Point(0,-150),Point(0,150));LineBres_1(Point(-150,0),Point(130,0));LineBres_1(Point(145,50),Point(-150,200));LineBres_1(Point(145,150),Point(-150,200));LineBres_1(Point(145,-50),Point(-150,-150));LineBres_1(Point(145,-120),Point(-150,-170));LineBres_1(Point(145,-150),Point(-150,-100));LineBres_1(Point(145,-150),Point(-150,-30));LineBres_1(Point(-150,-30), Point(145,150));LineBres_1(Point(-150,-30), Point(-145,150));LineBres_1(Point(-0,-130), Point(-145,-150));*/// glColor3f(0, 0, 1); //green// 剪裁后线段LiangBarsky(Point(150,-100),Point(150,0));LiangBarsky(Point(145,-100),Point(-150,100));LiangBarsky(Point(0,-150),Point(0,150));LiangBarsky(Point(-150,0),Point(130,0));LiangBarsky(Point(145,50),Point(-150,200));LiangBarsky(Point(145,150),Point(-150,200));LiangBarsky(Point(145,-50),Point(-150,-150));LiangBarsky(Point(145,-120),Point(-150,-170));LiangBarsky(Point(145,-150),Point(-150,-100));LiangBarsky(Point(145,-150),Point(-150,-30));LiangBarsky(Point(-150,-30), Point(145,150));LiangBarsky(Point(-150,-30), Point(-145,150));LiangBarsky(Point(-0,-130), Point(-145,-150));/******交换缓冲区,更新window上的内容******/glfwSwapBuffers(window);//break;}glfwTerminate();return 0;
}

算法效果

剪裁前效果

剪裁后效果


本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。

线段求交应用之Liang-barsky裁剪算法相关推荐

  1. AK F.*ing leetcode 流浪计划之线段求交

    欢迎关注更多精彩 关注我,学习常用算法与数据结构,一题多解,降维打击. 本期话题:2条线段求交点 我有两种线段求交方法. 这两种方法在图形中窗口剪裁中应用. 分别是 Cohen-Suther land ...

  2. 线段求交算法对比研究

    线段求交算法对比研究 -----by wangsh 一.介绍 线段求交算法在计算几何,地理信息系统算法等相关应用中占有重要的位置,本文简单给出算法说明. Bentley & Ottmann于1 ...

  3. C++line segment intersection线段求交(交点)(附完整源码)

    C++line segment intersection线段求交的实现 C++line segment intersection线段求交实现的完整源码(定义,实现,main函数测试) C++line ...

  4. 计算几何 - XOJ 1171 线段求交

    问题 Description 线段求交即给定一组线段求出这些线段的相交情况,它是计算几何的基础问题之一,有着广泛的应用. Input第一行为一个正整数n表示线段的个数(n<=10000)第二行到 ...

  5. N条线段求交的扫描线算法

    转载自:http://johnhany.net/2013/11/sweep-algorithm-for-segments-intersection/ N条线段求交的扫描线算法 在对图进行计算时,很常用 ...

  6. 计算几何2:扫描线线段求交算法

    相比于上一篇中介绍的求凸包算法,本次介绍的扫描线线段求交算法的实现难度明显更高,实际上最终本人也未能完美的实现该算法,下面给出的版本经测试是存在一些问题的. 首先介绍一下扫描线算法的基本原理,具体内容 ...

  7. 理解梁友栋-Barsky裁剪算法

    学习图形学窗口裁剪算法时,很多教材只是对梁友栋-Barsky裁剪算法过程做了简单的介绍,并没有对原理过多的解释.老而学者如秉烛夜行,用了两三天时间终于搞明白算法原理. 消除指定区域内或区域外的图形部分 ...

  8. 计算机图形学:Cohen-Sutherland直线段剪裁算法及梁友栋-Barsky裁剪算法(算法原理及代码实现)

    一.算法实现原理 Cohen-Sutherland直线段剪裁算法: 算法原理: (1)判断线段两端是否都落在窗口内,如果是,则线段完全可见,否则进行下一步 (2)判断线段两端是否都落在窗口外,如果是, ...

  9. 梁友栋-Barsky裁剪算法原理分析

    梁友栋-Barsky算法是一种参数线裁剪算法. 为两个参数方程,为两点的差值,其中的由来是整理参数方程中三角函数的值得出,因为取值需要在裁剪框内,所以u只需要取0~1范围内的就足够. 是结合四条边界线 ...

最新文章

  1. pdm生成mysql sql语句_如何用Powerdesigner的PDM生成数据库
  2. [论文阅读] (17)CCS2019 针对PowerShell脚本的轻量级去混淆和语义感知攻击检测(经典)
  3. android path平移,安卓自定义View进阶:Path基本操作(下)
  4. 洛谷模板,树状数组二 差分
  5. 让人欲罢不能的量子学
  6. 【译】你不知道的 Chrome 调试工具技巧 第二十天:Workspace的黑魔法
  7. 重新分区_电脑磁盘分区指南!一分钟就学会
  8. 对于基于模板的前端框架的补充
  9. Java 中时间处理SimpleDateFormat 中HH和hh的区别
  10. 前端避免XSS(跨站脚本攻击)
  11. C语言程序设计(第三版)何钦铭著 习题2-6
  12. PDFDOC365工具箱
  13. python安装pygame教程_Pygame安装教程
  14. 07.JavaScript弹窗——alert、prompt、confirm
  15. Technica:以太网解决方案
  16. 解决打开html文件为乱码(完美)
  17. 基于DragonBoard410C的智能管家
  18. 数据结构—双向链表的基本操作
  19. 第五届“强网”拟态防御国际精英挑战赛在南京举行——开辟网络安全新赛道 引领网络弹性新优势
  20. Oracle命令行工具基本操作及SQL 命令

热门文章

  1. 恒大健康:自称股东的FF员工提起集体诉讼 无理控告我们
  2. 洛谷9月月赛 康娜的线段树
  3. java执行sql列名无效_sqlserver列名无效调试及解决方法
  4. 微信小程序实现侦听属性
  5. 磊科wifi linux驱动下载,磊科随身WiFi驱动
  6. Golang学习笔记之GORM基础使用(一)
  7. 无胁科技-TVD每日漏洞情报-2022-9-15
  8. Unity | unityC++内存共享及中文乱码解决方式
  9. 文件误删没进回收站怎么恢复?
  10. 考toeic心得。。。。