Bezier-rs: Implement miter-limit (#1096)
* Implement miter-limit approximation * Refactor to use Join enum and address other comments * Rustdocs improvements * Tweaks --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
3733804d18
commit
97be83c404
|
|
@ -14,7 +14,7 @@ impl Bezier {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a quadratic bezier using the provided DVec2s as the start, handle, and end points.
|
/// Create a quadratic bezier using the provided DVec2s as the start, handle, and end points.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/constructor/solo" title="Constructor Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#bezier/constructor/solo" title="Constructor Demo"></iframe>
|
||||||
pub fn from_linear_dvec2(p1: DVec2, p2: DVec2) -> Self {
|
pub fn from_linear_dvec2(p1: DVec2, p2: DVec2) -> Self {
|
||||||
Bezier {
|
Bezier {
|
||||||
start: p1,
|
start: p1,
|
||||||
|
|
@ -69,7 +69,7 @@ impl Bezier {
|
||||||
/// - `t` - A representation of how far along the curve the provided point should occur at. The default value is 0.5.
|
/// - `t` - A representation of how far along the curve the provided point should occur at. The default value is 0.5.
|
||||||
/// Note that when `t = 0` or `t = 1`, the expectation is that the `point_on_curve` should be equal to `start` and `end` respectively.
|
/// Note that when `t = 0` or `t = 1`, the expectation is that the `point_on_curve` should be equal to `start` and `end` respectively.
|
||||||
/// In these cases, if the provided values are not equal, this function will use the `point_on_curve` as the `start`/`end` instead.
|
/// In these cases, if the provided values are not equal, this function will use the `point_on_curve` as the `start`/`end` instead.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/bezier-through-points/solo" title="Through Points Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#bezier/bezier-through-points/solo" title="Through Points Demo"></iframe>
|
||||||
pub fn quadratic_through_points(start: DVec2, point_on_curve: DVec2, end: DVec2, t: Option<f64>) -> Self {
|
pub fn quadratic_through_points(start: DVec2, point_on_curve: DVec2, end: DVec2, t: Option<f64>) -> Self {
|
||||||
let t = t.unwrap_or(DEFAULT_T_VALUE);
|
let t = t.unwrap_or(DEFAULT_T_VALUE);
|
||||||
if t == 0. {
|
if t == 0. {
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ impl Bezier {
|
||||||
|
|
||||||
/// Calculate the coordinates of the point `t` along the curve.
|
/// Calculate the coordinates of the point `t` along the curve.
|
||||||
/// Expects `t` to be within the inclusive range `[0, 1]`.
|
/// Expects `t` to be within the inclusive range `[0, 1]`.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/evaluate/solo" title="Evaluate Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#bezier/evaluate/solo" title="Evaluate Demo"></iframe>
|
||||||
pub fn evaluate(&self, t: TValue) -> DVec2 {
|
pub fn evaluate(&self, t: TValue) -> DVec2 {
|
||||||
let t = self.t_value_to_parametric(t);
|
let t = self.t_value_to_parametric(t);
|
||||||
self.unrestricted_parametric_evaluate(t)
|
self.unrestricted_parametric_evaluate(t)
|
||||||
|
|
@ -73,7 +73,7 @@ impl Bezier {
|
||||||
|
|
||||||
/// Return a selection of equidistant points on the bezier curve.
|
/// Return a selection of equidistant points on the bezier curve.
|
||||||
/// If no value is provided for `steps`, then the function will default `steps` to be 10.
|
/// If no value is provided for `steps`, then the function will default `steps` to be 10.
|
||||||
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#bezier/lookup-table/solo" title="Lookup-Table Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#bezier/lookup-table/solo" title="Lookup-Table Demo"></iframe>
|
||||||
pub fn compute_lookup_table(&self, steps: Option<usize>, tvalue_type: Option<TValueType>) -> Vec<DVec2> {
|
pub fn compute_lookup_table(&self, steps: Option<usize>, tvalue_type: Option<TValueType>) -> Vec<DVec2> {
|
||||||
let steps = steps.unwrap_or(DEFAULT_LUT_STEP_SIZE);
|
let steps = steps.unwrap_or(DEFAULT_LUT_STEP_SIZE);
|
||||||
let tvalue_type = tvalue_type.unwrap_or(TValueType::Parametric);
|
let tvalue_type = tvalue_type.unwrap_or(TValueType::Parametric);
|
||||||
|
|
@ -91,7 +91,7 @@ impl Bezier {
|
||||||
|
|
||||||
/// Return an approximation of the length of the bezier curve.
|
/// Return an approximation of the length of the bezier curve.
|
||||||
/// - `num_subdivisions` - Number of subdivisions used to approximate the curve. The default value is 1000.
|
/// - `num_subdivisions` - Number of subdivisions used to approximate the curve. The default value is 1000.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/length/solo" title="Length Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#bezier/length/solo" title="Length Demo"></iframe>
|
||||||
pub fn length(&self, num_subdivisions: Option<usize>) -> f64 {
|
pub fn length(&self, num_subdivisions: Option<usize>) -> f64 {
|
||||||
match self.handles {
|
match self.handles {
|
||||||
BezierHandles::Linear => self.start.distance(self.end),
|
BezierHandles::Linear => self.start.distance(self.end),
|
||||||
|
|
@ -118,7 +118,7 @@ impl Bezier {
|
||||||
|
|
||||||
/// Returns the parametric `t`-value that corresponds to the closest point on the curve to the provided point.
|
/// Returns the parametric `t`-value that corresponds to the closest point on the curve to the provided point.
|
||||||
/// Uses a searching algorithm akin to binary search that can be customized using the optional [ProjectionOptions] struct.
|
/// Uses a searching algorithm akin to binary search that can be customized using the optional [ProjectionOptions] struct.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/project/solo" title="Project Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#bezier/project/solo" title="Project Demo"></iframe>
|
||||||
pub fn project(&self, point: DVec2, options: Option<ProjectionOptions>) -> f64 {
|
pub fn project(&self, point: DVec2, options: Option<ProjectionOptions>) -> f64 {
|
||||||
let options = options.unwrap_or_default();
|
let options = options.unwrap_or_default();
|
||||||
let ProjectionOptions {
|
let ProjectionOptions {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ impl Bezier {
|
||||||
/// Returns a list of lists of points representing the De Casteljau points for all iterations at the point `t` along the curve using De Casteljau's algorithm.
|
/// Returns a list of lists of points representing the De Casteljau points for all iterations at the point `t` along the curve using De Casteljau's algorithm.
|
||||||
/// The `i`th element of the list represents the set of points in the `i`th iteration.
|
/// The `i`th element of the list represents the set of points in the `i`th iteration.
|
||||||
/// More information on the algorithm can be found in the [De Casteljau section](https://pomax.github.io/bezierinfo/#decasteljau) in Pomax's primer.
|
/// More information on the algorithm can be found in the [De Casteljau section](https://pomax.github.io/bezierinfo/#decasteljau) in Pomax's primer.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/de-casteljau-points/solo" title="De Casteljau Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#bezier/de-casteljau-points/solo" title="De Casteljau Demo"></iframe>
|
||||||
pub fn de_casteljau_points(&self, t: TValue) -> Vec<Vec<DVec2>> {
|
pub fn de_casteljau_points(&self, t: TValue) -> Vec<Vec<DVec2>> {
|
||||||
let t = self.t_value_to_parametric(t);
|
let t = self.t_value_to_parametric(t);
|
||||||
let bezier_points = match self.handles {
|
let bezier_points = match self.handles {
|
||||||
|
|
@ -34,7 +34,7 @@ impl Bezier {
|
||||||
|
|
||||||
/// Returns a [Bezier] representing the derivative of the original curve.
|
/// Returns a [Bezier] representing the derivative of the original curve.
|
||||||
/// - This function returns `None` for a linear segment.
|
/// - This function returns `None` for a linear segment.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/derivative/solo" title="Derivative Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#bezier/derivative/solo" title="Derivative Demo"></iframe>
|
||||||
pub fn derivative(&self) -> Option<Bezier> {
|
pub fn derivative(&self) -> Option<Bezier> {
|
||||||
match self.handles {
|
match self.handles {
|
||||||
BezierHandles::Linear => None,
|
BezierHandles::Linear => None,
|
||||||
|
|
@ -61,7 +61,7 @@ impl Bezier {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a normalized unit vector representing the tangent at the point `t` along the curve.
|
/// Returns a normalized unit vector representing the tangent at the point `t` along the curve.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/tangent/solo" title="Tangent Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#bezier/tangent/solo" title="Tangent Demo"></iframe>
|
||||||
pub fn tangent(&self, t: TValue) -> DVec2 {
|
pub fn tangent(&self, t: TValue) -> DVec2 {
|
||||||
let t = self.t_value_to_parametric(t);
|
let t = self.t_value_to_parametric(t);
|
||||||
let tangent = self.non_normalized_tangent(t);
|
let tangent = self.non_normalized_tangent(t);
|
||||||
|
|
@ -73,14 +73,14 @@ impl Bezier {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a normalized unit vector representing the direction of the normal at the point `t` along the curve.
|
/// Returns a normalized unit vector representing the direction of the normal at the point `t` along the curve.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/normal/solo" title="Normal Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#bezier/normal/solo" title="Normal Demo"></iframe>
|
||||||
pub fn normal(&self, t: TValue) -> DVec2 {
|
pub fn normal(&self, t: TValue) -> DVec2 {
|
||||||
self.tangent(t).perp()
|
self.tangent(t).perp()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the curvature, a scalar value for the derivative at the point `t` along the curve.
|
/// Returns the curvature, a scalar value for the derivative at the point `t` along the curve.
|
||||||
/// Curvature is 1 over the radius of a circle with an equivalent derivative.
|
/// Curvature is 1 over the radius of a circle with an equivalent derivative.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/curvature/solo" title="Curvature Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#bezier/curvature/solo" title="Curvature Demo"></iframe>
|
||||||
pub fn curvature(&self, t: TValue) -> f64 {
|
pub fn curvature(&self, t: TValue) -> f64 {
|
||||||
let t = self.t_value_to_parametric(t);
|
let t = self.t_value_to_parametric(t);
|
||||||
let (d, dd) = match &self.derivative() {
|
let (d, dd) = match &self.derivative() {
|
||||||
|
|
@ -127,7 +127,7 @@ impl Bezier {
|
||||||
|
|
||||||
/// Returns two lists of `t`-values representing the local extrema of the `x` and `y` parametric curves respectively.
|
/// Returns two lists of `t`-values representing the local extrema of the `x` and `y` parametric curves respectively.
|
||||||
/// The list of `t`-values returned are filtered such that they fall within the range `[0, 1]`.
|
/// The list of `t`-values returned are filtered such that they fall within the range `[0, 1]`.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/local-extrema/solo" title="Local Extrema Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#bezier/local-extrema/solo" title="Local Extrema Demo"></iframe>
|
||||||
pub fn local_extrema(&self) -> [Vec<f64>; 2] {
|
pub fn local_extrema(&self) -> [Vec<f64>; 2] {
|
||||||
self.unrestricted_local_extrema()
|
self.unrestricted_local_extrema()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
@ -138,7 +138,7 @@ impl Bezier {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the min and max corners that represent the bounding box of the curve.
|
/// Return the min and max corners that represent the bounding box of the curve.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/bounding-box/solo" title="Bounding Box Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#bezier/bounding-box/solo" title="Bounding Box Demo"></iframe>
|
||||||
pub fn bounding_box(&self) -> [DVec2; 2] {
|
pub fn bounding_box(&self) -> [DVec2; 2] {
|
||||||
// Start by taking min/max of endpoints.
|
// Start by taking min/max of endpoints.
|
||||||
let mut endpoints_min = self.start.min(self.end);
|
let mut endpoints_min = self.start.min(self.end);
|
||||||
|
|
@ -199,7 +199,7 @@ impl Bezier {
|
||||||
|
|
||||||
/// Returns list of parametric `t`-values representing the inflection points of the curve.
|
/// Returns list of parametric `t`-values representing the inflection points of the curve.
|
||||||
/// The list of `t`-values returned are filtered such that they fall within the range `[0, 1]`.
|
/// The list of `t`-values returned are filtered such that they fall within the range `[0, 1]`.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/inflections/solo" title="Inflections Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#bezier/inflections/solo" title="Inflections Demo"></iframe>
|
||||||
pub fn inflections(&self) -> Vec<f64> {
|
pub fn inflections(&self) -> Vec<f64> {
|
||||||
self.unrestricted_inflections().into_iter().filter(|&t| t > 0. && t < 1.).collect::<Vec<f64>>()
|
self.unrestricted_inflections().into_iter().filter(|&t| t > 0. && t < 1.).collect::<Vec<f64>>()
|
||||||
}
|
}
|
||||||
|
|
@ -256,7 +256,7 @@ impl Bezier {
|
||||||
/// If the provided curve is linear, then zero intersection points will be returned along colinear segments.
|
/// If the provided curve is linear, then zero intersection points will be returned along colinear segments.
|
||||||
/// - `error` - For intersections where the provided bezier is non-linear, `error` defines the threshold for bounding boxes to be considered an intersection point.
|
/// - `error` - For intersections where the provided bezier is non-linear, `error` defines the threshold for bounding boxes to be considered an intersection point.
|
||||||
/// - `minimum_separation` - The minimum difference between adjacent `t` values in sorted order
|
/// - `minimum_separation` - The minimum difference between adjacent `t` values in sorted order
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/intersect-cubic/solo" title="Intersections Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#bezier/intersect-cubic/solo" title="Intersections Demo"></iframe>
|
||||||
pub fn intersections(&self, other: &Bezier, error: Option<f64>, minimum_separation: Option<f64>) -> Vec<f64> {
|
pub fn intersections(&self, other: &Bezier, error: Option<f64>, minimum_separation: Option<f64>) -> Vec<f64> {
|
||||||
// TODO: Consider using the `intersections_between_vectors_of_curves` helper function here
|
// TODO: Consider using the `intersections_between_vectors_of_curves` helper function here
|
||||||
// Otherwise, use bounding box to determine intersections
|
// Otherwise, use bounding box to determine intersections
|
||||||
|
|
@ -355,7 +355,7 @@ impl Bezier {
|
||||||
// TODO: Use an `impl Iterator` return type instead of a `Vec`
|
// TODO: Use an `impl Iterator` return type instead of a `Vec`
|
||||||
/// Returns a list of parametric `t` values that correspond to the self intersection points of the current bezier curve. For each intersection point, the returned `t` value is the smaller of the two that correspond to the point.
|
/// Returns a list of parametric `t` values that correspond to the self intersection points of the current bezier curve. For each intersection point, the returned `t` value is the smaller of the two that correspond to the point.
|
||||||
/// - `error` - For intersections with non-linear beziers, `error` defines the threshold for bounding boxes to be considered an intersection point.
|
/// - `error` - For intersections with non-linear beziers, `error` defines the threshold for bounding boxes to be considered an intersection point.
|
||||||
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#bezier/intersect-self/solo" title="Self Intersection Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/intersect-self/solo" title="Self Intersection Demo"></iframe>
|
||||||
pub fn self_intersections(&self, error: Option<f64>) -> Vec<[f64; 2]> {
|
pub fn self_intersections(&self, error: Option<f64>) -> Vec<[f64; 2]> {
|
||||||
if self.handles == BezierHandles::Linear || matches!(self.handles, BezierHandles::Quadratic { .. }) {
|
if self.handles == BezierHandles::Linear || matches!(self.handles, BezierHandles::Quadratic { .. }) {
|
||||||
return vec![];
|
return vec![];
|
||||||
|
|
@ -387,7 +387,7 @@ impl Bezier {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a list of parametric `t` values that correspond to the intersection points between the curve and a rectangle defined by opposite corners.
|
/// Returns a list of parametric `t` values that correspond to the intersection points between the curve and a rectangle defined by opposite corners.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/intersect-rectangle/solo" title="Intersection (Rectangle) Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#bezier/intersect-rectangle/solo" title="Intersection (Rectangle) Demo"></iframe>
|
||||||
pub fn rectangle_intersections(&self, corner1: DVec2, corner2: DVec2) -> Vec<f64> {
|
pub fn rectangle_intersections(&self, corner1: DVec2, corner2: DVec2) -> Vec<f64> {
|
||||||
[
|
[
|
||||||
Bezier::from_linear_coordinates(corner1.x, corner1.y, corner2.x, corner1.y),
|
Bezier::from_linear_coordinates(corner1.x, corner1.y, corner2.x, corner1.y),
|
||||||
|
|
@ -402,7 +402,7 @@ impl Bezier {
|
||||||
|
|
||||||
/// Returns a cubic bezier which joins this with the provided bezier curve.
|
/// Returns a cubic bezier which joins this with the provided bezier curve.
|
||||||
/// The resulting path formed by the Bezier curves is continuous up to the first derivative.
|
/// The resulting path formed by the Bezier curves is continuous up to the first derivative.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/join/solo" title="Join Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#bezier/join/solo" title="Join Demo"></iframe>
|
||||||
pub fn join(&self, other: &Bezier) -> Bezier {
|
pub fn join(&self, other: &Bezier) -> Bezier {
|
||||||
let handle1 = self.non_normalized_tangent(1.) / 3. + self.end;
|
let handle1 = self.non_normalized_tangent(1.) / 3. + self.end;
|
||||||
let handle2 = other.start - other.non_normalized_tangent(0.) / 3.;
|
let handle2 = other.start - other.non_normalized_tangent(0.) / 3.;
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ impl Bezier {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the pair of Bezier curves that result from splitting the original curve at the point `t` along the curve.
|
/// Returns the pair of Bezier curves that result from splitting the original curve at the point `t` along the curve.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/split/solo" title="Split Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#bezier/split/solo" title="Split Demo"></iframe>
|
||||||
pub fn split(&self, t: TValue) -> [Bezier; 2] {
|
pub fn split(&self, t: TValue) -> [Bezier; 2] {
|
||||||
let t = self.t_value_to_parametric(t);
|
let t = self.t_value_to_parametric(t);
|
||||||
let split_point = self.evaluate(TValue::Parametric(t));
|
let split_point = self.evaluate(TValue::Parametric(t));
|
||||||
|
|
@ -83,7 +83,7 @@ impl Bezier {
|
||||||
|
|
||||||
/// Returns the Bezier curve representing the sub-curve between the two provided points.
|
/// Returns the Bezier curve representing the sub-curve between the two provided points.
|
||||||
/// It will start at the point corresponding to the smaller of `t1` and `t2`, and end at the point corresponding to the larger of `t1` and `t2`.
|
/// It will start at the point corresponding to the smaller of `t1` and `t2`, and end at the point corresponding to the larger of `t1` and `t2`.
|
||||||
/// <iframe frameBorder="0" width="100%" height="450px" src="https://graphite.rs/bezier-rs-demos#bezier/trim/solo" title="Trim Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/trim/solo" title="Trim Demo"></iframe>
|
||||||
pub fn trim(&self, t1: TValue, t2: TValue) -> Bezier {
|
pub fn trim(&self, t1: TValue, t2: TValue) -> Bezier {
|
||||||
let (mut t1, mut t2) = (self.t_value_to_parametric(t1), self.t_value_to_parametric(t2));
|
let (mut t1, mut t2) = (self.t_value_to_parametric(t1), self.t_value_to_parametric(t2));
|
||||||
// If t1 is equal to t2, return a bezier comprised entirely of the same point
|
// If t1 is equal to t2, return a bezier comprised entirely of the same point
|
||||||
|
|
@ -122,7 +122,7 @@ impl Bezier {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a Bezier curve that results from rotating the curve around the origin by the given angle (in radians).
|
/// Returns a Bezier curve that results from rotating the curve around the origin by the given angle (in radians).
|
||||||
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#bezier/rotate/solo" title="Rotate Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/rotate/solo" title="Rotate Demo"></iframe>
|
||||||
pub fn rotate(&self, angle: f64) -> Bezier {
|
pub fn rotate(&self, angle: f64) -> Bezier {
|
||||||
let rotation_matrix = DMat2::from_angle(angle);
|
let rotation_matrix = DMat2::from_angle(angle);
|
||||||
self.apply_transformation(|point| rotation_matrix.mul_vec2(point))
|
self.apply_transformation(|point| rotation_matrix.mul_vec2(point))
|
||||||
|
|
@ -251,7 +251,7 @@ impl Bezier {
|
||||||
/// The function takes the following parameter:
|
/// The function takes the following parameter:
|
||||||
/// - `step_size` - Dictates the granularity at which the function searches for reducible subcurves. The default value is `0.01`.
|
/// - `step_size` - Dictates the granularity at which the function searches for reducible subcurves. The default value is `0.01`.
|
||||||
/// A small granularity may increase the chance the function does not introduce gaps, but will increase computation time.
|
/// A small granularity may increase the chance the function does not introduce gaps, but will increase computation time.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/reduce/solo" title="Reduce Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#bezier/reduce/solo" title="Reduce Demo"></iframe>
|
||||||
pub fn reduce(&self, step_size: Option<f64>) -> Vec<Bezier> {
|
pub fn reduce(&self, step_size: Option<f64>) -> Vec<Bezier> {
|
||||||
self.reduced_curves_and_t_values(step_size).0
|
self.reduced_curves_and_t_values(step_size).0
|
||||||
}
|
}
|
||||||
|
|
@ -355,7 +355,7 @@ impl Bezier {
|
||||||
/// Offset takes the following parameter:
|
/// Offset takes the following parameter:
|
||||||
/// - `distance` - The offset's distance from the curve. Positive values will offset the curve in the same direction as the endpoint normals,
|
/// - `distance` - The offset's distance from the curve. Positive values will offset the curve in the same direction as the endpoint normals,
|
||||||
/// while negative values will offset in the opposite direction.
|
/// while negative values will offset in the opposite direction.
|
||||||
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#bezier/offset/solo" title="Offset Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/offset/solo" title="Offset Demo"></iframe>
|
||||||
pub fn offset<ManipulatorGroupId: crate::Identifier>(&self, distance: f64) -> Subpath<ManipulatorGroupId> {
|
pub fn offset<ManipulatorGroupId: crate::Identifier>(&self, distance: f64) -> Subpath<ManipulatorGroupId> {
|
||||||
if self.is_point() {
|
if self.is_point() {
|
||||||
return Subpath::from_bezier(self);
|
return Subpath::from_bezier(self);
|
||||||
|
|
@ -422,7 +422,7 @@ impl Bezier {
|
||||||
/// The 'caps', the linear segments at opposite ends of the outline, intersect the original curve at the midpoint of the cap.
|
/// The 'caps', the linear segments at opposite ends of the outline, intersect the original curve at the midpoint of the cap.
|
||||||
/// Outline takes the following parameter:
|
/// Outline takes the following parameter:
|
||||||
/// - `distance` - The outline's distance from the curve.
|
/// - `distance` - The outline's distance from the curve.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/outline/solo" title="Outline Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#bezier/outline/solo" title="Outline Demo"></iframe>
|
||||||
pub fn outline<ManipulatorGroupId: crate::Identifier>(&self, distance: f64, cap: Cap) -> Subpath<ManipulatorGroupId> {
|
pub fn outline<ManipulatorGroupId: crate::Identifier>(&self, distance: f64, cap: Cap) -> Subpath<ManipulatorGroupId> {
|
||||||
let (pos_offset, neg_offset) = if self.is_point() {
|
let (pos_offset, neg_offset) = if self.is_point() {
|
||||||
(
|
(
|
||||||
|
|
@ -442,13 +442,13 @@ impl Bezier {
|
||||||
|
|
||||||
/// Version of the `outline` function which draws the outline at the specified distances away from the curve.
|
/// Version of the `outline` function which draws the outline at the specified distances away from the curve.
|
||||||
/// The outline begins `start_distance` away, and gradually move to being `end_distance` away.
|
/// The outline begins `start_distance` away, and gradually move to being `end_distance` away.
|
||||||
/// <iframe frameBorder="0" width="100%" height="450px" src="https://graphite.rs/bezier-rs-demos#bezier/graduated-outline/solo" title="Graduated Outline Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/graduated-outline/solo" title="Graduated Outline Demo"></iframe>
|
||||||
pub fn graduated_outline<ManipulatorGroupId: crate::Identifier>(&self, start_distance: f64, end_distance: f64, cap: Cap) -> Subpath<ManipulatorGroupId> {
|
pub fn graduated_outline<ManipulatorGroupId: crate::Identifier>(&self, start_distance: f64, end_distance: f64, cap: Cap) -> Subpath<ManipulatorGroupId> {
|
||||||
self.skewed_outline(start_distance, end_distance, end_distance, start_distance, cap)
|
self.skewed_outline(start_distance, end_distance, end_distance, start_distance, cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Version of the `graduated_outline` function that allows for the 4 corners of the outline to be different distances away from the curve.
|
/// Version of the `graduated_outline` function that allows for the 4 corners of the outline to be different distances away from the curve.
|
||||||
/// <iframe frameBorder="0" width="100%" height="550px" src="https://graphite.rs/bezier-rs-demos#bezier/skewed-outline/solo" title="Skewed Outline Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="475px" src="https://graphite.rs/bezier-rs-demos#bezier/skewed-outline/solo" title="Skewed Outline Demo"></iframe>
|
||||||
pub fn skewed_outline<ManipulatorGroupId: crate::Identifier>(&self, distance1: f64, distance2: f64, distance3: f64, distance4: f64, cap: Cap) -> Subpath<ManipulatorGroupId> {
|
pub fn skewed_outline<ManipulatorGroupId: crate::Identifier>(&self, distance1: f64, distance2: f64, distance3: f64, distance4: f64, cap: Cap) -> Subpath<ManipulatorGroupId> {
|
||||||
let (pos_offset, neg_offset) = if self.is_point() {
|
let (pos_offset, neg_offset) = if self.is_point() {
|
||||||
(
|
(
|
||||||
|
|
@ -468,7 +468,7 @@ impl Bezier {
|
||||||
|
|
||||||
/// Approximate a bezier curve with circular arcs.
|
/// Approximate a bezier curve with circular arcs.
|
||||||
/// The algorithm can be customized using the [ArcsOptions] structure.
|
/// The algorithm can be customized using the [ArcsOptions] structure.
|
||||||
/// <iframe frameBorder="0" width="100%" height="450px" src="https://graphite.rs/bezier-rs-demos#bezier/arcs/solo" title="Arcs Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/arcs/solo" title="Arcs Demo"></iframe>
|
||||||
pub fn arcs(&self, arcs_options: ArcsOptions) -> Vec<CircleArc> {
|
pub fn arcs(&self, arcs_options: ArcsOptions) -> Vec<CircleArc> {
|
||||||
let ArcsOptions {
|
let ArcsOptions {
|
||||||
strategy: maximize_arcs,
|
strategy: maximize_arcs,
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a cubic spline from a list of points.
|
/// Construct a cubic spline from a list of points.
|
||||||
/// Based on https://mathworld.wolfram.com/CubicSpline.html
|
/// Based on <https://mathworld.wolfram.com/CubicSpline.html>.
|
||||||
pub fn new_cubic_spline(points: Vec<DVec2>) -> Self {
|
pub fn new_cubic_spline(points: Vec<DVec2>) -> Self {
|
||||||
// Number of points = number of points to find handles for
|
// Number of points = number of points to find handles for
|
||||||
let len_points = points.len();
|
let len_points = points.len();
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use glam::DVec2;
|
||||||
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
/// Return a selection of equidistant points on the bezier curve.
|
/// Return a selection of equidistant points on the bezier curve.
|
||||||
/// If no value is provided for `steps`, then the function will default `steps` to be 10.
|
/// If no value is provided for `steps`, then the function will default `steps` to be 10.
|
||||||
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#subpath/lookup-table/solo" title="Lookup-Table Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#subpath/lookup-table/solo" title="Lookup-Table Demo"></iframe>
|
||||||
pub fn compute_lookup_table(&self, steps: Option<usize>, tvalue_type: Option<TValueType>) -> Vec<DVec2> {
|
pub fn compute_lookup_table(&self, steps: Option<usize>, tvalue_type: Option<TValueType>) -> Vec<DVec2> {
|
||||||
let steps = steps.unwrap_or(DEFAULT_LUT_STEP_SIZE);
|
let steps = steps.unwrap_or(DEFAULT_LUT_STEP_SIZE);
|
||||||
let tvalue_type = tvalue_type.unwrap_or(TValueType::Parametric);
|
let tvalue_type = tvalue_type.unwrap_or(TValueType::Parametric);
|
||||||
|
|
@ -26,7 +26,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
|
|
||||||
/// Return the sum of the approximation of the length of each `Bezier` curve along the `Subpath`.
|
/// Return the sum of the approximation of the length of each `Bezier` curve along the `Subpath`.
|
||||||
/// - `num_subdivisions` - Number of subdivisions used to approximate the curve. The default value is `1000`.
|
/// - `num_subdivisions` - Number of subdivisions used to approximate the curve. The default value is `1000`.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#subpath/length/solo" title="Length Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#subpath/length/solo" title="Length Demo"></iframe>
|
||||||
pub fn length(&self, num_subdivisions: Option<usize>) -> f64 {
|
pub fn length(&self, num_subdivisions: Option<usize>) -> f64 {
|
||||||
self.iter().fold(0., |accumulator, bezier| accumulator + bezier.length(num_subdivisions))
|
self.iter().fold(0., |accumulator, bezier| accumulator + bezier.length(num_subdivisions))
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
|
|
||||||
/// Returns the segment index and `t` value that corresponds to the closest point on the curve to the provided point.
|
/// Returns the segment index and `t` value that corresponds to the closest point on the curve to the provided point.
|
||||||
/// Uses a searching algorithm akin to binary search that can be customized using the [ProjectionOptions] structure.
|
/// Uses a searching algorithm akin to binary search that can be customized using the [ProjectionOptions] structure.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#subpath/project/solo" title="Project Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#subpath/project/solo" title="Project Demo"></iframe>
|
||||||
pub fn project(&self, point: DVec2, options: Option<ProjectionOptions>) -> Option<(usize, f64)> {
|
pub fn project(&self, point: DVec2, options: Option<ProjectionOptions>) -> Option<(usize, f64)> {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,17 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
self.closed = new_closed;
|
self.closed = new_closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access a [ManipulatorGroup] from a [ManipulatorGroupId].
|
/// Access a [ManipulatorGroup] from a ManipulatorGroupId.
|
||||||
pub fn manipulator_from_id(&self, id: ManipulatorGroupId) -> Option<&ManipulatorGroup<ManipulatorGroupId>> {
|
pub fn manipulator_from_id(&self, id: ManipulatorGroupId) -> Option<&ManipulatorGroup<ManipulatorGroupId>> {
|
||||||
self.manipulator_groups.iter().find(|manipulator_group| manipulator_group.id == id)
|
self.manipulator_groups.iter().find(|manipulator_group| manipulator_group.id == id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access a mutable [ManipulatorGroup] from a [ManipulatorGroupId].
|
/// Access a mutable [ManipulatorGroup] from a ManipulatorGroupId.
|
||||||
pub fn manipulator_mut_from_id(&mut self, id: ManipulatorGroupId) -> Option<&mut ManipulatorGroup<ManipulatorGroupId>> {
|
pub fn manipulator_mut_from_id(&mut self, id: ManipulatorGroupId) -> Option<&mut ManipulatorGroup<ManipulatorGroupId>> {
|
||||||
self.manipulator_groups.iter_mut().find(|manipulator_group| manipulator_group.id == id)
|
self.manipulator_groups.iter_mut().find(|manipulator_group| manipulator_group.id == id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the index of a [ManipulatorGroup] from a [ManipulatorGroupId].
|
/// Access the index of a [ManipulatorGroup] from a ManipulatorGroupId.
|
||||||
pub fn manipulator_index_from_id(&self, id: ManipulatorGroupId) -> Option<usize> {
|
pub fn manipulator_index_from_id(&self, id: ManipulatorGroupId) -> Option<usize> {
|
||||||
self.manipulator_groups.iter().position(|manipulator_group| manipulator_group.id == id)
|
self.manipulator_groups.iter().position(|manipulator_group| manipulator_group.id == id)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use std::f64::consts::PI;
|
||||||
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
/// Calculate the point on the subpath based on the parametric `t`-value provided.
|
/// Calculate the point on the subpath based on the parametric `t`-value provided.
|
||||||
/// Expects `t` to be within the inclusive range `[0, 1]`.
|
/// Expects `t` to be within the inclusive range `[0, 1]`.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#subpath/evaluate/solo" title="Evaluate Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#subpath/evaluate/solo" title="Evaluate Demo"></iframe>
|
||||||
pub fn evaluate(&self, t: SubpathTValue) -> DVec2 {
|
pub fn evaluate(&self, t: SubpathTValue) -> DVec2 {
|
||||||
let (segment_index, t) = self.t_value_to_parametric(t);
|
let (segment_index, t) = self.t_value_to_parametric(t);
|
||||||
self.get_segment(segment_index).unwrap().evaluate(TValue::Parametric(t))
|
self.get_segment(segment_index).unwrap().evaluate(TValue::Parametric(t))
|
||||||
|
|
@ -23,7 +23,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
/// - `error`: an optional f64 value to provide an error bound
|
/// - `error`: an optional f64 value to provide an error bound
|
||||||
/// - `minimum_separation`: the minimum difference two adjacent `t`-values must have when comparing adjacent `t`-values in sorted order.
|
/// - `minimum_separation`: the minimum difference two adjacent `t`-values must have when comparing adjacent `t`-values in sorted order.
|
||||||
/// If the comparison condition is not satisfied, the function takes the larger `t`-value of the two.
|
/// If the comparison condition is not satisfied, the function takes the larger `t`-value of the two.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#subpath/intersect-cubic/solo" title="Intersection Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#subpath/intersect-cubic/solo" title="Intersection Demo"></iframe>
|
||||||
pub fn intersections(&self, other: &Bezier, error: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
|
pub fn intersections(&self, other: &Bezier, error: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
|
||||||
self.iter()
|
self.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
@ -35,7 +35,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
/// This function expects the following:
|
/// This function expects the following:
|
||||||
/// - other: a [Bezier] curve to check intersections against
|
/// - other: a [Bezier] curve to check intersections against
|
||||||
/// - error: an optional f64 value to provide an error bound
|
/// - error: an optional f64 value to provide an error bound
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#subpath/intersect-cubic/solo" title="Intersection Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#subpath/intersect-cubic/solo" title="Intersection Demo"></iframe>
|
||||||
pub fn subpath_intersections(&self, other: &Subpath<ManipulatorGroupId>, error: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
|
pub fn subpath_intersections(&self, other: &Subpath<ManipulatorGroupId>, error: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
|
||||||
let mut intersection_t_values: Vec<(usize, f64)> = other.iter().flat_map(|bezier| self.intersections(&bezier, error, minimum_separation)).collect();
|
let mut intersection_t_values: Vec<(usize, f64)> = other.iter().flat_map(|bezier| self.intersections(&bezier, error, minimum_separation)).collect();
|
||||||
intersection_t_values.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
intersection_t_values.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||||
|
|
@ -48,7 +48,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
/// If the comparison condition is not satisfied, the function takes the larger `t`-value of the two
|
/// If the comparison condition is not satisfied, the function takes the larger `t`-value of the two
|
||||||
///
|
///
|
||||||
/// **NOTE**: if an intersection were to occur within an `error` distance away from an anchor point, the algorithm will filter that intersection out.
|
/// **NOTE**: if an intersection were to occur within an `error` distance away from an anchor point, the algorithm will filter that intersection out.
|
||||||
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#subpath/self-intersect/solo" title="Self-Intersection Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#subpath/self-intersect/solo" title="Self-Intersection Demo"></iframe>
|
||||||
pub fn self_intersections(&self, error: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
|
pub fn self_intersections(&self, error: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
|
||||||
let mut intersections_vec = Vec::new();
|
let mut intersections_vec = Vec::new();
|
||||||
let err = error.unwrap_or(MAX_ABSOLUTE_DIFFERENCE);
|
let err = error.unwrap_or(MAX_ABSOLUTE_DIFFERENCE);
|
||||||
|
|
@ -69,14 +69,14 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a normalized unit vector representing the tangent on the subpath based on the parametric `t`-value provided.
|
/// Returns a normalized unit vector representing the tangent on the subpath based on the parametric `t`-value provided.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#subpath/tangent/solo" title="Tangent Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#subpath/tangent/solo" title="Tangent Demo"></iframe>
|
||||||
pub fn tangent(&self, t: SubpathTValue) -> DVec2 {
|
pub fn tangent(&self, t: SubpathTValue) -> DVec2 {
|
||||||
let (segment_index, t) = self.t_value_to_parametric(t);
|
let (segment_index, t) = self.t_value_to_parametric(t);
|
||||||
self.get_segment(segment_index).unwrap().tangent(TValue::Parametric(t))
|
self.get_segment(segment_index).unwrap().tangent(TValue::Parametric(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a normalized unit vector representing the direction of the normal on the subpath based on the parametric `t`-value provided.
|
/// Returns a normalized unit vector representing the direction of the normal on the subpath based on the parametric `t`-value provided.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#subpath/normal/solo" title="Normal Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#subpath/normal/solo" title="Normal Demo"></iframe>
|
||||||
pub fn normal(&self, t: SubpathTValue) -> DVec2 {
|
pub fn normal(&self, t: SubpathTValue) -> DVec2 {
|
||||||
let (segment_index, t) = self.t_value_to_parametric(t);
|
let (segment_index, t) = self.t_value_to_parametric(t);
|
||||||
self.get_segment(segment_index).unwrap().normal(TValue::Parametric(t))
|
self.get_segment(segment_index).unwrap().normal(TValue::Parametric(t))
|
||||||
|
|
@ -84,7 +84,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
|
|
||||||
/// Returns two lists of `t`-values representing the local extrema of the `x` and `y` parametric subpaths respectively.
|
/// Returns two lists of `t`-values representing the local extrema of the `x` and `y` parametric subpaths respectively.
|
||||||
/// The list of `t`-values returned are filtered such that they fall within the range `[0, 1]`.
|
/// The list of `t`-values returned are filtered such that they fall within the range `[0, 1]`.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#subpath/local-extrema/solo" title="Local Extrema Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#subpath/local-extrema/solo" title="Local Extrema Demo"></iframe>
|
||||||
pub fn local_extrema(&self) -> [Vec<f64>; 2] {
|
pub fn local_extrema(&self) -> [Vec<f64>; 2] {
|
||||||
let number_of_curves = self.len_segments() as f64;
|
let number_of_curves = self.len_segments() as f64;
|
||||||
|
|
||||||
|
|
@ -99,7 +99,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the min and max corners that represent the bounding box of the subpath.
|
/// Return the min and max corners that represent the bounding box of the subpath.
|
||||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#subpath/bounding-box/solo" title="Bounding Box Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#subpath/bounding-box/solo" title="Bounding Box Demo"></iframe>
|
||||||
pub fn bounding_box(&self) -> Option<[DVec2; 2]> {
|
pub fn bounding_box(&self) -> Option<[DVec2; 2]> {
|
||||||
self.iter().map(|bezier| bezier.bounding_box()).reduce(|bbox1, bbox2| [bbox1[0].min(bbox2[0]), bbox1[1].max(bbox2[1])])
|
self.iter().map(|bezier| bezier.bounding_box()).reduce(|bbox1, bbox2| [bbox1[0].min(bbox2[0]), bbox1[1].max(bbox2[1])])
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +138,15 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the manipulator point that is needed for a miter join if it is possible.
|
/// Returns the manipulator point that is needed for a miter join if it is possible.
|
||||||
pub(crate) fn miter_line_join(&self, other: &Subpath<ManipulatorGroupId>) -> Option<ManipulatorGroup<ManipulatorGroupId>> {
|
/// - `miter_limit`: Defines a limit for the ratio between the miter length and the stroke width.
|
||||||
|
/// Alternatively, this can be interpreted as limiting the angle that the miter can form.
|
||||||
|
/// When the limit is exceeded, no manipulator group will be returned.
|
||||||
|
/// This value should be at least 1. If not, the default of 4 will be used.
|
||||||
|
pub(crate) fn miter_line_join(&self, other: &Subpath<ManipulatorGroupId>, miter_limit: Option<f64>) -> Option<ManipulatorGroup<ManipulatorGroupId>> {
|
||||||
|
let miter_limit = match miter_limit {
|
||||||
|
Some(miter_limit) if miter_limit >= 1. => miter_limit,
|
||||||
|
_ => 4.,
|
||||||
|
};
|
||||||
let in_segment = self.get_segment(self.len_segments() - 1).unwrap();
|
let in_segment = self.get_segment(self.len_segments() - 1).unwrap();
|
||||||
let out_segment = other.get_segment(0).unwrap();
|
let out_segment = other.get_segment(0).unwrap();
|
||||||
let in_tangent = in_segment.tangent(TValue::Parametric(1.));
|
let in_tangent = in_segment.tangent(TValue::Parametric(1.));
|
||||||
|
|
@ -151,9 +159,13 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
if !normalized_in_tangent.abs_diff_eq(normalized_out_tangent, MAX_ABSOLUTE_DIFFERENCE) && !normalized_in_tangent.abs_diff_eq(-normalized_out_tangent, MAX_ABSOLUTE_DIFFERENCE) {
|
if !normalized_in_tangent.abs_diff_eq(normalized_out_tangent, MAX_ABSOLUTE_DIFFERENCE) && !normalized_in_tangent.abs_diff_eq(-normalized_out_tangent, MAX_ABSOLUTE_DIFFERENCE) {
|
||||||
let intersection = line_intersection(in_segment.end(), in_tangent, out_segment.start(), out_tangent);
|
let intersection = line_intersection(in_segment.end(), in_tangent, out_segment.start(), out_tangent);
|
||||||
|
|
||||||
|
let start_to_intersection = intersection - in_segment.end();
|
||||||
|
let intersection_to_end = out_segment.start() - intersection;
|
||||||
|
|
||||||
// Draw the miter join if the intersection occurs in the correct direction with respect to the path
|
// Draw the miter join if the intersection occurs in the correct direction with respect to the path
|
||||||
if (intersection - in_segment.end()).normalize().abs_diff_eq(in_tangent, MAX_ABSOLUTE_DIFFERENCE)
|
if start_to_intersection.normalize().abs_diff_eq(in_tangent, MAX_ABSOLUTE_DIFFERENCE)
|
||||||
&& (out_segment.start() - intersection).normalize().abs_diff_eq(out_tangent, MAX_ABSOLUTE_DIFFERENCE)
|
&& intersection_to_end.normalize().abs_diff_eq(out_tangent, MAX_ABSOLUTE_DIFFERENCE)
|
||||||
|
&& miter_limit >= 1. / (start_to_intersection.angle_between(-intersection_to_end).abs() / 2.).sin()
|
||||||
{
|
{
|
||||||
return Some(ManipulatorGroup {
|
return Some(ManipulatorGroup {
|
||||||
anchor: intersection,
|
anchor: intersection,
|
||||||
|
|
@ -225,7 +237,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
|
|
||||||
/// Returns the curvature, a scalar value for the derivative at the point `t` along the subpath.
|
/// Returns the curvature, a scalar value for the derivative at the point `t` along the subpath.
|
||||||
/// Curvature is 1 over the radius of a circle with an equivalent derivative.
|
/// Curvature is 1 over the radius of a circle with an equivalent derivative.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#subpath/curvature/solo" title="Curvature Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#subpath/curvature/solo" title="Curvature Demo"></iframe>
|
||||||
pub fn curvature(&self, t: SubpathTValue) -> f64 {
|
pub fn curvature(&self, t: SubpathTValue) -> f64 {
|
||||||
let (segment_index, t) = self.t_value_to_parametric(t);
|
let (segment_index, t) = self.t_value_to_parametric(t);
|
||||||
self.get_segment(segment_index).unwrap().curvature(TValue::Parametric(t))
|
self.get_segment(segment_index).unwrap().curvature(TValue::Parametric(t))
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
/// Returns either one or two Subpaths that result from splitting the original Subpath at the point corresponding to `t`.
|
/// Returns either one or two Subpaths that result from splitting the original Subpath at the point corresponding to `t`.
|
||||||
/// If the original Subpath was closed, a single open Subpath will be returned.
|
/// If the original Subpath was closed, a single open Subpath will be returned.
|
||||||
/// If the original Subpath was open, two open Subpaths will be returned.
|
/// If the original Subpath was open, two open Subpaths will be returned.
|
||||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#subpath/split/solo" title="Split Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/bezier-rs-demos#subpath/split/solo" title="Split Demo"></iframe>
|
||||||
pub fn split(&self, t: SubpathTValue) -> (Subpath<ManipulatorGroupId>, Option<Subpath<ManipulatorGroupId>>) {
|
pub fn split(&self, t: SubpathTValue) -> (Subpath<ManipulatorGroupId>, Option<Subpath<ManipulatorGroupId>>) {
|
||||||
let (segment_index, t) = self.t_value_to_parametric(t);
|
let (segment_index, t) = self.t_value_to_parametric(t);
|
||||||
let curve = self.get_segment(segment_index).unwrap();
|
let curve = self.get_segment(segment_index).unwrap();
|
||||||
|
|
@ -126,7 +126,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
/// The resulting Subpath will wind from the given `t1` to `t2`.
|
/// The resulting Subpath will wind from the given `t1` to `t2`.
|
||||||
/// That means, if the value of `t1` > `t2`, it will cross the break between endpoints from `t1` to `t = 1 = 0` to `t2`.
|
/// That means, if the value of `t1` > `t2`, it will cross the break between endpoints from `t1` to `t = 1 = 0` to `t2`.
|
||||||
/// If a path winding in the reverse direction is desired, call `trim` on the `Subpath` returned from `Subpath::reverse`.
|
/// If a path winding in the reverse direction is desired, call `trim` on the `Subpath` returned from `Subpath::reverse`.
|
||||||
/// <iframe frameBorder="0" width="100%" height="450px" src="https://graphite.rs/bezier-rs-demos#subpath/trim/solo" title="Trim Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#subpath/trim/solo" title="Trim Demo"></iframe>
|
||||||
pub fn trim(&self, t1: SubpathTValue, t2: SubpathTValue) -> Subpath<ManipulatorGroupId> {
|
pub fn trim(&self, t1: SubpathTValue, t2: SubpathTValue) -> Subpath<ManipulatorGroupId> {
|
||||||
// Return a clone of the Subpath if it is not long enough to be a valid Bezier
|
// Return a clone of the Subpath if it is not long enough to be a valid Bezier
|
||||||
if self.manipulator_groups.is_empty() {
|
if self.manipulator_groups.is_empty() {
|
||||||
|
|
@ -334,7 +334,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a subpath that results from rotating this subpath around the origin by the given angle (in radians).
|
/// Returns a subpath that results from rotating this subpath around the origin by the given angle (in radians).
|
||||||
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#subpath/rotate/solo" title="Rotate Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#subpath/rotate/solo" title="Rotate Demo"></iframe>
|
||||||
pub fn rotate(&self, angle: f64) -> Subpath<ManipulatorGroupId> {
|
pub fn rotate(&self, angle: f64) -> Subpath<ManipulatorGroupId> {
|
||||||
let mut rotated_subpath = self.clone();
|
let mut rotated_subpath = self.clone();
|
||||||
|
|
||||||
|
|
@ -411,8 +411,8 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
drop_common_point[j] = false;
|
drop_common_point[j] = false;
|
||||||
match join {
|
match join {
|
||||||
Join::Bevel => {}
|
Join::Bevel => {}
|
||||||
Join::Miter => {
|
Join::Miter(miter_limit) => {
|
||||||
let miter_manipulator_group = subpaths[i].miter_line_join(&subpaths[j]);
|
let miter_manipulator_group = subpaths[i].miter_line_join(&subpaths[j], miter_limit);
|
||||||
if let Some(miter_manipulator_group) = miter_manipulator_group {
|
if let Some(miter_manipulator_group) = miter_manipulator_group {
|
||||||
subpaths[i].manipulator_groups.push(miter_manipulator_group);
|
subpaths[i].manipulator_groups.push(miter_manipulator_group);
|
||||||
}
|
}
|
||||||
|
|
@ -448,9 +448,9 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
drop_common_point[0] = false;
|
drop_common_point[0] = false;
|
||||||
match join {
|
match join {
|
||||||
Join::Bevel => {}
|
Join::Bevel => {}
|
||||||
Join::Miter => {
|
Join::Miter(miter_limit) => {
|
||||||
let last_subpath_index = subpaths.len() - 1;
|
let last_subpath_index = subpaths.len() - 1;
|
||||||
let miter_manipulator_group = subpaths[last_subpath_index].miter_line_join(&subpaths[0]);
|
let miter_manipulator_group = subpaths[last_subpath_index].miter_line_join(&subpaths[0], miter_limit);
|
||||||
if let Some(miter_manipulator_group) = miter_manipulator_group {
|
if let Some(miter_manipulator_group) = miter_manipulator_group {
|
||||||
subpaths[last_subpath_index].manipulator_groups.push(miter_manipulator_group);
|
subpaths[last_subpath_index].manipulator_groups.push(miter_manipulator_group);
|
||||||
}
|
}
|
||||||
|
|
@ -526,7 +526,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||||
/// an approximate outline around the subpath at a specified distance from the curve. Outline takes the following parameters:
|
/// an approximate outline around the subpath at a specified distance from the curve. Outline takes the following parameters:
|
||||||
/// - `distance` - The outline's distance from the curve.
|
/// - `distance` - The outline's distance from the curve.
|
||||||
/// - `join` - The join type used to cap the endpoints of open bezier curves, and join successive subpath segments.
|
/// - `join` - The join type used to cap the endpoints of open bezier curves, and join successive subpath segments.
|
||||||
/// <iframe frameBorder="0" width="100%" height="450px" src="https://graphite.rs/bezier-rs-demos#subpath/outline/solo" title="Outline Demo"></iframe>
|
/// <iframe frameBorder="0" width="100%" height="425px" src="https://graphite.rs/bezier-rs-demos#subpath/outline/solo" title="Outline Demo"></iframe>
|
||||||
pub fn outline(&self, distance: f64, join: Join, cap: Cap) -> (Subpath<ManipulatorGroupId>, Option<Subpath<ManipulatorGroupId>>) {
|
pub fn outline(&self, distance: f64, join: Join, cap: Cap) -> (Subpath<ManipulatorGroupId>, Option<Subpath<ManipulatorGroupId>>) {
|
||||||
let is_point = self.is_point();
|
let is_point = self.is_point();
|
||||||
let (pos_offset, neg_offset) = if is_point {
|
let (pos_offset, neg_offset) = if is_point {
|
||||||
|
|
|
||||||
|
|
@ -36,17 +36,25 @@ pub enum SubpathTValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
/// Enum to represent the join type between subpaths.
|
/// Represents the shape of the join between two segments of a path which meet at an angle.
|
||||||
/// As defined in SVG: https://www.w3.org/TR/SVG2/painting.html#LineJoin.
|
/// Bevel provides a flat connection, Miter provides a sharp connection, and Round provides a rounded connection.
|
||||||
|
/// As defined in SVG: <https://www.w3.org/TR/SVG2/painting.html#LineJoin>.
|
||||||
pub enum Join {
|
pub enum Join {
|
||||||
|
/// The join is a straight line between the end points of the offset path sides from the two connecting segments.
|
||||||
Bevel,
|
Bevel,
|
||||||
Miter,
|
/// Optional f64 is the miter limit, which defaults to 4 if `None` or a value less than 1 is provided.
|
||||||
|
/// The miter limit is used to prevent highly sharp angles from resulting in excessively long miter joins.
|
||||||
|
/// If the miter limit is exceeded, the join will be converted to a bevel join.
|
||||||
|
/// The value is the ratio of the miter length to the stroke width.
|
||||||
|
/// When that ratio is greater than the miter limit, a bevel join is used instead.
|
||||||
|
Miter(Option<f64>),
|
||||||
|
/// The join is a circular arc between the end points of the offset path sides from the two connecting segments.
|
||||||
Round,
|
Round,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
/// Enum to represent the cap type at the ends of an outline
|
/// Enum to represent the cap type at the ends of an outline
|
||||||
/// As defined in SVG: https://www.w3.org/TR/SVG2/painting.html#LineCaps.
|
/// As defined in SVG: <https://www.w3.org/TR/SVG2/painting.html#LineCaps>.
|
||||||
pub enum Cap {
|
pub enum Cap {
|
||||||
Butt,
|
Butt,
|
||||||
Round,
|
Round,
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ const subpathFeatures = {
|
||||||
},
|
},
|
||||||
offset: {
|
offset: {
|
||||||
name: "Offset",
|
name: "Offset",
|
||||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.offset(options.distance, options.join),
|
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.offset(options.distance, options.join, options.miter_limit),
|
||||||
inputOptions: [
|
inputOptions: [
|
||||||
{
|
{
|
||||||
variable: "distance",
|
variable: "distance",
|
||||||
|
|
@ -136,11 +136,18 @@ const subpathFeatures = {
|
||||||
default: 10,
|
default: 10,
|
||||||
},
|
},
|
||||||
joinOptions,
|
joinOptions,
|
||||||
|
{
|
||||||
|
variable: "join: Miter - limit",
|
||||||
|
min: 1,
|
||||||
|
max: 10,
|
||||||
|
step: 0.25,
|
||||||
|
default: 4,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
outline: {
|
outline: {
|
||||||
name: "Outline",
|
name: "Outline",
|
||||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.outline(options.distance, options.join, options.cap),
|
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.outline(options.distance, options.join, options.cap, options.miter_limit),
|
||||||
inputOptions: [
|
inputOptions: [
|
||||||
{
|
{
|
||||||
variable: "distance",
|
variable: "distance",
|
||||||
|
|
@ -150,6 +157,13 @@ const subpathFeatures = {
|
||||||
default: 10,
|
default: 10,
|
||||||
},
|
},
|
||||||
joinOptions,
|
joinOptions,
|
||||||
|
{
|
||||||
|
variable: "join: Miter - limit",
|
||||||
|
min: 1,
|
||||||
|
max: 10,
|
||||||
|
step: 0.25,
|
||||||
|
default: 4,
|
||||||
|
},
|
||||||
{ ...capOptions, isDisabledForClosed: true },
|
{ ...capOptions, isDisabledForClosed: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { BEZIER_T_VALUE_VARIANTS, SUBPATH_T_VALUE_VARIANTS } from "@/utils/types";
|
import { BEZIER_T_VALUE_VARIANTS, CAP_VARIANTS, JOIN_VARIANTS, SUBPATH_T_VALUE_VARIANTS } from "@/utils/types";
|
||||||
|
|
||||||
export const tSliderOptions = {
|
export const tSliderOptions = {
|
||||||
min: 0,
|
min: 0,
|
||||||
|
|
@ -50,12 +50,12 @@ export const joinOptions = {
|
||||||
variable: "join",
|
variable: "join",
|
||||||
default: 0,
|
default: 0,
|
||||||
inputType: "dropdown",
|
inputType: "dropdown",
|
||||||
options: ["Bevel", "Miter", "Round"],
|
options: JOIN_VARIANTS,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const capOptions = {
|
export const capOptions = {
|
||||||
variable: "cap",
|
variable: "cap",
|
||||||
default: 0,
|
default: 0,
|
||||||
inputType: "dropdown",
|
inputType: "dropdown",
|
||||||
options: ["Butt", "Round", "Square"],
|
options: CAP_VARIANTS,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -95,3 +95,6 @@ export interface DemoPane extends HTMLElement {
|
||||||
|
|
||||||
export const BEZIER_T_VALUE_VARIANTS = ["Parametric", "Euclidean"] as const;
|
export const BEZIER_T_VALUE_VARIANTS = ["Parametric", "Euclidean"] as const;
|
||||||
export const SUBPATH_T_VALUE_VARIANTS = ["GlobalParametric", "GlobalEuclidean"] as const;
|
export const SUBPATH_T_VALUE_VARIANTS = ["GlobalParametric", "GlobalEuclidean"] as const;
|
||||||
|
|
||||||
|
export const CAP_VARIANTS = ["Butt", "Round", "Square"] as const;
|
||||||
|
export const JOIN_VARIANTS = ["Bevel", "Miter", "Round"] as const;
|
||||||
|
|
|
||||||
|
|
@ -441,8 +441,8 @@ impl WasmSubpath {
|
||||||
wrap_svg_tag(format!("{}{}", self.to_default_svg(), trimmed_subpath_svg))
|
wrap_svg_tag(format!("{}{}", self.to_default_svg(), trimmed_subpath_svg))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset(&self, distance: f64, join: i32) -> String {
|
pub fn offset(&self, distance: f64, join: i32, miter_limit: f64) -> String {
|
||||||
let join = parse_join(join);
|
let join = parse_join(join, miter_limit);
|
||||||
let offset_subpath = self.0.offset(distance, join);
|
let offset_subpath = self.0.offset(distance, join);
|
||||||
|
|
||||||
let mut offset_svg = String::new();
|
let mut offset_svg = String::new();
|
||||||
|
|
@ -451,8 +451,8 @@ impl WasmSubpath {
|
||||||
wrap_svg_tag(format!("{}{offset_svg}", self.to_default_svg()))
|
wrap_svg_tag(format!("{}{offset_svg}", self.to_default_svg()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn outline(&self, distance: f64, join: i32, cap: i32) -> String {
|
pub fn outline(&self, distance: f64, join: i32, cap: i32, miter_limit: f64) -> String {
|
||||||
let join = parse_join(join);
|
let join = parse_join(join, miter_limit);
|
||||||
let cap = parse_cap(cap);
|
let cap = parse_cap(cap);
|
||||||
let (outline_piece1, outline_piece2) = self.0.outline(distance, join, cap);
|
let (outline_piece1, outline_piece2) = self.0.outline(distance, join, cap);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use bezier_rs::{Cap, Join};
|
use bezier_rs::{Cap, Join};
|
||||||
|
|
||||||
pub fn parse_join(join: i32) -> Join {
|
pub fn parse_join(join: i32, miter_limit: f64) -> Join {
|
||||||
match join {
|
match join {
|
||||||
0 => Join::Bevel,
|
0 => Join::Bevel,
|
||||||
1 => Join::Miter,
|
1 => Join::Miter(Some(miter_limit)),
|
||||||
2 => Join::Round,
|
2 => Join::Round,
|
||||||
_ => panic!("Unexpected Join value: '{}'", join),
|
_ => panic!("Unexpected Join value: '{}'", join),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue