pub struct Bezier<F, V, const D: usize>where
F: Float,
V: Vector<F, D>,{ /* private fields */ }
Expand description
A Bezier is an implementation of a linear, quadratic or cubic Bezier curve using a parameter which has the Float trait, and consists of points that have the Vector trait.
To split a quadratic bezier at t is simple: the split point is p(t), and the two control points (cl, cr) are:
cl(t) = u.p0 + t.c ; cr = u.c + t.p1
Hence the Quadratic Bezier between t0 and t1 can be calculated by splitting to get the right-hand Bezier of t0->1, and splitting this to get the left-hand Bezier at (t1-t0)/u0 = (t2,u2)
Note t2 = (t1-t0)/u0; u2=1-t2 = (u0+t0-t1)/u0 = (1-t1)/u0 = u1/u0
cl(t0) = u0.p0 + t0.c
cr(t0) = u0.c + t1.p1
p(t0) = u0.cl(t0) + t0.cr(t0)
Bezier t0->1 : p(t0), cr(t0), p1
c(t0,t1) = u2.p(t0) + t2.cr(t0)
= u2.u0.cl(t0) + u2.t0.cr(t0) + t2.cr(t0)
= u2.u0.cl(t0) + (u2.t0+t2).cr(t0)
But u2.u0 = u1
And u2.t0+t2 = u1/u0.t0+(t1-t0)/u0
= (t0.u1+t1-t0)/u0
= (t0 - t1.t0 + t1 - t0) / u0
= (t1 - t1.t0) / u0
= t1(1-t0) / (1-t0)
= t1
Hence
c(t0,t1) = u1.cl(t0) + t1.cr(t0)
= u0.u1.p0 + u1.t0.c + u0.t1.c + t0.t1.p1
= u0.u1.p0 + (u1.t0+u0.t1).c + t0.t1.p1
And the points are:
p(t0) = u0.u0.p0 + 2(u0.t0).c + t0.t0.p1
p(t1) = u1.u1.p0 + 2(u1.t1).c + t1.t1.p1
Implementations§
source§impl<F, V, const D: usize> Bezier<F, V, D>where
F: Float,
V: Vector<F, D>,
impl<F, V, const D: usize> Bezier<F, V, D>where F: Float, V: Vector<F, D>,
sourcepub fn borrow_pt(&self, index: usize) -> &V
pub fn borrow_pt(&self, index: usize) -> &V
Borrow the start or end point of the Bezier - index 0 gives the start point, index 1 the end point
It can also be used to borrow the control points (which are index 2 and 3) if they are used for the Bezier; this is not generally required, as a Bezier is designed to be rendered into straight lines.
sourcepub fn get_distance(&self) -> F
pub fn get_distance(&self) -> F
Get the distance between the start and end points
This is not the same as the length of the Bezier, as it may be a curve.
sourcepub fn quadratic(p0: &V, c: &V, p1: &V) -> Self
pub fn quadratic(p0: &V, c: &V, p1: &V) -> Self
Create a new Quadratic Bezier that is a line between two points with one absolute control points
sourcepub fn cubic(p0: &V, c0: &V, c1: &V, p1: &V) -> Self
pub fn cubic(p0: &V, c0: &V, c1: &V, p1: &V) -> Self
Create a new Cubic Bezier that is a line between two points with two absolute control points
sourcepub fn degree(&self) -> usize
pub fn degree(&self) -> usize
Returns number of points used for the Bezier (2 to 4)
Cubic beziers return 3 Quadratic beziers return 2 Linear beziers (lines…) return 1
sourcepub fn scale(&mut self, s: F)
pub fn scale(&mut self, s: F)
Scale the Bezier by applying the scale factor to all of the points
This is an example of the Bezier::map_pts method
sourcepub fn map_pts<Map: Fn(V) -> V>(&mut self, map: Map)
pub fn map_pts<Map: Fn(V) -> V>(&mut self, map: Map)
Apply a function to all of the points in the Bezier
sourcepub fn tangent_at(&self, t: F) -> V
pub fn tangent_at(&self, t: F) -> V
Returns the tangent vector at parameter ‘t’ along the Bezier
Note that this is not necessarily a unit vector
sourcepub fn bisect(&self) -> (Self, Self)
pub fn bisect(&self) -> (Self, Self)
Returns two Bezier’s that split the curve at parameter t=0.5
For quadratics the midpoint is 1/4(p0 + 2*c + p1)
sourcepub fn bezier_between(&self, t0: F, t1: F) -> Self
pub fn bezier_between(&self, t0: F, t1: F) -> Self
Returns the Bezier that is a subset of this Bezier between two parameters 0 <= t0 < t1 <= 1
sourcepub fn as_lines(&self, straightness: F) -> BezierLineIter<F, V, D> ⓘ
pub fn as_lines(&self, straightness: F) -> BezierLineIter<F, V, D> ⓘ
Return a BezierLineIter iterator that provides line segments when the Bezier is broken down into ‘straight’ enough through bisection.
sourcepub fn as_points(&self, straightness: F) -> BezierPointIter<F, V, D> ⓘ
pub fn as_points(&self, straightness: F) -> BezierPointIter<F, V, D> ⓘ
Return a BezierPointIter iterator that provides points along the curve when the Bezier is broken down into ‘straight’ enough through bisection.
sourcepub fn is_straight(&self, straightness: F) -> bool
pub fn is_straight(&self, straightness: F) -> bool
Returns true if the Bezier is straighter than a ‘straightness’ measure
A linear bezier is always straight.
A straightness measure for a quadratic bezier (one control point) can be thought of as the ratio between the area of the triangle formed by the two endpoints and the control point (three points must form a triangle on a plane) in relation to the distance between the endpoints (the curve will be entirely within the triangle.
A straightness measure for a cubic bezier (two control points) can be though of similarly, except that the curve now must fit within a volume given by the two control points and the endpoints; hence the straightness is measured in some way by the volume in relation to the distance between the endpoints, but also should be no straighter than the area of any one control point in relation to the disnance between the endpoints (the Bezier may be a planar curve that is quite unstraight but with a volume of zero).
Hence the straightness here is defined as the sum of (the ratio between (the distance of each control point from the straight line between the two endpoints) and (the distance between the two endpoints))
straightness
is thus independent of the length of the Bezier
sourcepub fn length(&self, straightness: F) -> F
pub fn length(&self, straightness: F) -> F
Calculates the length of the Bezier when it is rendered down to the given a straightness
straightness
is independent of the length of the Bezier
sourcepub fn t_of_distance(&self, straightness: F, distance: F) -> (F, bool)
pub fn t_of_distance(&self, straightness: F, distance: F) -> (F, bool)
Calculates the parameter ‘t’ at a certain distance along the Bezier given a straightness
straightness
is independent of the length of the Bezier
Returns t,true if the distance is along the Bezier
Returns 0.,false if the distance is before the start of the Bezier
Returns 1.,false if the distance is beyond the end of the Bezier
sourcepub fn arc(angle: F, radius: F, center: &V, unit: &V, normal: &V, rotate: F) -> Self
pub fn arc(angle: F, radius: F, center: &V, unit: &V, normal: &V, rotate: F) -> Self
Create a Cubic Bezier that approximates closely a circular arc
The arc has a center C, a radius R, and is of an angle (should be <= PI/2).
The arc sweeps through points a distance R from C, in a circle using a pair of the planar unit vectors in the vector space for the points.
The arc will be between an angle A1 and A2, where A2-A1 == angle, and A1==rotate
sourcepub fn of_round_corner(corner: &V, v0: &V, v1: &V, radius: F) -> Self
pub fn of_round_corner(corner: &V, v0: &V, v1: &V, radius: F) -> Self
Create a Cubic Bezier that is a circular arc focused on the corner point, with v0 and v1 are vectors IN to the point (P)
As it is a circular arc we have a kite P, P+k.v0, C, P+k.v1, where
|P+k.v0 - C| = |P+k.v1 - C| = r; |P-C| = d (i.e. side lengths are r, r, k, k)
with two corners being right-angles. (and d is the length of the kite diagonal opposite these right-angles).
The kite is formed from two d, r, k right-angled triangles; it has two other angles, alpha and 2theta, (alpha = angle between v0 and v1). Hence alpha = 180 - 2theta, theta = 90-(alpha/2)
Hence d^2 = r^2 + k^2; r/d = cos(theta), k/d=sin(theta)
We know cos(alpha) = v0.v1 (assuming unit vectors).
cos(alpha) = cos(180-2*theta)
= -cos(2*theta)
= -(2cos^2(theta) - 1)
= 1 - 2cos^2(theta)
cos^2(theta) = (1 - cos(alpha)) / 2 = r^2/d^2
sin^2(theta) = (1 + cos(alpha)) / 2
=> d^2 = 2*r^2 / (1 - cos(alpha))
Hence also k^2, and hence d and k.
Then we require an arc given the angle of the arc is 2*theta
sourcepub fn center_radius_of_bezier_arc(&self) -> (V, F)
pub fn center_radius_of_bezier_arc(&self) -> (V, F)
Find the center and radius of a Bezier if it is assumed to be a circular arc
what is the center of the circle given point p0 and unit tangent t0 and point p1 and unit tangent t1
|p0-c|^2 = |p1-c|^2 = r^2
(p0-c) . t0 = 0
(p1-c) . t1 = 0
Consider c = k0.t0 + k1.t1
(given t0.t0 == 1 and t1.t1==1)
(p0-c) . t0 = (p0 - k0.t0 - k1.t1).t0 = 0
p0.t0 = k0 + k1(t1.t0)
similarly
p1.t1 = k1 + k0(t1.t0)
hence
(t1.t0) * (p1.t1) = k0.(t1.t0)^2 + k1(t1.t0)
p0.t0 - (t1.t0) * (p1.t1) = k0 ( 1 - (t1.t0)^2)
k0 = (p0.t0 - p1.t1 * t1.t0) / ( 1 - (t1.t0)^2)
k1 = (p1.t1 - p0.t0 * t1.t0) / ( 1 - (t1.t0)^2)