Problem Statement
draw and animate offer no way to set the camera. The body-fixed rendering uses a fixed default view, the free flight path frames the trajectory automatically, and the only way to get any other view is to orient the window by hand with show set to True, which cannot be scripted and cannot be repeated exactly. The hero graphic pipeline is the motivating case: scripts/hero_generation/load_and_visualize_hero.py depends on a hand-oriented view, and because its oversized re-render loop renders through a fresh Plotter, every attempt needs the view oriented again and no two attempts frame it identically.
Location(s): pterasoftware/output.py, pterasoftware/_output_rendering.py, pterasoftware/_parameter_validation.py, scripts/hero_generation/load_and_visualize_hero.py
Proposed Solution
Add optional camera parameters to draw and animate, defaulting to None, which leaves all three camera regimes (plain, image surface, and free flight) untouched, so the feature is default preserving and needs no expected-output regeneration on its own. The rendering mechanics already exist in the free flight path, which sets the camera explicitly, passes show a cpos of None so the camera survives, fits the parallel scale, and sizes the clipping range afterward, and _output_rendering.get_free_flight_fit_parallel_scale already fits the projected extent for an arbitrary view direction and up vector, so auto-framing from a caller's direction is a reuse rather than new math.
Three design decisions do the real work:
- Both functions render under parallel projection, so a position, a focal point, and an up vector alone do not determine the framing: the parallel scale does, and the camera's distance means nearly nothing. Today the scale is always auto-fitted so the whole scene fills the window, and interactive zooming happens through the scroll wheel, which adjusts the parallel scale while the window is open. A scripted caller has neither lever, so the camera parameters should include an optional parallel scale, with
None meaning fit it from the caller's view direction, and the recovered parallel scale from the other half of this issue round-trips through the same parameter.
- The coordinates' axes and reference point depend on the solver: the body-fixed rendering works in the first
Airplane's geometry axes relative to its CG, while free flight works in Earth axes relative to the Earth origin. The parameter names and docstrings need the care docs/AXES_POINTS_AND_FRAMES.md and docs/ANGLE_VECTORS_AND_TRANSFORMATIONS.md prescribe.
_parameter_validation has no vector helper, so the shape validation, including rejecting an up vector parallel to the view direction and a position equal to the focal point, is new, with its own unit tests.
The recovery half: give a caller a way to capture a camera that was oriented by hand, as the position, focal point, up vector, and parallel scale, so one interactive session can be fed back through the new parameters for every later scripted run. Two designs are on the table, and this issue should settle the choice. Logging the state through _logger when the window closes matches how the output functions already report and leaves the signatures alone. A flag that makes the function return the camera state is the cleaner path but has the downside of adding another parameter.
Additional Context
With both halves in place, the hero graphic becomes fully scripted and reproducible.
Problem Statement
drawandanimateoffer no way to set the camera. The body-fixed rendering uses a fixed default view, the free flight path frames the trajectory automatically, and the only way to get any other view is to orient the window by hand withshowset toTrue, which cannot be scripted and cannot be repeated exactly. The hero graphic pipeline is the motivating case:scripts/hero_generation/load_and_visualize_hero.pydepends on a hand-oriented view, and because its oversized re-render loop renders through a freshPlotter, every attempt needs the view oriented again and no two attempts frame it identically.Location(s):
pterasoftware/output.py,pterasoftware/_output_rendering.py,pterasoftware/_parameter_validation.py,scripts/hero_generation/load_and_visualize_hero.pyProposed Solution
Add optional camera parameters to
drawandanimate, defaulting toNone, which leaves all three camera regimes (plain, image surface, and free flight) untouched, so the feature is default preserving and needs no expected-output regeneration on its own. The rendering mechanics already exist in the free flight path, which sets the camera explicitly, passesshowacposofNoneso the camera survives, fits the parallel scale, and sizes the clipping range afterward, and_output_rendering.get_free_flight_fit_parallel_scalealready fits the projected extent for an arbitrary view direction and up vector, so auto-framing from a caller's direction is a reuse rather than new math.Three design decisions do the real work:
Nonemeaning fit it from the caller's view direction, and the recovered parallel scale from the other half of this issue round-trips through the same parameter.Airplane's geometry axes relative to its CG, while free flight works in Earth axes relative to the Earth origin. The parameter names and docstrings need the caredocs/AXES_POINTS_AND_FRAMES.mdanddocs/ANGLE_VECTORS_AND_TRANSFORMATIONS.mdprescribe._parameter_validationhas no vector helper, so the shape validation, including rejecting an up vector parallel to the view direction and a position equal to the focal point, is new, with its own unit tests.The recovery half: give a caller a way to capture a camera that was oriented by hand, as the position, focal point, up vector, and parallel scale, so one interactive session can be fed back through the new parameters for every later scripted run. Two designs are on the table, and this issue should settle the choice. Logging the state through
_loggerwhen the window closes matches how the output functions already report and leaves the signatures alone. A flag that makes the function return the camera state is the cleaner path but has the downside of adding another parameter.Additional Context
With both halves in place, the hero graphic becomes fully scripted and reproducible.