mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-07 15:55:00 +00:00
Bezier-rs: Release version 0.2
This commit is contained in:
parent
d0863165a4
commit
3cf955e5bb
11 changed files with 66 additions and 63 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -284,7 +284,7 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
|||
|
||||
[[package]]
|
||||
name = "bezier-rs"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"dyn-any",
|
||||
"glam",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bezier-rs"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
rust-version = "1.66.0"
|
||||
edition = "2021"
|
||||
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||
|
@ -11,10 +11,10 @@ keywords = ["bezier", "curve", "geometry", "2d", "graphics"]
|
|||
categories = ["graphics", "mathematics"]
|
||||
homepage = "https://github.com/GraphiteEditor/Graphite/tree/master/libraries/bezier-rs"
|
||||
repository = "https://github.com/GraphiteEditor/Graphite/tree/master/libraries/bezier-rs"
|
||||
documentation = "https://graphite.rs/bezier-rs-demos/"
|
||||
documentation = "https://graphite.rs/libraries/bezier-rs/"
|
||||
|
||||
[dependencies]
|
||||
glam = { version = "0.22", features = ["serde"] }
|
||||
|
||||
dyn-any = { path = "../dyn-any", optional = true }
|
||||
dyn-any = { version = "0.2.1", path = "../dyn-any", optional = true }
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
[crates.io](https://crates.io/crates/bezier-rs) • [docs.rs](https://docs.rs/bezier-rs/latest/bezier_rs/) • [repo](https://github.com/GraphiteEditor/Graphite/tree/master/libraries/bezier-rs)
|
||||
|
||||
# Bezier-rs
|
||||
|
||||
Computational geometry algorithms for Bézier segments and shapes useful in the context of 2D graphics.
|
||||
|
||||
Play with the [interactive documentation](https://graphite.rs/bezier-rs-demos/) which visualizes each API function in a fun manner.
|
||||
Play with the interactive documentation which visualizes each API function in a fun manner:
|
||||
|
||||
Bezier-rs is built for the needs of [Graphite](https://graphite.rs), an open source 2D vector graphics editor. We hope it may be useful to others, but presently Graphite is its primary user. Pull requests are welcomed for new features, code cleanup, ergonomics enhancements, performance improvements, and documentation clarifications.
|
||||
### [**View the interactive API**](https://graphite.rs/libraries/bezier-rs/)
|
||||
|
||||
The library currently provides functions dealing with single Bézier curve segments, as well as open-or-closed multi-segment paths (which we call _subpaths_). In the future, the library will be expanded to include compound paths (multiple subpaths forming a single shape, where the winding order determines inside-or-outside-ness) and operations between paths (e.g. boolean functions).
|
||||
---
|
||||
|
||||
Bezier-rs is built for the needs of [Graphite](https://graphite.rs), an open source 2D vector graphics editor. We hope it may be useful to others, but presently Graphite is its primary user. Pull requests are welcomed for new features, code cleanup, ergonomic enhancements, performance improvements, and documentation clarifications.
|
||||
|
||||
The library currently provides functions dealing with single Bézier curve segments and open-or-closed multi-segment paths (which we call _subpaths_).
|
||||
|
||||
In the future, the library will be expanded to include compound paths (multiple subpaths forming a single shape, where the winding order determines inside-or-outside-ness) and operations between paths (e.g. boolean operations, convex hull). Pull requests for these additional features would be highly desirable.
|
||||
|
||||
Bezier-rs is inspired by [Bezier.js](https://pomax.github.io/bezierjs/) and [_A Primer on Bézier Curves_](https://pomax.github.io/bezierinfo/) by Pomax. Bezier-rs is not a port of Bezier.js so the API for single-segment Bézier curves has some differences, and the intention is to offer a broader scope that provides algorithms beyond single curve segments (as noted above) to eventually service full vector shapes.
|
||||
|
||||
|
@ -14,13 +22,7 @@ Bezier-rs is inspired by [Bezier.js](https://pomax.github.io/bezierjs/) and [_A
|
|||
|
||||
Graphite and Bezier-rs use the following terminology for vector data. These depictions are given for cubic Bézier curves.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
|
|
@ -14,7 +14,7 @@ impl Bezier {
|
|||
}
|
||||
|
||||
/// Create a quadratic bezier using the provided DVec2s as the start, handle, and end points.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#bezier/constructor/solo" title="Constructor Demo"></iframe>
|
||||
pub fn from_linear_dvec2(p1: DVec2, p2: DVec2) -> Self {
|
||||
Bezier {
|
||||
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.
|
||||
/// 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.
|
||||
/// <iframe frameBorder="0" width="100%" height="375px" 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/libraries/bezier-rs#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 {
|
||||
let t = t.unwrap_or(DEFAULT_T_VALUE);
|
||||
if t == 0. {
|
||||
|
|
|
@ -65,7 +65,7 @@ impl Bezier {
|
|||
|
||||
/// Calculate the coordinates of the point `t` along the curve.
|
||||
/// Expects `t` to be within the inclusive range `[0, 1]`.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#bezier/evaluate/solo" title="Evaluate Demo"></iframe>
|
||||
pub fn evaluate(&self, t: TValue) -> DVec2 {
|
||||
let t = self.t_value_to_parametric(t);
|
||||
self.unrestricted_parametric_evaluate(t)
|
||||
|
@ -73,7 +73,7 @@ impl Bezier {
|
|||
|
||||
/// 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.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#bezier/lookup-table/solo" title="Lookup-Table Demo"></iframe>
|
||||
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 tvalue_type = tvalue_type.unwrap_or(TValueType::Parametric);
|
||||
|
@ -91,7 +91,7 @@ impl Bezier {
|
|||
|
||||
/// 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.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#bezier/length/solo" title="Length Demo"></iframe>
|
||||
pub fn length(&self, num_subdivisions: Option<usize>) -> f64 {
|
||||
match self.handles {
|
||||
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.
|
||||
/// Uses a searching algorithm akin to binary search that can be customized using the optional [ProjectionOptions] struct.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#bezier/project/solo" title="Project Demo"></iframe>
|
||||
pub fn project(&self, point: DVec2, options: Option<ProjectionOptions>) -> f64 {
|
||||
let options = options.unwrap_or_default();
|
||||
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.
|
||||
/// 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.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#bezier/de-casteljau-points/solo" title="De Casteljau Demo"></iframe>
|
||||
pub fn de_casteljau_points(&self, t: TValue) -> Vec<Vec<DVec2>> {
|
||||
let t = self.t_value_to_parametric(t);
|
||||
let bezier_points = match self.handles {
|
||||
|
@ -34,7 +34,7 @@ impl Bezier {
|
|||
|
||||
/// Returns a [Bezier] representing the derivative of the original curve.
|
||||
/// - This function returns `None` for a linear segment.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#bezier/derivative/solo" title="Derivative Demo"></iframe>
|
||||
pub fn derivative(&self) -> Option<Bezier> {
|
||||
match self.handles {
|
||||
BezierHandles::Linear => None,
|
||||
|
@ -61,7 +61,7 @@ impl Bezier {
|
|||
}
|
||||
|
||||
/// Returns a normalized unit vector representing the tangent at the point `t` along the curve.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#bezier/tangent/solo" title="Tangent Demo"></iframe>
|
||||
pub fn tangent(&self, t: TValue) -> DVec2 {
|
||||
let t = self.t_value_to_parametric(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.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#bezier/normal/solo" title="Normal Demo"></iframe>
|
||||
pub fn normal(&self, t: TValue) -> DVec2 {
|
||||
self.tangent(t).perp()
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#bezier/curvature/solo" title="Curvature Demo"></iframe>
|
||||
pub fn curvature(&self, t: TValue) -> f64 {
|
||||
let t = self.t_value_to_parametric(t);
|
||||
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.
|
||||
/// The list of `t`-values returned are filtered such that they fall within the range `[0, 1]`.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#bezier/local-extrema/solo" title="Local Extrema Demo"></iframe>
|
||||
pub fn local_extrema(&self) -> [Vec<f64>; 2] {
|
||||
self.unrestricted_local_extrema()
|
||||
.into_iter()
|
||||
|
@ -138,7 +138,7 @@ impl Bezier {
|
|||
}
|
||||
|
||||
/// Return the min and max corners that represent the bounding box of the curve.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#bezier/bounding-box/solo" title="Bounding Box Demo"></iframe>
|
||||
pub fn bounding_box(&self) -> [DVec2; 2] {
|
||||
// Start by taking min/max of endpoints.
|
||||
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.
|
||||
/// The list of `t`-values returned are filtered such that they fall within the range `[0, 1]`.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#bezier/inflections/solo" title="Inflections Demo"></iframe>
|
||||
pub fn inflections(&self) -> 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.
|
||||
/// - `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
|
||||
/// <iframe frameBorder="0" width="100%" height="375px" 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/libraries/bezier-rs#bezier/intersect-cubic/solo" title="Intersections Demo"></iframe>
|
||||
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
|
||||
// Otherwise, use bounding box to determine intersections
|
||||
|
@ -355,7 +355,7 @@ impl Bezier {
|
|||
// 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.
|
||||
/// - `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="325px" 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/libraries/bezier-rs#bezier/intersect-self/solo" title="Self Intersection Demo"></iframe>
|
||||
pub fn self_intersections(&self, error: Option<f64>) -> Vec<[f64; 2]> {
|
||||
if self.handles == BezierHandles::Linear || matches!(self.handles, BezierHandles::Quadratic { .. }) {
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#bezier/intersect-rectangle/solo" title="Intersection (Rectangle) Demo"></iframe>
|
||||
pub fn rectangle_intersections(&self, corner1: DVec2, corner2: DVec2) -> Vec<f64> {
|
||||
[
|
||||
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.
|
||||
/// The resulting path formed by the Bezier curves is continuous up to the first derivative.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#bezier/join/solo" title="Join Demo"></iframe>
|
||||
pub fn join(&self, other: &Bezier) -> Bezier {
|
||||
let handle1 = self.non_normalized_tangent(1.) / 3. + self.end;
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#bezier/split/solo" title="Split Demo"></iframe>
|
||||
pub fn split(&self, t: TValue) -> [Bezier; 2] {
|
||||
let t = self.t_value_to_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.
|
||||
/// 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="400px" 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/libraries/bezier-rs#bezier/trim/solo" title="Trim Demo"></iframe>
|
||||
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));
|
||||
// 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).
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" 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/libraries/bezier-rs#bezier/rotate/solo" title="Rotate Demo"></iframe>
|
||||
pub fn rotate(&self, angle: f64) -> Bezier {
|
||||
let rotation_matrix = DMat2::from_angle(angle);
|
||||
self.apply_transformation(|point| rotation_matrix.mul_vec2(point))
|
||||
|
@ -251,7 +251,7 @@ impl Bezier {
|
|||
/// 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`.
|
||||
/// A small granularity may increase the chance the function does not introduce gaps, but will increase computation time.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#bezier/reduce/solo" title="Reduce Demo"></iframe>
|
||||
pub fn reduce(&self, step_size: Option<f64>) -> Vec<Bezier> {
|
||||
self.reduced_curves_and_t_values(step_size).0
|
||||
}
|
||||
|
@ -355,7 +355,7 @@ impl Bezier {
|
|||
/// 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,
|
||||
/// while negative values will offset in the opposite direction.
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" 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/libraries/bezier-rs#bezier/offset/solo" title="Offset Demo"></iframe>
|
||||
pub fn offset<ManipulatorGroupId: crate::Identifier>(&self, distance: f64) -> Subpath<ManipulatorGroupId> {
|
||||
if self.is_point() {
|
||||
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.
|
||||
/// Outline takes the following parameter:
|
||||
/// - `distance` - The outline's distance from the curve.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#bezier/outline/solo" title="Outline Demo"></iframe>
|
||||
pub fn outline<ManipulatorGroupId: crate::Identifier>(&self, distance: f64, cap: Cap) -> Subpath<ManipulatorGroupId> {
|
||||
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.
|
||||
/// The outline begins `start_distance` away, and gradually move to being `end_distance` away.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" 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/libraries/bezier-rs#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> {
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="475px" 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/libraries/bezier-rs#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> {
|
||||
let (pos_offset, neg_offset) = if self.is_point() {
|
||||
(
|
||||
|
@ -468,7 +468,7 @@ impl Bezier {
|
|||
|
||||
/// Approximate a bezier curve with circular arcs.
|
||||
/// The algorithm can be customized using the [ArcsOptions] structure.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" 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/libraries/bezier-rs#bezier/arcs/solo" title="Arcs Demo"></iframe>
|
||||
pub fn arcs(&self, arcs_options: ArcsOptions) -> Vec<CircleArc> {
|
||||
let ArcsOptions {
|
||||
strategy: maximize_arcs,
|
||||
|
|
|
@ -8,7 +8,7 @@ use glam::DVec2;
|
|||
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
/// 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.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#subpath/lookup-table/solo" title="Lookup-Table Demo"></iframe>
|
||||
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 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`.
|
||||
/// - `num_subdivisions` - Number of subdivisions used to approximate the curve. The default value is `1000`.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#subpath/length/solo" title="Length Demo"></iframe>
|
||||
pub fn length(&self, num_subdivisions: Option<usize>) -> f64 {
|
||||
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.
|
||||
/// Uses a searching algorithm akin to binary search that can be customized using the [ProjectionOptions] structure.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#subpath/project/solo" title="Project Demo"></iframe>
|
||||
pub fn project(&self, point: DVec2, options: Option<ProjectionOptions>) -> Option<(usize, f64)> {
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::f64::consts::PI;
|
|||
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
/// Calculate the point on the subpath based on the parametric `t`-value provided.
|
||||
/// Expects `t` to be within the inclusive range `[0, 1]`.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#subpath/evaluate/solo" title="Evaluate Demo"></iframe>
|
||||
pub fn evaluate(&self, t: SubpathTValue) -> DVec2 {
|
||||
let (segment_index, t) = self.t_value_to_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
|
||||
/// - `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.
|
||||
/// <iframe frameBorder="0" width="100%" height="375px" 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/libraries/bezier-rs#subpath/intersect-cubic/solo" title="Intersection Demo"></iframe>
|
||||
pub fn intersections(&self, other: &Bezier, error: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
|
||||
self.iter()
|
||||
.enumerate()
|
||||
|
@ -35,7 +35,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
|||
/// This function expects the following:
|
||||
/// - other: a [Bezier] curve to check intersections against
|
||||
/// - error: an optional f64 value to provide an error bound
|
||||
/// <iframe frameBorder="0" width="100%" height="375px" 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/libraries/bezier-rs#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)> {
|
||||
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());
|
||||
|
@ -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
|
||||
///
|
||||
/// **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="375px" 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/libraries/bezier-rs#subpath/self-intersect/solo" title="Self-Intersection Demo"></iframe>
|
||||
pub fn self_intersections(&self, error: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
|
||||
let mut intersections_vec = Vec::new();
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#subpath/tangent/solo" title="Tangent Demo"></iframe>
|
||||
pub fn tangent(&self, t: SubpathTValue) -> DVec2 {
|
||||
let (segment_index, t) = self.t_value_to_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.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#subpath/normal/solo" title="Normal Demo"></iframe>
|
||||
pub fn normal(&self, t: SubpathTValue) -> DVec2 {
|
||||
let (segment_index, t) = self.t_value_to_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.
|
||||
/// The list of `t`-values returned are filtered such that they fall within the range `[0, 1]`.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#subpath/local-extrema/solo" title="Local Extrema Demo"></iframe>
|
||||
pub fn local_extrema(&self) -> [Vec<f64>; 2] {
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" 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/libraries/bezier-rs#subpath/bounding-box/solo" title="Bounding Box Demo"></iframe>
|
||||
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])])
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
|||
|
||||
/// Returns list of `t`-values representing the inflection points of the subpath.
|
||||
/// The list of `t`-values returned are filtered such that they fall within the range `[0, 1]`.
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/bezier-rs-demos#subpath/inflections/solo" title="Inflections Demo"></iframe>
|
||||
/// <iframe frameBorder="0" width="100%" height="300px" src="https://graphite.rs/libraries/bezier-rs#subpath/inflections/solo" title="Inflections Demo"></iframe>
|
||||
pub fn inflections(&self) -> Vec<f64> {
|
||||
let number_of_curves = self.len_segments() as f64;
|
||||
let inflection_t_values: Vec<f64> = self
|
||||
|
@ -237,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.
|
||||
/// Curvature is 1 over the radius of a circle with an equivalent derivative.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#subpath/curvature/solo" title="Curvature Demo"></iframe>
|
||||
pub fn curvature(&self, t: SubpathTValue) -> f64 {
|
||||
let (segment_index, t) = self.t_value_to_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`.
|
||||
/// 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.
|
||||
/// <iframe frameBorder="0" width="100%" height="350px" 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/libraries/bezier-rs#subpath/split/solo" title="Split Demo"></iframe>
|
||||
pub fn split(&self, t: SubpathTValue) -> (Subpath<ManipulatorGroupId>, Option<Subpath<ManipulatorGroupId>>) {
|
||||
let (segment_index, t) = self.t_value_to_parametric(t);
|
||||
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`.
|
||||
/// 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`.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" 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/libraries/bezier-rs#subpath/trim/solo" title="Trim Demo"></iframe>
|
||||
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
|
||||
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).
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" 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/libraries/bezier-rs#subpath/rotate/solo" title="Rotate Demo"></iframe>
|
||||
pub fn rotate(&self, angle: f64) -> Subpath<ManipulatorGroupId> {
|
||||
let mut rotated_subpath = self.clone();
|
||||
|
||||
|
@ -358,7 +358,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
|||
|
||||
/// Reduces the segments of the subpath into simple subcurves, then scales each subcurve a set `distance` away.
|
||||
/// The intersections of segments of the subpath are joined using the method specified by the `join` argument.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#subpath/offset/solo" title="Offset Demo"></iframe>
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/libraries/bezier-rs#subpath/offset/solo" title="Offset Demo"></iframe>
|
||||
pub fn offset(&self, distance: f64, join: Join) -> Subpath<ManipulatorGroupId> {
|
||||
assert!(self.len_segments() > 1, "Cannot offset an empty Subpath.");
|
||||
|
||||
|
@ -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:
|
||||
/// - `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.
|
||||
/// <iframe frameBorder="0" width="100%" height="425px" 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/libraries/bezier-rs#subpath/outline/solo" title="Outline Demo"></iframe>
|
||||
pub fn outline(&self, distance: f64, join: Join, cap: Cap) -> (Subpath<ManipulatorGroupId>, Option<Subpath<ManipulatorGroupId>>) {
|
||||
let is_point = self.is_point();
|
||||
let (pos_offset, neg_offset) = if is_point {
|
||||
|
|
|
@ -17,4 +17,5 @@ npm ci
|
|||
echo 👷 Build Bezier-rs Demos
|
||||
export NODE_ENV=production
|
||||
npm run build
|
||||
mv dist ../../public/bezier-rs-demos
|
||||
mkdir ../../public/libraries
|
||||
mv dist ../../public/libraries/bezier-rs
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue