plot3d_basic


```python

"""
このモジュールは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()


```