Bezier-rs: Added function to find intersections between a bezier and a rectangle (#897)

* added function to find intersections for a rectangle

* Added is_contained_within function
This commit is contained in:
Rob Nadal 2022-12-21 14:44:11 -08:00 committed by Keavon Chambers
parent 951c49a979
commit ef99c91226
3 changed files with 54 additions and 0 deletions

View File

@ -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<f64> {
[
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)]

View File

@ -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, number>): string => bezier.rotate(options.angle * Math.PI, 100, 100),

View File

@ -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