Skip to main content

NurbCurve

Struct NurbCurve 

Source
pub struct NurbCurve<const D: usize> {
    pub degree: usize,
    pub control_points: Vec<Vector<D>>,
    pub knot_vector: Vec<Scalar>,
    /* private fields */
}
Expand description

A NURBS curve whose control points live in D-dimensional homogeneous space.

  • D = 4: 3-D curve — control points are (wx, wy, wz, w).
  • D = 3: 2-D curve (pcurve) — control points are (wu, wv, w).

Fields§

§degree: usize§control_points: Vec<Vector<D>>§knot_vector: Vec<Scalar>

Implementations§

Source§

impl NurbCurve<3>

Source

pub fn area_term(&self, origin: &Vector2) -> GeopResult<Scalar>

½ ∫ ((u − o_u) v' − (v − o_v) u') dt over the curve, o the origin: its part of the signed area of a closed loop it belongs to (see the module docs). The parts of one loop must be measured from the same origin; their sum then does not depend on it.

The curve is moved to origin once, so that every point of the integrand comes with its tangent from one pass over the spline.

Source§

impl NurbCurve<4>

Source

pub fn closest_point(&self, target: &Vector3) -> GeopResult<(Scalar, Vector3)>

The point of the curve nearest target, with its parameter.

Subdivision isolates, Newton refines: least_distance bounds the distance over every span of the curve, so no dip of it is missed, and Newton on (C(t) - target) · C'(t) = 0 refines each span left at the handoff, kept inside the domain — which also reaches an end of the curve, where the nearest point is one. The parameter is a free choice of which point of the curve is returned, so every iterate is sharpened: the point returned lies on the curve, and its distance to target is attained.

Source

pub fn closest_parameter_from( &self, target: &Vector3, t: Scalar, ) -> GeopResult<Scalar>

The parameter of the point of the curve nearest target around the seed t, sharp: the local search NurbCurve::closest_point refines each of its seeds with, for a caller that already knows where to look.

Damped Newton on f(t) = (C(t) - target) · C'(t) from t: the full derivative f' where it is definitely positive — near the nearest point — and |C'|² where not (Gauss–Newton, a descent direction wherever the curve is regular). A step that does not bring the point nearer is halved, and the search stops once halving does not help. Every iterate is sharpened — which point is returned is a free choice — and clamped into the domain.

Source§

impl<const D: usize> NurbCurve<D>

Source

pub fn with_unit_domain(&self) -> GeopResult<Self>

The same curve reparametrized from its domain onto [0, 1]: its knots mapped linearly, its control points unchanged. A curve already on exactly [0, 1] is returned as it is: mapping its knots would only round them outwards.

Source

pub fn elevate_degree(&self) -> GeopResult<Self>

The same curve one degree higher, for a curve clamped at both ends.

Cut into its Bézier pieces first — every interior knot inserted up to multiplicity degree — each piece is raised on its own: Q_i = i/(p+1) P_{i-1} + (1 - i/(p+1)) P_i on the homogeneous control points, exact for rational curves too. The pieces stay joined as they were, every interior knot now of multiplicity degree + 1.

Source

pub fn with_unit_end_weights(&self) -> GeopResult<Self>

The same single-span (Bézier) curve with its end weights one, as a clamped curve’s ends are in a profile — what a split of a rational curve, an arc, leaves otherwise.

Scaling the i-th weight by c^i — every homogeneous control point with it — reparametrizes a rational Bézier curve without changing it, for any c > 0: c^p = w_0 / w_p brings both ends to one weight, and dividing by w_0 makes that one. c is a free choice; the ends are divided by their own weights, so they come out at one whatever c rounded to. A curve whose ends already weigh exactly one is returned as it is.

Source

pub fn compatible(curves: &[Self]) -> GeopResult<Vec<Self>>

curves, on one domain and clamped, made compatible: each raised to the highest degree among them, and given every knot any of them has, at the highest multiplicity any of them has it — so that they all end up on one knot vector. Every curve stays the curve it was.

