#include // 引入 OpenGL 头文件,提供窗口和绘图函数。 // 定义区域编码 #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; // 保存裁剪窗口的左、右、下、上边界。 }; // 结构体定义结束。 struct Point { // 定义二维点结构体。 float x, y; // 保存点的横坐标和纵坐标。 }; // 点结构体定义结束。 ClipWindow rect; // 定义全局裁剪窗口对象。 Point lineStart = {60, 80}; // 定义原始线段起点。 Point lineEnd = {520, 360}; // 定义原始线段终点。 int computeCode(Point p, ClipWindow rect) { // 根据点坐标和裁剪窗口计算区域编码。 int code = INSIDE; // 初始认为点在窗口内部。 if (p.x < rect.xmin) code |= LEFT; // 如果点在左边界外,则添加 LEFT 标记。 else if (p.x > rect.xmax) code |= RIGHT; // 如果点在右边界外,则添加 RIGHT 标记。 if (p.y < rect.ymin) code |= BOTTOM; // 如果点在下边界外,则添加 BOTTOM 标记。 else if (p.y > rect.ymax) code |= TOP; // 如果点在上边界外,则添加 TOP 标记。 return code; // 返回最终区域编码。 } void drawLine(Point p1, Point p2, float r, float g, float b) { // 按指定颜色绘制一条线段。 glColor3f(r, g, b); // 设置线段颜色。 glBegin(GL_LINES); // 开始绘制线段。 glVertex2f(p1.x, p1.y); // 提交第一个端点。 glVertex2f(p2.x, p2.y); // 提交第二个端点。 glEnd(); // 结束绘制。 } void drawClipWindow(ClipWindow rect) { // 绘制裁剪窗口。 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(); // 结束闭合线框顶点提交。 } void cohenSutherlandClip(Point p1, Point p2, ClipWindow rect) { // 定义 Cohen-Sutherland 线段裁剪函数。 int code1 = computeCode(p1, rect); // 计算第一个端点的区域编码。 int code2 = computeCode(p2, 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; // 保存当前选中的窗口外端点区域编码。 Point crossPoint; // 保存线段与裁剪边界的交点坐标。 // 选择窗口外的一个端点。 if (code1 != 0) // 如果第一个端点在窗口外。 code_out = code1; // 选择第一个端点作为待裁剪端点。 else // 否则说明第二个端点在窗口外。 code_out = code2; // 选择第二个端点作为待裁剪端点。 // 计算交点。 if (code_out & TOP) { // 如果端点在上边界外,则与上边界求交。 crossPoint.x = p1.x + (p2.x - p1.x) * (rect.ymax - p1.y) / (p2.y - p1.y); // 根据直线参数方程计算交点 x 坐标。 crossPoint.y = rect.ymax; // 交点 y 坐标等于上边界。 } // 上边界求交结束。 else if (code_out & BOTTOM) { // 如果端点在下边界外,则与下边界求交。 crossPoint.x = p1.x + (p2.x - p1.x) * (rect.ymin - p1.y) / (p2.y - p1.y); // 根据直线参数方程计算交点 x 坐标。 crossPoint.y = rect.ymin; // 交点 y 坐标等于下边界。 } // 下边界求交结束。 else if (code_out & RIGHT) { // 如果端点在右边界外,则与右边界求交。 crossPoint.y = p1.y + (p2.y - p1.y) * (rect.xmax - p1.x) / (p2.x - p1.x); // 根据直线参数方程计算交点 y 坐标。 crossPoint.x = rect.xmax; // 交点 x 坐标等于右边界。 } // 右边界求交结束。 else if (code_out & LEFT) { // 如果端点在左边界外,则与左边界求交。 crossPoint.y = p1.y + (p2.y - p1.y) * (rect.xmin - p1.x) / (p2.x - p1.x); // 根据直线参数方程计算交点 y 坐标。 crossPoint.x = rect.xmin; // 交点 x 坐标等于左边界。 } // 左边界求交结束。 // 用交点替换原来的外部端点。 if (code_out == code1) { // 如果被裁剪的是第一个端点。 p1 = crossPoint; // 用交点更新第一个端点坐标。 code1 = computeCode(p1, rect); // 重新计算第一个端点的区域编码。 } // 第一个端点更新结束。 else { // 否则被裁剪的是第二个端点。 p2 = crossPoint; // 用交点更新第二个端点坐标。 code2 = computeCode(p2, rect); // 重新计算第二个端点的区域编码。 } } } // 如果接受,则绘制裁剪后的线段(蓝色)。 if (accept) { // 如果线段最终被接受。 drawLine(p1, p2, 0.0f, 0.0f, 1.0f); // 绘制裁剪后的蓝色线段。 } // 接受线段绘制结束。 } // 显示回调函数 void myDisplay() { // 定义窗口重绘时调用的显示函数。 glClear(GL_COLOR_BUFFER_BIT); // 清除颜色缓冲区。 drawClipWindow(rect); // 绘制红色裁剪窗口。 drawLine(lineStart, lineEnd, 0.0f, 1.0f, 0.0f); // 绘制绿色原始线段。 // 执行裁剪并绘制结果 (蓝色)。 glLineWidth(3.0); // 设置线宽为 3 像素。 cohenSutherlandClip(lineStart, lineEnd, rect); // 执行裁剪算法并绘制裁剪后的线段。 glLineWidth(1.0); // 恢复线宽。 glFlush(); // 强制执行所有尚未执行的 OpenGL 绘图命令。 } // 显示回调函数结束。 // 初始化设置 void Init() { // 定义初始化函数。 glShadeModel(GL_FLAT); // 设置平面着色模式。 rect.xmin = 150; // 设置裁剪窗口左边界。 rect.xmax = 450; // 设置裁剪窗口右边界。 rect.ymin = 120; // 设置裁剪窗口下边界。 rect.ymax = 320; // 设置裁剪窗口上边界。 } // 窗口大小调整 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; }