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:

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

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

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

  • SplitOutsideBezier

    The requested split point is outside the curve’s 0.0..1.0 parameter range.

  • DegenerateTangent

    A tangent direction was too small to normalize.

  • UnderdeterminedCubicFit

    The 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_end(curve: BezierData) -> BezierPoint

Return the curve’s end point.

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.

Search Document