完善实验一
This commit is contained in:
+175
-175
@@ -1,175 +1,175 @@
|
||||
#include <GL/glut.h>
|
||||
#include <iostream>
|
||||
|
||||
// --- 定义区域编码 ---
|
||||
#define INSIDE 0 // 0000
|
||||
#define LEFT 1 // 0001
|
||||
#define RIGHT 2 // 0010
|
||||
#define BOTTOM 4 // 0100
|
||||
#define TOP 8 // 1000
|
||||
|
||||
// --- 定义裁剪窗口结构 ---
|
||||
struct ClipWindow {
|
||||
float xmin, xmax, ymin, ymax;
|
||||
};
|
||||
|
||||
// --- 全局变量设置 ---
|
||||
ClipWindow rect;
|
||||
int x_0, y_0, x_1, y_1; // 线段端点
|
||||
|
||||
/**
|
||||
* 计算点 (x, y) 的区域编码
|
||||
*/
|
||||
int computeCode(float x, float y, ClipWindow rect) {
|
||||
int code = INSIDE;
|
||||
if (x < rect.xmin) code |= LEFT;
|
||||
else if (x > rect.xmax) code |= RIGHT;
|
||||
if (y < rect.ymin) code |= BOTTOM;
|
||||
else if (y > rect.ymax) code |= TOP;
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cohen-Sutherland 算法核心
|
||||
* 注意:这里传入坐标的引用,直接修改坐标值以得到裁剪后的结果
|
||||
*/
|
||||
void cohenSutherlandClip(float& x_0, float& y_0, float& x_1, float& y_1, ClipWindow rect) {
|
||||
int code1 = computeCode(x_0, y_0, rect);
|
||||
int code2 = computeCode(x_1, y_1, rect);
|
||||
bool accept = false;
|
||||
|
||||
while (true) {
|
||||
// 如果两端点都在窗口内 (0000 | 0000 == 0)
|
||||
if (!(code1 | code2)) {
|
||||
accept = true;
|
||||
break;
|
||||
}
|
||||
// 如果两端点都在窗口的同一侧外部 (例如都在左边:0001 & 0001 != 0) 按位与运算
|
||||
else if (code1 & code2) {
|
||||
break; // 直接舍弃
|
||||
}
|
||||
// 3. 线段部分在窗口内,需要求交点
|
||||
else {
|
||||
int code_out;
|
||||
float x, y;
|
||||
|
||||
// 选择窗口外的一个端点
|
||||
if (code1 != 0)
|
||||
code_out = code1;
|
||||
else
|
||||
code_out = code2;
|
||||
|
||||
// 计算交点
|
||||
if (code_out & TOP) {
|
||||
x = x_0 + (x_1 - x_0) * (rect.ymax - y_0) / (y_1 - y_0);
|
||||
y = rect.ymax;
|
||||
}
|
||||
else if (code_out & BOTTOM) {
|
||||
x = x_0 + (x_1 - x_0) * (rect.ymin - y_0) / (y_1 - y_0);
|
||||
y = rect.ymin;
|
||||
}
|
||||
else if (code_out & RIGHT) {
|
||||
y = y_0 + (y_1 - y_0) * (rect.xmax - x_0) / (x_1 - x_0);
|
||||
x = rect.xmax;
|
||||
}
|
||||
else if (code_out & LEFT) {
|
||||
y = y_0 + (y_1 - y_0) * (rect.xmin - x_0) / (x_1 - x_0);
|
||||
x = rect.xmin;
|
||||
}
|
||||
|
||||
// 用交点替换原来的外部端点
|
||||
if (code_out == code1) {
|
||||
x_0 = x; y_0 = y;
|
||||
code1 = computeCode(x_0, y_0, rect);
|
||||
}
|
||||
else {
|
||||
x_1 = x; y_1 = y;
|
||||
code2 = computeCode(x_1, y_1, rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果接受,则绘制裁剪后的线段(蓝色)
|
||||
if (accept) {
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(0.0f, 0.0f, 1.0f); // 蓝色
|
||||
glVertex2f(x_0, y_0);
|
||||
glVertex2f(x_1, y_1);
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// --- 显示回调函数 ---
|
||||
void myDisplay() {
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// 1. 绘制裁剪窗口 (红色线框)
|
||||
glColor3f(1.0f, 0.0f, 0.0f); // 红色
|
||||
glBegin(GL_LINE_LOOP);
|
||||
glVertex2f(rect.xmin, rect.ymin);
|
||||
glVertex2f(rect.xmax, rect.ymin);
|
||||
glVertex2f(rect.xmax, rect.ymax);
|
||||
glVertex2f(rect.xmin, rect.ymax);
|
||||
glEnd();
|
||||
|
||||
// 2. 绘制原始线段 (绿色)
|
||||
// 注意:为了演示效果,我们在调用裁剪函数前,先保存原始坐标,
|
||||
// 或者在这里直接用初始值绘制。这里我们使用初始值绘制。
|
||||
int orig_x_0 = 50, orig_y_0 = 350;
|
||||
int orig_x_1 = 550, orig_y_1 = 50;
|
||||
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(0.0f, 1.0f, 0.0f); // 绿色
|
||||
glVertex2f(orig_x_0, orig_y_0);
|
||||
glVertex2f(orig_x_1, orig_y_1);
|
||||
glEnd();
|
||||
|
||||
// 3. 执行裁剪并绘制结果 (蓝色)
|
||||
// 使用 float 变量传入,因为算法内部会修改它们
|
||||
float cx_0 = orig_x_0, cy_0 = orig_y_0;
|
||||
float cx_1 = orig_x_1, cy_1 = orig_y_1;
|
||||
|
||||
// 加粗蓝色线条以便区分
|
||||
glLineWidth(3.0);
|
||||
cohenSutherlandClip(cx_0, cy_0, cx_1, cy_1, rect);
|
||||
glLineWidth(1.0); // 恢复线宽
|
||||
|
||||
glFlush();
|
||||
}
|
||||
|
||||
// --- 初始化设置 ---
|
||||
void Init() {
|
||||
glShadeModel(GL_FLAT);
|
||||
|
||||
rect.xmin = 100;
|
||||
rect.xmax = 400;
|
||||
rect.ymin = 100;
|
||||
rect.ymax = 300;
|
||||
}
|
||||
|
||||
// --- 窗口大小调整 ---
|
||||
void myReshape(int w, int h) {
|
||||
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
// 设置二维投影范围,与坐标数值对应
|
||||
gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
// --- 主函数 ---
|
||||
int main(int argc, char* argv[]) {
|
||||
glutInit(&argc, argv);
|
||||
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
|
||||
glutInitWindowPosition(100, 100);
|
||||
glutInitWindowSize(600, 400);
|
||||
glutCreateWindow("实验2_03毕爽爽 Cohen-Sutherland 算法");
|
||||
|
||||
Init();
|
||||
|
||||
glutDisplayFunc(myDisplay);
|
||||
glutReshapeFunc(myReshape);
|
||||
|
||||
glutMainLoop();
|
||||
return 0;
|
||||
}
|
||||
#include <GL/glut.h> // 引入 GLUT/OpenGL 头文件,提供窗口和绘图函数。
|
||||
#include <iostream> // 引入输入输出流库,便于需要时输出调试信息。
|
||||
// 空行:分隔头文件和区域编码定义。
|
||||
// --- 定义区域编码 ---
|
||||
#define INSIDE 0 // 0000,表示点在裁剪窗口内部。
|
||||
#define LEFT 1 // 0001,表示点在裁剪窗口左侧。
|
||||
#define RIGHT 2 // 0010,表示点在裁剪窗口右侧。
|
||||
#define BOTTOM 4 // 0100,表示点在裁剪窗口下方。
|
||||
#define TOP 8 // 1000,表示点在裁剪窗口上方。
|
||||
// 空行:分隔宏定义和结构体定义。
|
||||
// --- 定义裁剪窗口结构 ---
|
||||
struct ClipWindow { // 定义用于保存裁剪窗口边界的结构体。
|
||||
float xmin, xmax, ymin, ymax; // 保存裁剪窗口的左、右、下、上边界。
|
||||
}; // 结构体定义结束。
|
||||
// 空行:分隔结构体和全局变量。
|
||||
// --- 全局变量设置 ---
|
||||
ClipWindow rect; // 定义全局裁剪窗口对象。
|
||||
int x_0, y_0, x_1, y_1; // 定义线段端点坐标变量。
|
||||
// 空行:分隔全局变量和函数说明。
|
||||
/** // 文档注释开始。
|
||||
* 计算点 (x, y) 的区域编码 // 说明 computeCode 函数的作用。
|
||||
*/ // 文档注释结束。
|
||||
int computeCode(float x, float y, ClipWindow rect) { // 根据点坐标和裁剪窗口计算区域编码。
|
||||
int code = INSIDE; // 初始认为点在窗口内部。
|
||||
if (x < rect.xmin) code |= LEFT; // 如果点在左边界外,则添加 LEFT 标记。
|
||||
else if (x > rect.xmax) code |= RIGHT; // 如果点在右边界外,则添加 RIGHT 标记。
|
||||
if (y < rect.ymin) code |= BOTTOM; // 如果点在下边界外,则添加 BOTTOM 标记。
|
||||
else if (y > rect.ymax) code |= TOP; // 如果点在上边界外,则添加 TOP 标记。
|
||||
return code; // 返回最终区域编码。
|
||||
} // 区域编码函数结束。
|
||||
// 空行:分隔区域编码函数和裁剪函数说明。
|
||||
/** // 文档注释开始。
|
||||
* Cohen-Sutherland 算法核心 // 说明该函数执行 Cohen-Sutherland 线段裁剪。
|
||||
* 注意:这里传入坐标的引用,直接修改坐标值以得到裁剪后的结果 // 说明引用参数会被函数内部更新。
|
||||
*/ // 文档注释结束。
|
||||
void cohenSutherlandClip(float& x_0, float& y_0, float& x_1, float& y_1, ClipWindow rect) { // 定义 Cohen-Sutherland 线段裁剪函数。
|
||||
int code1 = computeCode(x_0, y_0, rect); // 计算第一个端点的区域编码。
|
||||
int code2 = computeCode(x_1, y_1, rect); // 计算第二个端点的区域编码。
|
||||
bool accept = false; // 定义是否接受裁剪结果的标志,初始为 false。
|
||||
// 空行:分隔初始化和裁剪循环。
|
||||
while (true) { // 不断处理线段,直到确定接受或舍弃。
|
||||
// 如果两端点都在窗口内 (0000 | 0000 == 0)。
|
||||
if (!(code1 | code2)) { // 两个区域编码按位或为 0,说明整条线段在窗口内。
|
||||
accept = true; // 标记线段可以被接受。
|
||||
break; // 退出裁剪循环。
|
||||
} // 完全可见判断结束。
|
||||
// 如果两端点都在窗口的同一侧外部 (例如都在左边:0001 & 0001 != 0) 按位与运算。
|
||||
else if (code1 & code2) { // 两个区域编码按位与不为 0,说明线段完全在同一外侧。
|
||||
break; // 直接舍弃该线段并退出循环。
|
||||
} // 完全不可见判断结束。
|
||||
// 3. 线段部分在窗口内,需要求交点。
|
||||
else { // 线段部分可见,需要把窗口外端点裁到边界上。
|
||||
int code_out; // 保存当前选中的窗口外端点区域编码。
|
||||
float x, y; // 保存线段与裁剪边界的交点坐标。
|
||||
// 空行:分隔局部变量和外部端点选择。
|
||||
// 选择窗口外的一个端点。
|
||||
if (code1 != 0) // 如果第一个端点在窗口外。
|
||||
code_out = code1; // 选择第一个端点作为待裁剪端点。
|
||||
else // 否则说明第二个端点在窗口外。
|
||||
code_out = code2; // 选择第二个端点作为待裁剪端点。
|
||||
// 空行:分隔外部端点选择和交点计算。
|
||||
// 计算交点。
|
||||
if (code_out & TOP) { // 如果端点在上边界外,则与上边界求交。
|
||||
x = x_0 + (x_1 - x_0) * (rect.ymax - y_0) / (y_1 - y_0); // 根据直线参数方程计算交点 x 坐标。
|
||||
y = rect.ymax; // 交点 y 坐标等于上边界。
|
||||
} // 上边界求交结束。
|
||||
else if (code_out & BOTTOM) { // 如果端点在下边界外,则与下边界求交。
|
||||
x = x_0 + (x_1 - x_0) * (rect.ymin - y_0) / (y_1 - y_0); // 根据直线参数方程计算交点 x 坐标。
|
||||
y = rect.ymin; // 交点 y 坐标等于下边界。
|
||||
} // 下边界求交结束。
|
||||
else if (code_out & RIGHT) { // 如果端点在右边界外,则与右边界求交。
|
||||
y = y_0 + (y_1 - y_0) * (rect.xmax - x_0) / (x_1 - x_0); // 根据直线参数方程计算交点 y 坐标。
|
||||
x = rect.xmax; // 交点 x 坐标等于右边界。
|
||||
} // 右边界求交结束。
|
||||
else if (code_out & LEFT) { // 如果端点在左边界外,则与左边界求交。
|
||||
y = y_0 + (y_1 - y_0) * (rect.xmin - x_0) / (x_1 - x_0); // 根据直线参数方程计算交点 y 坐标。
|
||||
x = rect.xmin; // 交点 x 坐标等于左边界。
|
||||
} // 左边界求交结束。
|
||||
// 空行:分隔交点计算和端点替换。
|
||||
// 用交点替换原来的外部端点。
|
||||
if (code_out == code1) { // 如果被裁剪的是第一个端点。
|
||||
x_0 = x; y_0 = y; // 用交点更新第一个端点坐标。
|
||||
code1 = computeCode(x_0, y_0, rect); // 重新计算第一个端点的区域编码。
|
||||
} // 第一个端点更新结束。
|
||||
else { // 否则被裁剪的是第二个端点。
|
||||
x_1 = x; y_1 = y; // 用交点更新第二个端点坐标。
|
||||
code2 = computeCode(x_1, y_1, rect); // 重新计算第二个端点的区域编码。
|
||||
} // 第二个端点更新结束。
|
||||
} // 部分可见处理结束。
|
||||
} // 裁剪循环结束。
|
||||
// 空行:分隔裁剪循环和结果绘制。
|
||||
// 如果接受,则绘制裁剪后的线段(蓝色)。
|
||||
if (accept) { // 如果线段最终被接受。
|
||||
glBegin(GL_LINES); // 开始按线段方式提交顶点。
|
||||
glColor3f(0.0f, 0.0f, 1.0f); // 设置绘制颜色为蓝色。
|
||||
glVertex2f(x_0, y_0); // 提交裁剪后线段的起点。
|
||||
glVertex2f(x_1, y_1); // 提交裁剪后线段的终点。
|
||||
glEnd(); // 结束线段顶点提交。
|
||||
} // 接受线段绘制结束。
|
||||
} // Cohen-Sutherland 裁剪函数结束。
|
||||
// 空行:分隔裁剪函数和显示回调函数。
|
||||
// --- 显示回调函数 ---
|
||||
void myDisplay() { // 定义窗口重绘时调用的显示函数。
|
||||
glClear(GL_COLOR_BUFFER_BIT); // 清除颜色缓冲区。
|
||||
// 空行:分隔清屏和裁剪窗口绘制。
|
||||
// 1. 绘制裁剪窗口 (红色线框)。
|
||||
glColor3f(1.0f, 0.0f, 0.0f); // 设置绘制颜色为红色。
|
||||
glBegin(GL_LINE_LOOP); // 开始绘制闭合线框。
|
||||
glVertex2f(rect.xmin, rect.ymin); // 提交裁剪窗口左下角顶点。
|
||||
glVertex2f(rect.xmax, rect.ymin); // 提交裁剪窗口右下角顶点。
|
||||
glVertex2f(rect.xmax, rect.ymax); // 提交裁剪窗口右上角顶点。
|
||||
glVertex2f(rect.xmin, rect.ymax); // 提交裁剪窗口左上角顶点。
|
||||
glEnd(); // 结束闭合线框顶点提交。
|
||||
// 空行:分隔裁剪窗口绘制和原始线段绘制。
|
||||
// 2. 绘制原始线段 (绿色)。
|
||||
// 注意:为了演示效果,我们在调用裁剪函数前,先保存原始坐标,
|
||||
// 或者在这里直接用初始值绘制。这里我们使用初始值绘制。
|
||||
int orig_x_0 = 50, orig_y_0 = 350; // 定义原始线段的第一个端点坐标。
|
||||
int orig_x_1 = 550, orig_y_1 = 50; // 定义原始线段的第二个端点坐标。
|
||||
// 空行:分隔原始坐标定义和原始线段绘制。
|
||||
glBegin(GL_LINES); // 开始按线段方式提交顶点。
|
||||
glColor3f(0.0f, 1.0f, 0.0f); // 设置绘制颜色为绿色。
|
||||
glVertex2f(orig_x_0, orig_y_0); // 提交原始线段起点。
|
||||
glVertex2f(orig_x_1, orig_y_1); // 提交原始线段终点。
|
||||
glEnd(); // 结束原始线段顶点提交。
|
||||
// 空行:分隔原始线段绘制和裁剪结果绘制。
|
||||
// 3. 执行裁剪并绘制结果 (蓝色)。
|
||||
// 使用 float 变量传入,因为算法内部会修改它们。
|
||||
float cx_0 = orig_x_0, cy_0 = orig_y_0; // 将原始起点复制为浮点变量。
|
||||
float cx_1 = orig_x_1, cy_1 = orig_y_1; // 将原始终点复制为浮点变量。
|
||||
// 空行:分隔裁剪坐标变量和线宽设置。
|
||||
// 加粗蓝色线条以便区分。
|
||||
glLineWidth(3.0); // 设置线宽为 3 像素。
|
||||
cohenSutherlandClip(cx_0, cy_0, cx_1, cy_1, rect); // 执行裁剪算法并绘制裁剪后的线段。
|
||||
glLineWidth(1.0); // 恢复线宽。
|
||||
// 空行:分隔裁剪结果绘制和刷新。
|
||||
glFlush(); // 强制执行所有尚未执行的 OpenGL 绘图命令。
|
||||
} // 显示回调函数结束。
|
||||
// 空行:分隔显示回调函数和初始化函数。
|
||||
// --- 初始化设置 ---
|
||||
void Init() { // 定义初始化函数。
|
||||
glShadeModel(GL_FLAT); // 设置平面着色模式。
|
||||
// 空行:分隔 OpenGL 状态和裁剪窗口边界。
|
||||
rect.xmin = 100; // 设置裁剪窗口左边界。
|
||||
rect.xmax = 400; // 设置裁剪窗口右边界。
|
||||
rect.ymin = 100; // 设置裁剪窗口下边界。
|
||||
rect.ymax = 300; // 设置裁剪窗口上边界。
|
||||
} // 初始化函数结束。
|
||||
// 空行:分隔初始化函数和窗口调整函数。
|
||||
// --- 窗口大小调整 ---
|
||||
void myReshape(int w, int h) { // 定义窗口尺寸变化时的回调函数。
|
||||
glViewport(0, 0, (GLsizei)w, (GLsizei)h); // 设置视口覆盖整个窗口。
|
||||
glMatrixMode(GL_PROJECTION); // 切换到投影矩阵模式。
|
||||
glLoadIdentity(); // 将当前投影矩阵重置为单位矩阵。
|
||||
// 设置二维投影范围,与坐标数值对应。
|
||||
gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h); // 根据窗口宽高设置二维正交投影范围。
|
||||
glMatrixMode(GL_MODELVIEW); // 切换回模型视图矩阵模式。
|
||||
} // 窗口大小调整回调函数结束。
|
||||
// 空行:分隔窗口调整函数和主函数。
|
||||
// --- 主函数 ---
|
||||
int main(int argc, char* argv[]) { // 程序入口函数,接收命令行参数。
|
||||
glutInit(&argc, argv); // 初始化 GLUT。
|
||||
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); // 设置显示模式为 RGB 颜色和单缓冲。
|
||||
glutInitWindowPosition(100, 100); // 设置窗口初始位置。
|
||||
glutInitWindowSize(600, 400); // 设置窗口初始大小为 600x400。
|
||||
glutCreateWindow("实验2_03毕爽爽 Cohen-Sutherland 算法"); // 创建窗口并设置标题。
|
||||
// 空行:分隔窗口创建和初始化。
|
||||
Init(); // 调用初始化函数,设置裁剪窗口。
|
||||
// 空行:分隔初始化和回调注册。
|
||||
glutDisplayFunc(myDisplay); // 注册显示回调函数。
|
||||
glutReshapeFunc(myReshape); // 注册窗口大小变化回调函数。
|
||||
// 空行:分隔回调注册和主循环。
|
||||
glutMainLoop(); // 进入 GLUT 事件处理主循环。
|
||||
return 0; // 正常结束程序。
|
||||
} // 主函数结束。
|
||||
|
||||
Reference in New Issue
Block a user