【计算机图形学】Two-Dimensional Viewing and Clippin


本博客基于课程"计算机图形学",教材使用为计算机图形学(第4版) [Computer Graphics with OpenGL, Fourth Edition],部分代码模板便来自于此教材,有所改动。大部分内容来自本人实验报告,有错误是难以避免的,若有表述错误或bug欢迎指出。

实验思路

编程思路

先设置裁剪窗口各条边的区域码,用十六进制表示,方便进行逻辑运算。encode()函数用来对单个点相对于裁剪窗口的位置进行判断,并返回十六进制数。用accept(),reject(), inside()函数依次表示完全在裁剪窗口内、完全在窗口外、穿过裁剪窗口。swapPts(),swapCodes()两函数作用分别是交换两点的坐标和两个二进制码。根据教材所给的line_clipping()函数模板,变量done表示是否完成裁剪,变量poltLine表示对判断线的接受或者丢弃,在while循环中,依次用accept(),reject(), inside()函数判断,当判断结果不为accept()reject()
进入第三个判断分支。在第三个判断分支中,先通过inside()判断把code1设置为窗口外的点;接着判断线段是否平行于窗口各边,若不平行则计算斜率,接着按照左右下上的顺序依次进行判断并裁剪,一次遍历裁剪完毕后,继续进行对code1code2的赋值,继续进行判断,直到线段完全在窗口内或窗口外。对于裁剪窗口,设置全局变量矩形clip_rect,通过Translaterect()函数,对矩形的各边坐标进行增或减。再通过special_key()函数,根据键盘输入的上下左右,调用Translaterect()函数,从而实现移动裁剪窗口的功能

问题及解决方案

  1. 在创建裁剪窗口时,要把裁剪窗口设置为全局变量,以便在后面调用移动窗口函数中的修改;同样,设置裁剪窗口各边的区域码时,设置成全局常量,便于访问;
  2. encode()函数中,GLubyte表示符号单字节整型,目的是为了得到一个十六进制的0/1串,便于后面进行逻辑操作。同时,不采用教材所给的(wcPt2D, wcPt2D, wcPt2D)三参数的形式,而使用(CPoint2D pt, CRect *cw)的两参数形式,并且由于在line_clipping()函数中,传入的参数是矩形变量的指针,会修改矩形变量的属性,而encode()的调用也在line_clipping()函数中,所以即使在encode()中不修改矩形的属性,也必须是传入矩形变量的指针。

实现代码

核心代码及关键步骤注释

class CRect {
public:
	float xmin, ymin, xmax, ymax;

	float width(void) { return xmax - xmin; }
	float height(void) { return ymax - ymin; }

	// Make (xmin, ymin) the lower left corner
	void normalize(void);

	// Draw the rectangle
	void draw(GLenum mode);
};

void CRect::normalize(void) {
	float ftemp;
	if (xmin > xmax) {
		ftemp = xmin;
		xmin = xmax;
		xmax = ftemp;
	}
	if (ymin > ymax) {
		ftemp = ymin;
		ymin = ymax;
		ymax = ftemp;
	}
}

void CRect::draw(GLenum mode) {
	glBegin(mode);
	glVertex2f(xmin, ymin);
	glVertex2f(xmax, ymin);
	glVertex2f(xmax, ymax);
	glVertex2f(xmin, ymax);
	glEnd();
}
int running_state = 0;
// 0 --- Normal state.
// 1 --- Rubber-band state.

// Size of the scene
float scene_size = 1000.0;

CRect clip_rect; // Clipping rectangle
CRect window; // Window
CRect viewport; // Viewport

// Program window size
int pw_width, pw_height;
const GLint left = 0x1;
const GLint right = 0x2;
const GLint bottom = 0x4;
const GLint top = 0x8;

GLint inside(GLint code) {
	return GLint(!code);
}
GLint reject(GLint code1, GLint code2) {
	return GLint(code1 & code2);
}
GLint accept(GLint code1, GLint code2) {
	return GLint(!(code1 | code2));
}

GLubyte encode(CPoint2D pt, CRect *cw) {
	GLubyte code = 0x00;
	if (pt.x < cw->xmin)
		code = code | left;
	if (pt.x > cw->xmax)
		code = code | right;
	if (pt.y < cw->ymin)
		code = code | bottom;
	if (pt.y > cw->ymax)
		code = code | top;
	return (code);
}

