实验内容:

  • 实现 LiangLiangLiang-BarskyBarskyBarsky 裁剪算法

画线过程:

  由于 LiangLiangLiang-BarskyBarskyBarsky 算法是矩形窗口对于直线段的裁剪,考虑到鼠标的交互性,需要添加画线程序。

  因此调用了 STL 中的 vector 来保存所有直线, 采用了 BresenhamBresenhamBresenham 算法来画出直线。

struct BASE{int x0,y0,x1,y1;
};
vector<BASE> Vline,VRectangle,Vline1;
//Vline —— 保存已经画完的直线, Vline1 —— 保存当前画的直线, VRectangle —— 保存窗口坐标

画矩形过程:

  因为此裁剪算法是用矩形窗口对直线进行裁剪,因此矩形的构造不可避免。 此处采用了两点确定一个矩形的原则,用矩形四个角上的两个端点来确定这个 矩形。

  这里会出现一个小问题,就是四个角上的两个端点不一定是左下角和右下 角,因此我们需要先对 x、y 坐标求一个最大和最小值,以此确定左下角和右下 角,便于绘制这个图形。然后再调用 Bresenham 算法画线即可。

if(VRectangle.size()){int x0 = VRectangle[0].x0, y0 = VRectangle[0].y0, x1 = VRectangle[0].x1, y1 = VRectangle[0].y1;int xlow = min(x0,x1), ylow = min(y0,y1), xh = max(x0,x1), yh = max(y0,y1);DrawLine(xlow, ylow, xlow, yh);DrawLine(xlow, ylow, xh, ylow);DrawLine(xlow, yh, xh, yh);DrawLine(xh, ylow, xh, yh);for(int i = 0; i < Vline.size(); i++){LiangBarsky(Vline[i].x0,Vline[i].y0,Vline[i].x1,Vline[i].y1,xlow,xh,ylow,yh);}}

裁剪算法:

裁剪算法即实现了 LiangLiangLiang-BarskyBarskyBarsky 的算法原理。基本步骤如下。

  • 输入 (x1,y1)、(x2,y2)、wxl、wxr、wyb、wyt(x_1,y_1)、(x_2,y_2)、wxl、wxr、wyb、wyt(x1​,y1​)、(x2​,y2​)、wxl、wxr、wyb、wyt。

  • 若Δx=0\Delta x = 0Δx=0,则 p1=p2=0p_1=p_2=0p1​=p2​=0,此时进一步判断 q1<0∣∣q2<0q_1 < 0 \ ||\ q_2 < 0q1​<0 ∣∣ q2​<0,则直线段不在窗口内,转⑦结束。否则,满足 0≤q10\leq q_10≤q1​ && 0≤q20\leq q_20≤q2​,进一步计算 umaxu_{max}umax​ 与 uminu_{min}umin​。

  • 若Δy=0\Delta y=0Δy=0,则 p3=p4=0p_3=p_4=0p3​=p4​=0,此时进一步判断是否满足 q3<0∣∣q4<0q_3 < 0 \ ||\ q_4 < 0q3​<0 ∣∣ q4​<0,若满足则该直线段不在窗口内,转⑦结束。否则,0≤q30\leq q_30≤q3​ && 0≤q40\leq q_40≤q4​,进一步计算 umaxu_maxum​ax 与 uminu_minum​in。

  • 若上述两条均不满足,则 pk=0̸(k=1,2,3,4)p_k =\not 0 \ (k = 1,2,3,4)pk​=​0 (k=1,2,3,4),则计算 umaxu_{max}umax​ 和 uminu_{min}umin​。 求得 umaxu_{max}umax​ 和 uminu_{min}umin​ 后,进行判断:若 umaxu_{max}umax​ > uminu_{min}umin​,则直线段在窗口外, 转⑦。若 umax≤uminu_{max} \leq u_{min}umax​≤umin​,则得到 xxx、yyy 坐标。

  • 画出直线段。

  • 算法结束。 代码实现过程中,该函数的参数为直线的两点坐标,窗口的左右上下边界, 然后在函数中直接判断此直线在窗外还是窗内,然后直接画出直线。

void LiangBarsky(int x1, int y1, int x2, int y2, int xleft, int xright, int ybottom, int ytop){int p[5] = {0,x1-x2,x2-x1,y1-y2,y2-y1},q[5] = {0,x1-xleft,xright-x1,y1-ybottom,ytop-y1}, L = 1, R = 4;db u[5], umin = 1, umax = 0;if(p[1] == 0 && p[3] == 0) return;if(p[1] == 0){if(q[1] < 0 || q[2] < 0) return;else {L = 3; R = 4;}}else if(p[3] == 0){if(q[3] < 0 || q[4] < 0) return;else {L = 1; R = 2;}}for(int i = L; i <= R; i++){u[i] = (db)q[i]/(db)p[i];if(p[i] < 0) umax = max(umax,u[i]);else umin = min(umin,u[i]);}if(sign(umax-umin) == 1) return;db xans1 = (db)x1+umin*(db)(x2-x1), yans1 = (db)y1+umin*(db)(y2-y1), xans2 = (db)x1+umax*(db)(x2-x1), yans2 = (db)y1+umax*(db)(y2-y1);DrawLine((int)xans1, (int)yans1, (int)xans2, (int)yans2);
}

功能演示:

  • 确定窗口
    选择画矩形按钮确定窗口。如果直接画线会默认无窗口。

    鼠标拖拽即可确定窗口矩形。
  • 画线
    选择画线按钮进行画线裁剪。

    鼠标拖拽即可画线,刚画好的直线不会直接裁剪,当松开鼠标画下一条直线时才会认为上一条直线已经画完,才开始裁剪。

  • 在画矩形部分重新确定窗口
    由于画出的直线都会被保存,因此可以重新确定窗口,每条直线都会被再次裁 剪后输出。拖拽鼠标画出窗口即可看到之前每条直线被裁剪之后显示的图形。



细节:

  • 回调函数中每次刷新屏幕,每次重新画线、画矩形。在矩形窗口存在的时候 才会画线,矩形窗口不存在的时候不会显示直线。
  • 由于 Liang-Barsky 算法中涉及到了浮点数比较,因此手写了比较函数,将 精度误差限制在了 1e-7。
typedef double db;
const db EPS = 1e-7;
inline int sign(db a) {return a < -EPS ? -1 : a > EPS; } //返回-1表示a < 0, 1表示a > 0, 0表示a = 0

完整代码:

