0
0

plot3d_basic

Last updated at Posted at 2024-01-31

"""
このモジュールは3Dビジュアライゼーションのためのクラスを提供します。

クラス:
    Visualizer3D - 3D空間にベクトル、軸、点、平面、立方体を描画するためのクラス。

使用例:

visualizer = Visualizer3D()
visualizer.add_axis([0, 0, 0])
visualizer.add_vector([0, 0, 0], [1, 1, 1], 'blue')
visualizer.show()
"""

__version__ = "1.1.0"


import plotly.graph_objects as go
import numpy as np
import random

class Visualizer3D:
    def __init__(self):
        self.fig = go.Figure()

        # 描写する空間を立方体に固定
        self.fig.update_layout(
                    scene=dict(
                        aspectmode='cube'
                    )
                )
        
    def config_set_xyz_range(self, x_range, y_range, z_range):
        # レイアウトの調整
        self.fig.update_layout(
            scene=dict(
                xaxis=dict(range=x_range),
                yaxis=dict(range=y_range),
                zaxis=dict(range=z_range)
            )
        )

    def config_set_window_size(self, width, height):
        self.fig.update_layout(
            width=width, # グラフの幅と高さの設定
            height=height,
            hoverlabel_font_size=20
        )

    def add_vector(self, start_point, end_point, color='black'):
        # コーンと線の色
        # color = 'blue'

        # ベクトルの方向を求める
        u = end_point[0] - start_point[0]
        v = end_point[1] - start_point[1]
        w = end_point[2] - start_point[2]

        # ベクトルの長さを計算
        length = np.linalg.norm([u, v, w])

        # コーンのサイズ調整(ベクトルの長さに応じてスケーリング)
        cone_size = length * 0.1
        cone_size=300
        if length <= 500:
            cone_size = cone_size * 0.5

        # コーンの追加
        self.fig.add_trace(go.Cone(
            x=[end_point[0]], y=[end_point[1]], z=[end_point[2]],  # コーンの位置(先端)
            u=[u * 0.1], v=[v * 0.1], w=[w * 0.1],  # コーンの向き
            showscale=False, 
            colorscale=[[0, color], [1, color]],  # コーンの色を設定
            # hoverinfo='skip',
            sizemode='absolute',  # 絶対サイズモードに設定
            sizeref=cone_size,  # サイズのスケールを調整
            anchor="tip"  # コーンをベクトルの先端に配置
        ))

        # ベクトル(線分)の追加
        self.fig.add_trace(go.Scatter3d(
            x=[start_point[0], end_point[0]], y=[start_point[1], end_point[1]], z=[start_point[2], end_point[2]],
            mode='lines',
            line=dict(color=color, width=2),  # 線の太さを調整し、色を設定
            hoverinfo='skip',
        ))

    def add_axis(self, origin, R=None, t=None):

        x_begin = np.array(origin)
        y_begin = np.array(origin)
        z_begin = np.array(origin)
        
        x_end = np.array([origin[0] + 1000, origin[1], origin[2]])
        y_end = np.array([origin[0], origin[1] + 1000, origin[2]])
        z_end = np.array([origin[0], origin[1], origin[2] + 1000])
        print(x_end)

        # 回転行列Rが与えられた場合の計算
        if R is not None:
            x_begin = np.dot(R, x_begin)
            y_begin = np.dot(R, y_begin)
            z_begin = np.dot(R, z_begin)

            x_end = np.dot(R, x_end)
            y_end = np.dot(R, y_end)
            z_end = np.dot(R, z_end)

        # 並進移動tが与えられた場合の計算
        if t is not None:
            x_begin = x_begin + t
            y_begin = y_begin + t
            z_begin = z_begin + t

            x_end = x_end + t
            y_end = y_end + t
            z_end = z_end + t
        
        print(x_end)
        self.add_vector(x_begin, x_end, 'red') # x-axis
        self.add_vector(y_begin, y_end, 'green') # y-axis
        self.add_vector(z_begin, z_end, 'blue') # z-axis


    def add_point(self, point, label=None, color='black'):
        
        if label is None:
            label = f"Point {len(self.fig.data) + 1}"
        
        self.fig.add_trace(go.Scatter3d(
            x=[point[0]], y=[point[1]], z=[point[2]],
            mode='markers',
            marker=dict(size=3, color=color, opacity=0.8),
            name=label
        ))

    def add_plain(self, vertices):
        # verticesは(n, 3)の形状のリストまたは配列
        vertices = np.array(vertices)
        
        x = vertices[:, 0]
        y = vertices[:, 1]
        z = vertices[:, 2]

        # 三角形のインデックスを生成
        n = len(vertices)
        if n < 3:
            raise ValueError("頂点の数が3未満です。")
        
        i = []
        j = []
        k = []
        
        for idx in range(1, n-1):
            i.append(0)
            j.append(idx)
            k.append(idx + 1)

        # メッシュを作成
        self.fig.add_trace(go.Mesh3d(
            x=x, y=y, z=z, 
            i=i, j=j, k=k, 
            opacity=0.5,
            color='rgba(255, 0, 0, 0.8)',  # RGB値を変更して色を濃くする
            # hoverinfo='skip'  # ホバー表示を無効化
        ))
        
    def add_circle(self, radius, z_position, origin=(0, 0, 0), color='rgb(255, 0, 0)', num_points=100):
        theta = np.linspace(0, 2*np.pi, num_points)
        x = radius * np.cos(theta) + origin[0]
        y = radius * np.sin(theta) + origin[1]
        z = np.full_like(x, z_position) + origin[2]

        self.fig.add_trace(go.Scatter3d(x=x, y=y, z=z, mode='lines', line=dict(color='black', width=3)))


    def add_cube(self, x0, y0, z0, length):
        # 立方体の各頂点の座標を定義
        x = [x0, x0+length, x0+length, x0, x0, x0+length, x0+length, x0]
        y = [y0, y0, y0+length, y0+length, y0, y0, y0+length, y0+length]
        z = [z0, z0, z0, z0, z0+length, z0+length, z0+length, z0+length]
        
        # メッシュを構成する頂点と面の組み合わせを指定(三角形の組み合わせで描写)
        i = [0, 0, 0, 0, 1, 1, 4, 4, 0 ,0, 3, 3]
        j = [1, 2, 1, 5, 2, 6, 5, 6, 3 ,4, 2, 6]
        k = [2, 3, 5, 4, 6, 5, 6, 7, 7 ,7, 6, 7]
        
        # Mesh3d オブジェクトを作成して描画
        self.fig.add_trace(go.Mesh3d(x=x, y=y, z=z, 
                                      i=i, j=j, k=k,
                                      opacity=0.5, color='blue'))
        

    def add_sphere(self, radius, origin=(0, 0, 0), color='rgb(0, 0, 255)', opacity=0.4,):
        r = radius
        phi, theta = np.mgrid[0:2*np.pi:100j, 0:np.pi:50j]
        x = r * np.sin(theta) * np.cos(phi) + origin[0]
        y = r * np.sin(theta) * np.sin(phi) + origin[1]
        z = r * np.cos(theta) + origin[2]
    
        # Define colors for the sphere
        colors = np.full(x.shape, color, dtype=str)
    
        self.fig.add_trace(go.Surface(
            x=x, y=y, z=z, 
            colorscale=[[0, color], [1, color]], 
            surfacecolor=colors, 
            opacity=opacity
        ))

    def show(self):
        self.fig.show()


