svg_path/ellipse

Lower-level helpers for SVG elliptical arcs.

Most users should work with svg_path.Arc values through the root module. This module is the more technical layer for users who want the ellipse math behind SVG arcs.

SVG path data uses endpoint parameterization for elliptical arcs, represented here by EndpointArcData. An A command stores the current point, an end point, two radii, an x_axis_rotation, a large_arc flag, and a sweep flag. The radii are the ellipse’s semi-axes. x_axis_rotation is the angle, in degrees, from the current coordinate system’s x-axis to the ellipse’s x-axis. The large_arc flag selects an arc spanning more than 180 degrees when it is True, and an arc spanning at most 180 degrees when it is False. The sweep flag selects increasing ellipse angles when it is True, and decreasing ellipse angles when it is False.

This endpoint form is compact and fits SVG paths nicely, but it is not the most convenient form for evaluation, splitting, or analysis. SVG’s implementation notes also define center parameterization, represented here by CenterArcData: an ellipse center, a corrected radius, the same x_axis_rotation, a start_angle, and a signed delta_angle.

endpoint_to_center converts EndpointArcData into CenterArcData. It follows SVG’s forgiving radius rules: radii are made positive, and if the requested ellipse is too small to connect the endpoints, both radii are scaled up uniformly until there is exactly one solution. CenterArcData.radius is therefore the corrected radius, not necessarily the input radius.

Units are intentionally mixed to match their sources. x_axis_rotation remains in degrees because SVG path data uses degrees. start_angle and delta_angle are in radians because they are used with trigonometric functions. start_angle is the angle before the ellipse is stretched and rotated. delta_angle is the signed angular travel from the start point to the end point.

For values returned by endpoint_to_center, these invariants hold:

The public CenterArcData constructor is intentionally available for advanced callers. The invariants above are guaranteed for values produced by endpoint_to_center; if you construct CenterArcData yourself, these helpers will use the values you provide without trying to repair them.

Evaluation with arc_point(arc, at: t) uses angular progress through CenterArcData:

angle = arc.start_angle +. t *. arc.delta_angle

This is not arc-length parameterization. Equal t steps correspond to equal angle steps in the unstretched ellipse coordinate system, not equal distances along the rendered curve. The at value is not clamped; values outside 0.0..1.0 extrapolate along the same ellipse. split_arc follows the same unclamped policy; use split_arc_inside when outside values should return an error. split_arc_many and split_arc_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 arcs.

Types

Equivalent of transform.Matrix, redefined by the ellipse module to avoid a circular dependency.

Has the same six-value layout as SVG matrix(a b c d e f).

pub opaque type Affine

An axis-aligned bounding box for an elliptical arc.

pub type BoundingBox {
  BoundingBox(min: Point, max: Point)
}

Constructors

Center-parameter representation of an SVG elliptical arc.

Values returned by endpoint_to_center use corrected positive radii and a sweep-consistent delta_angle. The constructor is public for advanced callers; hand-constructed values are not normalized or repaired by this module.

pub type CenterArcData {
  CenterArcData(
    center: Point,
    radius: Point,
    x_axis_rotation: Float,
    start_angle: Float,
    delta_angle: Float,
  )
}

Constructors

  • CenterArcData(
      center: Point,
      radius: Point,
      x_axis_rotation: Float,
      start_angle: Float,
      delta_angle: Float,
    )

A cubic Bezier curve produced by the ellipse math helpers.

pub type Cubic {
  Cubic(
    start: Point,
    control1: Point,
    control2: Point,
    end: Point,
  )
}

Constructors

Endpoint-parameter representation of an SVG elliptical arc.

This is the same shape as an SVG A path command plus its explicit start point. radius values are not corrected until conversion to CenterArcData.

pub type EndpointArcData {
  EndpointArcData(
    start: Point,
    radius: Point,
    x_axis_rotation: Float,
    large_arc: Bool,
    sweep: Bool,
    end: Point,
  )
}

Constructors

  • EndpointArcData(
      start: Point,
      radius: Point,
      x_axis_rotation: Float,
      large_arc: Bool,
      sweep: Bool,
      end: Point,
    )

Errors returned by ellipse and collapsed-arc helpers.

pub type Error {
  DegenerateInputArc
  NotCollapsedToLine
  SplitOutsideArc
}

Constructors

  • DegenerateInputArc

    The source arc cannot be converted to center-parameter form.

  • NotCollapsedToLine

    The transformed arc did not collapse to a line.

  • SplitOutsideArc

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

A lightweight point used by the ellipse math helpers.

pub type Point {
  Point(x: Float, y: Float)
}

Constructors

  • Point(x: Float, y: Float)

Values

pub fn angle_at(arc: CenterArcData, t t: Float) -> Float

Return the angle at t using this module’s angular-progress parameterization.

pub fn arc_bounding_box(arc: CenterArcData) -> BoundingBox

Return the arc’s exact axis-aligned bounding box.

pub fn arc_derivative(arc: CenterArcData, at t: Float) -> Point

Return the derivative with respect to angular progress t.

This is the tangent direction followed from the arc start to the arc end. For the raw derivative with respect to the ellipse angle, use derivative_at_angle.