#include <GLUT/GLUT.h>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <vector>
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;int WinWidth = 1024, Winheight = 720; //窗口宽高
int X0,Y0,X1,Y1,Xvalue; //起始坐标与终止坐标
void init();
void Mymenu(int);
void DrawLine(int,int,int,int);
void BresenhamLine(int,int,int,int);
void LiangBarsky(int,int,int,int,int,int,int,int);typedef double db;
const db EPS = 1e-7;
inline int sign(db a) {return a < -EPS ? -1 : a > EPS; } //返回-1表示a < 0, 1表示a > 0, 0表示a = 0void swap(GLint& a,GLint &b) {GLint t = a; a = b; b = t;}
void setPixel(GLint x,GLint y){glBegin(GL_POINTS); //把每个顶点作为一个点来处理glVertex2i(x,y); //int坐标画点glEnd();
}
struct BASE{int x0,y0,x1,y1;
};
vector<BASE> Vline,VRectangle,Vline1;
//Vline —— 保存已经画完的直线, Vline1 —— 保存当前画的直线, VRectangle —— 保存窗口坐标void ReDraw(){//    printf("Line.size(): %d\n",(int)Vline.size());if(VRectangle.size()){int x0 = VRectangle[0].x0, y0 = VRectangle[0].y0, x1 = VRectangle[0].x1, y1 = VRectangle[0].y1;int xlow = min(x0,x1), ylow = min(y0,y1), xh = max(x0,x1), yh = max(y0,y1);DrawLine(xlow, ylow, xlow, yh);DrawLine(xlow, ylow, xh, ylow);DrawLine(xlow, yh, xh, yh);DrawLine(xh, ylow, xh, yh);for(int i = 0; i < Vline.size(); i++){LiangBarsky(Vline[i].x0,Vline[i].y0,Vline[i].x1,Vline[i].y1,xlow,xh,ylow,yh);}}if(Vline1.size()) DrawLine(Vline1[0].x0, Vline1[0].y0, Vline1[0].x1, Vline1[0].y1);
}void display(){glClear (GL_COLOR_BUFFER_BIT);glRectf (-1.0, -1.0, 1.0, 1.0);ReDraw();glutSwapBuffers ();
}void update(){if(Xvalue == 1){ //LineVline1[0].x1 = X1; Vline1[0].y1 = Y1;if(Vline.size() == 0 || (Vline[Vline.size()-1].x0 != X0 && Vline[Vline.size()-1].y0 != Y0))Vline.push_back({X0,Y0,X1,Y1});else{Vline[Vline.size()-1].x1 = X1;Vline[Vline.size()-1].y1 = Y1;}}else if(Xvalue == 2){VRectangle[0].x1 = X1; VRectangle[0].y1 = Y1;}display();
}void Dragmouse(int x,int y){ //鼠标拖拽X1 = x; Y1 = y; update();
}void Mymouse(int button, int state, int x, int y){if(button == GLUT_LEFT_BUTTON && state == GLUT_UP){X1 = x; Y1 = y;update();}else if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN){X0 = x; Y0 = y;if(Xvalue == 1){Vline1.clear();Vline1.push_back((BASE){X0,Y0,X0,Y0});}else if(Xvalue == 2){Vline1.clear();VRectangle.clear();VRectangle.push_back((BASE){X0,Y0,X0,Y0});}}
}int main(int argc, char** argv){glutInit(&argc, argv); //初始化glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); //指定单缓存窗口与RGB颜色模式的窗口glutInitWindowPosition(500, 300); //设置初始窗口的位置,(0,0)为屏幕左上角位置glutInitWindowSize(WinWidth, Winheight); //设置窗口宽度与高度,单位为像素glutCreateWindow("图形学实验二"); //创建窗口init();glutDisplayFunc(display);//注册显示回调函数glutCreateMenu(Mymenu);//注册菜单回调函数glutAddMenuEntry("DrawLine",1);//添加菜单项glutAddMenuEntry("DrawRectangle",2);glutAddMenuEntry("ClearScreen",3);glutAddMenuEntry("Exit",4);glutAttachMenu(GLUT_RIGHT_BUTTON);//把当前菜单注册到指定的鼠标键glutMainLoop();
}void init(){glClearColor(0,0,0,1); //(red green blue alpha) alpha表示混合因子glClear(GL_COLOR_BUFFER_BIT); //将屏幕所有像素点还原为"底色"glPointSize(3.0f); //指定栅格化点的直径glMatrixMode(GL_PROJECTION); //声明接下来要进行的操作,GL_PROJECTION 投影, GL_MODELVIEW 模型视图, GL_TEXTURE 纹理glLoadIdentity(); //加载一个单位矩阵gluOrtho2D(0,WinWidth,Winheight,0); //定义裁剪面glColor3f(255,215,0); //设置画点颜色 R-G-BglutMouseFunc(Mymouse);glutMotionFunc(Dragmouse);
}void DrawLine(int x1,int y1,int x2,int y2){ //画直线函数BresenhamLine(x1,y1,x2,y2);
}void Mymenu(int value){Xvalue = value;if (value == 3){glClear(GL_COLOR_BUFFER_BIT);glutSwapBuffers();Vline1.clear();Vline.clear();VRectangle.clear();}else if (value == 4){exit(0);}
}void BresenhamLine(int x1,int y1,int x2,int y2){int x = x1, y = y1, dx = abs(x2-x1), dy = abs(y2-y1), s1 = 1, s2 = 1, e, flag = 0;if(x1 >= x2) s1 = -1;if(y1 >= y2) s2 = -1;if(dy > dx) {swap(dx,dy); flag = 1;}e = -dx;int DX = 2*dx, DY = 2*dy;for(int i = 1; i <= dx; i++){setPixel(x, y);if(e >= 0){if(!flag) y += s2;else x += s1;e = e-DX;}if(!flag) x += s1;else y += s2;e = e+DY;}
}void LiangBarsky(int x1, int y1, int x2, int y2, int xleft, int xright, int ybottom, int ytop){int p[5] = {0,x1-x2,x2-x1,y1-y2,y2-y1},q[5] = {0,x1-xleft,xright-x1,y1-ybottom,ytop-y1}, L = 1, R = 4;db u[5], umin = 1, umax = 0;if(p[1] == 0 && p[3] == 0) return;if(p[1] == 0){if(q[1] < 0 || q[2] < 0) return;else {L = 3; R = 4;}}else if(p[3] == 0){if(q[3] < 0 || q[4] < 0) return;else {L = 1; R = 2;}}for(int i = L; i <= R; i++){u[i] = (db)q[i]/(db)p[i];if(p[i] < 0) umax = max(umax,u[i]);else umin = min(umin,u[i]);}if(sign(umax-umin) == 1) return;db xans1 = (db)x1+umin*(db)(x2-x1), yans1 = (db)y1+umin*(db)(y2-y1), xans2 = (db)x1+umax*(db)(x2-x1), yans2 = (db)y1+umax*(db)(y2-y1);DrawLine((int)xans1, (int)yans1, (int)xans2, (int)yans2);
}