Knots of different curves that could be equal are taken as one, the union of their enclosures, which every curve then carries: an honest enclosure of each curve’s own knot.

Source

pub fn join(curves: &[Self]) -> GeopResult<Self>

The chain curves, each starting where the one before ends, as one curve on [0, 1], the i-th of n curves on [i / n, (i + 1) / n] (see Self::join_at).

Source

pub fn join_at(curves: &[Self], breaks: &[Scalar]) -> GeopResult<Self>

The chain curves, each starting where the one before ends, as one curve, the i-th on [breaks[i], breaks[i + 1]]: each brought onto its own unit domain with its ends weighted one (see Self::with_unit_end_weights) and raised to the highest degree among them, their knot vectors laid end to end, every joint a knot of full multiplicity. A joint’s control point is the union of the end of the curve before it and the start of the next — two enclosures of the one point — and an error if they could not be equal. Every curve stays the curve it was, on its new piece of the domain.

Source

pub fn reparametrized(&self, from: &[Scalar], to: &[Scalar]) -> GeopResult<Self>

The same curve between the parameters from[0] and from[n - 1], reparametrized piece by piece: the stretch between from[i] and from[i + 1] onto [to[i], to[i + 1]], linearly, so that the curve is at to[i] where it was at from[i]. What lies outside from is cut off. Both are strictly increasing; a from that could be an end of the domain is that end.

Every from[i] becomes a joint of full multiplicity (see Self::join_at). A rational curve is cut at its knots as well, so that each piece can be weighted one at its ends: its weight is then one at every joint, and so at every to[i], as a polynomial curve’s is everywhere. Where a piece’s own knots land between to[i] and to[i + 1] is a free choice, so they are sharpened.

The from are used as given: like Self::split’s, they must be narrow, refined rather than sharpened.

Source§

impl NurbCurve<4>

Source

pub fn line(a: Vector3, b: Vector3) -> GeopResult<Self>

The straight line from a to b, over [0, 1].

Source

pub fn arc( p0: Vector3, mid: Vector3, p2: Vector3, w: Scalar, ) -> GeopResult<Self>

The rational quadratic from p0 to p2 over [0, 1], its middle control point mid weighted w. With mid where the tangents of a circle at p0 and p2 meet and w the cosine of half the angle between them, it is that circle’s arc exactly: for a quarter, mid = p0 + p2 - centre and w = super::sqrt2_over_2.

Source

pub fn start(&self) -> GeopResult<Vector3>

The point the curve starts at, for the clamped knot vectors every curve here has: its first control point, divided by its weight.

Source

pub fn end(&self) -> GeopResult<Vector3>

The point the curve ends at: its last control point (see Self::start).

Source§

impl NurbCurve<3>

Source

pub fn line(a: Vector2, b: Vector2) -> GeopResult<Self>

The straight line from a to b, over [0, 1].

Source

pub fn arc( p0: Vector2, mid: Vector2, p2: Vector2, w: Scalar, ) -> GeopResult<Self>

The rational quadratic from p0 to p2 through the weighted control point mid (see NurbCurve3D::arc).

Source

pub fn start(&self) -> GeopResult<Vector2>

The point the curve starts at (see NurbCurve3D::start).

Source

pub fn end(&self) -> GeopResult<Vector2>

The point the curve ends at (see NurbCurve3D::start).

Source§

impl<const D: usize> NurbCurve<D>

Source

pub fn weight(&self, t: Scalar) -> GeopResult<Scalar>

The weight at t: the homogeneous coordinate the curve’s point there is divided by — the union of its value on every knot span t reaches. A polynomial curve’s is one everywhere; a rational one’s says how its parameter runs: its points, multiplied by it, are a polynomial in t on each span, which is what makes a rational quadratic an exact circle.

Source§

impl NurbCurve<4>

Source

pub fn evaluate(&self, t: Scalar) -> GeopResult<Vector3>

