///|
/// One instant of a single-axis motion, with every derivative a drive may need.
///
/// The jerk is what separates a smooth profile from a trapezoid: a drive that
/// bounds it never excites the resonance the way a step in acceleration does.
#derive(Debug)
pub(all) struct Sample {
  ///
  /// Position, in metres for a linear axis and radians for a joint.
  position : Double
  ///
  /// First derivative of the position.
  velocity : Double
  ///
  /// Second derivative of the position.
  acceleration : Double
  ///
  /// Third derivative of the position.
  jerk : Double
}

///|
/// A sample from its four derivatives.
pub fn Sample::new(
  position~ : Double,
  velocity~ : Double = 0.0,
  acceleration~ : Double = 0.0,
  jerk~ : Double = 0.0,
) -> Sample {
  { position, velocity, acceleration, jerk, }
}

///|
/// A sample that is not moving.
pub fn Sample::still(position : Double) -> Sample {
  { position, velocity: 0.0, acceleration: 0.0, jerk: 0.0, }
}

///|
/// A single-axis motion profile: a duration, and the state at any time in it.
///
/// Sampling outside `[0, duration]` is defined rather than refused: before the
/// start the motion is held at its first sample and after the end at its last,
/// so a caller can run a clock past the end without a branch.
pub(open) trait Trajectory {
  ///|
  /// The length of the motion in seconds.
  fn duration(Self) -> Double

  ///|
  /// The state at `t` seconds from the start.
  fn sample(Self, t : Double) -> Sample
}

///|
pub extend Sample with @debug.Debug::{to_repr}