pub struct Curve {
pub points: BTreeSet<Point2D>,
pub x_range: (Decimal, Decimal),
}Expand description
Represents a mathematical curve as a collection of 2D points.
§Overview
The Curve struct is a fundamental representation of a curve, defined as a series
of points in a two-dimensional Cartesian coordinate system. Each curve is associated
with an x_range, specifying the inclusive domain of the curve in terms of its x-coordinates.
This structure supports precise mathematical and computational operations, including
interpolation, analysis, transformations, and intersections. The use of Decimal
for coordinates ensures high-precision calculations, making it particularly suitable
for scientific, financial, or mathematical applications.
§Usage
The Curve struct acts as the basis for high-level operations provided within
the crate::curves module. These include (but are not limited to):
- Generating statistical analyses (
CurveAnalysisResult) - Performing curve interpolation
- Logical manipulations, such as merging curves (
MergeOperation) - Visualizing graphs or curve plots using libraries like
plotters
§Example Applications
The Curve type fits into mathematical or graphical operations such as:
- Modeling data over a range of x-values
- Comparing curves through transformations or intersections
- Calculating derivatives, integrals, and extrema along the curve
§Constraints
- All points in the
pointsvector must lie within the specifiedx_range. - Methods working with
Curvedata will assume that thepointsvector is ordered by thex-coordinate. Non-ordered inputs may lead to undefined behavior in specific operations. - A curve is a function of its abscissa: at most one point per
x. SeeCurve::newfor what breaking that rule does to each consumer, and for why no constructor can enforce it.
§See Also
Point2D: The fundamental data type for representing points in 2D space.MergeOperation: Enum for combining multiple curves.
Fields§
§points: BTreeSet<Point2D>A ordered set of Point2D objects that defines the curve in terms of its x-y plane coordinates.
Points are stored in a BTreeSet which automatically maintains them in sorted order by their
(x, y) pair. The set rejects an exact duplicate of that pair; it does not stop two ordinates
from sharing an abscissa.
Public, so a struct literal can populate it without passing through
Curve::new. No constructor can therefore guarantee one point per
abscissa.
x_range: (Decimal, Decimal)A tuple (min_x, max_x) that specifies the minimum and maximum x-coordinate values
for the curve. Operations performed on the curve should ensure they fall within this range.
Both values are of type Decimal to ensure high precision in boundary calculations.
Implementations§
Source§impl Curve
impl Curve
Sourcepub fn new(points: BTreeSet<Point2D>) -> Self
pub fn new(points: BTreeSet<Point2D>) -> Self
Creates a new curve from a vector of points.
This constructor initializes a Curve instance using a list of 2D points
provided as a BTreeSet<Point2D>. Additionally, the x-range of the curve is calculated
and stored. The x-range is determined by evaluating the minimum and maximum
x-coordinates among the provided points.
§Parameters
points(BTreeSet<Point2D>): A vector of points that define the curve in a two-dimensional Cartesian coordinate plane.
§Returns
Curve: A newly instantiated curve containing the provided points and the computed x-range.
§Behavior
- Calculates the x-range of the points using
calculate_range(). - Stores the provided points for later use in curve-related calculations.
§See Also
Point2D: The type of points used to define the curve.crate::curves::Curve::calculate_range: Computes the x-range of a set of points.
§One point per abscissa
A Curve is a function of its abscissa: at most one point per x.
Every consumer reads it that way.
get_point and
contains_point
answer from the first point matching x,
merge resamples every curve
onto one shared x-grid with one y per x, and the interpolators in
crate::geometrics bracket x between two consecutive points and
divide by the width of that bracket.
Nothing enforces the rule. Point2D orders on the full (x, y)
pair, so points may hold several ordinates for one abscissa and
new stores them all; a curve built that way is outside the contract
and the consumers behave as follows:
- all four interpolators return
InterpolationError::DegenerateIntervalwhen asked about a repeated abscissa, and pick no survivor: the bracket around it has zero width and the slope across it is undefined; get_pointandmerge_axis_interpolateread the first match in(x, y)order, which is the lowest ordinate of the stack, and the rest are invisible to the caller. Useget_valuesto see every ordinate at an abscissa.
Normalizing here would not help: points is a pub field, so a
struct literal reaches the same state without going through any
constructor. Rejecting or collapsing a duplicate can only ever be a
convenience on this path, never an invariant of the type.
A projection of a surface is genuinely multi-valued and is therefore
not a curve: crate::surfaces::Surface::project_onto returns a
Vec<Point2D>, and aggregating it to one ordinate per abscissa is
the caller’s job.
Trait Implementations§
Source§impl Arithmetic<Curve> for Curve
Implements the CurveArithmetic trait for the Curve type, providing
functionality for merging multiple curves using a specified mathematical
operation and performing arithmetic operations between two curves.
impl Arithmetic<Curve> for Curve
Implements the CurveArithmetic trait for the Curve type, providing
functionality for merging multiple curves using a specified mathematical
operation and performing arithmetic operations between two curves.
Source§fn merge(
curves: &[&Curve],
operation: MergeOperation,
) -> Result<Curve, CurveError>
fn merge( curves: &[&Curve], operation: MergeOperation, ) -> Result<Curve, CurveError>
Merges a collection of curves into a single curve based on the specified mathematical operation.
§Parameters
curves(&[&Curve]): A slice of references to the curves to be merged. Each curve must have defined x-ranges and interpolation capabilities.operation(MergeOperation): The arithmetic operation to perform on the interpolated y-values for the provided curves. Operations include addition, subtraction, multiplication, division, and aggregation (e.g., max, min).
§Returns
Ok(Curve): Returns a new curve resulting from the merging operation.Err(CurvesError): If input parameters are invalid or the merge operation encounters an error (e.g., incompatible x-ranges or interpolation failure), an error is returned.
§Behavior
- Parameter Validation:
- Verifies that at least one curve is provided in the
curvesparameter. - Returns an error if no curves are included or if x-ranges are incompatible.
- Cloning Single Curve:
- If only one curve is provided, its clone is returned as the result without performing any further calculations.
- Range Computation:
- Computes the intersection of x-ranges across the curves by finding the
maximum lower bound (
min_x) and minimum upper bound (max_x). - If the x-range intersection is invalid (i.e.,
min_x >= max_x), an error is returned.
- Interpolation and Arithmetic:
- Divides the x-range into
stepsequally spaced intervals (default: 100). - Interpolates the y-values for all curves at each x-value in the range.
- Applies the specified
operationto the aggregated y-values at each x-point.
- Parallel Processing:
- Uses parallel iteration over the grid to interpolate the operands efficiently, leveraging the Rayon library. The grid points are independent of one another and are collected back in x order.
- Error Handling:
- Any errors during interpolation or arithmetic operations are propagated back to the caller.
§Determinism
The same operands in the same order give the same result, digit for
digit, with the one exception noted for Max and Min below. Within
a grid point the operands are accumulated in the order they were
passed in, never in an order chosen by the thread pool.
This matters for Multiply and Divide in particular. Decimal
multiplication rounds once a product needs more than 28 decimal
digits, so it is not associative, and a parallel reducer would take
its bracketing from whatever chunking rayon picked on the day: three
or more curves could then merge to different last digits on
consecutive runs of one binary over one input. Add and Subtract
fold in order for the same reason.
Max and Min still reduce in parallel. They select an operand
instead of accumulating one, so no rounding enters and the extremum
is the same whatever the grouping. What is not pinned is which of
several operands that compare equal is the one returned, and two
Decimal values comparing equal can still carry different scales, so
a tie between 2.0 and 2.00 is the one case where the stored digits
are left to the reducer.
§Errors
- Invalid Parameter (
CurvesError): Returned when no curves are provided or x-ranges are incompatible. - Interpolation Failure (
CurvesError): Raised if interpolation fails for a specific curve or x-value.
§Example Use Case
This function enables combining multiple curves for tasks such as:
- Summing y-values across different curves to compute a composite curve.
- Finding the maximum/minimum y-value at each x-point for a collection of curves.
§One point per abscissa
This assumes the one-point-per-abscissa rule of Curve::new and
re-establishes it in the result: the merged curve is sampled on an
evenly spaced grid over the common x-range, with one y per grid
point, so the original abscissae do not survive the merge.
An input carrying several ordinates at one abscissa never has one of
them silently chosen. Each sample is produced by cubic interpolation,
which reports InterpolationError::DegenerateInterval, wrapped in
a CurveError, whenever the sample lands on the repeated abscissa
or brackets across it. A sample far enough from the stack is
unaffected and reads the ordinate on its own side, so a merge of such
a curve can still succeed on a grid that misses the stack. Aggregate
the curve to one ordinate per abscissa before merging it.
Source§fn merge_with(
&self,
other: &Curve,
operation: MergeOperation,
) -> Result<Curve, CurveError>
fn merge_with( &self, other: &Curve, operation: MergeOperation, ) -> Result<Curve, CurveError>
Combines the current Curve instance with another curve using a mathematical
operation, resulting in a new curve.
§Parameters
self(&Self): A reference to the current curve instance.other(&Curve): A reference to the second curve for the arithmetic operation.operation(MergeOperation): The operation to apply when merging the curves.
§Returns
Ok(Curve): Returns a new curve that represents the result of the operation.Err(CurvesError): If the merge operation fails (e.g., incompatible x-ranges or interpolation errors), an error is returned.
§Behavior
This function is a convenience wrapper around merge_curves that operates
specifically on two curves. It passes self and other as an array to
merge_curves and applies the desired operation.
§Errors
- Inherits all errors returned by the
merge_curvesmethod, including parameter validation and interpolation errors.
§Examples
Use this method to easily perform arithmetic operations between two curves, such as summing their y-values or finding their pointwise maximum.
Source§type Error = CurveError
type Error = CurveError
Source§impl AxisOperations<Point2D, Decimal> for Curve
impl AxisOperations<Point2D, Decimal> for Curve
Source§fn contains_point(&self, x: &Decimal) -> bool
fn contains_point(&self, x: &Decimal) -> bool
Reports whether the curve has a point at the abscissa x.
Answers on x alone, so it says nothing about how many ordinates sit
there. On a curve honouring the one-point-per-abscissa rule of
Curve::new there is at most one.
Source§fn get_index_values(&self) -> Vec<Decimal>
fn get_index_values(&self) -> Vec<Decimal>
Returns the abscissa of every point, in (x, y) order.
A curve holding several ordinates at one abscissa yields that abscissa once per ordinate; the duplicates are visible here rather than dropped.
Source§fn get_values(&self, x: Decimal) -> Vec<&Decimal>
fn get_values(&self, x: Decimal) -> Vec<&Decimal>
Returns every ordinate at the abscissa x, in ascending order.
This is the multi-valued reader: unlike Self::get_point, it never
picks a survivor. A curve honouring the one-point-per-abscissa rule
of Curve::new returns at most one value; anything longer says the
curve is not a function of its abscissa.
Source§fn get_point(&self, x: &Decimal) -> Option<&Point2D>
fn get_point(&self, x: &Decimal) -> Option<&Point2D>
Returns the point at the abscissa x, if there is one.
Assumes the one-point-per-abscissa rule of Curve::new. If the
curve breaks it, this returns the first match in (x, y) order,
which is the lowest ordinate of the stack, and the rest are invisible
to the caller. Use Self::get_values to see all of them.
Source§type Error = CurveError
type Error = CurveError
Source§impl BiLinearInterpolation<Point2D, Decimal> for Curve
Implementation of the BiLinearInterpolation trait for the Curve struct.
impl BiLinearInterpolation<Point2D, Decimal> for Curve
Implementation of the BiLinearInterpolation trait for the Curve struct.
§What it computes
Bilinear interpolation is defined on a cell of a two-dimensional grid: the answer is the mean of four corner samples, weighted by the query’s fractional position along each axis. A curve has one axis, so the cell is built out of two of its segments and the second fraction is fixed at one half:
- the near edge is the segment that brackets
x, from sampleito samplei + 1; - the far edge is the segment two positions further along, from sample
fto samplef + 1; - the answer is the mean of the two edges read at the same fraction.
t = (x - x[i]) / (x[i+1] - x[i])
near = y[i] + t * (y[i+1] - y[i])
far = y[f] + t * (y[f+1] - y[f])
y = (near + far) / 2§Behaviour near the upper boundary
The far edge is f = i + 2 while such a segment exists. Over the last two
segments of the curve it does not, and f is clamped to len - 2, the
curve’s last segment. Two consequences a caller can act on:
- on the final segment the two edges coincide,
nearequalsfar, and the answer is the linear interpolant there. Not always to the last digit: this method normalisesxbefore scaling by the rise andLinearInterpolation::linear_interpolatescales first, so where the fraction along the segment has no finite decimal expansion the two round at scale 28 on different quantities and can part company in the 28th place; - on the second-to-last segment the far edge is the segment immediately after the near one instead of the one after that.
§Why not clamp the window start
The obvious repair, and the one
CubicInterpolation::cubic_interpolate applies at its own boundary, is
to slide the whole window back to the last four samples. Do not reach for
it here. The window start is also the denominator of t, so moving it
back off the segment holding x pushes t past 1. Written out, the
answer weights the four ordinates it reads by
y = (1 - t)/2 * y[i] + t/2 * y[i+1] + (1 - t)/2 * y[f] + t/2 * y[f+1]which sums to one for any t, but has every weight non-negative only
while t is in [0, 1]. Past 1 the two (1 - t)/2 weights go
negative, the answer stops being a convex combination of the cell’s
corners, and what is left is an extrapolation of a cell that does not
contain x: on (0,0), (1,1), (2,4), (3,9) the window-start clamp
returns 9.5 at x = 2.5, above every ordinate the curve has. Being a
convex combination of its cell’s corners is what makes bilinear
interpolation bilinear interpolation, so it is the near edge that has to
stay put and the far edge that gives way. That is why the two boundary
rules differ.
Before this rule existed the last two segments returned
InterpolationError::Bilinear because the window ran off the end.
§What this is not
The far edge sits elsewhere on the curve, so the result does not follow
the samples between knots and this is not an interpolant through them: on
(0,0), (1,1), (2,4), (3,9) the value at x = 0.5 is 3.5, not the
0.5 a straight reading of the bracketing segment gives.
An x landing exactly on a sample returns that sample, which neither
one-sided limit approaching it matches, so the result jumps at every
sample. That jump is as old as the method and owes nothing to the
boundary rule above: on (0,0), (1,1), (2,4), (3,9), (4,16) both limits
at x = 1 are 5 against a sample of 1, and both segments meeting
there are far from the clamp. What the clamp does add is that at the last
two samples before the end of the curve the two limits no longer agree
with each other either, because the segments on either side get different
far edges.
Reach for LinearInterpolation or SplineInterpolation when a curve
through the samples is what is wanted.
§Parameters
x: The x-coordinate to interpolate at. Must lie within the range of the curve’s x-coordinates.
§Returns
Ok(Point2D): The interpolated point atx, both coordinates asDecimal.Err(InterpolationError): See the error list below.
§Errors
InterpolationError::Bilinear: the curve holds fewer than four samples, or a checked-arithmetic step left the representableDecimalrange.InterpolationError::OutOfRange:xfalls outside the curve’s x-range.InterpolationError::DegenerateInterval: several samples share the abscissax, so the curve has no value there.
§One point per abscissa
A curve is a function of its abscissa; see Curve::new. Asking for a
repeated abscissa returns InterpolationError::DegenerateInterval, as
it does for the three sibling algorithms; no ordinate of the stack is
chosen. A stack elsewhere on the curve does not stop this method
answering: only the near edge’s width is divided by, and a stack sitting
on the far edge is read for its two ordinates alone.
§Related Traits
BiLinearInterpolation: The trait defining this method.Interpolate: Ensures compatibility of the curve with multiple interpolation methods.
§See Also
Curve: The overarching structure that represents the curve.Point2D: The data type used to represent individual points on the curve.find_bracket_points: A helper method used to locate the two points that bracket the given x-coordinate.
Source§fn bilinear_interpolate(
&self,
x: Decimal,
) -> Result<Point2D, InterpolationError>
fn bilinear_interpolate( &self, x: Decimal, ) -> Result<Point2D, InterpolationError>
Reads the curve at x as the mean of the bracketing segment and the
segment two positions further along, both at the same fraction.
The far segment is clamped to the curve’s last one where it would run off the end, which makes the final segment a plain linear interpolation. The impl-level documentation states the rule and its consequences in full.
§Errors
InterpolationError::Bilinearwhen the curve holds fewer than four samples, or when a checked-arithmetic step leaves the representableDecimalrange.InterpolationError::OutOfRangewhenxis outside the curve’s x-range.InterpolationError::DegenerateIntervalwhen several samples share the abscissax.
Source§impl CubicInterpolation<Point2D, Decimal> for Curve
Implements the CubicInterpolation trait for the Curve struct,
providing an algorithm for cubic interpolation utilizing a Catmull-Rom spline.
impl CubicInterpolation<Point2D, Decimal> for Curve
Implements the CubicInterpolation trait for the Curve struct,
providing an algorithm for cubic interpolation utilizing a Catmull-Rom spline.
§Method: cubic_interpolate
§Parameters
x: The x-value at which the interpolation is performed. This value must be within the range of x-values in the curve’s defined points, and it is passed as aDecimalto allow for high-precision computation.
§Returns
Ok(Point2D): Returns aPoint2Drepresenting the interpolated x and y values.Err(CurvesError): Returns an error if:- There are fewer than 4 points available for interpolation.
- The x-value is outside the curve’s range, or interpolation fails for any other reason.
§Behavior
- Point Validation: Ensures at least four points exist for cubic interpolation, as this is a fundamental requirement for computing the Catmull-Rom spline.
- Exact Match Check: If the x-value matches an existing point in the curve, the
method directly returns the corresponding
Point2Dwithout further computation. - Bracket Points: Determines the bracketing points (4 points total) around the
provided x-value. Depending on the position of the x-value in the curve, the
method dynamically adjusts the selected points to ensure they form a proper bracket:
- If near the start of the curve, uses the first four points.
- If near the end, uses the last four points.
- Else, selects points before and after x to define the bracket.
- Parameter Calculation: Computes a normalized parameter
tthat represents the relative position of the target x-value betweenp1andp2. - Catmull-Rom Spline: Performs cubic interpolation using a Catmull-Rom spline, a widely used, smooth spline algorithm. The coefficients are calculated based on the relative x position and the y-values of the four surrounding points.
- Interpolation: Calculates the interpolated y-value using the cubic formula:Here,
y(t) = 0.5 * ( 2 * p1.y + (-p0.y + p2.y) * t + (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t^2 + (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t^3 )tis the normalized x position, andp0,p1,p2,p3are the four bracketed points.
§Errors
- Returns an error of type
CurvesError::InterpolationErrorif any issues are encountered, such as insufficient points or the inability to locate bracket points.
§Example
This method is part of the Curve struct, which defines a set of points and supports interpolation.
It is often used in applications requiring smooth manifolds or animations.
§Notes
- The computed y-value ensures smooth transitions and continuity between interpolated segments.
- Catmull-Rom splines are particularly effective for creating visually smooth transitions, making this method suitable for curves, animations, and numerical analysis.
§See Also
CubicInterpolation: The trait defining this method.Point2D: Represents the points used for interpolation.find_bracket_points: Determines the bracketing points required for interpolation.
Source§fn cubic_interpolate(&self, x: Decimal) -> Result<Point2D, InterpolationError>
fn cubic_interpolate(&self, x: Decimal) -> Result<Point2D, InterpolationError>
Performs cubic interpolation on a set of points to estimate the y-coordinate for a given x value using a Catmull-Rom spline.
§Parameters
x: The x-coordinate for which the interpolation is performed. This value should lie within the range of the points on the curve.
§Returns
Ok(Point2D): APoint2Dinstance representing the interpolated position(x, y), whereyis estimated using cubic interpolation.Err(CurvesError): An error indicating issues with the interpolation process, such as insufficient points or an out-of-range x value.
§Requirements
- The number of points in the curve must be at least 4, as cubic interpolation requires four points for accurate calculations.
- The specified
xvalue should be inside the range defined by the curve’s points. - If the specified x matches an existing point on the curve, the interpolated result directly returns that exact point.
§Functionality
This method performs cubic interpolation using the general properties of the Catmull-Rom spline, which is well-suited for smooth curve fitting. It operates as follows:
-
Exact Point Check: If the x value matches an existing point, the method returns that point without further processing.
-
Bracketing Points Selection:
- Searches for two points that bracket the given x value (using
find_bracket_pointsfrom theInterpolatetrait). The method ensures that there are always enough points before and after the target x value to perform cubic interpolation.
- Searches for two points that bracket the given x value (using
-
Point Selection for Interpolation:
- Depending on the position of the target x value, four points (
p0, p1, p2, p3) are selected:- When
xis near the start of the points, select the first four. - When
xis near the end, select the last four. - Otherwise, select the two points just before and after the x value and include an additional adjacent point on either side.
- When
- Depending on the position of the target x value, four points (
-
Parameter Calculation:
- The
tparameter is derived, representing the normalized position of x betweenp1andp2.
- The
-
Cubic Interpolation:
- The interpolated y-coordinate is computed using the Catmull-Rom spline formula,
leveraging the
t-value and the y-coordinates of the four selected points.
- The interpolated y-coordinate is computed using the Catmull-Rom spline formula,
leveraging the
§Error Handling
This method returns an error in the following circumstances:
- If fewer than 4 points are available, it returns a
CurvesError::InterpolationErrorwith a corresponding message. - If the bracketing points cannot be identified (e.g., when
xis outside the range of points), the appropriate interpolation error is propagated. - If the curve is not a function of its abscissa at the point it is
asked about, it returns
InterpolationError::DegenerateInterval: several points atxleave the exact-match branch with no single value to return, and two consecutive points sharing an abscissa leave the interpolation bracket with zero width. Neither case picks an ordinate. SeeCurve::newfor the rule.
§Example
-
Interpolating smoothly along a curve defined by a set of points, avoiding sharp transitions between segments.
-
Provides a high degree of precision due to the use of the
Decimaltype forxandycalculations.
Source§impl<'de> Deserialize<'de> for Curve
impl<'de> Deserialize<'de> for Curve
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl GeometricObject<Point2D, Decimal> for Curve
impl GeometricObject<Point2D, Decimal> for Curve
Source§fn from_vector<T>(points: Vec<T>) -> Self
fn from_vector<T>(points: Vec<T>) -> Self
Builds a curve from a vector of convertible points.
§One point per abscissa
Same rule and same non-enforcement as Curve::new: a curve is a
function of its abscissa, and this constructor does not check it.
points is collected into a BTreeSet, which drops an exact
duplicate of an (x, y) pair but keeps two ordinates that merely
share an x. Aggregate before calling if the input can carry
several ordinates per abscissa, as the projection returned by
crate::surfaces::Surface::project_onto does.
Source§type Error = CurveError
type Error = CurveError
Source§fn get_points(&self) -> BTreeSet<&Point2D>
fn get_points(&self) -> BTreeSet<&Point2D>
BTreeSet containing references to the points that constitute the geometric object.
The BTreeSet ensures that the points are ordered and unique.Source§fn construct<T>(method: T) -> Result<Self, Self::Error>
fn construct<T>(method: T) -> Result<Self, Self::Error>
Source§fn vector(&self) -> Vec<&Point>
fn vector(&self) -> Vec<&Point>
Vec containing references to the points that constitute the geometric object.
This method simply converts the BTreeSet from get_points into a Vec.Source§impl GeometricTransformations<Point2D> for Curve
impl GeometricTransformations<Point2D> for Curve
Source§fn intersect_with(&self, other: &Self) -> Result<Vec<Point2D>, Self::Error>
fn intersect_with(&self, other: &Self) -> Result<Vec<Point2D>, Self::Error>
Reports the samples the two curves share.
§What it computes
Every pair of samples (p1, p2), p1 from self and p2 from
other, whose abscissas differ by less than 1e-6 and whose
ordinates differ by less than 1e-6, contributes p1 to the result.
The result is in (p1, p2) order: by self’s (x, y) order, then by
other’s for one p1.
§What it does not compute
Only coinciding samples are compared, never the segments between
them. Two curves that cross between their samples are not reported,
so (0, 0), (2, 2) and (0, 2), (2, 0), which cross at (1, 1),
return nothing. A sample of self that coincides with several samples
of other appears once per match. Whether a segment-crossing variant
should exist is an open owner decision (#858); this method documents
the current behaviour.
§Cost
Both point sets are sorted by abscissa, so the samples of other
within 1e-6 of p1 form a window that only slides forward as p1
advances. The walk is O(n + m) plus one step per matching pair, with
no allocation beyond the result (#858). It visits the pairs the
all-pairs comparison tested on ordinates, in the same order, so the
result and any error are the same. When some abscissa difference
could overflow, the all-pairs comparison runs instead, so the error
names the same pair.
§Errors
CurveError::AnalysisError when a coordinate difference leaves the
Decimal range.
Source§type Error = CurveError
type Error = CurveError
Source§fn translate(&self, deltas: Vec<&Decimal>) -> Result<Self, Self::Error>
fn translate(&self, deltas: Vec<&Decimal>) -> Result<Self, Self::Error>
Source§fn scale(&self, factors: Vec<&Decimal>) -> Result<Self, Self::Error>
fn scale(&self, factors: Vec<&Decimal>) -> Result<Self, Self::Error>
Source§fn derivative_at(&self, point: &Point2D) -> Result<Vec<Decimal>, Self::Error>
fn derivative_at(&self, point: &Point2D) -> Result<Vec<Decimal>, Self::Error>
Source§impl Index<usize> for Curve
Allows indexed access to the points in a Curve using usize indices.
impl Index<usize> for Curve
Allows indexed access to the points in a Curve using usize indices.
§Overview
This implementation provides intuitive, array-like access to the points
within a Curve. By using the Index<usize> trait, users can directly
reference specific points by their index within the internal points collection
without manually iterating or managing indices themselves.
§Behavior
- The
indexmethod fetches thePoint2Dat the specified position in the order of the curve’spoints(sorted by thePoint2Dordering, typically based on thexvalues). - If the specified index exceeds the range of available points, it triggers a panic
with the message
"Index out of bounds".
§Constraints
- The index must be a valid value between
0andself.points.len() - 1. - The
Curve’spointsare internally stored as aBTreeSet<Point2D>, so indexing reflects the natural order of the set, which is determined by theOrdtrait implementation forPoint2D.
§Fields Accessed
points: ABTreeSetofPoint2Dstructs representing the curve’s 2D points.
§Panics
This implementation will panic if:
- The index provided is out of bounds (less than
0or greater than/equal to the number of points in the curve).
§Use Cases
- Quickly accessing specific points on a curve during visualization, interpolation, or analysis operations.
- Performing operations that require stepwise access to points, such as slicing or filtering points along the curve.
§Example
Suppose you have a Curve instance curve with multiple points:
let point = curve[0]; // Access the first point§Important Notes
- This indexing implementation provides read-only access (
&Self::Output). - Modifying the
pointscollection or its contents directly is not allowed through this implementation, ensuring immutability when using indexed access.
§Type Associations
- Input:
- The input type for the
Indexoperation isusize, the standard for indexing.
- The input type for the
- Output:
- The output type for the
Indexoperation is a reference toPoint2D, specifically&Point2D.
- The output type for the
§Key Implementations
Index<usize>: Provides indexing-based access to curve points.
§Panics
Panics if index >= self.points.len(), the contract of
std::ops::Index and of Vec. Index has no error channel; for a
lookup that reports a missing index, use get_points().into_iter().nth(index).
Source§impl Interpolate<Point2D, Decimal> for Curve
Implementation of the Interpolate trait for the Curve struct.
impl Interpolate<Point2D, Decimal> for Curve
Implementation of the Interpolate trait for the Curve struct.
This implementation integrates the get_points method for the Curve structure,
providing access to its internal points. The Interpolate trait ensures compatibility
with various interpolation methods such as Linear, BiLinear, Cubic, and Spline
interpolations. By implementing this trait, Curve gains the ability to perform
interpolation operations and access bracketing points.
§Traits Involved
The Interpolate trait is an aggregation of multiple interpolation-related traits:
These underlying traits implement specific interpolation algorithms,
enabling Curve to support a robust set of interpolation options through the associated methods.
Depending on the use case and provided parameters (e.g., interpolation type and target x-coordinate),
the appropriate algorithm is invoked.
§See Also
Curve: The underlying mathematical structure being interpolated.Point2D: The fundamental data type for the curve’s points.Interpolate: The trait defining interpolation operations.
Source§fn find_bracket_points(
&self,
x: Decimal,
) -> Result<(usize, usize), InterpolationError>
fn find_bracket_points( &self, x: Decimal, ) -> Result<(usize, usize), InterpolationError>
Same contract and same indices as the trait’s default, including the
degenerate window over a repeated abscissa, without collecting the
points into a Vec first: the pair is found in O(log n) and its
index by counting the points before it (#858).
Source§fn interpolate(
&self,
x: Input,
interpolation_type: InterpolationType,
) -> Result<Point, InterpolationError>
fn interpolate( &self, x: Input, interpolation_type: InterpolationType, ) -> Result<Point, InterpolationError>
Source§impl LinearInterpolation<Point2D, Decimal> for Curve
Implements the LinearInterpolation trait for the Curve struct.
impl LinearInterpolation<Point2D, Decimal> for Curve
Implements the LinearInterpolation trait for the Curve struct.
This implementation provides linear interpolation functionality for a given set
of points on a curve. The interpolation computes the y value corresponding
to a given x value using the linear interpolation formula. The method ensures
that the input x is within the range of the curve’s defined points.
y = p1.y + (x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x)§Parameters
x: ADecimalrepresenting thex-coordinate for which the corresponding interpolatedyvalue is to be computed.
§Returns
Ok(Point2D): APoint2Dinstance containing the inputxvalue and the interpolatedyvalue.Err(CurvesError): Returns an error of typeCurvesError::InterpolationErrorin any of the following cases:- The curve does not have enough points for interpolation.
- The provided
xvalue is outside the range of the curve’s points. - Bracketing points for
xcannot be found.
§Working Mechanism
- The method calls
find_bracket_points(implemented in theInterpolatetrait) to locate the index pair(i, j)of two points that bracket thexvalue. - From the located points
p1andp2, the method calculates the interpolatedyvalue using the linear interpolation formula. - Finally, a
Point2Dis created and returned with the providedxand the computedyvalue.
§Implementation Details
- The function leverages
Decimalarithmetic for high precision in calculations. - It assumes that the provided points on the curve are sorted in ascending order
based on their
xvalues.
§Errors
This method returns a CurvesError in the following cases:
- Insufficient Points: When the curve has fewer than two points.
- Out-of-Range
x: When the inputxvalue lies outside the range of the defined points. - No Bracketing Points Found: When the method fails to find two points
that bracket the given
x. - Degenerate Bracket: When several points share the requested
abscissa. The curve has no value there and the bracket around it has
zero width, so the method reports
InterpolationError::DegenerateIntervalinstead of picking one of the ordinates stacked there.
§One point per abscissa
A curve is a function of its abscissa; see Curve::new. Asking for a
repeated abscissa returns InterpolationError::DegenerateInterval; no
ordinate of the stack is chosen.
Away from the stack the method still answers, reading the stack as a
vertical jump: an x strictly inside an interval bounded by a repeated
abscissa brackets against the point on its own side, the highest
ordinate when the stack bounds the interval on the left and the lowest
when it bounds it on the right.
§Example (How it works internally)
Suppose the curve is defined by the following points:
p1 = (2.0, 4.0)p2 = (5.0, 10.0)
Given x = 3.0, the method computes:
y = 4.0 + (3.0 - 2.0) * (10.0 - 4.0) / (5.0 - 2.0)
= 4 + 1 * 6 / 3
= 4 + 2
= 6.0It will return Point2D { x: 3.0, y: 6.0 }.
§See Also
find_bracket_points: Finds two points that bracket a value.Point2D: Represents points in 2D space.CurvesError: Represents errors related to curve operations.
Source§fn linear_interpolate(&self, x: Decimal) -> Result<Point2D, InterpolationError>
fn linear_interpolate(&self, x: Decimal) -> Result<Point2D, InterpolationError>
Source§impl MergeAxisInterpolate<Point2D, Decimal> for Curvewhere
Self: Sized,
impl MergeAxisInterpolate<Point2D, Decimal> for Curvewhere
Self: Sized,
Source§fn merge_axis_interpolate(
&self,
other: &Self,
interpolation: InterpolationType,
) -> Result<(Self, Self), Self::Error>
fn merge_axis_interpolate( &self, other: &Self, interpolation: InterpolationType, ) -> Result<(Self, Self), Self::Error>
Resamples both curves onto the shared x-grid of their merged indices.
Each result holds one point per abscissa of that grid, so it is a
function of its abscissa whatever the inputs were: where a curve
already has a point at an abscissa, the one kept is the first match
in (x, y) order, so a stack of ordinates collapses to its lowest,
and elsewhere the point is interpolated. Aggregate such a curve
before merging if a different ordinate is wanted.
Source§fn merge_axis_index<'a>(&'a self, other: &'a Self) -> Vec<Input>
fn merge_axis_index<'a>(&'a self, other: &'a Self) -> Vec<Input>
Source§impl MetricsExtractor for Curve
A default implementation for the Curve type using a provided default strategy.
impl MetricsExtractor for Curve
A default implementation for the Curve type using a provided default strategy.
This implementation provides a basic approach to computing curve metrics by using interpolation and statistical methods available in the standard curve analysis library.
§Note
This is a minimal implementation that may need to be customized or enhanced based on specific requirements or domain-specific analysis needs.
Source§fn compute_basic_metrics(&self) -> Result<BasicMetrics, MetricsError>
fn compute_basic_metrics(&self) -> Result<BasicMetrics, MetricsError>
Source§fn compute_shape_metrics(&self) -> Result<ShapeMetrics, MetricsError>
fn compute_shape_metrics(&self) -> Result<ShapeMetrics, MetricsError>
Source§fn compute_range_metrics(&self) -> Result<RangeMetrics, MetricsError>
fn compute_range_metrics(&self) -> Result<RangeMetrics, MetricsError>
Source§fn compute_trend_metrics(&self) -> Result<TrendMetrics, MetricsError>
fn compute_trend_metrics(&self) -> Result<TrendMetrics, MetricsError>
Source§fn compute_risk_metrics(&self) -> Result<RiskMetrics, MetricsError>
fn compute_risk_metrics(&self) -> Result<RiskMetrics, MetricsError>
Source§fn compute_curve_metrics(&self) -> Result<Metrics, MetricsError>
fn compute_curve_metrics(&self) -> Result<Metrics, MetricsError>
CurveMetrics struct. Read moreSource§fn compute_surface_metrics(&self) -> Result<Metrics, MetricsError>
fn compute_surface_metrics(&self) -> Result<Metrics, MetricsError>
Source§impl SplineInterpolation<Point2D, Decimal> for Curve
Implements the SplineInterpolation trait for the Curve struct, providing functionality
to perform cubic spline interpolation.
impl SplineInterpolation<Point2D, Decimal> for Curve
Implements the SplineInterpolation trait for the Curve struct, providing functionality
to perform cubic spline interpolation.
§Overview
This method calculates the interpolated y value for a given x value by using cubic
spline interpolation on the points in the Curve. The method ensures a smooth transition
between points by computing second derivatives of the curve at each point, and uses those
derivatives in the spline interpolation formula.
§Parameters
x: The x-coordinate at which the curve should be interpolated. This value is passed as aDecimalfor precise calculations.
§Returns
- On success, returns a
Point2Dinstance representing the interpolated point. - On error, returns a
CurvesErrorindicating the reason for failure (e.g., insufficient points or an out-of-range x-coordinate).
§Errors
- Returns
CurvesError::InterpolationErrorwith an appropriate error message in the following cases:- If the curve contains fewer than three points, as spline interpolation requires at least three points.
- If the given
xvalue lies outside the range of x-coordinates spanned by the points in the curve. - If a valid segment for interpolation cannot be located.
§Details
- Validation:
- Ensures that there are at least three points in the curve for spline interpolation.
- Validates that the provided
xvalue is within the range ofxvalues of the curve.
- Exact Match: If the
xvalue matches the x-coordinate of an existing point, the correspondingPoint2Dis returned immediately. - Second Derivative Calculation:
- Uses a tridiagonal matrix to compute the second derivatives at each point. This step involves setting up the system of equations based on the boundary conditions (natural spline) and solving it using the Thomas algorithm.
- Segment Identification:
- Determines the segment (interval between two consecutive points) in which the provided
xvalue lies.
- Determines the segment (interval between two consecutive points) in which the provided
- Interpolation:
- Computes the interpolated y-coordinate using the cubic spline formula, which is based on the second derivatives and the positions of the surrounding points.
§Implementation Notes
- This implementation uses
Decimalfrom therust_decimalcrate to ensure high precision in calculations, making it suitable for scientific and financial applications. - The Thomas algorithm is employed to solve the tridiagonal matrix system efficiently.
- The method assumes natural spline boundary conditions, where the second derivatives at the endpoints are set to zero, ensuring a smooth and continuous curve shape.
§Example Usage
Refer to the documentation for how to use the SplineInterpolation trait, as examples
are not provided inline with this implementation.
§See Also
SplineInterpolation: The trait definition for spline interpolation.Point2D: Represents a point in 2D space.Curve: Represents a mathematical curve made up of points for interpolation.CurveError: Enumerates possible errors during curve operations.
Source§fn spline_interpolate(&self, x: Decimal) -> Result<Point2D, InterpolationError>
fn spline_interpolate(&self, x: Decimal) -> Result<Point2D, InterpolationError>
Performs cubic spline interpolation for a given x-coordinate and returns the interpolated
Point2D value. This function computes the second derivatives of the curve points, solves
a tridiagonal system to derive the interpolation parameters, and evaluates the spline
function for the provided x value.
§Parameters
x:- The x-coordinate at which the interpolation is to be performed.
- Must be of type
Decimal.
§Returns
-
Ok(Point2D):- The
Point2Dinstance representing the interpolated point at the givenxvalue. - The interpolated
yvalue is calculated based on the cubic spline interpolation algorithm.
- The
-
Err(CurvesError):- Returned when an error occurs during the interpolation process, such as:
- Insufficient points provided (less than 3 points).
- The given
xis outside the valid range of the points. - Unable to determine the correct segment for interpolation.
- Returned when an error occurs during the interpolation process, such as:
§Errors
CurvesError::InterpolationError:- Occurs under the following conditions:
- “Need at least three points for spline interpolation”: Requires at least 3 points to perform cubic spline interpolation.
- “x is outside the range of points”:
The provided
xvalue lies outside the domain of the curve points. - “Could not find valid segment for interpolation”: Spline interpolation fails due to an invalid segment determination.
- Occurs under the following conditions:
§Pre-conditions
- The curve must contain at least three points for cubic spline interpolation.
- The
xvalue must fall within the range of the curve’s x-coordinates.
§Implementation Details
- Inputs:
- Uses the
get_pointsmethod of the curve to retrieve the list ofPoint2Dinstances that define the interpolation curve. - Computes the second derivatives (
m) for each point using the Thomas algorithm to solve a tridiagonal system.
- Uses the
- Boundary Conditions:
- Natural spline boundary conditions are used, with the second derivatives on the boundary set to zero.
- Interpolation:
- Determines the segment
[x_i, x_{i+1}]to which the inputxbelongs. - Uses the cubic spline equation to calculate the interpolated
yvalue.
- Determines the segment
§Mathematical Formulation
Let x_i, x_{i+1}, y_i, y_{i+1} refer to the points of the segment where x lies.
The cubic spline function at x is computed as follows:
S(x) = m_i * (x_{i+1} - x)^3 / (6 * h)
+ m_{i+1} * (x - x_i)^3 / (6 * h)
+ (y_i / h - h * m_i / 6) * (x_{i+1} - x)
+ (y_{i+1} / h - h * m_{i+1} / 6) * (x - x_i)Where:
m_i,m_{i+1}are the second derivatives atx_iandx_{i+1}.h = x_{i+1} - x_iis the distance between the two points.(x_{i+1} - x)and(x - x_i)are the relative distances within the segment.
§Example Usages (Non-code)
This method is typically used for high-precision curve fitting or graphical rendering where smooth transitions between points are essential. Common applications include:
- Signal processing.
- Data interpolation for missing values.
- Smooth graphical representations of mathematical functions.
§Related Types
Point2D: Represents a 2D point and is used as input/output for this function.CurveErrorRepresents any error encountered during interpolation.
§Performance
- The function operates with
O(n)complexity, wherenis the number of points. The tridiagonal system is solved efficiently using the Thomas algorithm. - Every call solves the system again. To read one curve at many
abscissas, solve it once with
CurveSpline, which returns the same answers.
§Notes
- Natural spline interpolation may introduce minor deviations beyond the range of existing data points due to its boundary conditions. For strictly constrained results, consider alternative interpolation methods, such as linear or cubic Hermite interpolation.
- The knots must be a function of their abscissa. Several points at
the requested
xleave the exact-match branch with no single value to return, and any repeated abscissa collapses a knot interval, so both returnInterpolationError::DegenerateIntervalrather than picking an ordinate. SeeCurve::newfor the rule.
Source§impl StatisticalCurve for Curve
impl StatisticalCurve for Curve
Source§fn get_x_values(&self) -> Vec<Decimal>
fn get_x_values(&self) -> Vec<Decimal>
Source§fn generate_statistical_curve(
&self,
basic_metrics: &BasicMetrics,
shape_metrics: &ShapeMetrics,
range_metrics: &RangeMetrics,
trend_metrics: &TrendMetrics,
num_points: usize,
seed: Option<u64>,
) -> Result<Curve, CurveError>
fn generate_statistical_curve( &self, basic_metrics: &BasicMetrics, shape_metrics: &ShapeMetrics, range_metrics: &RangeMetrics, trend_metrics: &TrendMetrics, num_points: usize, seed: Option<u64>, ) -> Result<Curve, CurveError>
Source§fn generate_refined_statistical_curve(
&self,
basic_metrics: &BasicMetrics,
shape_metrics: &ShapeMetrics,
range_metrics: &RangeMetrics,
trend_metrics: &TrendMetrics,
num_points: usize,
max_attempts: usize,
tolerance: Decimal,
seed: Option<u64>,
) -> Result<Curve, CurveError>
fn generate_refined_statistical_curve( &self, basic_metrics: &BasicMetrics, shape_metrics: &ShapeMetrics, range_metrics: &RangeMetrics, trend_metrics: &TrendMetrics, num_points: usize, max_attempts: usize, tolerance: Decimal, seed: Option<u64>, ) -> Result<Curve, CurveError>
Source§fn verify_curve_metrics(
&self,
curve: &Curve,
target_metrics: &BasicMetrics,
tolerance: Decimal,
) -> Result<bool, CurveError>
fn verify_curve_metrics( &self, curve: &Curve, target_metrics: &BasicMetrics, tolerance: Decimal, ) -> Result<bool, CurveError>
Auto Trait Implementations§
impl Freeze for Curve
impl RefUnwindSafe for Curve
impl Send for Curve
impl Sync for Curve
impl Unpin for Curve
impl UnsafeUnpin for Curve
impl UnwindSafe for Curve
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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 moreSource§impl<T> PartialSchema for Twhere
T: ComposeSchema + ?Sized,
impl<T> PartialSchema for Twhere
T: ComposeSchema + ?Sized,
Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.