Evaluate the 3-D NURBS curve at t, returning a Cartesian Vector3.

Source

pub fn ends(&self) -> GeopResult<(Vector3, Vector3)>

Where it starts and where it ends.

Source§

impl NurbCurve<3>

Source

pub fn evaluate(&self, t: Scalar) -> GeopResult<Vector2>

Evaluate the 2-D pcurve at t, returning a Cartesian Vector2.

Source

pub fn ends(&self) -> GeopResult<(Vector2, Vector2)>

Where it starts and where it ends.

Source§

impl<const D: usize> NurbCurve<D>

Source

pub fn extended(&self, at_end: bool, by: Scalar) -> GeopResult<Self>

The same curve carried on by by of its parameter past its end (at_end) or before its start: its end piece continued as the polynomial it is, so the curve stays as smooth as it was where it used to end. The domain grows by by on that side; the rest of the curve keeps its parametrization.

The end piece is isolated as a Bézier piece by knot insertion, then subdivided at a parameter past its end — de Casteljau extrapolates as exactly as it interpolates, on the homogeneous control points, so a rational curve continues as itself: an arc continues round its circle. The curve as it was is kept as it was, control point for control point, and the continuation joined on as a piece of its own at a knot of full multiplicity: where it used to run, the carried-on curve is exactly as narrow as before. Fails for a knot vector not clamped at that end, and where the continued weights stop being positive — an arc carried round too far.

Source§

impl<const D: usize> NurbCurve<D>

Source

pub fn stationary_parameters<const C: usize>( &self, direction: &Vector<C>, max_nodes: usize, min_subdivision_size: Scalar, ) -> GeopResult<Vec<Scalar>>

Every parameter at which the curve’s coordinate along direction is stationary — its tangent perpendicular to direction — at an isolated point, as a parameter box: the curve’s extremes along direction, and where its projection onto a plane containing direction turns back on itself. Sorted, and each box at most min_subdivision_size wide unless several merged.

A stretch along which the coordinate is constant (a straight piece perpendicular to direction) has no isolated stationary point and contributes none. A box is a candidate, not a proof: it is where the search stopped being able to exclude one. Exhausting max_nodes is an error, since the result would be incomplete. C must be D - 1.

Source§

impl NurbCurve<4>

Source

pub fn bounds( &self, max_nodes: usize, min_subdivision_size: Scalar, ) -> GeopResult<[Scalar; 3]>

The box the curve itself spans, per axis: the union of its coordinate at both ends and at every stationary parameter along that axis (NurbCurve::stationary_parameters) — its extremes, each enclosed, where the control points’ box is only a bound.

Source§

impl NurbCurve<4>

Source

pub fn fit_enclosing<T: Clone>( stations: &[(Vector3, T)], locate: impl Fn(&(Vector3, T), &(Vector3, T), Scalar) -> GeopResult<(Vector3, T)>, widest_between: &mut WidestBetween, ctx: &dyn Fn(GeopError) -> GeopError, ) -> GeopResult<NurbCurve<4>>

A cubic through stations of a curve — each a point of it and whatever locate needs to find more — that encloses the curve between them too. locate(from, to, frac) is the point of the curve a fraction frac of the way along the leg from station from to to, with what it needs to go on from there.

Every leg is first split into pieces: a curve given in only a few stations would leave too few points for a cubic to follow it, so every fit has at least [MIN_FITTED_LEGS] legs. The cubic drifts from the curve between its points, which is enclosed as width, honestly (see NurbCurve3D::interpolate_enclosing); while that width is more than [FIT_DRIFT] and refining the legs still halves it, the legs that drift are halved: those a halving of all, which shrinks a cubic’s drift sixteen times, would leave the widest. Where the curve bends evenly that is every leg. Where it turns sharply in one place — a blend’s arc, tangent to a plane it is cut by at three degrees, meets it in an ellipse nineteen times as long as it is wide, its tip of radius 5e-3 — it is those at the tip, which legs of an even length reach only when there are hundreds of them. Once refining no longer halves the width, the width is the points’ own, and the coarser fit is kept.

