三维图形几何变换是二维图形几何变换的扩展。在二维空间中绕一个中心点旋转,实际上就是绕原点旋转再平移,那么在三维空间中我们可以采用类似的思路,任意一种旋转可以分解为旋转+平移两步,其中一次空间的旋转可以分解为X、Y、Z三个方向上的旋转。
对X、Y、Z轴旋转变换
旋转的正方向:右手拇指指向转轴正向,其余四指缠绕方向便是θ角正向。
三维变换矩阵的功能分块
有时候我们用规范化齐次坐标(x,y,z,1)表示三维点,变换矩阵用4×4阶矩阵表示,即:
1)左上角的3X3子块实现比例、旋转、对称、错切等基本变换
2)左下角的1X3子块实现平移变换
3)右上角的3X1子块实现透视变换
4)右下角的1X1子块实现全比例变换
绕X轴旋转θ角的旋转矩阵为
绕Y轴旋转θ角的旋转矩阵为
绕Z轴旋转θ角的旋转矩阵为
代码实现
构造一个正六面体
使其绕X轴旋转30度
# -*- coding: utf-8 -*-
import numpy as np
import math
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(16, 12))
ax = fig.gca(projection='3d')
ax.set_aspect('equal')
#========================================================
# 三维旋转
#========================================================
def CW_rotate_X(angle, x, y, z):
'''
绕X轴正向旋转坐标计算
INPUT --> 旋转角度, 原坐标
'''
angle = math.radians(angle) # 以弧度作为参数
x = np.array(x)
y = np.array(y)
z = np.array(z)
new_x = x
new_y = y*math.cos(angle) + z*math.sin(angle)
new_z = -(y*math.sin(angle)) + z*math.cos(angle)
return new_x, new_y, new_z
def CW_rotate_Y(angle, x, y, z):
'''
绕Y轴正向旋转坐标计算
INPUT --> 旋转角度, 原坐标
'''
angle = math.radians(angle) # 以弧度作为参数
x = np.array(x)
y = np.array(y)
z = np.array(z)
new_x = x*math.cos(angle) - z*math.sin(angle)
new_y = y
new_z = x*math.sin(angle) + z*math.cos(angle)
return new_x, new_y, new_z
def CW_rotate_Z(angle, x, y, z):
'''
绕Z轴正向旋转坐标计算
INPUT --> 旋转角度, 原坐标
'''
angle = math.radians(angle) # 以弧度作为参数
x = np.array(x)
y = np.array(y)
z = np.array(z)
new_x = x*math.cos(angle) + y*math.sin(angle)
new_y = -(x*math.sin(angle)) + y*math.cos(angle)
new_z = z
return new_x, new_y, new_z
#========================================================
# 三维结构绘制
#========================================================
def plot_3D(verts, poly3d):
'''
绘制3D图形
INPUT --> 顶点坐标, 三维结构
'''
from mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3DCollection
# 绘制顶点
x, y, z = zip(*verts)
ax.scatter(x, y, z)
# 绘制多边形面
ax.add_collection3d(Poly3DCollection(poly3d, facecolors='w', linewidths=1, alpha=0.3))
# 绘制多边形的边
ax.add_collection3d(Line3DCollection(poly3d, colors='k', linewidths=0.5, linestyles=':'))
def show_3D():
# 设置图形坐标范围
ax.set_xlabel('X')
ax.set_xlim3d(-0.5, 1.5)
ax.set_ylabel('Y')
ax.set_ylim3d(-0.5, 1.5)
ax.set_zlabel('Z')
ax.set_zlim3d(-0.5, 1.5)
plt.show()
#========================================================
# 主程序
#========================================================
if __name__ == '__main__':
# 六面体顶点和面
verts = [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1), (1, 0, 1)]
faces = [[0, 1, 2, 3], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [0, 3, 7, 4]]
# 旋转后六面体顶点和面
verts2 = [CW_rotate_X(30, vert[0], vert[1], vert[2]) for vert in verts]
# verts2 = [CW_rotate_Y(30, vert[0], vert[1], vert[2]) for vert in verts2]
# 获得每个面的顶点
poly3d = [[verts[vert_id] for vert_id in face] for face in faces]
poly3d2 = [[verts2[vert_id] for vert_id in face] for face in faces]
# plot_3D(verts, poly3d)
plot_3D(verts2, poly3d2)
show_3D()