void swapPts(CPoint2D* p1, CPoint2D* p2) {
	CPoint2D temp;
	temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

void swapCodes(GLubyte* p1, GLubyte* p2) {
	GLubyte temp;
	temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

// Cohen-Sutherland line clipping algorithm.
bool line_clipping(CPoint2D p1, CPoint2D p2, CRect* cw,
	CPoint2D* q1, CPoint2D* q2)
// p1, p2: End points of input line segment
// cw:	   Clipping rectangle
// q1, q2: End points of output line segment
// Return value: true --- accept, false --- reject
{
	// Write your code here
	GLubyte code1, code2;
	GLint done = false, poltLine = false;
	GLfloat m;
	while (!done) {
		code1 = encode(p1, cw);
		code2 = encode(p2, cw);
		if (accept(code1, code2)) {
			done = true;
			poltLine = true;
		} else if (reject(code1, code2)) {
			done = true;
		} else {
			if (inside(code1)) {
				swapPts(&p1, &p2);
				swapCodes(&code1, &code2);
			}
			if (p1.x != p2.x)
				m = (p2.y - p1.y) / (p2.x - p1.x);
			if (code1 & left) {
				p1.y += (cw->xmin - p1.x) * m;
				p1.x = cw->xmin;
			} else if (code1 & right) {
				p1.y += (cw->xmax - p1.x) * m;
				p1.x = cw->xmax;
			} else if (code1 & bottom) {
				if (p2.x != p1.x)
					p1.x += (cw->ymin - p1.y) / m;
				p1.y = cw->ymin;
			} else if (code1 & top) {
				if (p2.x != p1.x)
					p1.x += (cw->ymax - p1.y) / m;
				p1.y = cw->ymax;
			}
		}
	}
	*q1 = p1;
	*q2 = p2;
	return poltLine;
}

//Translate the clip rectangle
void Translaterect(int dx, int dy) {
	clip_rect.xmin += dx;
	clip_rect.xmax += dx;
	clip_rect.ymin += dy;
	clip_rect.ymax += dy;
}

全部代码

// ====== Computer Graphics Experiment #6 ======
// |   Two-Dimensional Viewing and Clipping    |
// =============================================
//
// Requirement:
// (1) Implement Cohen-Sutherland line clipping algorithm.
// (2) Change position and size of window and viewport
//     and observe the effects.

#include <GL/glut.h>
#include <math.h>
#include <windows.h>

// 2D point class
class CPoint2D {
public:
	float x, y;
};

// Rectangle class
class CRect {
public:
	float xmin, ymin, xmax, ymax;

	float width(void) { return xmax - xmin; }
	float height(void) { return ymax - ymin; }

	// Make (xmin, ymin) the lower left corner
	void normalize(void);

	// Draw the rectangle
	void draw(GLenum mode);
};

void CRect::normalize(void) {
	float ftemp;
	if (xmin > xmax) {
		ftemp = xmin;
		xmin = xmax;
		xmax = ftemp;
	}
	if (ymin > ymax) {
		ftemp = ymin;
		ymin = ymax;
		ymax = ftemp;
	}
}

void CRect::draw(GLenum mode) {
	glBegin(mode);
	glVertex2f(xmin, ymin);
	glVertex2f(xmax, ymin);
	glVertex2f(xmax, ymax);
	glVertex2f(xmin, ymax);
	glEnd();
}

#define PI 3.14159265359

int running_state = 0;
// 0 --- Normal state.
// 1 --- Rubber-band state.

// Size of the scene
float scene_size = 1000.0;

CRect clip_rect; // Clipping rectangle
CRect window; // Window
CRect viewport; // Viewport

// Program window size
int pw_width, pw_height;

// Set window
void set_window(CRect* cw) {
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(cw->xmin, cw->xmax, cw->ymin, cw->ymax);
}

// Set viewport
void set_viewport(CRect* vp) {
	glViewport(vp->xmin, vp->ymin,
		vp->width(), vp->height());
}

const GLint left = 0x1;
const GLint right = 0x2;
const GLint bottom = 0x4;
const GLint top = 0x8;

GLint inside(GLint code) {
	return GLint(!code);
}
GLint reject(GLint code1, GLint code2) {
	return GLint(code1 & code2);
}
GLint accept(GLint code1, GLint code2) {
	return GLint(!(code1 | code2));
}

GLubyte encode(CPoint2D pt, CRect* cw) {
	GLubyte code = 0x00;
	if (pt.x < cw->xmin)
		code = code | left;
	if (pt.x > cw->xmax)
		code = code | right;
	if (pt.y < cw->ymin)
		code = code | bottom;
	if (pt.y > cw->ymax)
		code = code | top;
	return (code);
}

void swapPts(CPoint2D* p1, CPoint2D* p2) {
	CPoint2D temp;
	temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

void swapCodes(GLubyte* p1, GLubyte* p2) {
	GLubyte temp;
	temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

// Cohen-Sutherland line clipping algorithm.
bool line_clipping(CPoint2D p1, CPoint2D p2, CRect* cw,
	CPoint2D* q1, CPoint2D* q2)
// p1, p2: End points of input line segment
// cw:	   Clipping rectangle
// q1, q2: End points of output line segment
// Return value: true --- accept, false --- reject
{
	// Write your code here
	GLubyte code1, code2;
	GLint done = false, poltLine = false;
	GLfloat m;
	while (!done) {
		code1 = encode(p1, cw);
		code2 = encode(p2, cw);
		if (accept(code1, code2)) {
			done = true;
			poltLine = true;
		} else if (reject(code1, code2)) {
			done = true;
		} else {
			if (inside(code1)) {
				swapPts(&p1, &p2);
				swapCodes(&code1, &code2);
			}
			if (p1.x != p2.x)
				m = (p2.y - p1.y) / (p2.x - p1.x);
			if (code1 & left) {
				p1.y += (cw->xmin - p1.x) * m;
				p1.x = cw->xmin;
			} else if (code1 & right) {
				p1.y += (cw->xmax - p1.x) * m;
				p1.x = cw->xmax;
			} else if (code1 & bottom) {
				if (p2.x != p1.x)
					p1.x += (cw->ymin - p1.y) / m;
				p1.y = cw->ymin;
			} else if (code1 & top) {
				if (p2.x != p1.x)
					p1.x += (cw->ymax - p1.y) / m;
				p1.y = cw->ymax;
			}
		}
	}
	*q1 = p1;
	*q2 = p2;
	return poltLine;
}

//Translate the clip rectangle
void Translaterect(int dx, int dy) {
	clip_rect.xmin += dx;
	clip_rect.xmax += dx;
	clip_rect.ymin += dy;
	clip_rect.ymax += dy;
}

// Initialization function
void init(void) {
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glEnable(GL_LINE_STIPPLE);
}

// Display callback function
void display(void) {
	int i;
	CPoint2D p1, p2, q1, q2;
	double a, r;

	glClear(GL_COLOR_BUFFER_BIT);

	// Draw blue rectangle to fill the background
	glColor3f(0.0, 0.0, 0.5);
	window.draw(GL_POLYGON);

	// Draw unclipped lines in green color
	p1.x = 0.0;
	p1.y = 0.0;
	r = 0.5 * scene_size;
	glLineStipple(1, 0x0f0f);
	glColor3f(0.0, 1.0, 0.0);
	glBegin(GL_LINES);
	for (i = 0; i < 360; i += 15) {
		a = (double)i / 180.0 * PI;
		p2.x = r * cos(a);
		p2.y = r * sin(a);
		if (i == 0 || i == 180)
			p2.y = 0;
		if (i == 90 || i == 270)
			p2.x = 0;
		p2.x += p1.x;
		p2.y += p1.y;
		glVertex2f(p1.x, p1.y);
		glVertex2f(p2.x, p2.y);
	}
	glEnd();
	glLineStipple(1, 0xffff);

	// Draw clipped lines in white color
	if (running_state == 0) {
		glColor3f(1.0, 1.0, 1.0);
		glLineWidth(2.0);
		glBegin(GL_LINES);
		for (i = 0; i < 360; i += 15) {
			a = (double)i / 180.0 * PI;
			p2.x = r * cos(a);
			p2.y = r * sin(a);
			if (i == 0 || i == 180)
				p2.y = 0;
			if (i == 90 || i == 270)
				p2.x = 0;
			p2.x += p1.x;
			p2.y += p1.y;
			if (line_clipping(p1, p2, &clip_rect, &q1, &q2)) {
				glVertex2f(q1.x, q1.y);
				glVertex2f(q2.x, q2.y);
			}
		}
		glEnd();
		glLineWidth(1.0);
	}

	// Draw clipping rectangle
	glLineStipple(1, 0x0f0f);
	glColor3f(1.0, 1.0, 0.0);
	clip_rect.draw(GL_LINE_LOOP);
	glLineStipple(1, 0xffff);

	glutSwapBuffers();
}

// Reshape callback function
void reshape(int w, int h) {
	// Store program window size
	pw_width = w;
	pw_height = h;

	// set viewport
	viewport.xmin = 0;
	viewport.xmax = w;
	viewport.ymin = 0;
	viewport.ymax = h;
	set_viewport(&viewport);

	// set clipping window
	window.xmin = -0.6 * scene_size;
	window.xmax = 0.6 * scene_size;
	window.ymin = -0.6 * scene_size;
	window.ymax = 0.6 * scene_size;
	set_window(&window);

	// set clipping rectangle
	clip_rect.xmin = 0.5 * window.xmin;
	clip_rect.xmax = 0.5 * window.xmax;
	clip_rect.ymin = 0.5 * window.ymin;
	clip_rect.ymax = 0.5 * window.ymax;
}

// Keyboard callback function
void keyboard(unsigned char key, int x, int y) {
	switch (key) {
	case 27:
		exit(0);
	}
}

// Special keyboard callback function
void special_key(int key, int x, int y) {
	switch (key) {
	case GLUT_KEY_LEFT:
		Translaterect(-5.0, 0.0);
		glutPostRedisplay();
		break;
	case GLUT_KEY_RIGHT:
		Translaterect(5.0, 0.0);
		glutPostRedisplay();
		break;
	case GLUT_KEY_DOWN:
		Translaterect(0.0, -5.0);
		glutPostRedisplay();
		break;
	case GLUT_KEY_UP:
		Translaterect(0.0, 5.0);
		glutPostRedisplay();
		break;
	}
}

// Main program entrance
int main(int argc, char* argv[]) {
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
	glutInitWindowSize(800, 800);
	glutCreateWindow("Test 2D Clipping and Viewing");
	init();
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutSpecialFunc(special_key);
	glutDisplayFunc(display);
	glutMainLoop();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值