if __name__ == "__main__":
    # デモンストレーション用のコード
    # 使用例
    visualizer = Visualizer3D()

    # 初期設定
    visualizer.config_set_xyz_range([-5000,5000],[-5000,5000],[-5000,5000])
    visualizer.config_set_window_size(1000, 1000)

    # 点を追加
    point01 = random.sample(range(3000), 3)
    point02 = random.sample(range(3000), 3)
    point03 = random.sample(range(3000), 3)
    # visualizer.add_point(point01)
    # visualizer.add_point(point02)
    # visualizer.add_point(point03)
    # visualizer.add_plane(point01, point02, point03)

    # 面を追加
    point10 = random.sample(range(3000), 3)
    point11 = random.sample(range(3000), 3)
    point12 = random.sample(range(3000), 3)
    # visualizer.add_plane(point10, point11, point12)

    # ベクトルを追加
    point1 = random.sample(range(3000), 3)
    point2 = random.sample(range(3000), 3)
    point3 = random.sample(range(3000), 3)
    point4 = random.sample(range(3000), 3)
    # visualizer.add_vector(point1, point2)
    # visualizer.add_vector(point3, point4)
    # visualizer.add_vector([0,0,0], [500, 0, 0])


    # 回転行列のテスト
    point_from = np.array([0, 0, 0])
    point_to = np.array([1000, 1000, 1000])
    visualizer.add_vector(point_from, point_to)
    print(point_to)

    # 回転行列
    # ロドリゲスの回転行列を計算
    axis = [0, 0, 1]  # 回転軸
    theta = 45  # 回転角
    rad = np.deg2rad(theta)
    R1 = vc.rodrigues_rotation_matrix(rad, axis)
    t1 = [0, 0, 2000]
    print("ロドリゲスの回転行列:")
    print(R1)
    print('-------------------------------')

    for i in range(3):
        point_to = np.dot(R1, point_to)
        visualizer.add_vector(point_from, point_to)
        print(point_to)


    # キューブを追加
    # vp.add_cube(1000, 2000, 4000, 400)

    # 座標軸を追加
    visualizer.add_axis([0, 0, 0])
    # 座標軸を追加
    visualizer.add_axis([0, 0, 0], R=R1, t=t1)

    # 描写
    visualizer.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