Skip to main content

NurbsCurve

Struct NurbsCurve 

Source
pub struct NurbsCurve {
    pub degree: usize,
    pub knots: Vec<f64>,
    pub control_points: Vec<Vec4>,
    /* private fields */
}

Fields§

§degree: usize§knots: Vec<f64>§control_points: Vec<Vec4>

Implementations§

Source§

impl NurbsCurve

Source

pub fn new( degree: usize, knots: Vec<f64>, control_points: Vec<Vec4>, ) -> Result<Self, String>

Source

pub fn domain(&self) -> Result<[f64; 2], String>

Source

pub fn evaluate_homogeneous(&self, parameter: f64) -> Result<Vec4, String>

Source

pub fn evaluate(&self, parameter: f64) -> Result<Vec3, String>

Source

pub fn evaluate_extended(&self, parameter: f64) -> Result<Vec3, String>

Value beyond the domain (Golovanov §2.15): every curve must answer out-of-domain queries because intersection and projection algorithms probe there. Closed curves wrap the parameter cyclically; open curves extend linearly along the end tangent.

Source

pub fn derivatives_extended( &self, parameter: f64, derivative_count: usize, ) -> Result<Vec<Vec3>, String>

Derivatives beyond the domain (§2.15). The open-end extension is linear: the first derivative is the boundary tangent and higher derivatives vanish.

Source

pub fn derivatives( &self, parameter: f64, derivative_count: usize, ) -> Result<Vec<Vec3>, String>

Source

pub fn unit_tangent( &self, parameter: f64, range_start: f64, range_end: f64, ) -> Result<Vec3, String>

The curve’s unit TANGENT at parameter, read as the one-sided limit when the first derivative vanishes there.

This is what derivatives(t, 1)?[1].normalized()? should always have been. That shape refuses on a curve class that is not exotic at all — any Hermite segment whose end tangent is the zero vector, of which an INVOLUTE flank is the standard source (speed r_base * t, so a profile sampled from the base circle outwards carries an exactly zero tangent at its start, one per flank per tooth). The direction of travel is still defined there, as the limit from inside the range, and Self::stationary_tangent_rescue reads it.

Pass the range the CALLER owns — an edge’s [t0, t1], not the curve’s domain, where the two differ (an EdgeRecord keeps the whole curve and records the sub-range it uses), so the probe never reads geometry outside the caller’s own span.

The error names the parameter and the range it probed, because the only curve that reaches it is stationary EVERYWHERE the range allows, and the caller’s own identifier is the half this function cannot supply.

Escape hatch for tamper-verification: BREP_CUSP_TANGENT_RESCUE=0 restores the bare normalized() refusal, the same hatch the imprint driver’s seed loop carries.

Source

pub fn point_and_unit_tangent_extended( &self, parameter: f64, range_start: f64, range_end: f64, ) -> Result<(Vec3, Vec3), String>

The point and unit tangent that derivatives_extended(parameter, 1) reads, with the tangent taken as Self::unit_tangent takes it.

The blend march reads its section plane — the point and the edge’s unit tangent — through the EXTENDED evaluation at every station, overshoot stations past the edge’s ends included, and unit_tangent cannot stand in for that read: it evaluates derivatives, which CLAMPS a parameter outside the domain, where derivatives_extended WRAPS it on a closed curve. A closed rim’s overshoot station would read a different tangent. This is the same one evaluation the march always made, so a non-zero derivative returns the vector derivatives[1].normalized() returned, to the bit, at no extra cost.

Where the derivative vanishes, the one-sided limit is read at the parameter the extended evaluation actually took its derivative from — the parameter itself inside the domain, the wrapped one on a closed curve, the boundary past an open end, where the extension’s direction IS the boundary’s one-sided limit. The caller’s range is widened to contain that parameter, because an overshoot station reads outside the caller’s span on purpose; the ladder still probes from the side the range lies on.

Source

pub fn stationary_tangent_rescue( &self, parameter: f64, range_start: f64, range_end: f64, ) -> Option<Vec3>

The unit tangent just inside [range_start, range_end], for a parameter where the first derivative VANISHES.

A curve can be stationary at a parameter where the curve itself is perfectly well behaved, and the direction of travel is then still defined as the one-sided limit. The standard case is a cubic Bezier whose first two control points coincide — the shape a Hermite segment takes when its end tangent is the zero vector — and an INVOLUTE is the standard source of one: the involute’s speed is r_base * t, so a gear flank sampled from the base circle outwards starts with an exactly zero tangent and every tooth profile carries one per flank. The 2026-09-14 herringbone report is that shape: a hit landing exactly on the cusp parameter made derivatives(t, 1)[1].normalized() fail and killed the whole union.

The range bounds the probe — pass an edge’s own [t0, t1] and the rescue never reads the curve outside the span the caller owns. The ladder walks OUTWARDS, and it starts at 1e-6 of the span rather than at the smallest representable step: the rescued direction is a ONE-SIDED limit, so a nearer probe truncates less, but the derivative at a stationary point is a CANCELLING sum that shrinks with the step while its rounding error does not — at 1e-9 of the span the involute cusp below reads its own limit direction only to 1e-6, worse than at 1e-6 of the span. Later rungs exist for a higher-order stationary point, whose derivative is still under the zero-length floor at the first. None means the curve is stationary everywhere the range allows, where no direction exists to report at all.

Source

pub fn reversed(&self) -> Result<Self, String>

Source

pub fn insert_knot( &self, parameter: f64, requested: usize, ) -> Result<Self, String>

Source

pub fn split(&self, parameter: f64) -> Result<(Self, Self), String>

Trait Implementations§

Source§

impl Clone for NurbsCurve

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

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

Performs copy-assignment from source. Read more
Source§

impl Debug for NurbsCurve

Source§

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

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

impl<'de> Deserialize<'de> for NurbsCurve

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for NurbsCurve

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

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> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

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> 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 = !

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

fn try_from(value: U) -> Result<T, !>

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.
Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more