widest_between is told the widest point of the curve located between stations of the last attempt: the curve is widened to hold it, so one that is wide makes the whole curve wide, and this says which.

Source§

impl NurbCurve<4>

Source

pub fn helix( frame: &CoordinateSystem, radius: Scalar, helix: &Helix, ) -> GeopResult<Self>

The helix of radius around frame.w() through frame.origin(): starting at origin + radius u, moving as helix says along w — right-handed turning from u towards v, left-handed the other way. One rational quadratic per span of at most a quarter turn, exactly on the cylinder, its height within 0.53 % of the pitch of the true helix (see the module docs); tapered, on the cone as closely. The parameter runs over [0, 1], a span’s end at every k / n. Refused where the radius could come to nothing.

Source§

impl<const D: usize> NurbCurve<D>

Source

pub fn interpolate_homogeneous( values: &[Vector<D>], params: &[Scalar], degree: usize, ) -> GeopResult<Self>

The rational curve of degree whose homogeneous control points interpolate the homogeneous points values — (w p, w), the point p of weight w — at the strictly increasing params, from 0 to 1, on the knots their averages give: it passes through each p at its parameter. Curves interpolated at the same parameters share one knot vector, so they are the rows of a surface skinned through curves whose control points are the values (see crate::nurb_surface::NurbSurface::try_new), its iso-curve at each parameter exactly the curve there.

The values are solved for as they are: interval values carry their width into the control points (sharpen a value that is a free choice first). An error if a control point’s weight comes out not positive.

Source§

impl NurbCurve<4>

Source

pub fn cubic_spline( points: &[Vector3], start: Option<Vector3>, end: Option<Vector3>, ) -> GeopResult<Self>

The cubic spline through points, in order: twice continuously differentiable, parameterized by chord length (see [chord_length_params]) with a knot at every point, so it bends only as much as passing through them needs.

Each end leaves along its tangent start / arrives along end if given — a direction, whose length does not matter — and is free otherwise: straight there, its second derivative zero (a natural spline). How fast it leaves along a given tangent is a free choice of the spline’s shape; it is the speed the parameterization travels at anyway, the polyline’s length per unit of parameter, worked out from the points’ midpoints so that the same points always give the same spline.

Two points and no tangents give the straight line between them.

Source

pub fn interpolate(points: &[Vector3], degree: usize) -> GeopResult<Self>

Fit a 3-D NURBS curve exactly through points — see interpolate above. Between the points it is only an approximation of whatever curve they were sampled from; use Self::interpolate_enclosing when the result has to enclose that curve.

Source

pub fn interpolate_enclosing( points: &[Vector3], between: &[Vec<Vector3>], degree: usize, ) -> GeopResult<Self>

Fit a 3-D NURBS curve through samples points of some true curve (through their centres; their full width is enclosed), widened so it also encloses that curve between them: between[i] are points of the true curve strictly between points[i] and points[i + 1], in order and roughly evenly spaced (see true_point_fractions for the usual choice of where).

An interpolant drifts from the curve it was sampled from between the samples. That drift is a genuine uncertainty about where the curve is, so it belongs in the curve’s interval width: otherwise a point on the true curve tests as not on its interpolant, and every containment or intersection question asked of it is answered for the wrong curve. A true point per interval measures it where it is largest; the enclosure is exact there and as good as that measurement in between.

Source

pub fn drift_by_interval( points: &[Vector3], between: &[Vec<Vector3>], degree: usize, ) -> GeopResult<Vec<f64>>

How far the true points between of each interval stick out of the interpolant Self::interpolate_enclosing fits through points, before it is widened, in the coordinate they stick out most: what each interval asks of the one width the whole curve is then given.

Source

pub fn enclosing_pad( &self, checks: &[(Scalar, Vector3)], ) -> GeopResult<[Scalar; 3]>

