#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct RegionOp {
pub add: bool,
pub x: i32,
pub y: i32,
pub w: i32,
pub h: i32,
}
pub(crate) fn contains(ops: &[RegionOp], x: f64, y: f64) -> bool {
let mut inside = false;
for op in ops {
if x >= op.x as f64
&& y >= op.y as f64
&& x < (op.x + op.w) as f64
&& y < (op.y + op.h) as f64
{
inside = op.add;
}
}
inside
}
#[cfg(test)]
mod tests {
use super::{RegionOp, contains};
fn add(x: i32, y: i32, w: i32, h: i32) -> RegionOp {
RegionOp {
add: true,
x,
y,
w,
h,
}
}
fn sub(x: i32, y: i32, w: i32, h: i32) -> RegionOp {
RegionOp {
add: false,
x,
y,
w,
h,
}
}
#[test]
fn an_empty_region_takes_no_input_anywhere() {
assert!(!contains(&[], 0.0, 0.0));
assert!(!contains(&[], 700.0, 500.0));
}
#[test]
fn a_rectangle_takes_input_inside_its_bounds() {
let r = [add(10, 10, 100, 50)];
assert!(contains(&r, 10.0, 10.0), "top-left corner is inside");
assert!(contains(&r, 109.9, 59.9));
assert!(!contains(&r, 9.9, 30.0));
assert!(!contains(&r, 110.0, 30.0), "right edge is exclusive");
assert!(!contains(&r, 30.0, 60.0), "bottom edge is exclusive");
}
#[test]
fn subtract_punches_a_hole() {
let r = [add(0, 0, 100, 100), sub(40, 40, 20, 20)];
assert!(contains(&r, 10.0, 10.0));
assert!(!contains(&r, 50.0, 50.0), "inside the hole");
assert!(contains(&r, 65.0, 50.0), "past the hole");
}
#[test]
fn a_later_add_fills_an_earlier_hole_back_in() {
let r = [add(0, 0, 100, 100), sub(40, 40, 20, 20), add(45, 45, 5, 5)];
assert!(contains(&r, 46.0, 46.0), "refilled");
assert!(!contains(&r, 55.0, 55.0), "still a hole");
}
#[test]
fn disjoint_rectangles_both_count() {
let r = [add(0, 0, 10, 10), add(100, 100, 10, 10)];
assert!(contains(&r, 5.0, 5.0));
assert!(contains(&r, 105.0, 105.0));
assert!(!contains(&r, 50.0, 50.0), "the gap between them");
}
}