diff --git a/libraries/bezier-rs/src/bezier/core.rs b/libraries/bezier-rs/src/bezier/core.rs index 140f4860..82486001 100644 --- a/libraries/bezier-rs/src/bezier/core.rs +++ b/libraries/bezier-rs/src/bezier/core.rs @@ -14,6 +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, @@ -68,6 +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 1158c416..352c162d 100644 --- a/libraries/bezier-rs/src/bezier/lookup.rs +++ b/libraries/bezier-rs/src/bezier/lookup.rs @@ -65,6 +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) @@ -72,6 +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) -> Vec { let steps_unwrapped = steps.unwrap_or(DEFAULT_LUT_STEP_SIZE); let ratio: f64 = 1. / (steps_unwrapped as f64); @@ -86,6 +88,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), @@ -112,6 +115,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 [ProjectionOptions] structure. + /// pub fn project(&self, point: DVec2, options: ProjectionOptions) -> f64 { let ProjectionOptions { lut_size, diff --git a/libraries/bezier-rs/src/bezier/solvers.rs b/libraries/bezier-rs/src/bezier/solvers.rs index 4b5c3a34..2d32003a 100644 --- a/libraries/bezier-rs/src/bezier/solvers.rs +++ b/libraries/bezier-rs/src/bezier/solvers.rs @@ -9,6 +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 { @@ -33,6 +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, @@ -51,6 +53,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); match self.handles { @@ -61,12 +64,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() { @@ -113,6 +118,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() @@ -123,6 +129,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); @@ -183,6 +190,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::>() } @@ -239,6 +247,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_seperation` - The minimum difference between adjacent `t` values in sorted order + /// pub fn intersections(&self, other: &Bezier, error: Option, minimum_seperation: Option) -> Vec { // TODO: Consider using the `intersections_between_vectors_of_curves` helper function here // Otherwise, use bounding box to determine intersections @@ -337,6 +346,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![]; @@ -364,6 +374,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), diff --git a/libraries/bezier-rs/src/bezier/transform.rs b/libraries/bezier-rs/src/bezier/transform.rs index 3e1a5dd3..7c3a8b59 100644 --- a/libraries/bezier-rs/src/bezier/transform.rs +++ b/libraries/bezier-rs/src/bezier/transform.rs @@ -7,6 +7,7 @@ use std::f64::consts::PI; /// Functionality that transform Beziers, such as split, reduce, offset, etc. 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)); @@ -52,6 +53,7 @@ impl Bezier { /// Returns the Bezier curve representing the sub-curve starting at the point `t1` and ending at the point `t2` along the curve. /// When `t1 < t2`, returns the reversed sub-curve starting at `t2` and ending at `t1`. + /// pub fn trim(&self, t1: TValue, t2: TValue) -> Bezier { let (t1, 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 @@ -102,6 +104,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)) @@ -227,6 +230,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 } @@ -326,6 +330,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) -> Vec { let mut reduced = self.reduce(None); reduced.iter_mut().for_each(|bezier| *bezier = bezier.scale(distance)); @@ -357,6 +362,7 @@ impl Bezier { /// /// Outline takes the following parameter: /// - `distance` - The outline's distance from the curve. + /// pub fn outline(&self, distance: f64) -> Vec { let first_segment = self.offset(distance); let third_segment = self.reverse().offset(distance); @@ -372,11 +378,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) -> Vec { self.skewed_outline(start_distance, end_distance, end_distance, start_distance) } /// 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) -> Vec { let first_segment = self.graduated_offset(distance1, distance2); let third_segment = self.reverse().graduated_offset(distance3, distance4); @@ -392,6 +400,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 7d1336eb..889c803a 100644 --- a/libraries/bezier-rs/src/subpath/lookup.rs +++ b/libraries/bezier-rs/src/subpath/lookup.rs @@ -8,6 +8,7 @@ use glam::DVec2; 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)) } @@ -78,6 +79,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: ProjectionOptions) -> 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 e2d5a59b..22244e59 100644 --- a/libraries/bezier-rs/src/subpath/solvers.rs +++ b/libraries/bezier-rs/src/subpath/solvers.rs @@ -8,6 +8,7 @@ use glam::DVec2; 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)) @@ -17,6 +18,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 intersections(&self, other: &Bezier, error: Option, minimum_seperation: Option) -> Vec { // TODO: account for either euclidean or parametric type let number_of_curves = self.len_segments() as f64; @@ -43,11 +45,15 @@ impl Subpath { intersection_t_values } + /// 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)) diff --git a/libraries/bezier-rs/src/subpath/transform.rs b/libraries/bezier-rs/src/subpath/transform.rs index 538d3f3c..98ecb3c8 100644 --- a/libraries/bezier-rs/src/subpath/transform.rs +++ b/libraries/bezier-rs/src/subpath/transform.rs @@ -7,6 +7,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(); diff --git a/website/other/bezier-rs-demos/src/style.css b/website/other/bezier-rs-demos/src/style.css index ad208ea3..3296ba5d 100644 --- a/website/other/bezier-rs-demos/src/style.css +++ b/website/other/bezier-rs-demos/src/style.css @@ -2,6 +2,7 @@ html, body { font-family: Arial, sans-serif; text-align: center; + background-color: white; } body > h1 { diff --git a/website/other/bezier-rs-demos/src/utils/render.ts b/website/other/bezier-rs-demos/src/utils/render.ts index a88c1cf7..1924b692 100644 --- a/website/other/bezier-rs-demos/src/utils/render.ts +++ b/website/other/bezier-rs-demos/src/utils/render.ts @@ -45,13 +45,15 @@ export function renderDemoPane(demoPane: DemoPane): void { const headerAnchorLink = document.createElement("a"); headerAnchorLink.innerText = "#"; const currentHash = window.location.hash.split("/"); - // Add href anchor if not on a solo example page - if (currentHash.length !== 3 && currentHash[2] !== "solo") headerAnchorLink.href = `#${demoPane.id}`; - - const header = document.createElement("h3"); - header.innerText = demoPane.name; - header.className = "demo-pane-header"; - header.append(headerAnchorLink); + // Add header and href anchor if not on a solo example page + if (currentHash.length !== 3 && currentHash[2] !== "solo") { + headerAnchorLink.href = `#${demoPane.id}`; + const header = document.createElement("h3"); + header.innerText = demoPane.name; + header.className = "demo-pane-header"; + header.append(headerAnchorLink); + container.append(header); + } const tVariantContainer = document.createElement("div"); tVariantContainer.className = "t-variant-choice"; @@ -95,11 +97,11 @@ export function renderDemoPane(demoPane: DemoPane): void { demoRow.append(demoComponent); }); - container.append(header); + container.append(demoRow); + if (demoPane.chooseTVariant) { container.append(tVariantContainer); } - container.append(demoRow); demoPane.append(container); }