我参考的文章:计算机图形学-二维图形变换 笔记总结与代码实战-CSDN博客
对于二维变换,先写了一个画大五角星的函数FivePointsStar,并通过fillpolygon方法进行填充。然后以这个五角星为例,开始进行缩放、平移、旋转、对称,画出另外四个小五角星。国旗的长宽比例是3:2,因此建立1050*700px的窗口,背景色填充为红色,再添加绘制好的五个星星,最后展示出国旗的图案。
一、算法思路
二维变换的算法思路可以概括为以下步骤:
1‘确定原点:选择一个原点,通常是二维图形的左下角或左上角。
2‘确定变换类型:根据需要选择适当的二维变换类型,如平移、旋转、缩放等。
3‘确定变换矩阵:根据变换类型,确定相应的变换矩阵。例如,平移变换可以使用一个平移矩阵,旋转变换可以使用一个旋转变换矩阵等。
4‘确定变换对象:确定需要进行变换的二维图形对象,如点、线、多边形等。
5‘进行变换操作:将二维图形对象乘以变换矩阵,得到变换后的图形对象。具体实现方式可以通过矩阵乘法和向量运算来完成。
6‘输出结果:将变换后的二维图形对象输出为所需的格式或文件格式。
1、缩放
对于给定的比例系数与参考点,修改参数中的横纵坐标数组,在x,y轴方向分别放缩Sx,Sy倍。具体来说就是一个点相对于某一个参考点横纵坐标分别变为原来的一定倍数。对于参考点为原点时,比例变换矩阵为(Sx和Sy分别表示横轴和纵轴方向的比例系数,也就是放大或缩小多少倍)。当参考点不为原点,而是指定点时,则变换后点的横纵坐标为:
。变换矩阵如下:
核心代码如图所示:
2、平移
直接将需要平移的距离传值,对原图形的横纵坐标加上需要平移的距离即可。具体来说是指将指定点从一个坐标位置移动到另一个坐标位置的过程,每个点都移动相同的坐标,平移变换的矩阵为(Tx和Ty分别表示在横轴方向和纵轴方向的平移距离):
核心代码如图所示:
3、旋转
对于给定的角度(逆时针为正)和旋转中心,修改参数中横纵坐标数组。具体来说是将某个点绕着指定点旋转一定角度,规定逆时针方向为正,顺时针方向为负。当参考点为原点时的旋转变换矩阵为:
对于参考点不是坐标原点,而是的情况,变换后点的坐标可以通过如下公式求出:
,其中a表示旋转角度,用角度制表示,角度=弧度*圆周率/180。
核心代码如图所示:
- 对称
对于给定的对称轴,修改参数中的横纵坐标数组。点的坐标可通过如下公式求出:
核心代码如图所示:
二、代码
//二维图形的变换
const double pi = 3.141593;//用符号常量定义圆周率,方便后续使用
const double ScaleFactor = 0.381966;//用一个符号常量定义一个比例系数,这个系数表示五角星内半径和外半径之比(也就是内部五边形的半径和外接圆的半径比)
//固定使用一个五角星函数,根据给定的横纵坐标数组绘制有填充的五角星(专门定义一个函数便于统一接口)
//注意:该函数中横纵坐标数组中都各自有十个元素而不是五个元素
void FivePointsStar(const int* x, const int* y)
{
POINT p[10];
for (int i = 0; i < 10; i++)
{
p[i].x = x[i];
p[i].y = y[i];
}
fillpolygon(p, 10);
_getch();
}
//平移变换函数,对于给定的横纵坐标平移距离,会修改参数中的横纵坐标数组
void Translation(int* x, int* y, const int& dx, const int& dy)
{
for (int i = 0; i < 10; i++)
{
x[i] += dx;
y[i] += dy;
}
}
//比例变换函数,对于给定的比例系数与参考点,会修改参数中的横纵坐标数组
void ScaleChange(int* x, int* y, const double& sx, const double& sy, const int& x0, const int& y0)
{
for (int i = 0; i < 10; i++)
{
x[i] = sx * x[i] - (sx - 1) * x0;
y[i] = sy * y[i] - (sy - 1) * y0;
}
}
//旋转变换函数,对于给定的角度(逆时针为正)和旋转中心,修改参数中横纵坐标数组
void Rotation(int* x, int* y, const double& angle, const int& x0, const int& y0)
{
for (int i = 0; i < 10; i++)
{
int tempX = int(double(x[i] - x0) * cos(angle) - double(y[i] - y0) * sin(angle) + x0 + 0.5);
int tempY = int(double(x[i] - x0) * sin(angle) + double(y[i] - y0) * cos(angle) + y0 + 0.5);
x[i] = tempX;
y[i] = tempY;
}
}
//对称变换函数,对于给定的对称轴(用一般式表示),修改参数中的横纵坐标数组
void Symmetry(int* x, int* y, const double& A, const double& B, const double& C)
{
for (int i = 0; i < 10; i++)
{
int tempx, tempy;
tempx = x[i] - int(2 * A * (A * x[i] + B * y[i] + C) / (A * A + B * B) + 0.5);
tempy = y[i] - int(2 * B * (A * x[i] + B * y[i] + C) / (A * A + B * B) + 0.5);
x[i] = tempx;
y[i] = tempy;
}
}
void CMFCApplication2View::On32782()
{
// TODO: 在此添加命令处理程序代码
int x[10], y[10];
double angle;
//对国旗要严肃对待,各个五角星的位置和方向都需要正确才行!
//对大五角星的坐标进行初始化,其顶点坐标通过数学计算获得
for (int i = 1; i < 10; i += 2)
{
double angle = (i * 36 + 18) * pi / 180.0;
x[i] = 175 + 105 * cos(angle);
y[i] = 175 + 105 * sin(angle);
}
int r = 105 * ScaleFactor;
for (int i = 0; i < 10; i += 2)
{
double angle = (i * 36 + 18) * pi / 180;
x[i] = 175 + r * cos(angle);
y[i] = 175 + r * sin(angle);
}
initgraph(1050, 700);//国旗的长宽比例是3:2,这个得保持不变
setbkcolor(RGB(255, 0, 0));//由于直接使用RED作为参数感觉颜色太暗了,所以用RGB体系自己设置背景颜色
cleardevice();
setfillcolor(YELLOW);
setlinecolor(YELLOW);
FivePointsStar(x, y);//绘制大五角星
//通过一次平移、一次比例变换和一次旋转,作出第一颗小五角星
Translation(x, y, 175, -105);
ScaleChange(x, y, 0.3333, 0.3333, 350, 70);
angle = atan(0.4);
Rotation(x, y, angle, 350, 70);
FivePointsStar(x, y);
//通过一次平移、一次旋转和两次对称,作出第二颗小五角星
Translation(x, y, 70, 70);
angle = atan(-1 / 7.0);
Rotation(x, y, angle, 420, 140);
Symmetry(x, y, 1, 0, -420);
FivePointsStar(x, y);
Symmetry(x, y, 1, 0, -420);
//通过一次平移和一次旋转作出第三颗小五角星
Translation(x, y, 0, 105);
angle = atan(-2.0 / 7);
Rotation(x, y, angle, 420, 245);
FivePointsStar(x, y);
//通过一次平移和一次旋转作出第四颗小五角星
Translation(x, y, -70, 70);
angle = atan(-1.25);
Rotation(x, y, angle, 350, 315);
FivePointsStar(x, y);
//closegraph();
}
三、结果展示
二维变换:绘制大五角星,经过缩放、旋转、平移变换出四颗小五角星,最终画出国旗的图案。调整背景红色,绘制一个黄底黄边的大五角星,作为变换的原图形。
通过一次平移、一次比例变换和一次旋转,作第一颗小五角星。
通过一次平移、一次旋转和两次对称,作第二颗小五角星;通过一次平移和一次旋转作出第三颗小五角星;通过一次平移和一次旋转作出第四颗小五角星,最后绘制出国旗图案。便于审核,最终结果就不在此展示了。