pub struct PathBuilder { /* private fields */ }Expand description
Accumulates verbs and points into a Path.
Implementations§
Source§impl PathBuilder
impl PathBuilder
pub fn new() -> Self
pub fn with_fill_rule(self, rule: FillRule) -> Self
pub fn move_to(&mut self, p: Vec2) -> &mut Self
Sourcepub fn line_to(&mut self, p: Vec2) -> &mut Self
pub fn line_to(&mut self, p: Vec2) -> &mut Self
Add a line segment.
A line before any move_to implicitly starts the subpath at the
origin, matching how path data from SVG and font outlines behaves when
it omits the opening move.
pub fn quad_to(&mut self, ctrl: Vec2, to: Vec2) -> &mut Self
Sourcepub fn conic_to(&mut self, ctrl: Vec2, to: Vec2, weight: f32) -> &mut Self
pub fn conic_to(&mut self, ctrl: Vec2, to: Vec2, weight: f32) -> &mut Self
Append a rational quadratic – a conic – through one control point.
The curve a circular arc actually is, and the one a font or an SVG
hands over. weight is the control point’s pull: one is an ordinary
quadratic, less than one is elliptical, more is hyperbolic, and
sqrt(2)/2 with the control at the corner of a square is exactly a
quarter circle.
§Why this becomes quadratics here rather than surviving as a verb
A conic of weight one is a quadratic, and splitting a conic in half moves its weight toward one – quadratically, so a handful of splits puts it within a thousandth. So this subdivides until the weight is near enough and emits ordinary quadratics, which every part of this crate and the tessellator already understand.
The alternative is a verb of its own, converted during flattening where the transform’s scale is known. That is what a renderer does when it wants an absolute error in device pixels. What is bought here instead is a relative error: the approximation is a fixed fraction of the curve’s own size, so it stays correct at every scale the path is later drawn at, and nothing downstream has to learn a fifth verb.
§Subdividing by the error rather than by the weight
The criterion below stops when the error is small enough, and that is worth spelling out because the obvious criterion – stop when the weight is near one – is what it replaced, and it was expensive by a factor of four.
A weight criterion counts halvings of the weight’s distance from one, and each halving doubles the output. So a curve gets subdivided by how hyperbolic it is rather than by how much the approximation misses. A rounded superellipse octant emits conics at weights around 0.7 and 8.3; under the weight criterion those became thirty-two and sixty-four quadratics, and the corpus scene cost 1340 vertices where a circle costs 40. It is 305 now, and the stroked one 274 against 922.
The error itself is four lines: a rational quadratic and its weight-one counterpart differ most at their midpoints, both midpoints have closed forms, and the distance between them measured against the chord is a relative bound – the same quantity the weight criterion was a proxy for, at the same tolerance of a thousandth.
It is a little less accurate where the proxy over-subdivided, which is most places. Measured against the old criterion: four pixels of the filled corpus scene and eight of the stroked one, out of sixteen thousand, and all of them at the shape’s tangent extremes where the outline lies along a pixel boundary and a sub-pixel shift moves every sample in the pixel together. Every comparison in the workspace passes unchanged, including the pin on upstream’s boundary points, which is what says the outline is still the shape upstream draws.
A weight that is zero or negative or not finite describes no curve, and gives the straight line between the ends.
pub fn cubic_to(&mut self, c0: Vec2, c1: Vec2, to: Vec2) -> &mut Self
Sourcepub fn arc(
&mut self,
center: Vec2,
radii: Vec2,
start: f32,
sweep: f32,
) -> &mut Self
pub fn arc( &mut self, center: Vec2, radii: Vec2, start: f32, sweep: f32, ) -> &mut Self
Append an elliptical arc, centered at center with the given radii.
Angles are in radians, measured from the positive X axis toward positive
Y, and sweep may be negative to travel the other way. A sweep of a
full turn or more is clamped to one: going round twice draws the same
pixels as going round once, and the extra segments are cost without a
picture.
If the path has a current point, a line joins it to the arc’s start, the
way SVG and Skia both behave; otherwise the arc begins with a move. That
is what makes a pie slice move_to(center) then arc(..) then close,
and a progress ring just arc(..) on an empty builder.
§Accuracy
The arc is emitted as cubics of at most a quarter turn each, with
control points at (4/3)·tan(θ/4) of the radius for a segment spanning
θ. That expression is where the familiar 0.5523 comes from — it is
this evaluated at a right angle — and using the constant for any other
angle is the usual way to draw an arc that is visibly wrong near its
ends. At a quarter turn the worst radial error is under three parts in
ten thousand, so a circle a thousand pixels across is off by less than a
third of a pixel.
Sourcepub fn close(&mut self) -> &mut Self
pub fn close(&mut self) -> &mut Self
Close the current subpath.
Closing an empty builder is a no-op rather than an error, so callers replaying arbitrary path data do not have to special-case it.
Sourcepub fn current_point(&self) -> Option<Vec2>
pub fn current_point(&self) -> Option<Vec2>
Current pen position, if the path has started.
Sourcepub fn as_rounded_rect(&mut self, rect: Rect, radius: f32) -> &mut Self
pub fn as_rounded_rect(&mut self, rect: Rect, radius: f32) -> &mut Self
Record that what was laid down is this rounded rectangle.
An assertion by the caller, not a check: nothing here reads the verbs back to confirm it, because the only caller is the one that just wrote them. A builder that marks a shape it did not draw gets that shape drawn wherever a route prefers the shape to the outline – so this is called beside the drawing, never afterwards from somewhere that inferred it.
A radius at or below zero is a plain rectangle and is recorded as such. Anything not finite records nothing, since it describes no shape.
pub fn build(self) -> Path
Trait Implementations§
Source§impl Clone for PathBuilder
impl Clone for PathBuilder
Source§impl Debug for PathBuilder
impl Debug for PathBuilder
Auto Trait Implementations§
impl Freeze for PathBuilder
impl RefUnwindSafe for PathBuilder
impl Send for PathBuilder
impl Sync for PathBuilder
impl Unpin for PathBuilder
impl UnsafeUnpin for PathBuilder
impl UnwindSafe for PathBuilder
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,
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 more