diff --git a/Cargo.lock b/Cargo.lock index 7694f6bb0..d5e8377ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -284,7 +284,7 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bezier-rs" -version = "0.1.0" +version = "0.2.0" dependencies = [ "dyn-any", "glam", diff --git a/libraries/bezier-rs/Cargo.toml b/libraries/bezier-rs/Cargo.toml index c1c5da950..08f6376a5 100644 --- a/libraries/bezier-rs/Cargo.toml +++ b/libraries/bezier-rs/Cargo.toml @@ -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 "] @@ -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 } diff --git a/libraries/bezier-rs/README.md b/libraries/bezier-rs/README.md index 07d802d0b..e7856741e 100644 --- a/libraries/bezier-rs/README.md +++ b/libraries/bezier-rs/README.md @@ -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. -![Handle and Anchor](https://static.graphite.rs/libraries/bezier-rs/handle-and-anchor.png) -![Manipulator](https://static.graphite.rs/libraries/bezier-rs/manipulator.png) -![Manipulator Group](https://static.graphite.rs/libraries/bezier-rs/manipulator-group.png) - -![Curve Segment](https://static.graphite.rs/libraries/bezier-rs/curve-segment.png) -![Bezier Segment](https://static.graphite.rs/libraries/bezier-rs/bezier-segment.png) - -![Subpath](https://static.graphite.rs/libraries/bezier-rs/subpath.png) -![Compound Path](https://static.graphite.rs/libraries/bezier-rs/compound-path.png) -![Open and Closed](https://static.graphite.rs/libraries/bezier-rs/open-and-closed.png) +![Manipulators](https://static.graphite.rs/libraries/bezier-rs/manipulator-groups.png) +![Curve/Bezier Segment](https://static.graphite.rs/libraries/bezier-rs/curve-bezier-segment.png) +![Subpath/Path](https://static.graphite.rs/libraries/bezier-rs/subpath-path.png) +![Open/Closed](https://static.graphite.rs/libraries/bezier-rs/closed-open-subpath.png) diff --git a/libraries/bezier-rs/src/bezier/core.rs b/libraries/bezier-rs/src/bezier/core.rs index 29e670608..78108f465 100644 --- a/libraries/bezier-rs/src/bezier/core.rs +++ b/libraries/bezier-rs/src/bezier/core.rs @@ -14,7 +14,7 @@ impl Bezier { } /// Create a quadratic bezier using the provided DVec2s as the start, handle, and end points. - /// + /// 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. - /// + /// pub fn quadratic_through_points(start: DVec2, point_on_curve: DVec2, end: DVec2, t: Option) -> Self { let t = t.unwrap_or(DEFAULT_T_VALUE); if t == 0. { diff --git a/libraries/bezier-rs/src/bezier/lookup.rs b/libraries/bezier-rs/src/bezier/lookup.rs index 3756cbb74..68c457545 100644 --- a/libraries/bezier-rs/src/bezier/lookup.rs +++ b/libraries/bezier-rs/src/bezier/lookup.rs @@ -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]`. - /// + /// 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. - /// + /// pub fn compute_lookup_table(&self, steps: Option, tvalue_type: Option) -> Vec { 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. - /// + /// pub fn length(&self, num_subdivisions: Option) -> 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. - /// + /// pub fn project(&self, point: DVec2, options: Option) -> f64 { let options = options.unwrap_or_default(); let ProjectionOptions { diff --git a/libraries/bezier-rs/src/bezier/solvers.rs b/libraries/bezier-rs/src/bezier/solvers.rs index 077f16cda..f8dcaa0d7 100644 --- a/libraries/bezier-rs/src/bezier/solvers.rs +++ b/libraries/bezier-rs/src/bezier/solvers.rs @@ -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. - /// + /// pub fn de_casteljau_points(&self, t: TValue) -> Vec> { 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. - /// + /// pub fn derivative(&self) -> Option { 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. - /// + /// 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. - /// + /// 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. - /// + /// 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]`. - /// + /// pub fn local_extrema(&self) -> [Vec; 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. - /// + /// 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]`. - /// + /// pub fn inflections(&self) -> Vec { self.unrestricted_inflections().into_iter().filter(|&t| t > 0. && t < 1.).collect::>() } @@ -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 - /// + /// pub fn intersections(&self, other: &Bezier, error: Option, minimum_separation: Option) -> Vec { // 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. - /// + /// pub fn self_intersections(&self, error: Option) -> 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. - /// + /// pub fn rectangle_intersections(&self, corner1: DVec2, corner2: DVec2) -> Vec { [ 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. - /// + /// 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.; diff --git a/libraries/bezier-rs/src/bezier/transform.rs b/libraries/bezier-rs/src/bezier/transform.rs index 2e7b7c3dc..a5fee6850 100644 --- a/libraries/bezier-rs/src/bezier/transform.rs +++ b/libraries/bezier-rs/src/bezier/transform.rs @@ -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. - /// + /// 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`. - /// + /// 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). - /// + /// 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. - /// + /// pub fn reduce(&self, step_size: Option) -> Vec { 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. - /// + /// pub fn offset(&self, distance: f64) -> Subpath { 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. - /// + /// pub fn outline(&self, distance: f64, cap: Cap) -> Subpath { 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. - /// + /// pub fn graduated_outline(&self, start_distance: f64, end_distance: f64, cap: Cap) -> Subpath { 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. - /// + /// pub fn skewed_outline(&self, distance1: f64, distance2: f64, distance3: f64, distance4: f64, cap: Cap) -> Subpath { 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. - /// + /// pub fn arcs(&self, arcs_options: ArcsOptions) -> Vec { let ArcsOptions { strategy: maximize_arcs, diff --git a/libraries/bezier-rs/src/subpath/lookup.rs b/libraries/bezier-rs/src/subpath/lookup.rs index 174a8f987..81b89e3f2 100644 --- a/libraries/bezier-rs/src/subpath/lookup.rs +++ b/libraries/bezier-rs/src/subpath/lookup.rs @@ -8,7 +8,7 @@ use glam::DVec2; impl Subpath { /// 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. - /// + /// pub fn compute_lookup_table(&self, steps: Option, tvalue_type: Option) -> Vec { let steps = steps.unwrap_or(DEFAULT_LUT_STEP_SIZE); let tvalue_type = tvalue_type.unwrap_or(TValueType::Parametric); @@ -26,7 +26,7 @@ impl 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`. - /// + /// pub fn length(&self, num_subdivisions: Option) -> f64 { self.iter().fold(0., |accumulator, bezier| accumulator + bezier.length(num_subdivisions)) } @@ -97,7 +97,7 @@ impl Subpath { /// 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. - /// + /// pub fn project(&self, point: DVec2, options: Option) -> Option<(usize, f64)> { if self.is_empty() { return None; diff --git a/libraries/bezier-rs/src/subpath/solvers.rs b/libraries/bezier-rs/src/subpath/solvers.rs index 5327aa113..46bafabc0 100644 --- a/libraries/bezier-rs/src/subpath/solvers.rs +++ b/libraries/bezier-rs/src/subpath/solvers.rs @@ -9,7 +9,7 @@ use std::f64::consts::PI; impl Subpath { /// Calculate the point on the subpath based on the parametric `t`-value provided. /// Expects `t` to be within the inclusive range `[0, 1]`. - /// + /// 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 Subpath { /// - `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. - /// + /// pub fn intersections(&self, other: &Bezier, error: Option, minimum_separation: Option) -> Vec<(usize, f64)> { self.iter() .enumerate() @@ -35,7 +35,7 @@ impl Subpath { /// This function expects the following: /// - other: a [Bezier] curve to check intersections against /// - error: an optional f64 value to provide an error bound - /// + /// pub fn subpath_intersections(&self, other: &Subpath, error: Option, minimum_separation: Option) -> 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 Subpath { /// 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. - /// + /// pub fn self_intersections(&self, error: Option, minimum_separation: Option) -> Vec<(usize, f64)> { let mut intersections_vec = Vec::new(); let err = error.unwrap_or(MAX_ABSOLUTE_DIFFERENCE); @@ -69,14 +69,14 @@ impl Subpath { } /// Returns a normalized unit vector representing the tangent on the subpath based on the parametric `t`-value provided. - /// + /// 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. - /// + /// 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 Subpath { /// 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]`. - /// + /// pub fn local_extrema(&self) -> [Vec; 2] { let number_of_curves = self.len_segments() as f64; @@ -99,7 +99,7 @@ impl Subpath { } /// Return the min and max corners that represent the bounding box of the subpath. - /// + /// 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 Subpath { /// 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]`. - /// + /// pub fn inflections(&self) -> Vec { let number_of_curves = self.len_segments() as f64; let inflection_t_values: Vec = self @@ -237,7 +237,7 @@ impl 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. - /// + /// 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)) diff --git a/libraries/bezier-rs/src/subpath/transform.rs b/libraries/bezier-rs/src/subpath/transform.rs index 2e8022720..fe7d6f1f1 100644 --- a/libraries/bezier-rs/src/subpath/transform.rs +++ b/libraries/bezier-rs/src/subpath/transform.rs @@ -22,7 +22,7 @@ impl Subpath { /// 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. - /// + /// pub fn split(&self, t: SubpathTValue) -> (Subpath, Option>) { let (segment_index, t) = self.t_value_to_parametric(t); let curve = self.get_segment(segment_index).unwrap(); @@ -126,7 +126,7 @@ impl Subpath { /// 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`. - /// + /// pub fn trim(&self, t1: SubpathTValue, t2: SubpathTValue) -> Subpath { // 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 Subpath { } /// Returns a subpath that results from rotating this subpath around the origin by the given angle (in radians). - /// + /// pub fn rotate(&self, angle: f64) -> Subpath { let mut rotated_subpath = self.clone(); @@ -358,7 +358,7 @@ impl Subpath { /// 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. - /// + /// pub fn offset(&self, distance: f64, join: Join) -> Subpath { assert!(self.len_segments() > 1, "Cannot offset an empty Subpath."); @@ -526,7 +526,7 @@ impl Subpath { /// 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. - /// + /// pub fn outline(&self, distance: f64, join: Join, cap: Cap) -> (Subpath, Option>) { let is_point = self.is_point(); let (pos_offset, neg_offset) = if is_point { diff --git a/website/other/build.sh b/website/other/build.sh index fbb2af9f7..4edf8b473 100644 --- a/website/other/build.sh +++ b/website/other/build.sh @@ -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