Skip to content

Commit e024f54

Browse files
authored
ENH: Interactive 3D Flight Trajectory and Attitude Animation. (#1066)
* ENH: Interactive 3D Flight Trajectory and Attitude Animation. * TST: update actions workflow dependencies for headless run. * MNT: solve plot animation review comments on styling.
1 parent 789aab6 commit e024f54

10 files changed

Lines changed: 2130 additions & 398 deletions

File tree

.github/workflows/test-pytest-slow.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ jobs:
2727
MPLBACKEND: Agg
2828
steps:
2929
- uses: actions/checkout@main
30+
- name: Set up headless display
31+
uses: pyvista/setup-headless-display-action@v4
3032
- name: Set up Python
3133
uses: actions/setup-python@main
3234
with:

.github/workflows/test_pytest.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ jobs:
2626
MPLBACKEND: Agg
2727
steps:
2828
- uses: actions/checkout@main
29+
- name: Set up headless display
30+
uses: pyvista/setup-headless-display-action@v4
2931
- name: Set up Python
3032
uses: actions/setup-python@main
3133
with:

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ Attention: The newest changes should be on top -->
3232

3333
### Added
3434

35+
- ENH: ENH: Interactive 3D Flight Trajectory and Attitude Animation [#1066](https://github.com/RocketPy-Team/RocketPy/pull/1066)
3536
- ENH: ENH: `Function` vectorized speed-up and refactor [#1049](https://github.com/RocketPy-Team/RocketPy/pull/1049)
36-
- ENH: Function vectorized speed-up and refactor [#1049](https://github.com/RocketPy-Team/RocketPy/pull/1049)
3737
- ENH: REV: Revert PR #958 [#1063](https://github.com/RocketPy-Team/RocketPy/pull/1063)
3838
- ENH: MNT: remove duplicate and merge-sync entries from Unreleased changelog [#1062](https://github.com/RocketPy-Team/RocketPy/pull/1062)
3939
### Changed

docs/notebooks/getting_started_colab.ipynb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -592,12 +592,12 @@
592592
},
593593
{
594594
"cell_type": "markdown",
595-
"source": "## 3D Flight Animation\n\nRocketPy can render an interactive 3D animation of the rocket's trajectory and attitude using [vedo](https://vedo.embl.es/). This feature requires the optional `animation` extra:\n\n```bash\npip install rocketpy[animation]\n```\n\n> **Note:** The interactive animation window opens in a desktop environment. It will not display inside Google Colab or other headless notebook servers — run it locally for the best experience.\n\nTwo animation modes are available:\n\n| Method | What it shows |\n|---|---|\n| `flight.plots.animate_trajectory()` | Rocket moves through 3D space following the simulated trajectory |\n| `flight.plots.animate_rotate()` | Rocket stays centred; only attitude (rotation) is animated |\n\nBoth methods accept an optional `file_name` argument pointing to a custom `.stl` model. When omitted, RocketPy uses a built-in default rocket shape.",
595+
"source": "## 3D Flight Animation\n\nRocketPy can render an interactive 3D animation of the rocket's trajectory and attitude using [PyVista](https://pyvista.org/). This feature requires the optional `animation` extra:\n\n```bash\npip install rocketpy[animation]\n```\n\n> **Note:** A local desktop Python or Jupyter environment provides the best interactive experience. Google Colab requires a compatible PyVista Jupyter backend.\n\nTwo animation modes are available:\n\n| Method | What it shows |\n|---|---|\n| `flight.plots.animate_trajectory()` | Simulated and flown paths, scalar coloring, telemetry, velocity and wind, flight events and georeferenced ground imagery |\n| `flight.plots.animate_rotate()` | Centred attitude view with body, velocity and wind vectors, stability markers, an inertial reference sphere and live angular rates |\n\nBoth views provide play/pause, a draggable flight-time control, 0.5x/1x/2x/3x playback speeds, optional kinematic charts, camera presets and deterministic GIF/MP4 export. Day/night colors are selected from local launch time and blend toward near-space navy with altitude. The trajectory defaults to speed coloring and also supports Mach, dynamic pressure, acceleration and altitude. The rotate view can show aerodynamic angles, 3-1-3 Euler angles, body rates, center of mass and center of pressure. Pass `color_scheme` to override the shared palette. The methods accept an optional `file_name` argument pointing to a custom `.stl` model. When omitted, RocketPy uses a built-in default rocket shape.",
596596
"metadata": {}
597597
},
598598
{
599599
"cell_type": "code",
600-
"source": "# Install the optional animation dependency (skip if already installed)\n!pip install \"rocketpy[animation]\"\n\n# Animate the full trajectory — rocket moves through 3D space\n# Press Escape or close the window to exit the animation\ntest_flight.plots.animate_trajectory(\n start=0,\n stop=test_flight.t_final,\n time_step=0.05,\n)\n\n# Alternatively, animate only the attitude changes (rocket stays centred)\n# test_flight.plots.animate_rotate(\n# start=0,\n# stop=test_flight.t_final,\n# time_step=0.05,\n# )\n\n# To use your own 3D model, pass its path via file_name:\n# test_flight.plots.animate_trajectory(file_name=\"my_rocket.stl\")",
600+
"source": "# Install the optional animation dependency (skip if already installed)\n!pip install \"rocketpy[animation]\"\n\n# Animate the full trajectory — rocket moves through 3D space\n# Press Escape or close the window to exit the animation\ntest_flight.plots.animate_trajectory(\n start=0,\n stop=test_flight.t_final,\n time_step=0.05,\n color_by=\"speed\",\n show_kinematic_plots=True,\n camera_mode=\"follow\",\n)\n\n# Alternatively, animate attitude and stability diagnostics\n# test_flight.plots.animate_rotate(\n# start=0,\n# stop=test_flight.t_final,\n# time_step=0.05,\n# show_attitude_plots=True,\n# show_cp_cm=True,\n# )\n\n# Deterministic export:\n# test_flight.plots.animate_trajectory(export_file=\"flight.mp4\", export_fps=30)\n\n# To use your own 3D model, pass its path via file_name:\n# test_flight.plots.animate_trajectory(file_name=\"my_rocket.stl\")",
601601
"metadata": {},
602602
"execution_count": null,
603603
"outputs": []
@@ -833,4 +833,4 @@
833833
},
834834
"nbformat": 4,
835835
"nbformat_minor": 2
836-
}
836+
}

docs/user/flight.rst

Lines changed: 176 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,8 @@ Flight Data Plots
458458
~~~~~~~~~~~~~~~~~~~
459459

460460
RocketPy can produce real-time interactive 3D animations of the simulated
461-
flight using `vedo <https://vedo.embl.es/>`_, a scientific visualization
462-
library built on top of VTK. Two complementary animation modes are provided:
461+
flight using `PyVista <https://pyvista.org/>`_, a scientific visualization
462+
library built on top of VTK. Two complementary animation modes are provided:
463463

464464
.. list-table::
465465
:header-rows: 1
@@ -469,27 +469,30 @@ library built on top of VTK. Two complementary animation modes are provided:
469469
- What it shows
470470
* - ``flight.plots.animate_trajectory()``
471471
- The rocket 3D model moves through space following the simulated
472-
trajectory; a black trail line is drawn behind it.
472+
trajectory. The scene includes the simulated and flown paths, live
473+
telemetry, velocity and wind vectors, and flight-event markers.
473474
* - ``flight.plots.animate_rotate()``
474475
- The rocket 3D model stays centred in the scene; only its attitude
475-
(orientation derived from the quaternion solution) is animated.
476+
is animated. A reference sphere, inertial horizon, body axes and live
477+
attitude/rate telemetry make the quaternion solution easier to inspect.
478+
Inertial velocity and wind directions are shown alongside the body axes.
476479

477480
.. note::
478481

479-
The animation window opens on the desktop via VTK. It will **not** render
480-
inside headless environments such as Google Colab. For notebook use, run
481-
the cell on a local Jupyter server or JupyterLab installation.
482+
The animation normally opens in an interactive VTK window. A local desktop
483+
Python or Jupyter environment provides the best experience. A compatible
484+
PyVista Jupyter backend is required for inline notebook rendering.
482485

483486
**Installation**
484487

485-
The ``vedo`` dependency is not installed by default. Add the optional extra
488+
The ``pyvista`` dependency is not installed by default. Add the optional extra
486489
before calling either animation method:
487490

488491
.. code-block:: bash
489492
490493
pip install rocketpy[animation]
491494
492-
If ``vedo`` is not available when an animation method is called, RocketPy
495+
If ``pyvista`` is not available when an animation method is called, RocketPy
493496
raises an :class:`ImportError` with the above install command embedded in the
494497
message.
495498

@@ -505,6 +508,13 @@ message.
505508
start=0.0, # start time in seconds (default: 0)
506509
stop=flight.t_final, # end time in seconds (default: t_final)
507510
time_step=0.05, # seconds per frame (default: 0.1)
511+
playback_speed=2.0, # twice real-time playback (default: 1)
512+
background_color="#A8CBE0", # optional launch-background override
513+
color_by="dynamic_pressure", # speed, Mach, q, acceleration or altitude
514+
show_kinematic_plots=True, # altitude, speed and acceleration
515+
camera_mode="follow", # static, follow, ground or body
516+
trajectory_line_width=8, # flown-path width (default: 4)
517+
show_subrocket_point=True, # ground projection (default: True)
508518
)
509519
510520
# Provide your own 3D model (any STL file)
@@ -518,15 +528,29 @@ message.
518528
**animate_rotate — attitude-only animation**
519529

520530
Useful for inspecting roll, pitch, and yaw behaviour without the distraction of
521-
the trajectory translation. The rocket mesh is fixed at its position at
522-
``start`` and only rotated according to the quaternion solution.
531+
the trajectory translation. The rocket mesh remains centred and rotates in an
532+
East-North-Up inertial reference scene according to the quaternion solution.
523533

524534
.. code-block:: python
525535
526536
flight.plots.animate_rotate(
527537
start=0.0,
528538
stop=flight.t_final,
529539
time_step=0.05,
540+
show_attitude_plots=True,
541+
show_cp_cm=True,
542+
)
543+
544+
Deterministic export uses a fixed simulation-time grid derived from the output
545+
frame rate and ``playback_speed``:
546+
547+
.. code-block:: python
548+
549+
flight.plots.animate_trajectory(
550+
export_file="flight.mp4",
551+
export_fps=30,
552+
export_resolution=(1920, 1080),
553+
camera_mode="follow",
530554
)
531555
532556
**Parameters**
@@ -554,18 +578,154 @@ Both methods share the same signature:
554578
``flight.t_final``.
555579
* - ``time_step``
556580
- ``0.1``
557-
- Duration of each frame in seconds. Smaller values produce smoother
558-
animations at the cost of longer render times. Must be > 0.
581+
- Simulation-time interval between frames, in seconds. Smaller values produce
582+
smoother interactive playback at the cost of more rendering work. Must
583+
be > 0.
584+
* - ``playback_speed``
585+
- ``1.0``
586+
- Ratio of simulation time to wall-clock time. For example, ``2`` plays at
587+
twice real time. Must be > 0. The animation control strip always provides
588+
``0.5x``, ``1x``, ``2x`` and ``3x`` choices.
559589
* - ``**kwargs``
560590
- —
561-
- Additional keyword arguments forwarded to ``vedo.Plotter.show``
562-
(e.g. ``viewup``, ``azimuth``, ``elevation``).
591+
- RocketPy visualization options listed below, plus arguments forwarded to
592+
:class:`pyvista.Plotter`, such as ``window_size``, ``notebook`` or
593+
``off_screen``.
594+
595+
The following RocketPy-specific options are supplied through ``**kwargs`` and
596+
are removed before the remaining arguments are forwarded to PyVista:
597+
598+
.. list-table::
599+
:header-rows: 1
600+
:widths: 28 15 57
601+
602+
* - Keyword
603+
- Default
604+
- Description
605+
* - ``background_color``
606+
- ``None``
607+
- Color-like launch-background override, such as ``"#A8CBE0"``,
608+
``"lightblue"`` or an RGB tuple. ``None`` selects daylight or night
609+
colors from the environment's local launch time.
610+
* - ``playback_controls``
611+
- ``True``
612+
- Show the play/pause button, draggable flight-time bar and playback-speed
613+
selector.
614+
* - ``show_subrocket_point``
615+
- ``True``
616+
- In the trajectory view, show the rocket's vertical projection onto the
617+
ground plane.
618+
* - ``ground_image``
619+
- ``None``
620+
- Path, :class:`pyvista.Texture`, or mapping with ``image``, ``bounds``,
621+
``coordinates`` and optional ``flip_y`` entries. Explicit bounds place
622+
the image geographically instead of fitting it to the trajectory.
623+
* - ``ground_image_bounds``
624+
- ``None``
625+
- Image bounds ``(west, east, south, north)`` in ENU metres or longitude
626+
and latitude, according to ``ground_image_coordinates``.
627+
* - ``ground_image_coordinates``
628+
- ``"enu"``
629+
- Ground-bound coordinate system: ``"enu"`` or ``"latlon"``. Latitude
630+
and longitude bounds use a local equirectangular conversion around the
631+
environment launch coordinates.
632+
* - ``ground_image_flip_y``
633+
- ``False``
634+
- Flip the texture vertically when required by the source image's row
635+
orientation.
636+
* - ``color_by``
637+
- ``"speed"``
638+
- Color the trajectory by ``"speed"``, ``"mach"``,
639+
``"dynamic_pressure"``, ``"acceleration"`` or ``"altitude"``. Use
640+
``False`` or ``None`` for fixed path colors.
641+
* - ``show_kinematic_plots``
642+
- ``False``
643+
- Show synchronized altitude AGL, speed and acceleration histories in SI
644+
units in either animation.
645+
* - ``camera_mode``
646+
- ``"static"``
647+
- Camera preset: ``"static"``, ``"follow"``, ``"ground"`` or ``"body"``.
648+
``True`` selects ``"follow"`` and ``False`` selects ``"static"``.
649+
* - ``camera_path``
650+
- ``None``
651+
- Callable receiving flight time and returning a PyVista camera position,
652+
or two or more camera positions interpolated uniformly over the selected
653+
time interval. Overrides ``camera_mode``.
654+
* - ``show_attitude_plots``
655+
- ``False``
656+
- In ``animate_rotate``, show synchronized angle-of-attack and sideslip,
657+
3-1-3 Euler-angle, and body-angular-rate histories.
658+
* - ``show_cp_cm``
659+
- ``False``
660+
- In ``animate_rotate``, show dynamic center-of-mass and center-of-pressure
661+
markers plus their physical stations and static margin in telemetry.
662+
* - ``export_file``
663+
- ``None``
664+
- Deterministically export to a ``.gif`` or ``.mp4`` instead of opening an
665+
interactive animation.
666+
* - ``export_fps``
667+
- ``30``
668+
- Export frame rate. Simulation time advances by ``playback_speed / fps``
669+
between output frames.
670+
* - ``export_resolution``
671+
- ``None``
672+
- Output ``(width, height)`` in pixels. The normal window size is used when
673+
omitted.
674+
* - ``transparent_background``
675+
- ``False``
676+
- Request an alpha background for GIF export. MP4 does not support this
677+
option.
678+
* - ``color_scheme``
679+
- ``None``
680+
- Mapping of keys from ``_animation_color_scheme()`` to replacement colors
681+
or colormap names. Overrides are merged into the default scheme.
682+
* - ``backend``
683+
- ``"auto"``
684+
- Visualization backend: ``"auto"``, ``"none"``, ``"trame"`` or
685+
``"client"``.
686+
* - ``force_external``
687+
- ``False``
688+
- Force an external rendering window. This sets the plotter's ``notebook``
689+
and ``off_screen`` options to ``False`` and overrides ``backend`` with
690+
``"none"``.
691+
* - ``shadows``
692+
- ``False``
693+
- Enable scene shadows. Colored paths, event points and direction vectors
694+
remain unlit so their colors do not darken as the camera moves.
695+
* - ``trajectory_line_width``
696+
- ``7``
697+
- Width of the flown trajectory line. The simulated-path width scales
698+
proportionally from this value.
563699

564700
**Tips**
565701

566702
- A ``time_step`` of ``0.05`` (20 fps) is a good balance between smoothness
567703
and performance for flights lasting tens of seconds.
568-
- Press **Escape** or close the vedo window to exit the animation loop early.
704+
- Use the mouse to orbit, pan and zoom; press **q** or close the PyVista window
705+
to exit.
706+
- Use **PLAY / PAUSE** to stop or resume the animation, drag **FLIGHT TIME** to
707+
inspect any simulated instant, and use the speed selector to change playback
708+
between ``0.5x``, ``1x``, ``2x`` and ``3x``. Replaying after the end restarts
709+
at ``start``.
710+
- The trajectory view display-scales the rocket so it remains visible while all
711+
coordinates, paths and telemetry retain their physical SI values.
712+
- Scalar trajectory coloring uses one fixed color range over the selected time
713+
interval, so colors remain comparable while scrubbing and exporting.
714+
- Fixed-size markers identify the selected interval's start and end, motor
715+
burnout, apogee, and each parachute trigger and fully-open time when present.
716+
- The cyan ground-projection point tracks the rocket's horizontal position;
717+
set ``show_subrocket_point=False`` to hide it.
718+
- Wind arrows point toward the direction in which the air mass is moving.
719+
- With no background override, launch times from 06:00 through 19:59 local time
720+
use a light sky palette and other launch times use a night palette. If the
721+
environment has no launch date, RocketPy assumes daylight. The background
722+
blends linearly toward near-space navy between launch altitude and 50 km AGL;
723+
this is a visual cue rather than an atmospheric or solar model.
724+
- Telemetry and legends use compact bordered annotation boxes so their values
725+
remain readable over both daylight and night backgrounds.
726+
- Center-of-mass and center-of-pressure markers preserve their physical axial
727+
ordering and separation but are mapped onto the display model, whose geometry
728+
may not match the simulated rocket exactly.
569729
- Both methods validate ``start``, ``stop``, ``time_step``, and the STL path
570730
before any rendering begins, raising a :class:`ValueError` with a descriptive
571731
message on invalid input.

docs/user/installation.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ RocketPy has several optional feature sets that can be installed individually.
153153
pip install rocketpy[env_analysis]
154154
155155
**3D Flight Animation** — interactive 3D animations of rocket trajectory and
156-
attitude using `vedo <https://vedo.embl.es/>`_ (requires a desktop environment):
156+
attitude using `PyVista <https://pyvista.org/>`_ (a desktop environment is
157+
recommended):
157158

158159
.. code-block:: shell
159160

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ monte-carlo = [
7272
]
7373

7474
animation = [
75-
"vedo>=2024.5.1"
75+
"pyvista>=0.45",
76+
"imageio-ffmpeg>=0.5"
7677
]
7778

7879
all = ["rocketpy[env-analysis]", "rocketpy[monte-carlo]", "rocketpy[animation]"]

0 commit comments

Comments
 (0)