How far, per coordinate, this curve has to be widened (see Self::widen) to enclose every check’s point: (t, q), the true point q, near the curve at about t — exactly as Self::interpolate_enclosing widens an interpolant to enclose the curve it was sampled from.

Source

pub fn widen(&mut self, pad: &[Scalar; 3])

This curve widened by ±pad[k] in each coordinate k, everywhere.

Source§

impl NurbCurve<3>

Source

pub fn interpolate(points: &[Vector2], degree: usize) -> GeopResult<Self>

Fit a 2-D NURBS curve exactly through points — see the 3-D NurbCurve::interpolate.

Source

pub fn interpolate_enclosing( points: &[Vector2], between: &[Vec<Vector2>], degree: usize, ) -> GeopResult<Self>

The 2-D counterpart of the 3-D NurbCurve::interpolate_enclosing.

Source§

impl<const D: usize> NurbCurve<D>

Source

pub fn joined(&self, next: &Self) -> GeopResult<Self>

This curve and next, which starts where this one ends, as one curve: the lower in degree raised to the other’s, next’s knots carried on from where this one’s end, its homogeneous control points scaled so its first weight is this one’s last — the same rational curve — and the two joined by a knot of full multiplicity, so the joined curve is exactly the two pieces, however they meet there. The control point where they meet is the one both pieces had there: two enclosures of one point, so what they share.

Fails for a knot vector not clamped where they meet, and for pieces that do not meet: their end and start control points apart.

Source§

impl<const D: usize> NurbCurve<D>
where Self: Length,

Source

pub fn length(&self) -> GeopResult<(Scalar, bool)>

The curve’s length, ∫ |C'(t)| dt over its domain, enclosed, and whether the quadrature converged (see Length).

Source

pub fn parameter_at_length_fraction(&self, fraction: f64) -> GeopResult<Scalar>

The parameter t at which the fraction fraction of the curve’s length, from its start, has been run, for fraction in (0, 1): a sharp value, for a caller choosing where to cut the curve or put something on it, found by bisection on the length up to t, which grows with t. An error for a fraction outside (0, 1).

Source§

impl NurbCurve<3>

Source

pub fn offset_miss( &self, distance: Scalar, (t0, t1): (Scalar, Scalar), fit: &NurbCurve2D, tolerance: Scalar, ) -> GeopResult<Scalar>

A bound on how far fit, over its domain, is from this curve’s offset by distance to its left — fit at the start of its domain standing for the offset of this curve at t0, at its end for that at t1, affinely between — proven over boxes of fit’s domain, each split in two while its bound exceeds tolerance (at most [MISS_SPLITS] times; then the bound is only wider) and its middle is not definitely further off than that. Fails where the curve has no tangent.

Source§

impl<const D: usize> NurbCurve<D>

Source

pub fn refine_parameter_at_point<const C: usize>( &self, t: Scalar, point: &Vector<C>, ) -> Scalar

Refine t — a parameter enclosure produced by a subdivision search — into the tightest enclosure of the parameter at which this curve passes through point: Gauss-Newton on |C(t) - P|², by newton::refine, which says why and how honestly.

A search’s parameter is as wide as the search’s tolerance; the refined one is as narrow as the curve and the point pin it down, and still an honest enclosure. Self::split_parameter_at_point cuts within it.

A point nowhere near the curve, or a vanishing tangent, leaves t as it is.

Source

pub fn split_parameter_at_point<const C: usize>( &self, t: Scalar, point: &Vector<C>, within: &Vector<C>, ) -> GeopResult<Scalar>

The parameter to split this curve at so a piece ends at point, from a parameter t that isolates it: t refined (Self::refine_parameter_at_point), then the sharp middle of it.