【OpenGL 实验二】Liang-Barsky 裁剪算法相关推荐

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

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

  2. 计算机图形学实验报告 裁剪,计算机图形学-实验报告三-图形裁剪算法

    <计算机图形学-实验报告三-图形裁剪算法>由会员分享,可在线阅读,更多相关<计算机图形学-实验报告三-图形裁剪算法(9页珍藏版)>请在人人文库网上搜索. 1.图形裁剪算法1. ...

  3. 《计算机图形学》实验报告 Cohen Sutherland裁剪算法

    一.实验目的和要求 熟悉光栅图形学中的相关直线段裁剪算法.理解Cohen-Sutherland裁剪算法. 二.实验内容 实现Cohen-Sutherland编码线段裁剪算法,能看到裁剪前后的屏幕显示效 ...

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

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

  5. 【模式识别】实验二:K近邻算法(KNN)

    KNN是模式识别中的经典算法,本次实验就MNIST数据集来做KNN算法的实验,并结合前一次的LDA降维对数据进行进一步处理. 实验报告图片版 pdf版本可以戳这:模式识别实验报告:KNN K近邻算法 ...

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

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

  7. 图形学opengl实验二-桌子的矩阵变换

    在OpenGL编程基础上,通过实现实验内容,掌握OpenGL的矩阵使用,并验证课程中矩阵变换的内容: #include <GL/glut.h> float size = 0.25; //缩 ...

  8. 实验二:群智能算法,第3关:粒子群算法 - 目标函数最优解计算

    文章目录 任务描述 相关知识 编码与适应度函数 粒子群算法原理 粒子群算法流程 使用python实现粒子群算法 编程要求 测试说明 完整代码 任务描述 本关任务:使用 python 实现粒子群算法,并 ...

  9. 中点分割裁剪算法 c语言,裁剪算法——中点分割算法/Liang-Barsky算法

    三.中点分割法 首先对直线段的端点进行编码. [核心思想:通过二分逼近来确定直线段与窗口的交点.] 具体方法: 1.若中点不在窗口内,则把[中点]和离窗口边界[最远点]构成的线段丢掉,以线段上的另一点 ...

  10. 实验二 实现中点分割直线段裁剪算法

    一.目的 1. 了解直线裁剪的基本原理和常用方法. 2. 掌握中点分割直线段裁剪算法的基本原理和步骤. 3. 使用C++.OpenGL编程实现如下内容:输入直线段的起始点和终止点坐标位置.用四边形模拟 ...

最新文章

  1. 手动将web项目的class文件打成jar包,手动打jar包,java -cvf,IDE打包底层指令
  2. 什么是USDT以及如何使用它?
  3. 如何复制静态文件以使用Webpack构建目录?
  4. JAG Practice Contest for ACM-ICPC Asia Regional 2016.K.Non-redundant Drive(点分治)
  5. SSM中抛出异常 java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoad
  6. python异步_Python通过Thread实现异步
  7. 您可能没有使用的最佳三星Galaxy功能
  8. 凤凰os linux界面,让deepin linux系统与凤凰os共用个人目录的方法
  9. [TaskList] 省选前板子补完计划
  10. win10怎么设置默认浏览器_vscode如何设置默认打开的浏览器为Chrome?
  11. 【博客美化】09.评论带头像,且支持旋转
  12. step6: item与pipeline
  13. 【李宏毅2020 ML/DL】P12 Brief Introduction of Deep Learning
  14. 无名接口.php,李无名
  15. 向量表示 运动抛物线_黄老师讲数学(460)向量、数列、双变量、坐标计算、极限等结合的一道题...
  16. 重磅!100位校高校教师晒工资,详细晒,全国各地!要进高校的博士们参考
  17. Tallest buildings/skyscrapers in the world
  18. 短期不看好 KotLin 原因
  19. 后端都需要学习什么?
  20. 晚上如何配置ubuntu,保护眼睛?黑(暗)色主题

热门文章

  1. Python单元测试框架之pytest -- fixtures
  2. 使用php glob函数查找文件,遍历文件目录(转)
  3. java log4j 热部署_Java 调式、热部署、JVM 背后的支持者 Java Agent
  4. linux 占用缓存前10_Linux查看内存使用情况应该使用什么命令
  5. java反射创建字符串_Java反射
  6. 反驳生命的起点是rna_科学家提出了生命分子的手性起源新假说,源自宇宙射线...
  7. python闭包函数的必要条件_Python闭包函数
  8. 计算机控制技术证书可以考吗,计算机控制技术专业可考哪些资格证书
  9. 故事到此为止了,谢谢遇见,让我成长。
  10. 斯皮尔曼等级相关(Spearman’s correlation coefficient for ranked data)