diff --git a/bezier-rs/docs/interactive-docs/src/App.vue b/bezier-rs/docs/interactive-docs/src/App.vue
index a69358a08f..11288672d4 100644
--- a/bezier-rs/docs/interactive-docs/src/App.vue
+++ b/bezier-rs/docs/interactive-docs/src/App.vue
@@ -3,19 +3,21 @@
Bezier-rs Interactive Documentation
This is the interactive documentation for the bezier-rs library. Click and drag on the endpoints of the example curves to visualize the various Bezier utilities and functions.
-
+
+
diff --git a/bezier-rs/docs/interactive-docs/src/components/ExamplePane.vue b/bezier-rs/docs/interactive-docs/src/components/ExamplePane.vue
index 958973e37a..1f1b2a2656 100644
--- a/bezier-rs/docs/interactive-docs/src/components/ExamplePane.vue
+++ b/bezier-rs/docs/interactive-docs/src/components/ExamplePane.vue
@@ -1,16 +1,16 @@
+
+
diff --git a/bezier-rs/docs/interactive-docs/src/utils/drawing.ts b/bezier-rs/docs/interactive-docs/src/utils/drawing.ts
index 1a2a2585c9..938d3c55c3 100644
--- a/bezier-rs/docs/interactive-docs/src/utils/drawing.ts
+++ b/bezier-rs/docs/interactive-docs/src/utils/drawing.ts
@@ -18,9 +18,9 @@ export const drawLine = (ctx: CanvasRenderingContext2D, p1: Point, p2: Point): v
ctx.stroke();
};
-export const drawPoint = (ctx: CanvasRenderingContext2D, p: Point): void => {
+export const drawPoint = (ctx: CanvasRenderingContext2D, p: Point, stroke = "black"): void => {
// Outline the point
- ctx.strokeStyle = p.selected ? "blue" : "black";
+ ctx.strokeStyle = p.selected ? "blue" : stroke;
ctx.lineWidth = p.r / 3;
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, 2 * Math.PI, false);
diff --git a/bezier-rs/docs/interactive-docs/src/utils/types.ts b/bezier-rs/docs/interactive-docs/src/utils/types.ts
index ef05fde1bd..a0e0cc8a59 100644
--- a/bezier-rs/docs/interactive-docs/src/utils/types.ts
+++ b/bezier-rs/docs/interactive-docs/src/utils/types.ts
@@ -4,7 +4,7 @@ export type WasmBezierInstance = InstanceType;
export type WasmBezierKey = keyof WasmBezierInstance;
export type WasmBezierMutatorKey = "set_start" | "set_handle1" | "set_handle2" | "set_end";
-export type BezierCallback = (canvas: HTMLCanvasElement, bezier: WasmBezierInstance) => void;
+export type BezierCallback = (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: string) => void;
export type Point = {
x: number;
diff --git a/bezier-rs/docs/interactive-docs/wasm/src/lib.rs b/bezier-rs/docs/interactive-docs/wasm/src/lib.rs
index 74e015eaae..66323b4a64 100644
--- a/bezier-rs/docs/interactive-docs/wasm/src/lib.rs
+++ b/bezier-rs/docs/interactive-docs/wasm/src/lib.rs
@@ -14,6 +14,10 @@ pub struct WasmBezier {
internal: Bezier,
}
+pub fn vec_to_point(p: &DVec2) -> JsValue {
+ JsValue::from_serde(&serde_json::to_string(&Point { x: p[0], y: p[1] }).unwrap()).unwrap()
+}
+
#[wasm_bindgen]
impl WasmBezier {
/// Expect js_points to be a list of 3 pairs
@@ -49,12 +53,7 @@ impl WasmBezier {
}
pub fn get_points(&self) -> Vec {
- self.internal
- .get_points()
- .iter()
- .flatten()
- .map(|p| JsValue::from_serde(&serde_json::to_string(&Point { x: p[0], y: p[1] }).unwrap()).unwrap())
- .collect()
+ self.internal.get_points().iter().flatten().map(vec_to_point).collect()
}
pub fn to_svg(&self) -> String {
@@ -64,4 +63,12 @@ impl WasmBezier {
pub fn length(&self) -> f64 {
self.internal.length()
}
+
+ pub fn compute(&self, t: f64) -> JsValue {
+ vec_to_point(&self.internal.compute(t))
+ }
+
+ pub fn compute_lookup_table(&self, steps: i32) -> Vec {
+ self.internal.compute_lookup_table(Some(steps)).iter().map(vec_to_point).collect()
+ }
}
diff --git a/bezier-rs/lib/src/lib.rs b/bezier-rs/lib/src/lib.rs
index 8b073111a2..330b9f0adf 100644
--- a/bezier-rs/lib/src/lib.rs
+++ b/bezier-rs/lib/src/lib.rs
@@ -156,7 +156,9 @@ impl Bezier {
/// Calculate the point on the curve based on the t-value provided
/// basis code based off of pseudocode found here: https://pomax.github.io/bezierinfo/#explanation
- pub fn get_basis(&self, t: f64) -> DVec2 {
+ pub fn compute(&self, t: f64) -> DVec2 {
+ assert!((0.0..=1.0).contains(&t));
+
let t_squared = t * t;
let one_minus_t = 1.0 - t;
let squared_one_minus_t = one_minus_t * one_minus_t;
@@ -171,6 +173,20 @@ 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(10);
+ let ratio: f64 = 1.0 / (steps_unwrapped as f64);
+ let mut steps_array = Vec::with_capacity((steps_unwrapped + 1) as usize);
+
+ for t in 0..steps_unwrapped + 1 {
+ steps_array.push(self.compute(f64::from(t) * ratio))
+ }
+
+ steps_array
+ }
+
/// Return an approximation of the length of the bezier curve
/// code example taken from: https://gamedev.stackexchange.com/questions/5373/moving-ships-between-two-planets-along-a-bezier-missing-some-equations-for-acce/5427#5427
pub fn length(&self) -> f64 {
@@ -178,21 +194,18 @@ impl Bezier {
// we split the curve into many subdivisions
// and calculate the euclidean distance between the two endpoints of the subdivision
const SUBDIVISIONS: i32 = 1000;
- const RATIO: f64 = 1.0 / (SUBDIVISIONS as f64);
- // start_point tracks the starting point of the subdivision
- let mut start_point = self.get_basis(0.0);
- let mut length_subtotal = 0.0;
+ let lookup_table = self.compute_lookup_table(Some(SUBDIVISIONS));
+ let mut approx_curve_length = 0.0;
+ let mut prev_point = lookup_table[0];
// calculate approximate distance between subdivision
- for subdivision in 1..SUBDIVISIONS + 1 {
- // get end point of the subdivision
- let end_point = self.get_basis(f64::from(subdivision) * RATIO);
+ for curr_point in lookup_table.iter().skip(1) {
// calculate distance of subdivision
- length_subtotal += (start_point - end_point).length();
- // update start_point for next subdivision
- start_point = end_point;
+ approx_curve_length += (*curr_point - prev_point).length();
+ // update the prev point
+ prev_point = *curr_point;
}
- length_subtotal
+ approx_curve_length
}
}