0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonを用いた回転行列(2)

Posted at

3D空間における座標変換例

任意の位置(x,y,z)の点をX軸・Y軸・Z軸まわりの
入力した任意の角度で回転するPythonプログラム

matplotlibパッケージを利用して3Dグラフを描画すると,
くるくるとマウスドラッグでビューを回転できるので,
空間上でどのように移動(回転)したのか分かりやすい.

下図は,画面のスクリーンキャブチャなので,ビューの回転はできませんが,
自身のPCでPythonプログラムを実行すると,プロットを回転させることができます.

スクリーンショット 2025-05-31 11.26.02.jpg

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()
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?