import plotly.graph_objects as go
from astropy import units as u
# Subsample orbit data and convert units
vorbits = orbits[::5]
x, y, z = [getattr(vorbits, coord).to(u.kpc).value for coord in ['x', 'y', 'z']]
time = vorbits.t.to(u.Myr).value
# Fixed camera view
camera = dict(eye=dict(x=1.2, y=1.2, z=1.2))
# Create animation frames
frames = [
go.Frame(
data=[
go.Scatter3d(
x=x[i], y=y[i], z=z[i], # Current position
mode='markers',
marker=dict(color='blue', size=4,opacity=0.25)
)
],
name=f"Frame {i}"
)
for i in range(len(time))
]
# Define the figure
fig = go.Figure(
data=[
go.Scatter3d(x=x[0], y=y[0], z=z[0], mode='markers', marker=dict(color='blue', size=7))
],
layout=dict(
title="Interactive Orbit Visualization",
scene=dict(
xaxis=dict(title="X [kpc]", range=[x.min(), x.max()]),
yaxis=dict(title="Y [kpc]", range=[y.min(), y.max()]),
zaxis=dict(title="Z [kpc]", range=[x.min(), x.max()]),
camera=camera
),
updatemenus=[{
"buttons": [
{"args": [None, {"frame": {"duration": 50}, "fromcurrent": True}], "label": "Play", "method": "animate"},
{"args": [[None], {"frame": {"duration": 0}, "mode": "immediate"}], "label": "Pause", "method": "animate"}
],
"type": "buttons",
}],
width=700, # Make the plot area larger
height=900
),
frames=frames
)
# Add slider
fig.update_layout(
sliders=[{
"steps": [
{
"args": [[f.name], {"frame": {"duration": 0}, "mode": "immediate"}],
"label": f"t={time[i]:.1f} Myr",
"method": "animate"
}
for i, f in enumerate(frames)
],
"len": 0.5, # Half the slider width
"currentvalue": {"prefix": "Time: ", "font": {"size": 14}},
"pad": {"t": 50} # Adjust padding for compactness,
}]
)
fig.show()