正等轴测图
简介
将形体放置成使它的三条坐标轴与轴测投影面具有相同的夹角(35°16′),然后向轴测投影面作正投影。用这种方法作出的轴测图称为正等轴测图。——百度百科
正等轴测图指将一个三维坐标系投影到平面时,3个坐标轴在平面上的投影相等的投影图。如图所示。
计算机图形学完整笔记(六):三维图形变换
35°16′的计算方法为,设该夹角为
θ
\theta
θ,则三条坐标轴的坐标分别为
z
=
(
0
,
c
o
s
θ
,
s
i
n
θ
)
x
=
(
−
c
o
s
θ
c
o
s
α
,
−
c
o
s
θ
s
i
n
α
,
s
i
n
θ
)
y
=
(
c
o
s
θ
c
o
s
α
,
−
c
o
s
θ
s
i
n
α
,
s
i
n
θ
)
z=(0,cos\theta,sin\theta)\\ x=(-cos\theta cos\alpha,-cos\theta sin\alpha,sin\theta)\\ y=(cos\theta cos\alpha,-cos\theta sin\alpha,sin\theta)
z=(0,cosθ,sinθ)x=(−cosθcosα,−cosθsinα,sinθ)y=(cosθcosα,−cosθsinα,sinθ)
其中
α
=
30
°
\alpha=30°
α=30°为坐标轴投影与投影平面x轴的夹角。由
x
⊥
z
x⊥z
x⊥z可解得
c
o
s
θ
=
6
3
cos\theta=\frac{\sqrt{6}}{3}
cosθ=36。这个值很重要,后面还会用到。我也不知道这个角叫什么,暂时称作正等轴测角。
使用matplotlib绘制正等轴测图
画这种坐标轴就是按照前文所述方法将该坐标轴投影到平面。三条坐标轴在平面上的坐标分别为
z
=
(
0
,
6
3
,
3
3
)
x
=
(
2
2
,
−
6
6
,
3
3
)
y
=
(
2
2
,
6
6
,
−
3
3
)
z=(0,\frac{\sqrt{6}}{3},\frac{\sqrt{3}}{3})\\ x=(\frac{\sqrt{2}}{2},-\frac{\sqrt{6}}{6},\frac{\sqrt{3}}{3})\\ y=(\frac{\sqrt{2}}{2},\frac{\sqrt{6}}{6},-\frac{\sqrt{3}}{3})
z=(0,36,33)x=(22,−66,33)y=(22,66,−33)
旋转矩阵
[
1
2
1
2
0
−
1
6
1
6
2
3
1
3
−
1
3
1
3
]
\left[\begin{matrix} \frac{1}{\sqrt{2}} & \frac{1}{\sqrt{2}} & 0\\ -\frac{1}{\sqrt{6}} & \frac{1}{\sqrt{6}} & \frac{\sqrt{2}}{\sqrt{3}} \\ \frac{1}{\sqrt{3}} & -\frac{1}{\sqrt{3}} & \frac{1}{\sqrt{3}} \end{matrix}\right]
⎣⎢⎡21−61312161−3103231⎦⎥⎤
在该坐标系下画一个长(
x
x
x)1,宽(
y
y
y)2,高(
z
z
z)3的长方体:
正等轴测图代码
import matplotlib.pyplot as plt
import numpy as np
def isometric_map(coordinate):
R = np.array([[1/np.sqrt(2), 1/np.sqrt(2), 0],
[-1/np.sqrt(6), 1/np.sqrt(6), np.sqrt(2)/np.sqrt(3)],
[1/np.sqrt(3), -1/np.sqrt(3), 1/np.sqrt(3)]])
cord = np.matmul(R, coordinate)
return cord[:2]
def isometric_draw_line(a, b):
p = isometric_map(a)
q = isometric_map(b)
ax.plot([p[0], q[0]], [p[1], q[1]], color='black', linewidth=1)
print('isometric.')
fig, ax = plt.subplots(figsize=(4, 4))
ax.xaxis.set_major_locator(plt.MultipleLocator(1))
ax.yaxis.set_major_locator(plt.MultipleLocator(1))
ax.set_xlim(-1, 5)
ax.set_ylim(-3, 5)
isometric_draw_line(np.array([0, 0, 0]), np.array([5, 0, 0]))
isometric_draw_line(np.array([0, 0, 0]), np.array([0, 5, 0]))
isometric_draw_line(np.array([0, 0, 0]), np.array([0, 0, 5]))
isometric_draw_line(np.array([1, 0, 0]), np.array([1, 2, 0]))
isometric_draw_line(np.array([1, 0, 0]), np.array([1, 0, 3]))
isometric_draw_line(np.array([1, 2, 0]), np.array([1, 2, 3]))
isometric_draw_line(np.array([1, 2, 3]), np.array([0, 2, 3]))
isometric_draw_line(np.array([1, 0, 3]), np.array([0, 0, 3]))
isometric_draw_line(np.array([0, 0, 3]), np.array([0, 2, 3]))
isometric_draw_line(np.array([1, 0, 3]), np.array([1, 2, 3]))
plt.show()
print('finished.')
任意角度的3D图
此时要用到欧拉角。matplotlib画3D图在显示时有两个参数:
ax.view_init(azim=-57, elev=14)
azim
(azimuth)表示方位角,elev
(elevation)表示仰角。为便于理解,欧拉角旋转顺序是先旋转方位角,再旋转仰角,旋转轴均为固定的世界坐标系。沿x轴负方向看进去如图所示
此时待旋转坐标系与世界坐标系重合。将待旋转坐标系先沿世界坐标系z轴旋转
−
π
4
-\frac{\pi}{4}
−4π,再沿世界坐标系y轴旋转
arccos
6
3
\arccos\frac{\sqrt{6}}{3}
arccos36,从世界坐标系x轴负方向看进去的待旋转坐标系就长下面这样了。
代码
import matplotlib.pyplot as plt
import numpy as np
def rotation(vec, a, e):
R = np.array([[np.cos(a), -np.sin(a), 0],
[np.sin(a), np.cos(a), 0],
[0, 0, 1]])
vec = np.matmul(R, vec)
R = np.array([[np.cos(e), 0, np.sin(e)],
[0, 1, 0],
[-np.sin(e), 0, np.cos(e)]])
vec = np.matmul(R, vec)
return vec
consttheta = np.arccos(np.sqrt(6)/3) # 正等轴测角
azimuth = -np.pi/4 # 方位角
elevation = consttheta # 仰角
fig, ax = plt.subplots()
linex = rotation(np.array([1, 0, 0]), azimuth, elevation)
liney = rotation(np.array([0, 1, 0]), azimuth, elevation)
linez = rotation(np.array([0, 0, 1]), azimuth, elevation)
# ax.plot([0, linex[1]], [0, linex[2]], color='black', linewidth=1)
# ax.plot([0, liney[1]], [0, liney[2]], color='black', linewidth=1)
# ax.plot([0, linez[1]], [0, linez[2]], color='black', linewidth=1)
ax.arrow(0, 0, linex[1], linex[2], color='black', head_width=0.05, head_length=0.05, linewidth=0.5)
ax.arrow(0, 0, liney[1], liney[2], color='black', head_width=0.05, head_length=0.05, linewidth=0.5)
ax.arrow(0, 0, linez[1], linez[2], color='black', head_width=0.05, head_length=0.05, linewidth=0.5)
plt.show()
画图实例
有了这个方法以后写几个函数就可以随意画3D图了。下面简单的画个箭头。
import matplotlib.pyplot as plt
import numpy as np
def rotation(vec, azim=-np.pi / 4, elev=np.arccos(np.sqrt(6) / 3)):
R = np.array([[np.cos(azim), -np.sin(azim), 0],
[np.sin(azim), np.cos(azim), 0],
[0, 0, 1]])
vec = np.matmul(R, vec)
R = np.array([[np.cos(elev), 0, np.sin(elev)],
[0, 1, 0],
[-np.sin(elev), 0, np.cos(elev)]])
vec = np.matmul(R, vec)
return vec
def isometric_draw_text(p, text, azim=-np.pi / 4, elev=np.arccos(np.sqrt(6) / 3)):
p = rotation(p, azim, elev)
ax.text(p[1], p[2], text, fontsize=20)
def isometric_draw_line(a, b, azim=-np.pi / 4, elev=np.arccos(np.sqrt(6) / 3)):
p = rotation(a, azim, elev)
q = rotation(b, azim, elev)
ax.plot([p[1], q[1]], [p[2], q[2]], color='black', linewidth=1)
fig, ax = plt.subplots()
plt.axis('off')
plt.xticks([])
plt.yticks([])
azimuth = -0.6
# 画坐标系
linex = rotation(np.array([1, 0, 0]), azim=azimuth)
liney = rotation(np.array([0, 1, 0]), azim=azimuth)
linez = rotation(np.array([0, 0, 1]), azim=azimuth)
ax.arrow(0, 0, linex[1], linex[2], color='black', head_width=0.05, head_length=0.05, linewidth=0.5)
ax.arrow(0, 0, liney[1], liney[2], color='black', head_width=0.05, head_length=0.05, linewidth=0.5)
ax.arrow(0, 0, linez[1], linez[2], color='black', head_width=0.05, head_length=0.05, linewidth=0.5)
isometric_draw_text(np.array([0, 0, 0]), 'O', azim=azimuth)
isometric_draw_text(np.array([1.1, 0, 0]), 'x', azim=azimuth)
isometric_draw_text(np.array([0, 1.1, 0]), 'y', azim=azimuth)
isometric_draw_text(np.array([0, 0, 1.1]), 'z', azim=azimuth)
# 画箭头
arrow_figure = np.array([[0, 1, 0.5],
[-3, 1, 0.5],
[-3, 2, 0.5],
[-5, 0, 0.5],
[-3, -2, 0.5],
[-3, -1, 0.5],
[0, -1, 0.5],
[0, 1, 0.5],
[0, 1, -0.5],
[-3, 1, -0.5],
[-3, 2, -0.5],
[-5, 0, -0.5],
[-3, -2, -0.5],
[-3, -1, -0.5],
[0, -1, -0.5],
[0, 1, -0.5]
])
bias = np.array([-5, 0, 0])
for n in range(len(arrow_figure)-1):
isometric_draw_line(arrow_figure[n] + bias, arrow_figure[n+1] + bias, azim=azimuth)
isometric_draw_line(np.array([-3, 1, 0.5]) + bias, np.array([-3, 1, -0.5]) + bias, azim=azimuth)
isometric_draw_line(np.array([-3, 2, 0.5]) + bias, np.array([-3, 2, -0.5]) + bias, azim=azimuth)
isometric_draw_line(np.array([-5, 0, 0.5]) + bias, np.array([-5, 0, -0.5]) + bias, azim=azimuth)
isometric_draw_line(np.array([-3, -2, 0.5]) + bias, np.array([-3, -2, -0.5]) + bias, azim=azimuth)
isometric_draw_line(np.array([-3, -1, 0.5]) + bias, np.array([-3, -1, -0.5]) + bias, azim=azimuth)
isometric_draw_line(np.array([0, -1, 0.5]) + bias, np.array([0, -1, -0.5]) + bias, azim=azimuth)
# mngr = plt.get_current_fig_manager()
# mngr.window.wm_geometry("+1920+0")
plt.show()