系列文章:
(二)计算机图形学基本图形的生成(直线DDA算法,直线中点算法,Bresenham画圆算法)--附源码
(三)计算机图形学基本图形的生成(二维图形裁剪Cohen-Sutherland算法+图形平移算法+图形旋转算法)--附源码
环境:Win10+Visual Studio 2022 Community
在本次实验中需要用到第一篇文章实验内容的代码及环境,详情请见:传送门
目录
一、实验目的
1.熟练掌握二维图形裁剪的Cohen-Sutherland算法
2.熟练掌握图形的平移算法
3.熟练掌握图形的旋转算法
二、实验步骤
1.Cohen-Sutherland算法
(1)打开工程项目,在菜单项“二维裁剪图形”下建立子菜单“Cohen算法”,在其属性窗口将属性项Name的属性值改为“CohenCut”。
(2)双击菜单项“Cohen算法”,系统建立一个空的菜单响应函数CohenCut_Click。在该函数中加入如下语句:
private void CohenCut_Click(object sender, EventArgs e)
{
MenuID = 21; PressNum = 0;
Graphics g = CreateGraphics(); //创建图形设备
XL = 100; XR = 400; YD = 100; YU = 400; //窗口参数
pointsgroup[0] = new Point(XL, YD);
pointsgroup[1] = new Point(XR, YD);
pointsgroup[2] = new Point(XR, YU);
pointsgroup[3] = new Point(XL, YU);
g.DrawPolygon(Pens.Blue, pointsgroup); //创建裁剪窗口
}
(3)用MenuID变量表示后续的操作都与该算法有关,因为要使用鼠标,因此初始化PressNum变量。用四个变量记录窗口参数,因为其他的很多算法也要使用窗口,因此将这四个变量变为Form类变量,为此要对Form类变量做添加如下;为了方便使用图形相关函数画窗口,设置了一个包含四个点的数组pointsgroup。由于其他算法也需要事先画窗口,将这个数组同样设置为Form类变量公共变量:
public int MenuID, PressNum, FirstX, FirstY, OldX, OldY, XL, XR, YU, YD;
Point[] pointsgroup = new Point[4]; //创建一个有4个点的点数组
(4)在Form1_MouseClick函数中,增加菜单指示变量MenuID为21(即开始 Cohen-Sutherland裁剪算法)时的程序操作语句如下:
if (MenuID == 21) //Cohen裁剪算法
{
if (PressNum == 0) //保留第一点
{
FirstX = e.X;
FirstY = e.Y;
PressNum++;
}
else //第二点,调用裁剪算法
{
CohenCut1(FirstX, FirstY, e.X, e.Y); //Cohencut裁剪
PressNum = 0; //清零,为画下一次裁剪做准备
}
}
(5)函数CohenCut1是在窗口参数和线段参数都已获取的前提下实现Cohen-Sutherland 裁剪算法的函数。在窗体实现程序Form1.cs中插入以下函数:
private void CohenCut1(int x1, int y1, int x2, int y2)
{
int code1 = 0, code2 = 0, code, x = 0, y = 0;
Graphics g = CreateGraphics(); //创建图形设备
g.DrawLine(Pens.Red, x1, y1, x2, y2); //画原始线段
code1 = encode(x1, y1); //对线段端点编码
code2 = encode(x2, y2);
while (code1 != 0 || code2 != 0)
{
if ((code1 & code2) != 0) return; //完全不可见
code = code1;
if (code1 == 0) code = code2;
if ((1 & code) != 0) //求线段与窗口左边的交点:0001=1
{
x = XL;
y = y1 + (y2 - y1) * (x - x1) / (x2 - x1);
}
else if ((2 & code) != 0)
{
x = XR;
y = y1 + (y2 - y1) * (x - x1) / (x2 - x1);
}
else if ((4 & code) != 0)
{
y = YD;
x = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
}
else if ((8 & code) != 0)
{
y = YU;
x = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
}
if (code == code1)
{
x1 = x;
y1 = y;
code1 = encode(x, y);
}
else
{
x2 = x;
y2 = y;
code2 = encode(x, y);
}
}
Pen MyPen = new Pen(Color.Black, 3);
g.DrawLine(MyPen, x1, y1, x2, y2);
}
(6)其中,函数encode的功能是对线段的一个端点进行编码,目前还没有,需要立即实现。如果严格按照算法描述方法逐个生成每个裁剪线段端点4个二进制码位,编程繁复。这里根据窗口参数先确定9个区域,然后根据端点落在哪个区域直接赋予编码。encode函数实现如下:
private int encode(int x, int y)
{
int code = 0;
if (x >= XL && x <= XR && y >= YD && y <= YU) code = 0; //窗口区域:0000
if (x < XL && y >= YD && y <= YU) code = 1; //窗口左区域:0001
if (x > XR && y >= YD && y <= YU) code = 2; //窗口右边域:0010
if (x >= XL && x <= XR && y > YU) code = 8; //窗口上区域:1000
if (x >= XL && x <= XR && y < YD) code = 4; //窗口下区域:0100
if (x <= XL && y > YU) code = 9; //窗口左上区域:1001
if (x >= XR && y > YU) code = 10; //窗口右上区域:1010
if (x <= XL && y < YD) code = 5; //窗口左下区域:0101
if (x >= XR && y < YD) code = 6; //窗口右下区域:0110
return code;
}
(6)运行结果
2.平移算法
(1)在菜单项“二维图形变换”下添加子菜单项“图形平移”,将其属性项Name的属性值改为英文字符“TransMove”。
(2)双击菜单项建立菜单响应函数,在系统建立的空的响应函数TransMove_Click中加入语句如下:
private void TransMove_Click(object sender, EventArgs e)
{
MenuID = 11;
PressNum = 0;
Graphics g = CreateGraphics(); //创建图形设备
pointsgroup[0] = new Point(100, 100); //设置变换图形
pointsgroup[1] = new Point(200, 100);
pointsgroup[2] = new Point(200, 200);
pointsgroup[3] = new Point(100, 200);
g.DrawPolygon(Pens.Red, pointsgroup); //显示图形
}
(3)平移量用鼠标操作确定,即用鼠标先后确定两个点,第二点减去第一点的坐标增量就是平移量,将坐标增量分别加到原图形坐标上,就得到平移以后的图形,显示这个图形,就完成了平移变换,具体做法是在Form1_MouseClick函数中加入如下语句:
if (MenuID == 11) //平移
{
if (PressNum == 0) //保留第一点
{
FirstX = e.X;
FirstY = e.Y;
}
else //第二点,确定平移量,改变图形参数
{
for (int i = 0; i < 4; i++)
{
pointsgroup[i].X += e.X - FirstX;
pointsgroup[i].Y += e.Y - FirstY;
}
g.DrawPolygon(Pens.Blue, pointsgroup);
}
PressNum++;
if (PressNum >= 2)
{
PressNum = 0; //完毕,清零,为下一次做准备
}
}
(4)运行结果
3.旋转算法
(1)在菜单项“二维图形变换”下建立子菜单项“图形旋转”,将其属性项Name的属性值改为英文字符“TransRotate”。
(2)双击菜单项建立菜单响应函数TransRotate_Click。在系统建立的空的响应函数中加入语句如下:
private void TransRotate_Click(object sender, EventArgs e)
{
MenuID = 12;
PressNum = 0;
Graphics g = CreateGraphics(); //创建图形设备
pointsgroup[0] = new Point(100, 100);
pointsgroup[1] = new Point(200, 100);
pointsgroup[2] = new Point(200, 200);
pointsgroup[3] = new Point(100, 200);
g.DrawPolygon(Pens.Red, pointsgroup); //画出被旋转图形
}
(3)在Form1_MouseClick函数中加入处理旋转的鼠标操作指令如下:
if (MenuID == 12) //旋转
{
if (PressNum == 0) //保留第一点
{
FirstX = e.X;
FirstX = e.Y;
}
else //第二点,确定旋转角度,改变图形参数
{
double a;
if (e.X == FirstX && e.Y == FirstY) return; //排除两点重合的异常情况
if (e.X == FirstX && e.Y > FirstY) //排除分母为0的异常情况
{
a = 3.1415926 / 2.0;
}
else if (e.X == FirstX && e.Y < FirstY)
{
a = 3.1415926 / 2.0 * 3.0;
}
else
{
//计算旋转弧度
a = Math.Atan((double)(e.Y - FirstY) / (double)(e.X - FirstX));
}
a = a / 3.1415926 * 180.0; //弧度转化为角度
int x0 = 150, y0 = 150; //指定旋转中心
Matrix myMatrix = new Matrix(); //创建矩阵对象,以利用矩阵工具
myMatrix.Translate(-x0, -y0); //创建平移量为(-x0,-y0)的平移矩阵
myMatrix.Rotate((float)a, MatrixOrder.Append); //右乘角度为a的旋转矩阵
myMatrix.Translate(x0, y0, MatrixOrder.Append); //右乘平移量为(x0,y0)的平移矩阵
g.Transform = myMatrix; //用复合矩阵变换图形设备
g.DrawPolygon(Pens.Blue, pointsgroup); //显示旋转结果
}
PressNum++;
if (PressNum >= 2)
{
PressNum = 0; //完毕,清零,为下一次做准备
}
}
(4)为了能够直接使用系统提供的矩阵设置、计算、处理工具,我们需要引进定义了矩阵的命名空间,添加语句如下:
using System.Drawing.Drawing2D;
(5)运行结果