这是计算机图形学基础的一个课后题,其实完全可以直接用OpenGL提供的几何变换的函数轻松的实现,但是毕竟学就要学明白,仔细写这个题一是为了回顾一下各种变换对应的变换矩阵和数学规律,二是加深一下对交互的使用,三是去体会OpenGL绘图时交换不同变换的顺序时会有什么变化(体会为什么有时候不能交换变换顺序)。

首先声明一个十分鸡肋的概念:什么是二维仿射变换?
仿射变换是一种二维坐标到二维坐标之间的线性变换。就是下图这个样子:

显然,平移、比例(缩放)、旋转、对称和错切仅是对x、y线性变换,所以是仿射变换。

下面就来逐个实现这些二维仿射变换:
随便的一个效果:

0.说明:

原理都是对一个点的变换,程序中为了更加直观,都是对一个正方形的变换GLfloat square[4][2];//定义一个正方形的顶点坐标数组。此外,因为直接在c++程序中调用矩阵的运算很难实现,所以在实现时并未与原理中的矩阵相符,而是用坐标变换表示(其实结果是一样的)。
以后我还会写shader(GSLS)的程序去实现各种二维仿射变换,在shader中我们就可以直接用其中的矩阵变量类型进行矩阵运算并很方便的处理所有顶点(GPU的强大)。
重点不在于编程实现,而在于线性代数在这里的应用以及那些小的细节问题(比如交互时合理的取值)。

1.平移:

(1)数学原理:


(2)实现:

//平移(用键盘的上下左右键控制)
void translate(GLfloat Tx,GLfloat Ty)
{for(int i=0;i<4;i++){square[i][0]+=Tx;square[i][1]+=Ty;}
}

2.比例(缩放):

(1)原理:


(2)实现:

//比例/缩放(用键盘的a/A键控制x的缩放,d/D键控制y的缩放)
void Scale(GLfloat Sx,GLfloat Sy)
{for(int i=0;i<4;i++){square[i][0]*=Sx;square[i][1]*=Sy;}
}

3.旋转(从这里开始就有些小插曲了):

(1)原理:




(2)实现:

首先来看一种错误的方式:

//旋转(用键盘的r/R键控制逆时针和正时针旋转)
void Rotate(GLfloat degree)
{for(int i=0;i<4;i++){int tmpX=square[i][0],tmpY=square[i][1];square[i][0]=tmpX*cos(degree)-tmpY*sin(degree);square[i][1]=tmpX*sin(degree)+tmpY*cos(degree);}
}

这样做你会发现一直按r/R这个正方形会随着旋转不断缩小???为什么???仔细想了想也无从下手,原理和公式都是对的,那为什么会不断变小呢?其实这时如果考虑实际情况,在实际系统中cos、sin的计算肯定是有微小的误差的,所以旋转次数多了,误差就会显现出来!!! 怎么解决?当误差累积过大时重新计算物体的准确位置。

4.对称:

(1)原理:


(2)实现:

//对称(x键关于x轴对称,y键关于y轴对称)
void Symmetry(int flag)
{for(int i=0;i<4;i++){if(flag){//等于1关于x轴对称square[i][1]*=-1;}else{//等于0关于y轴对称square[i][0]*=-1;}}
}

5.错切:


//错切(z/Z键向x轴负/正方向错切,c/C键向y轴负/正方向错切)
void Shear(GLfloat b,GLfloat c)
{for(int i=0;i<4;i++){int tmpX=square[i][0],tmpY=square[i][1];square[i][0]=tmpX+tmpY*c;square[i][1]=tmpX*b+tmpY;}
}

最后总的代码:

#include <GL\freeglut.h>
#include <math.h>
GLfloat square[4][2];//定义一个正方形的顶点坐标数组
//平移(用键盘的上下左右键控制)
void Translate(GLfloat Tx,GLfloat Ty)
{for(int i=0;i<4;i++){square[i][0]+=Tx;square[i][1]+=Ty;}
}
//比例/缩放(用键盘的a/A键控制x的缩放,d/D键控制y的缩放)
void Scale(GLfloat Sx,GLfloat Sy)
{for(int i=0;i<4;i++){square[i][0]*=Sx;square[i][1]*=Sy;}
}
//旋转(用键盘的r/R键控制逆时针和正时针旋转)
void Rotate(GLfloat degree)
{for(int i=0;i<4;i++){int tmpX=square[i][0],tmpY=square[i][1];square[i][0]=tmpX*cos(degree)-tmpY*sin(degree);square[i][1]=tmpX*sin(degree)+tmpY*cos(degree);}
}
//对称(x键关于x轴对称,y键关于y轴对称)
void Symmetry(int flag)
{for(int i=0;i<4;i++){if(flag){//等于1关于x轴对称square[i][1]*=-1;}else{//等于0关于y轴对称square[i][0]*=-1;}}
}
//错切(z/Z键向x轴负/正方向错切,c/C键向y轴负/正方向错切)
void Shear(GLfloat b,GLfloat c)
{for(int i=0;i<4;i++){int tmpX=square[i][0],tmpY=square[i][1];square[i][0]=tmpX+tmpY*c;square[i][1]=tmpX*b+tmpY;}
}
//用户自定义初始化
void myinit()
{//初始化正方形顶点数组square[0][0]=-100.0;square[0][1]=100.0;square[1][0]=100.0;square[1][1]=100.0;square[2][0]=100.0;square[2][1]=-100.0;square[3][0]=-100.0;square[3][1]=-100.0;}
//窗口大小变化的回调函数
void reshape(GLsizei w,GLsizei h)
{glViewport(0,0,w,h);//设置视口大小比例始终与窗口一致glMatrixMode(GL_PROJECTION);glLoadIdentity();gluOrtho2D(-400,400,-400,400);
}void display()
{//设置背景颜色为白色并清除颜色缓冲glClearColor(1.0,1.0,1.0,1.0);glClear(GL_COLOR_BUFFER_BIT);//画坐标系glColor3f(0.0,0.0,1.0);glBegin(GL_LINES);//画x轴glVertex2f(400.0,0.0);glVertex2f(-400.0,0.0);//画y轴glVertex2f(0.0,400.0);glVertex2f(0.0,-400.0);glEnd();glColor3f(0.0,1.0,0.0);glPointSize(4.0);glBegin(GL_POINTS);//画x、y轴上的坐标for(GLfloat i=-400.0;i<=400.0;i+=100.0){glVertex2f(i,0.0);glVertex2f(0.0,i);}glEnd();//画正方形glColor4f(1.0,0.0,0.0,0.1);glLineWidth(2.0);glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);glBegin(GL_POLYGON);glVertex2f(square[0][0],square[0][1]);glVertex2f(square[1][0],square[1][1]);glVertex2f(square[2][0],square[2][1]);glVertex2f(square[3][0],square[3][1]);glEnd();//交换前后缓冲区glutSwapBuffers();
}
//键盘上特殊按键的回调函数
void processSpecialKeys(int key, int x, int y)
{switch(key) {case GLUT_KEY_LEFT:Translate(-100.0,0);break;case GLUT_KEY_RIGHT:Translate(100.0,0.0);break;case GLUT_KEY_UP:Translate(0.0,100.0);break;case GLUT_KEY_DOWN:Translate(0.0,-100.0);break;}glutPostRedisplay();
}
//键盘普通按键的回调函数
void processNormalKeys(unsigned char key,int x,int y)
{switch(key) {case 97:  //"a"Scale(0.5,1.0);break;case 65:    //"A"Scale(2.0,1.0);break;case 100:   //"d"Scale(1.0,0.5);break;case 68:    //"D"Scale(1.0,2.0);break;case 114:   //"r"Rotate(15.0);break;case 82:  //"R"Rotate(-15.0);break;case 120:    //"x"Symmetry(1);break;case 121:  //"y"Symmetry(0);break;case 122:  //"z"Shear(0.0,-1.1);break;case 90:   //"Z"Shear(0.0,1.1);break;case 99:    //"c"Shear(-1.1,0.0);break;case 67:   //"C"Shear(1.1,0.0);break;case 27:    //"esc"exit(0);}glutPostRedisplay();
}
//主函数
int main(int argc, char* argv[])
{glutInit(&argc, argv);//初始化OPENGL显示方式glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA);//设定OPENGL窗口位置和大小glutInitWindowSize (400, 400); glutInitWindowPosition (100, 100);//打开窗口glutCreateWindow ("多边形的平移、比例(缩放)、旋转、对称和错切等二维仿射变换");//调用初始化函数myinit();//设定窗口大小变化的回调函数glutReshapeFunc(reshape);//设定键盘控制的回调函数glutSpecialFunc(processSpecialKeys);glutKeyboardFunc(processNormalKeys);glutDisplayFunc(display); glutMainLoop();return 0;
}

编程实现多边形的平移、比例(缩放)、旋转、对称和错切等二维仿射变换(大有门道)相关推荐

  1. 多边形轮廓等比例缩放

    多边形(轮廓点)等距离外扩 1.需要安装一个python包 安装 pyclipper python 的话,直接pip install pyclipper 地址:https://pypi.org/pro ...

  2. 旋转——绕原点二维旋转,绕任意点的二维旋转,三维基本旋转,绕任意轴的三维旋转

    1 简介 计算机图形学中的应用非常广泛的变换是一种称为仿射变换的特殊变换,在仿射变换中的基本变换包括平移.旋转.缩放.剪切这几种.本文以及接下来的几篇文章重点介绍一下关于旋转的变换,包括二维旋转变换. ...

  3. 计算机图形学 学习笔记(七):二维图形变换:平移,比例,旋转,坐标变换等

    接上文 计算机图形学 学习笔记(六):消隐算法:Z-buffer,区间扫描线,Warnock,光栅图形学小结 在图形学中,有两大基本工具:向量分析,图形变换.本文将重点讲解向量和二维图形的变换. 5. ...

  4. Android单点触控技术,对图片进行平移,缩放,旋转操作

    转载请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/42833893),请尊重他人的辛勤劳动成果,谢谢! 相信大家 ...

  5. 利用Turbo C进行平面二维图形的平移,旋转,缩放,对称

    [实验内容] 以矩阵运算为数学基础,采用旋转.平移.缩放.对称等基本几何变换,对一简单的二维图形做变换. [实验步骤] 1. 编写二维图形基本变换(包括旋转.平移.缩放.对称)的通用子程序: 2. 以 ...

  6. 几何变换详解:平移、缩放、旋转

    声明:此文章主要参考两位大佬的博客,参考文章在文末. 文章目录 平移变换 缩放变换 旋转变换 绕X轴旋转 绕Y轴旋转 绕Z轴旋转 绕坐标轴旋转的矩阵推导 逆矩阵 代码实战 1.平移 2.缩放 3.旋转 ...

  7. Android 让图片等比例缩放的三种方法

    方法一:客户端等比例 前提条件:服务器端需要返回原始图片的"宽和高"或者"宽高缩放比例",客户端要显示的图片的宽或者高只要其一是固定的(例如:高度为200,宽度 ...

  8. ArcBall二维控制三维旋转

    ArcBall二维控制三维旋转 由于目前大多的显示器是二维的,要控制三维物体的旋转就显得不那么直接了.ArcBall是一种将二维鼠标位置的变化映射到三维物体旋转的方法,让用户通过很直观的方法控制物体旋 ...

  9. 【计算机图形学】c++ OpenGL 二维变换(包括多边形绘制、平移、旋转及缩放)

    运行结果演示 源代码 // 二维变换.cpp : 定义控制台应用程序的入口点. //#include "stdafx.h" #include<GL/glut.h> #i ...

最新文章

  1. 清华芯片研究再获顶会MICRO加持:软件定义芯片团队出品,最佳论文提名后又一突破...
  2. Android自定义View:ViewGroup(三)
  3. php如何读写excel
  4. 便携式不锈钢管道焊接机器人_核电站双相不锈钢管道的焊接工艺及焊接接头性能探讨...
  5. 【随笔】深度学习的数据增强还分在线和离线?
  6. C#LeetCode刷题之#136-只出现一次的数字(Single Number)
  7. 零基础Python学习方法,Python入门必读
  8. 文件共享存储主备实时热备实现方案
  9. 克劳斯比的零缺陷——《可以量化的管理学》
  10. lintcode:Search Insert Position 搜索插入位置
  11. Drools规则引擎介绍及实践
  12. 谷歌浏览器安装与扩展程序
  13. arduinouno的地是相连的吗_如何连接地线是最标准的,能起到保障的作用吗?
  14. GitHub 漫游指南
  15. IntelliJ IDEA 自定义注释作者名字
  16. golangci-lint timeout
  17. 一个留美女博士的七年----分享给所有还相信梦想的朋友(zz 喜欢~~)
  18. 你不得不知的网络编程三剑客
  19. MacBookPro M1芯片安装brew
  20. UE4开发PSVR游戏流程

热门文章

  1. 关于Mysql使用时出现部分错误的总结
  2. 南京16家需要转诊才能享受统筹的三甲医院
  3. 什么是模糊神经网络结构,模糊神经网络应用实例
  4. 使用python语言创建空列表score_使用NLP创建摘要
  5. pet shop 4.0架构信息-转
  6. Command-click是什么意思?
  7. 由旧版本环信对接之后没有声音,探索AVAudioSession的Category和Option
  8. mysql中flush tables和flush tables with read lock详解
  9. TensorFlow练手项目三:使用VGG19迁移学习实现图像风格迁移
  10. 城乡规划现状数据移动调研系统开发