From c05c93c8a27703bcb515a10a3a45a0020e2d73d2 Mon Sep 17 00:00:00 2001 From: Linda Zheng Date: Wed, 27 Jul 2022 22:27:50 -0400 Subject: [PATCH] Implement bounding box function for bezier-rs (#726) * Implement bounding box function for bezier-rs * Fix eslint errors Co-authored-by: Linda Zheng * Rename bbox to bounding_box * Address Keavon's comments * Fix eslint issue Co-authored-by: Hannah Li Co-authored-by: Linda Zheng --- bezier-rs/docs/interactive-docs/src/App.vue | 13 ++++++++++++ .../docs/interactive-docs/wasm/src/lib.rs | 21 ++++++++++++------- bezier-rs/lib/src/lib.rs | 20 ++++++++++++++++++ 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/bezier-rs/docs/interactive-docs/src/App.vue b/bezier-rs/docs/interactive-docs/src/App.vue index 9340d028..9112b841 100644 --- a/bezier-rs/docs/interactive-docs/src/App.vue +++ b/bezier-rs/docs/interactive-docs/src/App.vue @@ -356,6 +356,19 @@ export default defineComponent({ }); }, }, + { + name: "Bounding Box", + callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance): void => { + const context = getContextFromCanvas(canvas); + const bboxPoints: Point[] = JSON.parse(bezier.bounding_box()); + const minPoint = bboxPoints[0]; + const maxPoint = bboxPoints[1]; + drawLine(context, minPoint, { x: minPoint.x, y: maxPoint.y }, COLORS.NON_INTERACTIVE.STROKE_1); + drawLine(context, minPoint, { x: maxPoint.x, y: minPoint.y }, COLORS.NON_INTERACTIVE.STROKE_1); + drawLine(context, maxPoint, { x: minPoint.x, y: maxPoint.y }, COLORS.NON_INTERACTIVE.STROKE_1); + drawLine(context, maxPoint, { x: maxPoint.x, y: minPoint.y }, COLORS.NON_INTERACTIVE.STROKE_1); + }, + }, ], subpathFeatures: [ { diff --git a/bezier-rs/docs/interactive-docs/wasm/src/lib.rs b/bezier-rs/docs/interactive-docs/wasm/src/lib.rs index 577a004c..acbc2862 100644 --- a/bezier-rs/docs/interactive-docs/wasm/src/lib.rs +++ b/bezier-rs/docs/interactive-docs/wasm/src/lib.rs @@ -128,6 +128,16 @@ impl WasmBezier { to_js_value(local_extrema) } + pub fn de_casteljau_points(&self, t: f64) -> JsValue { + let hull = self + .0 + .de_casteljau_points(t) + .iter() + .map(|level| level.iter().map(|&point| Point { x: point.x, y: point.y }).collect::>()) + .collect::>>(); + to_js_value(hull) + } + pub fn rotate(&self, angle: f64) -> WasmBezier { WasmBezier(self.0.rotate(angle)) } @@ -142,13 +152,8 @@ impl WasmBezier { to_js_value(bezier_points) } - pub fn de_casteljau_points(&self, t: f64) -> JsValue { - let hull = self - .0 - .de_casteljau_points(t) - .iter() - .map(|level| level.iter().map(|&point| Point { x: point.x, y: point.y }).collect::>()) - .collect::>>(); - to_js_value(hull) + pub fn bounding_box(&self) -> JsValue { + let bbox_points: [Point; 2] = self.0.bounding_box().map(|p| Point { x: p.x, y: p.y }); + to_js_value(bbox_points) } } diff --git a/bezier-rs/lib/src/lib.rs b/bezier-rs/lib/src/lib.rs index f4fdc67b..8a290204 100644 --- a/bezier-rs/lib/src/lib.rs +++ b/bezier-rs/lib/src/lib.rs @@ -773,6 +773,26 @@ impl Bezier { }); result } + + /// 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); + let mut endpoints_max = self.start.max(self.end); + + // Iterate through extrema points. + let extrema = self.local_extrema(); + for t_values in extrema { + for t in t_values { + let point = self.evaluate(t); + // Update bounding box if new min/max is found. + endpoints_min = endpoints_min.min(point); + endpoints_max = endpoints_max.max(point); + } + } + + [endpoints_min, endpoints_max] + } } #[cfg(test)]