Enum CurvatureLaw

Source
#[non_exhaustive]
pub enum CurvatureLaw { Constant { curvature: f64, }, Polynomial { coefficients: Vec<f64>, }, Sinusoid { mean: f64, amplitude: f64, angular_frequency: f64, phase: f64, }, Composite { polynomial: Vec<f64>, harmonics: Vec<Harmonic>, }, Piecewise { breaks: Vec<f64>, laws: Vec<CurvatureLaw>, }, }
Expand description

Curvature as a closed-form function of arc length.

Sign follows the usual plane convention: positive curvature turns the tangent counter-clockwise. s is arc length measured from the curve start, so a law is meaningful on [0, length] of the curve that carries it.

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

Constant

k(s) = curvature.

Zero is a straight line; any other value is a circular arc of radius 1 / curvature.

Fields

§curvature: f64

The constant curvature.

§

Polynomial

k(s) = coefficients[0] + coefficients[1] * s + coefficients[2] * s^2 + ...

Degree 1 is the clothoid (Euler spiral), whose curvature is linear in arc length; that is the transition spiral used between straight and circular track so lateral acceleration ramps linearly instead of stepping. Higher degrees cover the Bloss and cubic-parabola families.

An empty coefficient list is the zero polynomial: a straight line.

Fields

§coefficients: Vec<f64>

Coefficients in ascending powers of arc length.

§

Sinusoid

k(s) = mean + amplitude * sin(angular_frequency * s + phase).

The sinusoidal transition family (Klein, cosine ramps). Kept distinct from Polynomial because it is exactly representable this way and a truncated series would not be.

Fields

§mean: f64

Curvature the oscillation is centred on.

§amplitude: f64

Peak deviation from mean.

§angular_frequency: f64

Radians of phase per unit arc length.

§phase: f64

Phase offset at s = 0, in radians.

§

Composite

k(s) = sum c_i s^i + sum A_j sin(w_j s + p_j).

A polynomial and any number of harmonic terms at once. Neither Polynomial nor Sinusoid can hold a law with both a secular trend and an oscillation, so a curve of that shape previously had to be refused or approximated; this variant stores it exactly.

The shape is flat and additive rather than a recursive Sum(Vec<Self>). A recursive sum would let the same function be written in unboundedly many ways, would make is_constant a search over arbitrary trees, and would admit nested sums that mean nothing extra. Flattening keeps one canonical slot per kind of term, keeps the family closed under differentiation and integration, and keeps the structural predicates a finite check over two lists.

Empty harmonics is exactly the polynomial law; an empty polynomial with empty harmonics is the zero law. Both are legal, so a caller assembling terms never has to special-case the empty stage.

Fields

§polynomial: Vec<f64>

Coefficients in ascending powers of arc length.

§harmonics: Vec<Harmonic>

Additive sinusoidal terms.

§

Piecewise

Pieces laid end to end along arc length, each with its own law.

breaks holds the INTERIOR seam positions in arc length from the curve start, so laws.len() == breaks.len() + 1 and piece i spans breaks[i - 1] .. breaks[i], the first starting at 0 and the last ending at the carrying curve’s length.

Each piece’s law is written in its OWN arc length, restarting at zero at its seam, so a piece does not depend on where it sits and moving one never rewrites its coefficients.

This variant exists because a piecewise profile genuinely cannot be decomposed into several Intrinsic2 values. Every piece after the first would need an absolute start frame whose origin is the position at the seam, and that position is the non-elementary integral this crate refuses to compute. Holding the pieces in ONE curve keeps a single absolute frame at the start and anchors the interior purely by arc length, so no interior position is ever required.

Unlike a summed law, pieces are disjoint and ordered: the seams are observable data, not a redundant re-encoding of one function. That is why nesting is meaningful here and was not for Composite – a piece may itself be piecewise, expressing refinement.

Mismatched lengths stay representable, as everywhere else in this crate; the operations report None/false rather than guessing.

Fields

§breaks: Vec<f64>

Interior seam positions in arc length, ascending.

§laws: Vec<CurvatureLaw>

One law per piece; laws.len() == breaks.len() + 1.

Implementations§

Source§

impl CurvatureLaw

Source

pub const fn straight() -> CurvatureLaw

The zero law: a straight line.

Source

pub const fn circular(curvature: f64) -> CurvatureLaw

A circular arc of the given signed curvature.

Source

