svg_path/bezier
Lower-level helpers for Bezier curves.
Most users should work with svg_path.Line, svg_path.QuadraticBezier,
and svg_path.CubicBezier values through the root module. This module is
the more technical layer for users who want the curve math behind line and
Bezier segments.
For cubic fitting with ordinary svg_path.Point values and root
svg_path.Error, use the root-module wrappers
svg_path.fit_cubic_with_endpoint_tangents and
svg_path.fit_cubic_with_endpoints.
A line segment is a degree-1 Bezier curve, a quadratic Bezier has one
control point, and a cubic Bezier has two control points. All evaluation
and splitting helpers use the standard Bezier parameter t:
bezier_point(curve, at: 0.0)is the curve start point.bezier_point(curve, at: 1.0)is the curve end point.bezier_derivative(curve, at: t)is the derivative with respect tot.split_bezier(curve, at: t)preserves the curve degree and divides it with de Casteljau’s algorithm.map_points(curve, with: f)maps the curve’s defining points.
The at value is not clamped. Values outside 0.0..1.0 extrapolate along
the same polynomial curve. split_bezier follows the same unclamped policy;
use split_bezier_inside when outside values should return an error.
split_bezier_many and split_bezier_inside_many sort their split points,
remove exact duplicates, and trim boundary 0.0 or 1.0 split points that
would only create zero-length boundary curves.
map_points maps the control points that define the curve. For nonlinear
functions, this is not the exact image of every point on the rendered curve;
it is the Bezier curve obtained by applying the function to the defining
points.
Types
Bezier-parameter representation of line, quadratic, and cubic curves.
pub type BezierData {
LinearBezierData(start: BezierPoint, end: BezierPoint)
QuadraticBezierData(
start: BezierPoint,
control: BezierPoint,
end: BezierPoint,
)
CubicBezierData(
start: BezierPoint,
control1: BezierPoint,
control2: BezierPoint,
end: BezierPoint,
)
}
Constructors
-
LinearBezierData(start: BezierPoint, end: BezierPoint)A degree-1 Bezier curve.
-
QuadraticBezierData( start: BezierPoint, control: BezierPoint, end: BezierPoint, )A degree-2 Bezier curve.
-
CubicBezierData( start: BezierPoint, control1: BezierPoint, control2: BezierPoint, end: BezierPoint, )A degree-3 Bezier curve.
A lightweight point used by the Bezier math helpers.
pub type BezierPoint {
BezierPoint(x: Float, y: Float)
}
Constructors
-
BezierPoint(x: Float, y: Float)
An axis-aligned bounding box for a Bezier curve.
pub type BoundingBox {
BoundingBox(min: BezierPoint, max: BezierPoint)
}
Constructors
-
BoundingBox(min: BezierPoint, max: BezierPoint)
Error measurements for a fitted cubic.
pub type CubicFitError {
CubicFitError(
root_sum_square: Float,
root_mean_square: Float,
max: Float,
)
}
Constructors
-
CubicFitError( root_sum_square: Float, root_mean_square: Float, max: Float, )Arguments
- root_sum_square
-
sqrt(sum(distance(sample, fitted)^2)). - root_mean_square
-
sqrt(sum(distance(sample, fitted)^2) / sample_count). - max
-
The largest sample distance.
A point where a cubic Bezier intersects itself.
pub type CubicSelfIntersection {
CubicSelfIntersection(s: Float, t: Float, point: BezierPoint)
}
Constructors
-
CubicSelfIntersection(s: Float, t: Float, point: BezierPoint)
Options for direct cubic self-intersection detection.
pub type CubicSelfIntersectionOptions {
CubicSelfIntersectionOptions(
minimum_arc_length_separation: Float,
distance_tolerance: Float,
)
}
Constructors
-
CubicSelfIntersectionOptions( minimum_arc_length_separation: Float, distance_tolerance: Float, )Arguments
- minimum_arc_length_separation
-
Minimum required arc length between the two visits to the intersection.
- distance_tolerance
-
Maximum allowed distance between the two evaluated points.
Errors returned by Bezier helpers.
pub type Error {
SplitOutsideBezier
DegenerateTangent
UnderdeterminedCubicFit
InvalidCubicSelfIntersectionMinimumArcLengthSeparation(Float)
InvalidCubicSelfIntersectionDistanceTolerance(Float)
}
Constructors
-
SplitOutsideBezierThe requested split point is outside the curve’s
0.0..1.0parameter range. -
DegenerateTangentA tangent direction was too small to normalize.
-
UnderdeterminedCubicFitThe provided samples do not determine stable cubic handle lengths.
-
InvalidCubicSelfIntersectionMinimumArcLengthSeparation(Float)Cubic self-intersection minimum arc length separation must be greater than zero.
-
InvalidCubicSelfIntersectionDistanceTolerance(Float)Cubic self-intersection distance tolerance must be greater than zero.
Values
pub fn bezier_bounding_box(curve: BezierData) -> BoundingBox
Return the curve’s exact axis-aligned bounding box over 0.0..1.0.
pub fn bezier_derivative(
curve: BezierData,
at t: Float,
) -> BezierPoint
Return the derivative with respect to Bezier parameter t.
pub fn bezier_point(
curve: BezierData,
at t: Float,
) -> BezierPoint
Evaluate a Bezier curve at parameter t.
t is not clamped. 0.0 evaluates the start of the curve, 1.0 evaluates
the end of the curve, and values outside that range extrapolate along the
same polynomial curve.
pub fn bezier_start(curve: BezierData) -> BezierPoint
Return the curve’s start point.
pub fn cubic_inflection_parameters(
curve: BezierData,
) -> List(Float)
Return the Bezier parameters of a cubic curve’s inflection points.
A cubic Bezier can have up to two inflection points. Values outside
0.0..1.0, values too close to the endpoints, and numerically duplicate
roots are not returned. Splitting at these parameters gives pieces with no
interior inflection, which is often the useful first step before treating
each piece as a convex curve plus its chord. Linear and quadratic curves
return an empty list.
pub fn cubic_self_intersections(
curve: BezierData,
) -> Result(List(CubicSelfIntersection), Error)
Return direct self-intersections of a cubic Bezier curve.
Linear and quadratic Beziers return an empty list. Cubics return either an
empty list or one ordinary self-intersection. Endpoint intersections are
treated the same as interior intersections: the two parameters only need to
be separated by minimum_arc_length_separation along the curve.
pub fn cubic_self_intersections_with(
curve: BezierData,
options options: CubicSelfIntersectionOptions,
) -> Result(List(CubicSelfIntersection), Error)
Return direct self-intersections of a cubic Bezier curve using explicit options.
minimum_arc_length_separation is measured in the same coordinate units as
the curve. Larger values are stricter. distance_tolerance is also measured
in the same coordinate units. Smaller values are stricter.
pub fn default_cubic_self_intersection_options() -> CubicSelfIntersectionOptions
Return the default options for direct cubic self-intersection detection.
pub fn fit_cubic_with_endpoint_tangents(
start start: BezierPoint,
end end: BezierPoint,
start_tangent start_tangent: BezierPoint,
end_tangent end_tangent: BezierPoint,
samples samples: List(#(Float, BezierPoint)),
) -> Result(#(BezierData, CubicFitError), Error)
Fit a cubic with fixed endpoints and endpoint tangent directions.
The fitted cubic has the form:
control1 = start + a * unit(start_tangent)
control2 = end - b * unit(end_tangent)
end_tangent has the usual Bezier derivative direction at t = 1, so it
points in the direction the curve is travelling as it reaches end.
The scalar handle lengths a and b are chosen by least squares against
the provided (t, point) samples. Samples are allowed at any t, but
endpoint samples do not add handle information.
pub fn fit_cubic_with_endpoints(
start start: BezierPoint,
end end: BezierPoint,
samples samples: List(#(Float, BezierPoint)),
) -> Result(#(BezierData, CubicFitError), Error)
Fit a cubic with fixed endpoints and no tangent constraints.
The fitted cubic has exactly the provided start and end. Its two control
points are chosen by least squares against the provided (t, point) samples.
Samples are allowed at any t, but endpoint samples do not add control
point information.
pub fn map_points(
curve: BezierData,
with f: fn(BezierPoint) -> BezierPoint,
) -> BezierData
Map a Bezier curve’s defining points.
For nonlinear functions, this is not the exact image of every point on the rendered curve. It maps the control polygon and preserves the curve degree.
pub fn split_bezier(
curve: BezierData,
at t: Float,
) -> #(BezierData, BezierData)
Split a Bezier curve at parameter t.
t is not clamped. Values outside 0.0..1.0 extrapolate along the same
polynomial curve, matching bezier_point.
pub fn split_bezier_inside(
curve: BezierData,
at t: Float,
) -> Result(#(BezierData, BezierData), Error)
Split a Bezier curve at parameter t, returning an error outside 0.0..1.0.
Values exactly at 0.0 or 1.0 are accepted and produce one zero-length
curve.
pub fn split_bezier_inside_many(
curve: BezierData,
at points: List(Float),
) -> Result(List(BezierData), Error)
Split a Bezier curve at multiple parameter values, erroring outside 0.0..1.0.
Split points are sorted, exact duplicates are removed, and boundary 0.0
or 1.0 split points are trimmed when they would only create zero-length
boundary curves. Values exactly at 0.0 or 1.0 are accepted.
pub fn split_bezier_many(
curve: BezierData,
at points: List(Float),
) -> List(BezierData)
Split a Bezier curve at multiple parameter values.
Split points are sorted, exact duplicates are removed, and boundary 0.0
or 1.0 split points are trimmed when they would only create zero-length
boundary curves. Values outside 0.0..1.0 are allowed and extrapolate along
the same polynomial curve, matching split_bezier.