Where in the refined enclosure the curve is cut is a free choice. point is an enclosure, and every parameter in the refined one gives a piece that lies on the curve and ends within point, so any of them serves; the check below asks it of the one chosen. Cutting at the whole enclosure instead keeps every such piece in one: the junction’s uncertainty along the curve becomes width in the pieces’ control points (through α = (t - a) / (b - a) in Boehm insertion) and in their end knots. The next cut’s parameter is then found on the wider piece, comes out wider, and widens the next pieces more: a quarter arc of radius 4 cut forty times went from 3e-15 wide to 0.08 (three times wider per cut), and a disc’s rim cut by thirty-two slots ended 4e-10 off its cap. A sharp cut adds the rounding of one subdivision per cut, nothing more.

A knot in the refined enclosure is the good choice there: the curve is cut at it as at the knot it already has, where cutting beside it would leave a piece a sliver of a span, a hair long, its control points one point and its end without a tangent (a chamfer’s contact edge cut where its run-out span begins, as a fillet then found).

Refused if t could be an end of the domain — then point is an end of the curve, nothing to cut — or if the curve at the chosen parameter does not reach within, the enclosure point was chosen in: the vertex. Not point itself: a pcurve running from u = 1 has parameters a hair 1e-16 apart near its end, and a point of u = 1e-4, sharper than that, no parameter reaches.

Source§

impl<const D: usize> NurbCurve<D>

Source

pub fn reverse(&self) -> Self

This curve traversed in the opposite direction: same domain, same shape, but reverse().evaluate(t) == evaluate(t_min + t_max - t). Reverses the control points and complements the knot vector (u -> t_min + t_max - u, then reversed) — the standard B-spline reversal construction.

Source§

impl NurbCurve<3>

Source

pub fn swap_xy(&self) -> Self

This 2-D curve mirrored across the diagonal x = y: every point (x, y) becomes (y, x), with the same parametrization. Mirroring flips orientation, so a counter-clockwise loop becomes clockwise.

Source§

impl<const D: usize> NurbCurve<D>

Source

pub fn split(&self, t: Scalar) -> GeopResult<(NurbCurve<D>, NurbCurve<D>)>

Split at parameter t (must be strictly inside the domain). Returns (left, right) sharing the junction value.

t is used exactly as given — it is not sharpened here. Whether t’s width may be discarded belongs to whoever produced it, since only they know what it means, so this function neither assumes nor imposes an answer.

A wide t gives every piece the curve could be cut into there, in one: its width flows into alpha = (t - e) / (s - e), into the pieces’ control points and into their end knot. A piece cut again is then found wider at the next cut, and its pieces wider still. To end a piece at a point, cut at a sharp parameter of the refined enclosure of where the curve reaches it (NurbCurve::split_parameter_at_point): which one is a free choice, and the pieces then carry no more than the rounding of the cut.

See “Sharpen only where the value is a free choice” in AGENTS.md.

Source

pub fn sub_curve(&self, t0: Scalar, t1: Scalar) -> GeopResult<NurbCurve<D>>

This curve restricted to [t0, t1], cut in one pass: each bound that lies strictly inside the domain (definitely_greater the start / definitely_less the end) is inserted to full multiplicity and the outside is dropped; a bound that doesn’t is not cut at, so the result always covers at least [t0, t1] ∩ domain. Cheaper than two Self::splits: no discarded piece or intermediate curve is built.

Like split, the bounds are used exactly as given and must be narrow.

Source

pub fn bezier_pieces(&self) -> GeopResult<Vec<Self>>

The curve cut at every interior knot: its polynomial (or rational) pieces, in order.

Source

pub fn split_mid(&self) -> GeopResult<(NurbCurve<D>, NurbCurve<D>)>

Split at the midpoint of the parameter domain.

Source§

impl NurbCurve<4>

Source

pub fn sweep(&self, offset: Vector3) -> NurbSurface3D

The ruled surface swept out by translating this curve along offset: Scalar(t, s) = self.evaluate(t) + s * offset. Degree (self.degree, 1) — same knot vector as self in t, s ∈ [0, 1] — control points [cp_i, cp_i + offset] row-major per original control point cp_i.

Source§

impl NurbCurve<4>

