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:
arc_sweep(arc)isTruewhendelta_angle >= 0.0.arc_large_arc(arc)isTruewhenabs(delta_angle) > pi.arc_end_angle(arc) == arc.start_angle + arc.delta_angle.arc_point(arc, at: 0.0)is the arc start point, modulo floating-point roundoff.arc_point(arc, at: 1.0)is the arc end point, modulo floating-point roundoff.split_arc(arc, at: t)preservescenter,radius, andx_axis_rotation, and dividesdelta_angleat angular progresst.
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
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
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
Errors returned by ellipse and collapsed-arc helpers.
pub type Error {
DegenerateInputArc
NotCollapsedToLine
SplitOutsideArc
}
Constructors
-
DegenerateInputArcThe source arc cannot be converted to center-parameter form.
-
NotCollapsedToLineThe transformed arc did not collapse to a line.
-
SplitOutsideArcThe requested split point is outside the arc’s
0.0..1.0parameter range.
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_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.