pub fn arc_end_angle(arc: CenterArcData) -> Float

Return the arc’s end angle in radians.

pub fn arc_large_arc(arc: CenterArcData) -> Bool

Return whether the arc spans more than 180 degrees.

pub fn arc_point(arc: CenterArcData, at t: Float) -> Point

Evaluate an arc at angular progress t.

t is not clamped. 0.0 evaluates the start of the arc, 1.0 evaluates the end of the arc, and values outside that range extrapolate along the same ellipse.

pub fn arc_sweep(arc: CenterArcData) -> Bool

Return whether the arc sweeps through increasing center-parameter angles.

pub fn arc_to_cubics(
  start start: Point,
  radius radius: Point,
  x_axis_rotation x_axis_rotation: Float,
  large_arc large_arc: Bool,
  sweep sweep: Bool,
  end end: Point,
) -> Result(List(Cubic), Error)

Convert an elliptical arc to one or more cubic Bezier curves.

The arc is split into chunks of at most a quarter turn. This is the common deterministic SVG arc approximation strategy. This function does not accept a tolerance; use a higher-level helper if you want SVG path segments back.

pub fn center_arc_data(
  center center: Point,
  radius radius: Point,
  x_axis_rotation x_axis_rotation: Float,
  start_angle start_angle: Float,
  delta_angle delta_angle: Float,
) -> CenterArcData

Create center arc data.

This does not normalize or repair the given values.

pub fn center_to_endpoint(data: CenterArcData) -> EndpointArcData

Convert center arc data back to endpoint arc data.

The returned endpoint data uses corrected radii from the center form, and derives large_arc and sweep from delta_angle.

pub fn collapsed_arc_line(
  start start: Point,
  radius radius: Point,
  x_axis_rotation x_axis_rotation: Float,
  large_arc large_arc: Bool,
  sweep sweep: Bool,
  end end: Point,
  by transform: Affine,
) -> Result(#(Point, Point), Error)

Convert an arc collapsed by an affine transform into a single line segment.

If the collapsed arc’s extrema require more than one segment to preserve its out-and-back motion, use collapsed_arc_subpath.

pub fn collapsed_arc_subpath(
  start start: Point,
  radius radius: Point,
  x_axis_rotation x_axis_rotation: Float,
  large_arc large_arc: Bool,
  sweep sweep: Bool,
  end end: Point,
  by transform: Affine,
) -> Result(List(Point), Error)

Convert an arc collapsed by an affine transform into a line-based subpath.

pub fn derivative_at_angle(
  arc: CenterArcData,
  angle angle: Float,
) -> Point

Return the derivative with respect to the center-parameter angle.

pub fn ellipse_affine(
  a a: Float,
  b b: Float,
  c c: Float,
  d d: Float,
  e e: Float,
  f f: Float,
) -> Affine

Create an affine matrix for ellipse helpers.

pub fn endpoint_arc_data(
  start start: Point,
  radius radius: Point,
  x_axis_rotation x_axis_rotation: Float,
  large_arc large_arc: Bool,
  sweep sweep: Bool,
  end end: Point,
) -> EndpointArcData

Create endpoint arc data.

pub fn endpoint_to_center(
  data: EndpointArcData,
) -> Result(CenterArcData, Error)

Convert endpoint arc data to center parameterization.

Radii are corrected according to SVG’s implementation notes: negative radii are made positive, and radii that are too small to reach between the endpoints are scaled up uniformly.

pub fn point(point: Point, by transform: Affine) -> Point

Transform a point by an affine matrix.

pub fn point_at_angle(
  arc: CenterArcData,
  angle angle: Float,
) -> Point

Evaluate an arc at a center-parameter angle in radians.

pub fn split_arc(
  arc: CenterArcData,
  at t: Float,
) -> #(CenterArcData, CenterArcData)

Split an arc at angular progress t.

t is not clamped. Values outside 0.0..1.0 extrapolate along the same ellipse, matching arc_point.

pub fn split_arc_inside(
  arc: CenterArcData,
  at t: Float,
) -> Result(#(CenterArcData, CenterArcData), Error)

Split an arc at angular progress t, returning an error outside 0.0..1.0.

Values exactly at 0.0 or 1.0 are accepted and produce one zero-length arc.

pub fn split_arc_inside_many(
  arc: CenterArcData,
  at points: List(Float),
) -> Result(List(CenterArcData), Error)

Split an arc at multiple angular progress 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 arcs. Values exactly at 0.0 or 1.0 are accepted.

pub fn split_arc_many(
  arc: CenterArcData,
  at points: List(Float),
) -> List(CenterArcData)

Split an arc at multiple angular progress 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 arcs. Values outside 0.0..1.0 are allowed and extrapolate along the same ellipse, matching split_arc.

pub fn transformed_axes(
  radius radius: Point,
  x_axis_rotation x_axis_rotation: Float,
  by transform: Affine,
) -> Result(#(Point, Float), Error)

Transform an arc’s radius and x-axis rotation.

Returns the new radius and x-axis rotation for the transformed ellipse.

Search Document