Skip to content

Commit 5161699

Browse files
GuilhermeAsuraGui-FernandesBR
authored andcommitted
feat: add animate_trajectory and animate_rotate to Flight class
- Ported methods from legacy 'animate_flight' branch - Adapted to new Flight class structure (removed postProcess) - Added vedo as optional dependency Co-authored-by: Patrick Sampaio
1 parent 8cd8ecc commit 5161699

1 file changed

Lines changed: 196 additions & 0 deletions

File tree

rocketpy/simulation/flight.py

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# pylint: disable=too-many-lines
22
import math
3+
import time
34
import warnings
45
from copy import deepcopy
56
from functools import cached_property
@@ -4039,7 +4040,202 @@ def export_kml(
40394040
color=color,
40404041
altitude_mode=altitude_mode,
40414042
)
4043+
def animate_trajectory(self, file_name, start=0, stop=None, time_step=0.1, **kwargs):
4044+
"""
4045+
6-DOF Animation of the flight trajectory using Vedo.
4046+
4047+
Parameters
4048+
----------
4049+
file_name : str
4050+
3D object file representing your rocket, usually in .stl format.
4051+
Example: "rocket.stl"
4052+
start : int, float, optional
4053+
Time for starting animation, in seconds. Default is 0.
4054+
stop : int, float, optional
4055+
Time for ending animation, in seconds. If None, uses self.t_final.
4056+
Default is None.
4057+
time_step : float, optional
4058+
Time step for data interpolation in the animation. Default is 0.1.
4059+
**kwargs : dict, optional
4060+
Additional keyword arguments to be passed to vedo.Plotter.show().
4061+
Common arguments:
4062+
- azimuth (float): Rotation in degrees around the vertical axis.
4063+
- elevation (float): Rotation in degrees above the horizon.
4064+
- roll (float): Rotation in degrees around the view axis.
4065+
- zoom (float): Zoom level (default 1).
4066+
4067+
Returns
4068+
-------
4069+
None
4070+
4071+
Raises
4072+
------
4073+
ImportError
4074+
If the 'vedo' package is not installed.
4075+
4076+
Notes
4077+
-----
4078+
This feature requires the 'vedo' package. Install it with:
4079+
pip install rocketpy[animation]
4080+
"""
4081+
try:
4082+
from vedo import Box, Line, Mesh, Plotter, settings
4083+
except ImportError as e:
4084+
raise ImportError(
4085+
"The animation feature requires the 'vedo' package. "
4086+
"Install it with:\n"
4087+
" pip install rocketpy[animation]\n"
4088+
"Or directly:\n"
4089+
" pip install vedo>=2024.5.1"
4090+
) from e
4091+
4092+
# Enable interaction if needed
4093+
try:
4094+
settings.allow_interaction = True
4095+
except AttributeError:
4096+
pass # Not available in newer versions of vedo
4097+
4098+
# Handle stop time
4099+
if stop is None:
4100+
stop = self.t_final
4101+
4102+
# Define the world bounds based on trajectory
4103+
max_x = max(self.x[:, 1])
4104+
max_y = max(self.y[:, 1])
4105+
# Use simple logic for bounds
4106+
world = Box(
4107+
pos=[self.x(start), self.y(start), self.apogee],
4108+
length=max_x * 2 if max_x != 0 else 1000,
4109+
width=max_y * 2 if max_y != 0 else 1000,
4110+
height=self.apogee,
4111+
).wireframe()
4112+
4113+
# Load rocket mesh
4114+
rocket = Mesh(file_name).c("green")
4115+
rocket.pos(self.x(start), self.y(start), 0).add_trail(n=len(self.x[:, 1]))
4116+
# Create trail
4117+
trail_points = [[self.x(t), self.y(t), self.z(t) - self.env.elevation]
4118+
for t in np.arange(start, stop, time_step)]
4119+
trail = Line(trail_points, c="k", alpha=0.5)
4120+
# Setup Plotter
4121+
plt = Plotter(axes=1, interactive=False)
4122+
plt.show(world, rocket, __doc__, viewup="z", **kwargs)
4123+
4124+
# Animation Loop
4125+
for t in np.arange(start, stop, time_step):
4126+
# Calculate rotation angle and vector from quaternions
4127+
# Note: This simple rotation logic mimics the old branch.
4128+
# Ideally, vedo handles orientation via matrix, but we stick
4129+
# to the provided logic for now.
4130+
4131+
# e0 is the scalar part of the quaternion
4132+
angle = np.arccos(2 * self.e0(t)**2 - 1)
4133+
k = np.sin(angle / 2) if np.sin(angle / 2) != 0 else 1
4134+
4135+
# Update position and rotation
4136+
# Adjusting for ground elevation
4137+
rocket.pos(self.x(t), self.y(t), self.z(t) - self.env.elevation)
4138+
rocket.rotate_x(self.e1(t) / k)
4139+
rocket.rotate_y(self.e2(t) / k)
4140+
rocket.rotate_z(self.e3(t) / k)
4141+
4142+
# update the scene
4143+
plt.show(world, rocket, trail)
4144+
4145+
# slow down to make animation visible
4146+
start_pause = time.time()
4147+
while time.time() - start_pause < time_step:
4148+
plt.render()
4149+
4150+
if getattr(plt, 'escaped', False):
4151+
break
4152+
4153+
plt.interactive().close()
4154+
return None
4155+
4156+
def animate_rotate(self, file_name, start=0, stop=None, time_step=0.1, **kwargs):
4157+
"""
4158+
Animation of rocket attitude (rotation) during the flight.
40424159
4160+
Parameters
4161+
----------
4162+
file_name : str
4163+
3D object file representing your rocket, usually in .stl format.
4164+
start : int, float, optional
4165+
Time for starting animation, in seconds. Default is 0.
4166+
stop : int, float, optional
4167+
Time for ending animation, in seconds. If None, uses self.t_final.
4168+
Default is None.
4169+
time_step : float, optional
4170+
Time step for data interpolation. Default is 0.1.
4171+
**kwargs : dict, optional
4172+
Additional keyword arguments to be passed to vedo.Plotter.show().
4173+
Common arguments:
4174+
- azimuth (float): Rotation in degrees around the vertical axis.
4175+
- elevation (float): Rotation in degrees above the horizon.
4176+
- roll (float): Rotation in degrees around the view axis.
4177+
- zoom (float): Zoom level (default 1).
4178+
4179+
Returns
4180+
-------
4181+
None
4182+
4183+
Raises
4184+
------
4185+
ImportError
4186+
If the 'vedo' package is not installed.
4187+
"""
4188+
try:
4189+
from vedo import Box, Mesh, Plotter, settings
4190+
except ImportError as e:
4191+
raise ImportError(
4192+
"The animation feature requires the 'vedo' package. "
4193+
"Install it with:\n"
4194+
" pip install rocketpy[animation]\n"
4195+
) from e
4196+
4197+
# Enable interaction if needed
4198+
try:
4199+
settings.allow_interaction = True
4200+
except AttributeError:
4201+
pass # Not available in newer versions of vedo
4202+
4203+
if stop is None:
4204+
stop = self.t_final
4205+
4206+
# Smaller box for rotation view
4207+
world = Box(
4208+
pos=[self.x(start), self.y(start), self.apogee],
4209+
length=max(self.x[:, 1]) * 0.2,
4210+
width=max(self.y[:, 1]) * 0.2,
4211+
height=self.apogee * 0.1,
4212+
).wireframe()
4213+
4214+
rocket = Mesh(file_name).c("green")
4215+
# Initialize at origin relative to view
4216+
rocket.pos(self.x(start), self.y(start), 0).add_trail(n=len(self.x[:, 1]))
4217+
4218+
plt = Plotter(axes=1, interactive=False)
4219+
plt.show(world, rocket, __doc__, viewup="z", **kwargs)
4220+
4221+
for t in np.arange(start, stop, time_step):
4222+
angle = np.arccos(2 * self.e0(t)**2 - 1)
4223+
k = np.sin(angle / 2) if np.sin(angle / 2) != 0 else 1
4224+
4225+
# Keep position static (relative start) to observe only rotation
4226+
rocket.pos(self.x(start), self.y(start), 0)
4227+
rocket.rotate_x(self.e1(t) / k)
4228+
rocket.rotate_y(self.e2(t) / k)
4229+
rocket.rotate_z(self.e3(t) / k)
4230+
4231+
plt.show(world, rocket)
4232+
4233+
if getattr(plt, 'escaped', False):
4234+
break
4235+
4236+
plt.interactive().close()
4237+
return None
4238+
40434239
def info(self):
40444240
"""Prints out a summary of the data available about the Flight."""
40454241
self.prints.all()

0 commit comments

Comments
 (0)