pub fn clothoid(start: f64, end: f64, length: f64) -> CurvatureLaw

A clothoid whose curvature runs from start to end over length.

This is the transition-spiral constructor: the rate is derived rather than asked for, because the two endpoint curvatures and the length are what an alignment actually specifies.

A non-finite or zero length cannot define a rate, so the result is a constant start law – the honest degenerate answer, not a division by zero smuggled into a coefficient.

Source

pub fn sine_corrected_transition( start: f64, end: f64, length: f64, ) -> CurvatureLaw

A transition whose linear ramp carries one full sine correction over its length: k(s) = start + (d/L) s - (d / 2pi) sin(2 pi s / L), where d = end - start.

The sine term removes the curvature-rate step a plain clothoid has at each end, so the rate starts and ends at zero instead of jumping. The mean rate is still d / L, so the total turning is unchanged from the clothoid’s (start + end) / 2 * L.

As with clothoid, a non-finite or zero length cannot define a rate, so the result degrades to a constant start law.

Source

pub fn piecewise(breaks: Vec<f64>, laws: Vec<CurvatureLaw>) -> CurvatureLaw

Pieces laid end to end, each carrying its own law.

The seams are interior positions in ascending arc length; the caller supplies one more law than seam. A mismatch is storable and reported by is_well_formed, not rejected here.

Source

pub fn is_well_formed(&self) -> bool

Whether the stored shape is internally consistent.

Only Piecewise can be malformed: it carries two lists whose lengths must agree and seams that must ascend. Every other variant is well-formed by construction, so this is true for them.

Structural, like the other predicates: it inspects stored data and never evaluates the law. Non-finite or descending seams are reported rather than silently sorted, because reordering would change which piece owns which arc length.

Source

pub fn is_straight(&self) -> bool

Whether the law is identically zero, i.e. a straight line.

Exact: this is a structural test on the stored coefficients, not a sampled one.

Source

pub fn is_constant(&self) -> bool

Whether the law is constant in arc length.

Source

pub fn constant_value(&self) -> Option<f64>

The law’s value, when it does not vary with arc length.

Returns None for a law that varies, so a caller that needs a single number cannot silently read one off a varying law. The variants that is_constant accepts are exactly the ones answered here: a frozen harmonic contributes A * sin(p), which is why a zero-FREQUENCY term is constant without being zero.

Source

pub fn seams_within(&self, span: f64) -> Vec<f64>

Interior seam positions strictly inside (0, span), ascending.

A piecewise law is only piecewise-smooth: its value can jump at a seam. Gauss-Legendre quadrature assumes the integrand is smooth across a panel, so a panel straddling a seam loses most of its accuracy – measured at 3.0e-3 on a joined curve, against 1e-9 once panels break at the seam. An integrator must therefore split its panels here rather than spreading them uniformly.

Nested piecewise laws report their inner seams too, in absolute arc length from this law’s origin, because a piece may itself be piecewise and its seams are just as discontinuous.

Source

pub fn derivative(&self) -> CurvatureLaw

The derivative law dk/ds, in closed form.

Exact symbolic differentiation. The sharpness of a clothoid is the constant this returns.

Source

pub fn shifted(&self, a: f64) -> Option<CurvatureLaw>

The same function re-written in a coordinate that starts at a.

Returns the law g with g(u) = self(a + u). This is what a TRIM needs: restricting a curve to [a, b] does not approximate the shape, it re-anchors the same law, so the trimmed curve is exactly the original one on that span (ADR 0062).

The family is closed under this operation, which is why trimming is exact rather than a refit:

  • a polynomial shifts by the binomial expansion of (a + u)^i;
  • a sinusoid shifts purely in PHASE, p -> p + w * a, because the amplitude and frequency do not depend on where the window starts;
  • a piecewise law drops the pieces that end before a, rebases the one containing a, and keeps the rest with their seams moved back.

Returns None when a is not finite, or when a piecewise law is malformed, since the shifted law would then be a guess.

Source

pub fn reversed_orientation(&self) -> CurvatureLaw

The mirrored law -k(s): the same curve reflected.

Trait Implementations§

Source§

impl Clone for CurvatureLaw

Source§

fn clone(&self) -> CurvatureLaw

Returns a copy of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for CurvatureLaw

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl PartialEq for CurvatureLaw

Source§

fn eq(&self, other: &CurvatureLaw) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for CurvatureLaw

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,