2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Plotly] 3D Surfaceを時間変化で動かす - 動く曲面で変化パターンを直感的に理解する

Last updated at Posted at 2025-11-25

はじめに

image.png

3D Surface(曲面)は静止画でも情報量が多いですが、時間軸で動かすと構造の変化が強烈にわかりやすくなります。

波の伝播、熱分布の変化、ポテンシャル場の時間発展、回転・膨張・変形など、動的なデータの理解にはアニメーションが最適です。

Plotlyでは、Surfaceをフレームごとに切り替えることで、動く3D Surfaceを簡単に作れます。

surface_animation.gif

この記事でできること

  • 3D Surfaceを時間変化させてアニメーション表示
  • フレームごとにZ値を更新して動きを表現
  • Colabでそのまま動くコード

基本:動かすためのSurfaceを定義する

ここでは「波が動くSurface」を例にします。Z = sin(sqrt(x^2 + y^2) - t) のように t を時間ステップにします。

import plotly.graph_objects as go
import numpy as np

# 格子の作成
x = np.linspace(-3, 3, 40)
y = np.linspace(-3, 3, 40)
X, Y = np.meshgrid(x, y)

def surface_at(t):
    """時刻tにおける曲面のZ値を計算"""
    R = np.sqrt(X**2 + Y**2)
    Z = np.sin(R - t)
    return Z

最初のSurface(t=0)を表示

まず、初期状態(t=0)のSurfaceを表示して、基本的な形状を確認します。

Z0 = surface_at(0)

fig = go.Figure(
    data=[go.Surface(x=X, y=Y, z=Z0, colorscale='Viridis', showscale=False)]
)

fig.update_layout(
    title="Dynamic 3D Surface (t = 0)",
    scene=dict(aspectmode='data')
)

fig.show()

image.png

フレームを生成してアニメーション化

時間ステップ t を 0 から 2π まで変化させます。Z値だけ更新すればOKです(Surfaceの x, y は固定のため)。

# フレームの生成
frames = []

for t in np.linspace(0, 2*np.pi, 40):
    Zt = surface_at(t)
    frames.append(
        go.Frame(
            data=[go.Surface(x=X, y=Y, z=Zt, colorscale='Viridis', showscale=False)],
            name=f"t={t:.2f}"
        )
    )

アニメーションボタンを設定

再生ボタンを追加して、ユーザーがアニメーションを制御できるようにします。

fig = go.Figure(
    data=[go.Surface(x=X, y=Y, z=surface_at(0), colorscale='Viridis', showscale=False)],
    frames=frames
)

fig.update_layout(
    title="Dynamic 3D Surface (Animation)",
    scene=dict(aspectmode='data'),
    updatemenus=[
        dict(
            type="buttons",
            buttons=[
                dict(
                    label="Play",
                    method="animate",
                    args=[
                        None,
                        dict(
                            frame=dict(duration=80, redraw=True),
                            transition=dict(duration=0),
                            fromcurrent=True
                        )
                    ]
                )
            ]
        )
    ]
)

fig.show()

image.png

結果

Playボタンを押すと、波が外へ広がりながら動く3D Surfaceが流れるように表示されます。

色を時間変化させる(応用)

色も t に応じて変化させると、流体シミュレーション風の表現になります。

frames = []

for t in np.linspace(0, 2*np.pi, 40):
    Zt = surface_at(t)
    frames.append(
        go.Frame(
            data=[go.Surface(
                x=X, y=Y, z=Zt,
                colorscale='Turbo',      # 動きのあるカラーマップ
                showscale=False,
                surfacecolor=Zt          # 色も随時更新
            )]
        )
    )

surfacecolor=Zt を更新すると、色が毎フレーム変わり、波・熱・密度の変化を表現できます。

カメラ回転と組み合わせる(さらに動的に)

Surfaceを動かしながらカメラも回転させると、プレゼンテーション向けの説得力のある可視化になります。

frames = []
angles = np.linspace(0, 2*np.pi, 40)

for t, theta in zip(np.linspace(0, 2*np.pi, 40), angles):
    Zt = surface_at(t)
    camera = dict(eye=dict(x=2*np.cos(theta), y=2*np.sin(theta), z=1.2))

    frames.append(go.Frame(
        data=[go.Surface(x=X, y=Y, z=Zt, colorscale='Viridis', showscale=False)],
        layout=dict(scene_camera=camera)
    ))

動くSurface × 回り込む視点 で、さらに説得力のある可視化が実現できます。

背景を調整して見やすくする

動画として見たときに背景を調整すると、より見やすくなります。

fig.update_layout(
    scene=dict(
        xaxis=dict(backgroundcolor='rgb(240,240,240)'),
        yaxis=dict(backgroundcolor='rgb(240,240,240)'),
        zaxis=dict(backgroundcolor='rgb(250,250,250)'),
        aspectmode='data'
    )
)

トラブルシューティング

Surfaceが点滅する

redraw=True を設定してください。

dict(frame=dict(duration=80, redraw=True))

Colabで動かない

点数を減らしてみてください(40×40 → 30×30)。

x = np.linspace(-3, 3, 30)
y = np.linspace(-3, 3, 30)

FPSを上げたい

duration を 80 → 40 に変更してください。

dict(frame=dict(duration=40, redraw=True))

色が飛ぶ

surfacecolor を更新しないか、colorscale を固定してください。

まとめ

svg02.png

Surfaceを時間発展させるだけで、動的現象が直感的に理解できます。

キーポイント

  • Z値をフレームごとに更新
  • 色も変えるとより動きが強調される
  • カメラ回転と組み合わせると可視化の説得力がアップ

主な用途

  • 波動方程式
  • 熱分布の時間変化
  • ポテンシャル・エネルギー場
  • 物理シミュレーション
  • 3D地形データの変遷

静止画では見えない変化は、アニメーションにしてみると新たな視点が得られるかもしれません。

実装例

import plotly.graph_objects as go
import numpy as np

# 格子の作成
x = np.linspace(-3, 3, 40)
y = np.linspace(-3, 3, 40)
X, Y = np.meshgrid(x, y)

def surface_at(t):
    """時刻tにおける曲面のZ値を計算"""
    R = np.sqrt(X**2 + Y**2)
    Z = np.sin(R - t)
    return Z

# フレームの生成
frames = []
for t in np.linspace(0, 2*np.pi, 40):
    Zt = surface_at(t)
    frames.append(
        go.Frame(
            data=[go.Surface(x=X, y=Y, z=Zt, colorscale='Viridis', showscale=False)],
            name=f"t={t:.2f}"
        )
    )

# Figureの作成
fig = go.Figure(
    data=[go.Surface(x=X, y=Y, z=surface_at(0), colorscale='Viridis', showscale=False)],
    frames=frames
)

# レイアウトの設定
fig.update_layout(
    title="Dynamic 3D Surface Animation",
    scene=dict(
        xaxis=dict(backgroundcolor='rgb(240,240,240)'),
        yaxis=dict(backgroundcolor='rgb(240,240,240)'),
        zaxis=dict(backgroundcolor='rgb(250,250,250)'),
        aspectmode='data'
    ),
    updatemenus=[
        dict(
            type="buttons",
            buttons=[
                dict(
                    label="Play",
                    method="animate",
                    args=[
                        None,
                        dict(
                            frame=dict(duration=80, redraw=True),
                            transition=dict(duration=0),
                            fromcurrent=True
                        )
                    ]
                )
            ]
        )
    ]
)

fig.show()

この記事が、動的な3D可視化の実装に役立てば幸いです。

参考情報

2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?