                        Changes in version 1.0.0                        

First stable release. The function names, arguments, and defaults in
this version are the ones the package will keep; future changes to them
will be deprecated first, not made silently.

New functions

  - get_energy(), get_momentum(), get_angular_momentum(): Total kinetic
    and potential energy, total linear momentum, and total angular
    momentum of the system at every time step of a simulation.
    get_energy() reads G and softening from the simulation output so the
    potential is computed with the same force law that produced the run.

  - conserved_quantities(): The three quantities above joined into one
    tibble with their relative errors against the initial values. Verlet
    and Euler-Cromer keep momentum and angular momentum to rounding
    error; the energy error is the integrator's report card.

  - get_orbital_elements(): The inverse of add_body_keplerian().
    Recovers the osculating Keplerian elements (a, e, i, lan, arg_pe,
    nu) and period of a body relative to a parent at every time step,
    from the simulated position and velocity.

  - continue_simulation(): Pick up a run from its last state with a new
    (or the same) time step and append the result, with time continuing
    from where the previous run ended. Makes segmented runs possible:
    large steps far from periapsis, small steps through it.

  - system_from_simulation(): Rebuild an orbit_system from any snapshot
    of a simulation, so a run can be modified and continued.

Enhancements

  - shift_reference_frame() accepts center_id = "barycenter" to
    re-center on the system's center of mass at every time step.

  - add_body_keplerian() accepts hyperbolic orbits (e > 1 with a
    negative semi-major axis), so interstellar visitors and flybys can
    be set up from elements. Exactly parabolic orbits (e = 1) remain
    unsupported.

  - simulate_system() records G, softening, method, and time_step as
    attributes of its output; the new analysis functions use them as
    defaults.

Documentation

  - New article, Checking a Simulation, covering conservation
    diagnostics, osculating elements, and segmented runs. The
    reference-frames article gains a section on the barycentric frame
    and the Keplerian-elements article a section on hyperbolic orbits.

                 Changes in version 0.3.0 (2026-05-03)                  

New functions

  - remove_body(): Remove one or more bodies from a system by name.
    Accepts a single name or a character vector for removing multiple
    bodies at once.

  - get_bodies(): Extract the bodies tibble from an orbit_system. Useful
    for inspecting, filtering, or saving body states without reaching
    into the object's internals.

  - save_system() / load_system(): Save a full orbit_system to an .rds
    file and restore it later. Preserves everything — bodies,
    gravitational constant, and class.

  - export_bodies(): Write the body table (id, mass, position, velocity)
    to a CSV file for sharing with collaborators or loading into other
    tools like Python or Excel.

  - print.orbit_system(): Custom print method that displays a clean
    summary with a header line and the bodies as a tibble, instead of
    dumping the raw list structure.

                 Changes in version 0.2.0 (2026-04-21)                  

New functions

  - add_body_keplerian(): Add a body to the system using classical
    Keplerian orbital elements (semi-major axis, eccentricity,
    inclination, longitude of ascending node, argument of periapsis,
    true anomaly) instead of raw Cartesian state vectors. The elements
    are converted to position and velocity relative to a specified
    parent body.

  - add_planet(): Add a known solar system body by name with real
    orbital data looked up automatically. Just specify the name and
    parent — mass, semi-major axis, eccentricity, inclination, and
    orientation are filled in from JPL DE440 values. Any element can be
    overridden for "what if" scenarios (e.g., add_planet("Mars", parent
    = "Sun", e = 0) for a circular Mars).

  - load_solar_system(): One-liner that builds a complete solar system —
    the Sun, all eight planets, the Moon, and optionally Pluto — using
    real orbital data from the JPL DE440 ephemeris. Returns a
    ready-to-simulate orbit_system. Use moon = FALSE or pluto = FALSE to
    exclude those bodies.

Bug fixes

  - three_d parameter on plot_orbits(), plot_system(), and
    animate_system() now supports FALSE to force 2D output even when the
    data has Z-axis motion. Previously, three_d = FALSE was the default
    but could not override the auto-detection.

  - Fixed frozen 3D animations in animate_system_3d() caused by redraw =
    FALSE in plotly animation options.

                        Changes in version 0.1.0                        

Initial release.

Core engine

  - N-body gravitational simulation with three integration methods:
    Velocity Verlet (default), Euler-Cromer, and standard Euler.
  - Compiled C++ acceleration engine via Rcpp with automatic fallback to
    vectorized R.
  - Optional gravitational softening to prevent singularities at close
    approach.

Workflow

  - create_system() / add_body() / simulate_system() pipe-friendly API.
  - Tidy tibble output (one row per body per time step) for use with
    dplyr, ggplot2, plotly, etc.
  - shift_reference_frame() to re-center simulations on any body.

Visualization

  - plot_orbits() / plot_orbits_3d() for quick trajectory plots
    (ggplot2 2D, plotly 3D).
  - plot_system() / plot_system_3d() for single-time-step snapshots.
  - animate_system() / animate_system_3d() for animated orbits via
    gganimate or plotly.
  - Automatic 2D/3D dispatch based on whether any body has non-zero Z
    motion.

Built-in constants

  - Real-world masses, orbital distances, and speeds for the Sun, all
    eight planets, and the Moon.
  - gravitational_constant for scaling gravity in create_system().
  - seconds_per_hour, seconds_per_day, seconds_per_year time helpers.
