3D空間における座標変換例
任意の位置(x,y,z)の点をX軸・Y軸・Z軸まわりの
入力した任意の角度で回転するPythonプログラム
matplotlibパッケージを利用して3Dグラフを描画すると,
くるくるとマウスドラッグでビューを回転できるので,
空間上でどのように移動(回転)したのか分かりやすい.
下図は,画面のスクリーンキャブチャなので,ビューの回転はできませんが,
自身のPCでPythonプログラムを実行すると,プロットを回転させることができます.
rot_matrix_input.py
import numpy as np
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import simpledialog, messagebox
def rotate_x(point, angle_deg):
"""X軸周りの回転行列を適用"""
angle_rad = np.radians(angle_deg)
rotation_matrix = np.array([
[1, 0, 0],
[0, np.cos(angle_rad), -np.sin(angle_rad)],
[0, np.sin(angle_rad), np.cos(angle_rad)]
])
return np.dot(rotation_matrix, point)
def rotate_y(point, angle_deg):
"""Y軸周りの回転行列を適用"""
angle_rad = np.radians(angle_deg)
rotation_matrix = np.array([
[np.cos(angle_rad), 0, np.sin(angle_rad)],
[0, 1, 0],
[-np.sin(angle_rad), 0, np.cos(angle_rad)]
])
return np.dot(rotation_matrix, point)
def rotate_z(point, angle_deg):
"""Z軸周りの回転行列を適用"""
angle_rad = np.radians(angle_deg)
rotation_matrix = np.array([
[np.cos(angle_rad), -np.sin(angle_rad), 0],
[np.sin(angle_rad), np.cos(angle_rad), 0],
[0, 0, 1]
])
return np.dot(rotation_matrix, point)
def get_user_inputs():
"""Tkinterのダイアログボックスを使ってユーザーから座標値と各軸の回転角度を取得する関数"""
root = tk.Tk()
root.withdraw() # メインウィンドウを表示しない
# 座標の入力
while True:
try:
x_str = simpledialog.askstring("入力", "元の点の X 座標を入力してください:")
if x_str is None: return None, None, None, None
x = float(x_str)
y_str = simpledialog.askstring("入力", "元の点の Y 座標を入力してください:")
if y_str is None: return None, None, None, None
y = float(y_str)
z_str = simpledialog.askstring("入力", "元の点の Z 座標を入力してください:")
if z_str is None: return None, None, None, None
z = float(z_str)
original_point = np.array([x, y, z])
break
except ValueError:
messagebox.showerror("エラー", "無効な入力です。数値を入力してください。")
except TypeError:
messagebox.showerror("エラー", "入力がキャンセルされました。")
return None, None, None, None
# 各軸の回転角度の入力
angles = {}
axes = ['X', 'Y', 'Z']
for axis in axes:
while True:
try:
angle_str = simpledialog.askstring("入力", f"{axis}軸周りの回転角度(度数法)を入力してください:")
if angle_str is None: return None, None, None, None
angles[axis] = float(angle_str)
break
except ValueError:
messagebox.showerror("エラー", "無効な入力です。数値を入力してください。")
except TypeError:
messagebox.showerror("エラー", "入力がキャンセルされました。")
return None, None, None, None
return original_point, angles['X'], angles['Y'], angles['Z']
## メイン処理
if __name__ == "__main__":
original_point, angle_x, angle_y, angle_z = get_user_inputs()
if original_point is None or angle_x is None or angle_y is None or angle_z is None:
print("ユーザーによって入力がキャンセルされました。プログラムを終了します。")
else:
# 順番に回転を適用
# X軸周りの回転
current_point = rotate_x(original_point, angle_x)
# Y軸周りの回転
current_point = rotate_y(current_point, angle_y)
# Z軸周りの回転
rotated_point = rotate_z(current_point, angle_z)
print(f"元の点: {original_point}")
print(f"X軸周り {angle_x}度, Y軸周り {angle_y}度, Z軸周り {angle_z}度 回転後の点: {rotated_point}")
# --- 3Dプロットの生成 ---
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# 軸の範囲を動的に設定
min_coord = np.min([original_point, rotated_point], axis=0) - 0.5
max_coord = np.max([original_point, rotated_point], axis=0) + 0.5
ax.set_xlim([min_coord[0], max_coord[0]])
ax.set_ylim([min_coord[1], max_coord[1]])
ax.set_zlim([min_coord[2], max_coord[2]])
# 元の点をプロット
ax.plot(
[original_point[0]], [original_point[1]], [original_point[2]],
'ro', markersize=8, label=f'Original Point ({original_point[0]:.2f}, {original_point[1]:.2f}, {original_point[2]:.2f})'
)
# 回転後の点をプロット
ax.plot(
[rotated_point[0]], [rotated_point[1]], [rotated_point[2]],
'bo', markersize=8, label=f'Rotated Point: ({rotated_point[0]:.2f}, {rotated_point[1]:.2f}, {rotated_point[2]:.2f})'
)
# 元の点から回転後の点への線分をプロット
ax.plot(
[original_point[0], rotated_point[0]],
[original_point[1], rotated_point[1]],
[original_point[2], rotated_point[2]],
'g--', linewidth=1.5, label='Path of Rotation'
)
ax.legend()
plt.title(f'3D Rotation of Point ({original_point[0]:.2f}, {original_point[1]:.2f}, {original_point[2]:.2f})\n'
f'X-axis: {angle_x} deg, Y-axis: {angle_y} deg, Z-axis: {angle_z} deg')
plt.show()