diff --git a/libraries/bezier-rs/src/bezier/solvers.rs b/libraries/bezier-rs/src/bezier/solvers.rs index 5494632b..40097b92 100644 --- a/libraries/bezier-rs/src/bezier/solvers.rs +++ b/libraries/bezier-rs/src/bezier/solvers.rs @@ -139,6 +139,12 @@ impl Bezier { [endpoints_min, endpoints_max] } + /// Returns `true` if the bounding box of the bezier is contained entirely within a rectangle defined by its minimum and maximum corners. + pub fn is_contained_within(&self, min_corner: DVec2, max_corner: DVec2) -> bool { + let [bounding_box_min, bounding_box_max] = self.bounding_box(); + min_corner.x <= bounding_box_min.x && min_corner.y <= bounding_box_min.y && bounding_box_max.x <= max_corner.x && bounding_box_max.y <= max_corner.y + } + // TODO: Use an `impl Iterator` return type instead of a `Vec` /// Returns list of `t`-values representing the inflection points of the curve. /// The inflection points are defined to be points at which the second derivative of the curve is equal to zero. @@ -355,6 +361,19 @@ impl Bezier { .flat_map(|(index, (subcurve, t_pair))| Bezier::intersections_between_vectors_of_curves(&[(subcurve, t_pair)], &combined_list2[index + 2..], error)) .collect() } + + /// Returns a list of `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), + Bezier::from_linear_coordinates(corner2.x, corner1.y, corner2.x, corner2.y), + Bezier::from_linear_coordinates(corner2.x, corner2.y, corner1.x, corner2.y), + Bezier::from_linear_coordinates(corner1.x, corner2.y, corner1.x, corner1.y), + ] + .iter() + .flat_map(|bezier| self.intersections(bezier, None, None)) + .collect() + } } #[cfg(test)] diff --git a/website/other/bezier-rs-demos/src/App.vue b/website/other/bezier-rs-demos/src/App.vue index c77c17e0..00880b3a 100644 --- a/website/other/bezier-rs-demos/src/App.vue +++ b/website/other/bezier-rs-demos/src/App.vue @@ -554,6 +554,14 @@ export default defineComponent({ }, }, }, + { + name: "Intersect (Rectangle)", + callback: (bezier: WasmBezierInstance): string => + bezier.intersect_rectangle([ + [50, 50], + [150, 150], + ]), + }, { name: "Rotate", callback: (bezier: WasmBezierInstance, options: Record): string => bezier.rotate(options.angle * Math.PI, 100, 100), diff --git a/website/other/bezier-rs-demos/wasm/src/bezier.rs b/website/other/bezier-rs-demos/wasm/src/bezier.rs index 10d62ee7..ee82625d 100644 --- a/website/other/bezier-rs-demos/wasm/src/bezier.rs +++ b/website/other/bezier-rs-demos/wasm/src/bezier.rs @@ -477,6 +477,33 @@ impl WasmBezier { wrap_svg_tag(intersect_self_svg) } + pub fn intersect_rectangle(&self, js_points: &JsValue) -> String { + let points: [DVec2; 2] = js_points.into_serde().unwrap(); + + let bezier_curve_svg = self.get_bezier_path(); + + let mut rectangle_svg = String::new(); + [ + Bezier::from_linear_coordinates(points[0].x, points[0].y, points[1].x, points[0].y), + Bezier::from_linear_coordinates(points[1].x, points[0].y, points[1].x, points[1].y), + Bezier::from_linear_coordinates(points[1].x, points[1].y, points[0].x, points[1].y), + Bezier::from_linear_coordinates(points[0].x, points[1].y, points[0].x, points[0].y), + ] + .iter() + .for_each(|line| line.to_svg(&mut rectangle_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new())); + + let intersections_svg = self + .0 + .rectangle_intersections(points[0], points[1]) + .iter() + .map(|intersection_t| { + let point = &self.0.evaluate(ComputeType::Parametric(*intersection_t)); + draw_circle(*point, 4., RED, 1.5, WHITE) + }) + .fold(String::new(), |acc, item| format!("{acc}{item}")); + wrap_svg_tag(format!("{bezier_curve_svg}{rectangle_svg}{intersections_svg}")) + } + pub fn reduce(&self) -> String { let original_curve_svg = self.get_bezier_path(); let bezier_curves_svg: String = self