Source

pub fn tangent(&self, t: Scalar) -> GeopResult<Vector3>

Cartesian tangent vector C'(t) (not normalized).

Source

pub fn second_derivative(&self, t: Scalar) -> GeopResult<Vector3>

Cartesian second derivative C''(t) (not normalized). Nonzero for a rational degree-1 curve too: a line with unequal weights is not traversed at constant speed.

Source§

impl NurbCurve<3>

Source

pub fn tangent(&self, t: Scalar) -> GeopResult<Vector2>

Cartesian tangent vector C'(t) (not normalized), for a 2-D pcurve.

Source

pub fn second_derivative(&self, t: Scalar) -> GeopResult<Vector2>

Cartesian second derivative C''(t) (not normalized), for a 2-D curve.

Source§

impl NurbCurve<4>

Source

pub fn translate(&self, offset: Vector3) -> Self

This 3-D curve shifted by offset — same shape and parametrization, every point moved by offset. Translates each (homogeneous) control point by offset * weight, leaving weights and the knot vector untouched.

Source§

impl NurbCurve<3>

Source

pub fn translate(&self, offset: Vector2) -> Self

This 2-D curve shifted by offset (see NurbCurve::translate for 3-D). Shifted by an exact zero, it is the curve itself, not widened by adding zeros.

Source

pub fn lifted(&self) -> GeopResult<NurbCurve<4>>

This 2-D curve as a 3-D one in the plane z = 0.

Source

pub fn embed( &self, origin: &Vector3, e1: &Vector3, e2: &Vector3, ) -> GeopResult<NurbCurve<4>>

This 2-D curve as the planar 3-D curve origin + x e1 + y e2 for (x, y) along it: a sketch’s curve where its plane puts it. An affine map of the homogeneous control points (w x, w y, w) to (w origin + (w x) e1 + (w y) e2, w), so it is exact for any NURBS and divides by no weight.

Source§

impl NurbCurve<4>

Source

pub fn transform(&self, motion: &Motion) -> Self

This 3-D curve moved by motion — a rigid motion, a translation or a mirror — with the same parametrization: each control point moved (see [transform_control_point]), weights and knots untouched.

Source§

impl NurbCurve<3>

Source

pub fn within( &self, region: &Convex, min_size: Scalar, max_nodes: usize, ) -> GeopResult<Within>

Where the curve lies with respect to region: split until each piece lies in it, lies past one of its sides, or has a point on either side — or is smaller than min_size, along the boundary. Visits at most max_nodes pieces: exhausting them is an error, the answer being unknown.

Source§

impl<const D: usize> NurbCurve<D>

Source

pub fn try_new( degree: usize, control_points: Vec<Vector<D>>, knot_vector: Vec<Scalar>, ) -> GeopResult<Self>

Source

pub fn recompute_aabb(&mut self)

Refresh the cached Self::aabb from the current control_points.

Every constructor in this module keeps aabb in sync automatically, but control_points is a pub field and at least one caller outside this crate (Model::reverse_face, mirroring a pcurve’s control points in place to flip a face) legitimately mutates it directly rather than building a new curve — call this afterwards or the cached box silently goes stale and the intersection search’s aabb_could_overlap prefilter starts pruning real overlaps.

Source

pub fn aabb(&self) -> &[Scalar; 3]

The box around the control points, and so around the curve (the convex hull property): one enclosure per axis, holding every control point’s coordinate there. Only the first D - 1 axes are the curve’s; a 2-D curve’s third is Scalar::ENTIRE.

Source

pub fn could_overlap(&self, other: &Self) -> bool

Whether the boxes around two curves’ control points could overlap: false proves the curves apart.

Source

pub fn domain(&self) -> (Scalar, Scalar)

Valid parameter range (start_t, end_t) of this curve.

Source

pub fn size(&self) -> GeopResult<Scalar>

The diagonal of the box around the control points: by the convex hull property, a length the whole curve fits within (see NurbSurface::size).

Source

pub fn mid_parameter(&self) -> GeopResult<Scalar>

The middle of Self::domain, sharp: a parameter inside the curve for a sample, a seed or a split point. Any value in the enclosure of the middle serves as well, so it is a free choice, and sharpening it loses nothing (see AGENTS.md).

Source

pub fn breakpoints(&self) -> Vec<Scalar>

Where the curve stops being one polynomial piece: the ends of its domain and every distinct knot between them (see [crate::spline::breakpoints]).

Source

pub fn domain_as_scalar(&self) -> Scalar

Source

pub fn everything() -> Self

A degenerate, maximally-unsharp curve: a single control point whose every coordinate is Scalar::ENTIRE, over a domain that accepts any parameter. evaluate() at any t returns ENTIRE in every coordinate, so it could_be_equals any point — a placeholder for geometry that is not yet known.

Source§

impl NurbCurve<4>

Source

pub fn as_line(&self) -> GeopResult<Option<Axis>>

The line the curve runs along, if it is straight: every control point on the line from the first to the last, which is then the curve’s direction. None for a curve that bends, or whose ends coincide.

Source

pub fn as_arc(&self) -> GeopResult<Option<Arc>>

The arc the curve traces, if it is a circular one: rational quadratic pieces, each an exact arc, all of one circle — how the kernel builds every arc and circle, and what splitting one leaves. None for any other curve.

Trait Implementations§

Source§

impl<const D: usize> Clone for NurbCurve<D>

Source§

fn clone(&self) -> NurbCurve<D>

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<const D: usize> Debug for NurbCurve<D>

Source§

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

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

impl<const D: usize> Display for NurbCurve<D>

Source§

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

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

impl<const D: usize> Exact for NurbCurve<D>

Source§

fn write<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>

Source§

fn read<'de, De: Deserializer<'de>>(deserializer: De) -> Result<Self, De::Error>

Source§

impl Length for NurbCurve<4>

Source§

fn length(&self) -> GeopResult<(Scalar, bool)>

Its length, ∫ |C'(t)| dt over its domain, enclosed (see NurbCurve::integrated), and whether the quadrature converged.
Source§

impl Length for NurbCurve<3>

Source§

fn length(&self) -> GeopResult<(Scalar, bool)>

Its length, ∫ |C'(t)| dt over its domain, enclosed (see NurbCurve::integrated), and whether the quadrature converged.
Source§

impl ParameterRefinable<2> for NurbCurve<3>

Source§

fn evaluate_cartesian(&self, t: Scalar) -> GeopResult<Vector<2>>

Source§

fn tangent_cartesian(&self, t: Scalar) -> GeopResult<Vector<2>>

The Cartesian tangent, via each dimension’s own tangent (the quotient rule over the homogeneous derivative()).
Source§

impl ParameterRefinable<3> for NurbCurve<4>

Source§

fn evaluate_cartesian(&self, t: Scalar) -> GeopResult<Vector<3>>

Source§

fn tangent_cartesian(&self, t: Scalar) -> GeopResult<Vector<3>>

The Cartesian tangent, via each dimension’s own tangent (the quotient rule over the homogeneous derivative()).
Source§

impl<const D: usize> PartialEq for NurbCurve<D>

Source§

fn eq(&self, other: &NurbCurve<D>) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

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

Inequality operator !=. Read more
Source§

impl<const D: usize> StructuralPartialEq for NurbCurve<D>

Auto Trait Implementations§

§

impl<const D: usize> Freeze for NurbCurve<D>

§

impl<const D: usize> RefUnwindSafe for NurbCurve<D>

§

impl<const D: usize> Send for NurbCurve<D>

§

impl<const D: usize> Sync for NurbCurve<D>

§

impl<const D: usize> Unpin for NurbCurve<D>

§

impl<const D: usize> UnsafeUnpin for NurbCurve<D>

§

impl<const D: usize> UnwindSafe for NurbCurve<D>

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> 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> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. 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.