mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 05:18:19 +00:00
Bezier-rs: Add iframes to rustdoc (#1036)
* Add iframes examples * Move TValue selection to bottom * Remove spaces * Add background, adjust heights * Minor lint fix
This commit is contained in:
parent
9a52cae9b9
commit
98f172414a
9 changed files with 47 additions and 9 deletions
|
@ -14,6 +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="325px" src="https://graphite.rs/bezier-rs-demos#bezier/constructor/solo" title="Constructor Demo"></iframe>
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" 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 {
|
||||
let t = t.unwrap_or(DEFAULT_T_VALUE);
|
||||
if t == 0. {
|
||||
|
|
|
@ -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]`.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#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)
|
||||
|
@ -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.
|
||||
/// <iframe frameBorder="0" width="100%" height="375px" 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>) -> Vec<DVec2> {
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#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),
|
||||
|
@ -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.
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/project/solo" title="Project Demo"></iframe>
|
||||
pub fn project(&self, point: DVec2, options: ProjectionOptions) -> f64 {
|
||||
let ProjectionOptions {
|
||||
lut_size,
|
||||
|
|
|
@ -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.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" 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>> {
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/derivative/solo" title="Derivative Demo"></iframe>
|
||||
pub fn derivative(&self) -> Option<Bezier> {
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#bezier/tangent/solo" title="Tangent Demo"></iframe>
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#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="400px" src="https://graphite.rs/bezier-rs-demos#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() {
|
||||
|
@ -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]`.
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#bezier/local-extrema/solo" title="Local Extrema Demo"></iframe>
|
||||
pub fn local_extrema(&self) -> [Vec<f64>; 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.
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#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);
|
||||
|
@ -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]`.
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#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>>()
|
||||
}
|
||||
|
@ -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
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" 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_seperation: Option<f64>) -> Vec<f64> {
|
||||
// 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.
|
||||
/// <iframe frameBorder="0" width="100%" height="375px" 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]> {
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" 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> {
|
||||
[
|
||||
Bezier::from_linear_coordinates(corner1.x, corner1.y, corner2.x, corner1.y),
|
||||
|
|
|
@ -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.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#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));
|
||||
|
@ -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`.
|
||||
/// <iframe frameBorder="0" width="100%" height="450px" src="https://graphite.rs/bezier-rs-demos#bezier/trim/solo" title="Trim Demo"></iframe>
|
||||
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).
|
||||
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#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))
|
||||
|
@ -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.
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#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
|
||||
}
|
||||
|
@ -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.
|
||||
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#bezier/offset/solo" title="Offset Demo"></iframe>
|
||||
pub fn offset(&self, distance: f64) -> Vec<Bezier> {
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="375px" src="https://graphite.rs/bezier-rs-demos#bezier/outline/solo" title="Outline Demo"></iframe>
|
||||
pub fn outline(&self, distance: f64) -> Vec<Bezier> {
|
||||
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.
|
||||
/// <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(&self, start_distance: f64, end_distance: f64) -> Vec<Bezier> {
|
||||
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.
|
||||
/// <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(&self, distance1: f64, distance2: f64, distance3: f64, distance4: f64) -> Vec<Bezier> {
|
||||
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.
|
||||
/// <iframe frameBorder="0" width="100%" height="450px" src="https://graphite.rs/bezier-rs-demos#bezier/arcs/solo" title="Arcs Demo"></iframe>
|
||||
pub fn arcs(&self, arcs_options: ArcsOptions) -> Vec<CircleArc> {
|
||||
let ArcsOptions {
|
||||
strategy: maximize_arcs,
|
||||
|
|
|
@ -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`.
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#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))
|
||||
}
|
||||
|
@ -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.
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#subpath/project/solo" title="Project Demo"></iframe>
|
||||
pub fn project(&self, point: DVec2, options: ProjectionOptions) -> Option<(usize, f64)> {
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
|
|
|
@ -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]`.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#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))
|
||||
|
@ -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
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" 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_seperation: Option<f64>) -> Vec<f64> {
|
||||
// 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.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#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="400px" src="https://graphite.rs/bezier-rs-demos#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))
|
||||
|
|
|
@ -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.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#subpath/split/solo" title="Split Demo"></iframe>
|
||||
pub fn split(&self, t: SubpathTValue) -> (Subpath, Option<Subpath>) {
|
||||
let (segment_index, t) = self.t_value_to_parametric(t);
|
||||
let curve = self.get_segment(segment_index).unwrap();
|
||||
|
|
|
@ -2,6 +2,7 @@ html,
|
|||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
text-align: center;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
body > h1 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue