use crate::utils::{abs_diff_eq, EQ_EPSILON};
use super::Point;
#[derive(Debug, Clone)]
pub struct BoundingBox {
center: Point,
top_right: Point,
bottom_left: Point,
}
impl Default for BoundingBox {
fn default() -> Self {
Self::new_centered_square(2.0) }
}
impl BoundingBox {
pub fn new(center: Point, width: f64, height: f64) -> Self {
Self {
top_right: Point { x: center.x + width / 2.0, y: center.y - height / 2.0 },
bottom_left: Point { x: center.x - width / 2.0, y: center.y + height / 2.0 },
center,
}
}
pub fn new_centered(width: f64, height: f64) -> Self {
Self::new(Point { x: 0.0, y: 0.0 }, width, height)
}
pub fn new_centered_square(width: f64) -> Self {
Self::new_centered(width, width)
}
#[inline]
pub fn center(&self) -> &Point {
&self.center
}
#[inline]
pub fn top_right(&self) -> &Point {
&self.top_right
}
#[inline]
pub fn bottom_left(&self) -> &Point {
&self.bottom_left
}
#[inline]
pub fn width(&self) -> f64 {
self.top_right.x - self.bottom_left.x
}
#[inline]
pub fn height(&self) -> f64 {
self.bottom_left.y - self.top_right.y
}
#[inline]
pub fn top(&self) -> f64 {
self.top_right.y
}
#[inline]
pub fn bottom(&self) -> f64 {
self.bottom_left.y
}
#[inline]
pub fn left(&self) -> f64 {
self.bottom_left.x
}
#[inline]
pub fn right(&self) -> f64 {
self.top_right.x
}
pub fn corners(&self) -> [Point; 4] {
[
Point { x: self.left(), y: self.top() }, self.bottom_left().clone(),
Point { x: self.right(), y: self.bottom() }, self.top_right().clone(),
]
}
#[inline]
pub fn is_inside(&self, point: &Point) -> bool {
let horizonal_ok = point.x >= self.left() && point.x <= self.right();
let vertical_ok = point.y >= self.top() && point.y <= self.bottom();
horizonal_ok && vertical_ok
}
#[inline]
pub fn is_exclusively_inside(&self, point: &Point) -> bool {
self.is_inside(point) && self.which_edge(point).is_none()
}
#[inline]
pub (crate) fn which_edge(&self, point: &Point) -> Option<usize> {
if abs_diff_eq(point.y, self.top(), EQ_EPSILON) {
Some(0)
} else if abs_diff_eq(point.y, self.bottom(), EQ_EPSILON) {
Some(2)
} else {
if abs_diff_eq(point.x, self.right(), EQ_EPSILON) {
Some(3)
} else if abs_diff_eq(point.x, self.left(), EQ_EPSILON) {
Some(1)
} else {
None
}
}
}
pub (crate) fn intersect_line(&self, a: &Point, b: &Point) -> (Option<Point>, Option<Point>) {
let c_x = b.x - a.x;
let c_y = b.y - a.y;
let c = c_y / c_x;
let d = a.y - (a.x * c);
let mut f = None;
let mut g = None;
let mut h = None;
let mut i = None;
if c_x.abs() > 4. * std::f64::EPSILON {
let right_y = (self.right() * c) + d;
let left_y = (self.left() * c) + d;
if right_y >= self.top()
&& right_y <= self.bottom() {
f = Some(Point { x: self.right(), y: right_y });
}
if left_y >= self.top()
&& left_y <= self.bottom() {
g = Some(Point { x: self.left(), y: left_y })
}
if g.is_some() && f.is_some() {
return (f, g);
}
}
if c_y.abs() > 4. * std::f64::EPSILON {
if c_x.abs() < 4. * std::f64::EPSILON {
if a.x <= self.right()
&& a.x >= self.left() {
return (
Some(Point { x: a.x, y: self.top() }),
Some(Point { x: a.x, y: self.bottom() })
);
} else {
return (None, None);
}
}
let top_x = (self.top() - d) / c;
let bottom_x = (self.bottom() - d) / c;
if top_x <= self.right()
&& top_x >= self.left() {
h = Some(Point { x: top_x, y: self.top() })
}
if bottom_x <= self.right()
&& bottom_x >= self.left() {
i = Some(Point { x: bottom_x, y: self.bottom() })
}
if h.is_some() && i.is_some() {
return (h, i);
}
}
(f.or(g), h.or(i))
}
pub (crate) fn project_ray(&self, point: &Point, direction: &Point) -> (Option<Point>, Option<Point>) {
let b = Point { x: point.x + direction.x, y: point.y + direction.y };
let (a, b) = self.intersect_line(point, &b);
order_points_on_ray(point, direction, a, b)
}
}
pub (crate) fn order_points_on_ray(point: &Point, direction: &Point, a: Option<Point>, b: Option<Point>) -> (Option<Point>, Option<Point>) {
match (a,b) {
(None, None) => { (None, None)
}
(Some(va), Some(vb)) => { let (d, da, db) = if direction.x.abs() > direction.y.abs() {
(direction.x, va.x - point.x, vb.x - point.x)
} else {
(direction.y, va.y - point.y, vb.y - point.y)
};
match (d.signum() == da.signum(), d.signum() == db.signum()) {
(true, true) => {
if da.abs() > db.abs() {
(Some(vb), Some(va))
} else {
(Some(va), Some(vb))
}
},
(true, false) => { (Some(va), None)
},
(false, true) => { (Some(vb), None)
},
(false, false) => { (None, None)
}
}
},
(Some(va), None) => {
if direction.x.signum() == va.x.signum() && direction.y.signum() == va.y.signum() {
(Some(va), None)
} else {
(None, None)
}
},
(None, Some(vb)) => {
if direction.x.signum() == vb.x.signum() && direction.y.signum() == vb.y.signum() {
(Some(vb), None)
} else {
(None, None)
}
}
}
}
#[inline]
pub (crate) fn next_edge(edge: usize) -> usize {
(edge + 1) % 4
}
#[cfg(test)]
mod tests {
use super::*;
fn line(x: f64, c: f64, d: f64) -> Point {
Point { x, y: (x * c) + d }
}
fn direction(a: &Point, b: &Point) -> Point {
Point { x: a.x - b.x, y: a.y - b.y }
}
#[test]
fn intersect_line_tests() {
let bbox = BoundingBox::new_centered_square(2.0);
let (a, b) = bbox.intersect_line(&Point { x: 5.0, y: 0.0 }, &Point { x: 5.0, y: 1.0 }); assert_eq!(a, None, "No intersection expected for a parallel line to X outside of the box");
assert_eq!(b, None, "No intersection expected for a parallel line to X outside of the box");
let (a, b) = bbox.intersect_line(&Point { x: 0.0, y: 5.0 }, &Point { x: 1.0, y: 5.0 }); assert_eq!(a, None, "No intersection expected for a parallel line to Y outside of the box");
assert_eq!(b, None, "No intersection expected for a parallel line to Y outside of the box");
let (a, b) = bbox.intersect_line(&Point { x: 0.0, y: 0.0 }, &Point { x: 0.0, y: 1.0 }); assert_eq!(Some(Point { x: 0.0, y: bbox.top() }), a, "Expected intersection with top edge");
assert_eq!(Some(Point { x: 0.0, y: bbox.bottom() }), b, "Expected intersection with bottom edge");
let (a, b) = bbox.intersect_line(&Point { x: 0.0, y: 0.0 }, &Point { x: 1.0, y: 0.0 }); assert_eq!(Some(Point { x: 1.0, y: 0.0 }), a, "Expected intersection with right edge");
assert_eq!(Some(Point { x: -1.0, y: 0.0 }), b, "Expected intersection with left edge");
let (a, b) = bbox.intersect_line(&Point { x: 0.0, y: bbox.top() }, &Point { x: 1.0, y: bbox.top() }); assert_eq!(Some(Point { x: bbox.right(), y: bbox.top() }), a, "Expected intersection with top right corner");
assert_eq!(Some(Point { x: bbox.left(), y: bbox.top() }), b, "Expected intersection with top left corner");
let (a, b) = bbox.intersect_line(&Point { x: 0.0, y: bbox.bottom() }, &Point { x: 1.0, y: bbox.bottom() }); assert_eq!(Some(Point { x: bbox.right(), y: bbox.bottom() }), a, "Expected intersection with bottom right corner");
assert_eq!(Some(Point { x: bbox.left(), y: bbox.bottom() }), b, "Expected intersection with bottom left corner");
let (a, b) = bbox.intersect_line(&Point { x: bbox.right(), y: 0.0 }, &Point { x: bbox.right(), y: 1.0 }); assert_eq!(Some(Point { x: bbox.right(), y: bbox.top() }), a, "Expected intersection with top right corner");
assert_eq!(Some(Point { x: bbox.right(), y: bbox.bottom() }), b, "Expected intersection with bottom right corner");
let (a, b) = bbox.intersect_line(&Point { x: bbox.left(), y: 0.0 }, &Point { x: bbox.left(), y: 1.0 }); assert_eq!(Some(Point { x: bbox.left(), y: bbox.top() }), a, "Expected intersection with top left corner");
assert_eq!(Some(Point { x: bbox.left(), y: bbox.bottom() }), b, "Expected intersection with bottom left corner");
let (a, b) = bbox.intersect_line(&Point { x: 0.0, y: 0.0 }, &Point { x: bbox.right(), y: bbox.top() });
assert_eq!(Some(Point { x: bbox.right(), y: bbox.top() }), a, "Expected intersection with top right corner");
assert_eq!(Some(Point { x: bbox.left(), y: bbox.bottom() }), b, "Expected intersection with left bottom corner");
let (a, b) = bbox.intersect_line(&Point { x: 0.0, y: 0.0 }, &Point { x: bbox.left(), y: bbox.bottom() });
assert_eq!(Some(Point { x: bbox.right(), y: bbox.top() }), a, "Expected intersection with top right corner");
assert_eq!(Some(Point { x: bbox.left(), y: bbox.bottom() }), b, "Expected intersection with left bottom corner");
let (a, b) = bbox.intersect_line(&Point { x: 0.5, y: 0.5 }, &Point { x: 0.4, y: 0.6 });
assert_eq!(Some(Point { x: 1.0, y: 0.0 }), a, "Expected intersection with middle of the right edge");
assert_eq!(Some(Point { x: 0.0, y: 1.0 }), b, "Expected intersection with middle of the top edge");
let (a, b) = bbox.intersect_line(&Point { x: -0.5, y: 0.5 }, &Point { x: -0.4, y: 0.6 });
assert_eq!(Some(Point { x: -1.0, y: 0.0 }), a, "Expected intersection with middle of the left edge");
assert_eq!(Some(Point { x: 0.0, y: 1.0 }), b, "Expected intersection with middle of the top edge");
let (a, b) = bbox.intersect_line(&Point { x: -0.5, y: -0.5 }, &Point { x: -0.4, y: -0.6 });
assert_eq!(Some(Point { x: -1.0, y: 0.0 }), a, "Expected intersection with middle of the left edge");
assert_eq!(Some(Point { x: 0.0, y: -1.0 }), b, "Expected intersection with middle of the bottom edge");
let (a, b) = bbox.intersect_line(&Point { x: 0.5, y: -0.5 }, &Point { x: 0.4, y: -0.6 });
assert_eq!(Some(Point { x: 1.0, y: 0.0 }), a, "Expected intersection with middle of the right edge");
assert_eq!(Some(Point { x: 0.0, y: -1.0 }), b, "Expected intersection with middle of the bottom edge");
}
#[test]
fn project_ray_tests_centered_square() {
let bbox = BoundingBox::new_centered_square(2.0);
let (a, b) = bbox.project_ray(&Point { x: 2.0, y: 0.0 }, &Point { x: -0.1, y: 0.0 });
assert_eq!(Some(Point { x: 1.0, y: 0.0 }), a, "Expected to hit right side first");
assert_eq!(Some(Point { x: -1.0, y: 0.0 }), b, "And then hit the left side");
let (a, b) = bbox.project_ray(&Point { x: 0.9, y: 0.0 }, &Point { x: -0.1, y: 0.0 });
assert_eq!(Some(Point { x: -1.0, y: 0.0 }), a, "Expected to hit left side first");
assert_eq!(None, b, "and only that");
let (a, b) = bbox.project_ray(&Point { x: -0.9, y: 0.0 }, &Point { x: 0.1, y: 0.0 });
assert_eq!(Some(Point { x: 1.0, y: 0.0 }), a, "Expected to hit right side first");
assert_eq!(None, b, "and only that");
let (a, b) = bbox.project_ray(&Point { x: -2.0, y: 0.0 }, &Point { x: 0.1, y: 0.0 });
assert_eq!(Some(Point { x: -1.0, y: 0.0 }), a, "Expected to hit left side first");
assert_eq!(Some(Point { x: 1.0, y: 0.0 }), b, "And then hit the right side");
let (a, b) = bbox.project_ray(&Point { x: 0.0, y: 3.0 }, &Point { x: 0.0, y: -10.0 });
assert_eq!(Some(Point { x: 0.0, y: 1.0 }), a, "Expected to hit top side first");
assert_eq!(Some(Point { x: 0.0, y: -1.0 }), b, "And then hit the bottom side");
let (a, b) = bbox.project_ray(&Point { x: 0.0, y: 0.5 }, &Point { x: 0.0, y: -10.0 });
assert_eq!(Some(Point { x: 0.0, y: -1.0 }), a, "Expected to hit bottom side first");
assert_eq!(None, b, "and only that");
let (a, b) = bbox.project_ray(&Point { x: 0.0, y: -0.5 }, &Point { x: 0.0, y: 0.2 });
assert_eq!(Some(Point { x: 0.0, y: 1.0 }), a, "Expected to hit top side first");
assert_eq!(None, b, "and only that");
let (a, b) = bbox.project_ray(&Point { x: 0.0, y: -3.0 }, &Point { x: 0.0, y: 10.0 });
assert_eq!(Some(Point { x: 0.0, y: -1.0 }), a, "Expected to hit bottom side first");
assert_eq!(Some(Point { x: 0.0, y: 1.0 }), b, "And then hit the top side");
let (a, b) = bbox.project_ray(&Point { x: 0.0, y: 0.5 }, &Point { x: 0.0, y: -10.0 });
assert_eq!(Some(Point { x: 0.0, y: -1.0 }), a, "Expected to hit bottom side first");
assert_eq!(None, b, "and only that");
let c = -0.8;
let d = 1.0;
let (a, b) = bbox.project_ray(&line(2.0, c, d), &direction(&line(-20.0, c, d), &line(2.0, c, d)));
assert!(a.is_some() && b.is_some(), "Expected two intersections, a: {:?}, b: {:?}", a, b);
assert!(
abs_diff_eq(line(1.0, c, d).x, a.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(line(1.0, c, d).y, a.unwrap().y, EQ_EPSILON)
, "Expected to hit right side first"
);
assert!(
abs_diff_eq(line(0.0, c, d).x, b.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(line(0.0, c, d).y, b.unwrap().y, EQ_EPSILON)
, "And then top side"
);
let (a, b) = bbox.project_ray(&Point { x: -0.5, y: 0.0 }, &Point { x: -1.0, y: 0.8 });
assert_eq!(Some(Point { x: -1.0, y: 0.4 }), a, "Expected to hit left side first");
assert_eq!(None, b, "And only that");
let (a, b) = bbox.project_ray(&Point { x: -10.0, y: 0.0 }, &Point { x: 1.0, y: 0.8 });
assert!(a.is_none() && b.is_none(), "Expected no intersection");
}
#[test]
fn project_ray_tests_non_centered_rect() {
let width_height_ratio = [
1.5, 1.1, std::f64::consts::E ];
let width = 1.;
let base_origin = Point {x: 3.1, y: 2.6};
for &ratio in width_height_ratio.iter() {
let height = width * ratio;
for i in 1..=2 {
for j in 1..=2 {
let origin = Point {
x: base_origin.x * -1_f64.powi(i),
y: base_origin.y * -1_f64.powi(j),
};
let bbox = BoundingBox::new(
origin.clone(),
width,
height
);
let top = origin.y + 0.5 * height;
let bottom = origin.y - 0.5 * height;
let right = origin.x + 0.5 * width;
let left = origin.x - 0.5 * width;
let point = &Point { x: origin.x + width, y: origin.y };
let dir = &Point { x: -0.1, y: 0.0 };
let (a, b) = bbox.project_ray(point, dir);
assert!(a.is_some() && b.is_some(), "Expected two intersections, a: {:?}, b: {:?}", a, b);
let expected_intersection_a = Point{x: right, y: origin.y};
assert!(
abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
, "Expected to hit right side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
);
let expected_intersection_b = Point{x: left, y: origin.y};
assert!(
abs_diff_eq(expected_intersection_b.x, b.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_b.y, b.clone().unwrap().y, EQ_EPSILON)
, "And then hit left side. found: {:?}, expected: {:?}", b.unwrap(), expected_intersection_b
);
let point = &Point { x: origin.x - width, y: origin.y };
let dir = &Point { x: 0.1, y: 0.0 };
let (a, b) = bbox.project_ray(point, dir);
assert!(a.is_some() && b.is_some(), "Expected two intersections, a: {:?}, b: {:?}", a, b);
let expected_intersection_a = Point{x: left, y: origin.y};
assert!(
abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
, "Expected to hit left side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
);
let expected_intersection_b = Point{x: right, y: origin.y};
assert!(
abs_diff_eq(expected_intersection_b.x, b.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_b.y, b.clone().unwrap().y, EQ_EPSILON)
, "And then hit right side. found: {:?}, expected: {:?}", b.unwrap(), expected_intersection_b
);
let point = &Point { x: origin.x + 0.4 * width, y: origin.y };
let dir = &Point { x: -0.1, y: 0.0 };
let (a, b) = bbox.project_ray(point, dir);
assert!(a.is_some(), "Expected one intersection");
let expected_intersection_a = Point{x: left, y: origin.y};
assert!(
abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
, "Expected to hit left side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
);
assert_eq!(None, b, "And only that");
let point = &Point { x: origin.x - 0.4 * width, y: origin.y };
let dir = &Point { x: 0.1, y: 0.0 };
let (a, b) = bbox.project_ray(point, dir);
assert!(a.is_some(), "Expected one intersection");
let expected_intersection_a = Point{x: right, y: origin.y};
assert!(
abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
, "Expected to hit right side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
);
assert_eq!(None, b, "And only that");
let point = &Point { x: origin.x, y: origin.y + height};
let dir = &Point { x: 0.0, y: -0.1 };
let (a, b) = bbox.project_ray(point, dir);
assert!(a.is_some() && b.is_some(), "Expected two intersections, a: {:?}, b: {:?}", a, b);
let expected_intersection_a = Point{x: origin.x, y: top};
assert!(
abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
, "Expected to hit top side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
);
let expected_intersection_b = Point{x: origin.x, y: bottom};
assert!(
abs_diff_eq(expected_intersection_b.x, b.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_b.y, b.clone().unwrap().y, EQ_EPSILON)
, "And then hit bottom side. found: {:?}, expected: {:?}", b.unwrap(), expected_intersection_b
);
let point = &Point { x: origin.x, y: origin.y - height};
let dir = &Point { x: 0.0, y: 0.1 };
let (a, b) = bbox.project_ray(point, dir);
assert!(a.is_some() && b.is_some(), "Expected two intersections, a: {:?}, b: {:?}", a, b);
let expected_intersection_a = Point{x: origin.x, y: bottom};
assert!(
abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
, "Expected to hit bottom side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
);
let expected_intersection_b = Point{x: origin.x, y: top};
assert!(
abs_diff_eq(expected_intersection_b.x, b.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_b.y, b.clone().unwrap().y, EQ_EPSILON)
, "And then hit top side. found: {:?}, expected: {:?}", b.unwrap(), expected_intersection_b
);
let point = &Point { x: origin.x, y: origin.y + 0.4 * height};
let dir = &Point { x: 0.0, y: -0.1 };
let (a, b) = bbox.project_ray(point, dir);
assert!(a.is_some(), "Expected one intersection");
let expected_intersection_a = Point{x: origin.x, y: bottom};
assert!(
abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
, "Expected to hit bottom side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
);
assert_eq!(None, b, "And only that");
let point = &Point { x: origin.x, y: origin.y - 0.4 * height};
let dir = &Point { x: 0.0, y: 0.1 };
let (a, b) = bbox.project_ray(point, dir);
assert!(a.is_some(), "Expected one intersection");
let expected_intersection_a = Point{x: origin.x, y: top};
assert!(
abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
, "Expected to hit top side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
);
assert_eq!(None, b, "And only that");
let point = &Point { x: origin.x + 0.3 * width, y: origin.y + 0.3 * height };
let dir = &Point { x: 0.0, y: -10.0 };
let (a, b) = bbox.project_ray(point, dir);
assert!(a.is_some(), "Expected one intersection");
let expected_intersection_a = Point{x: origin.x + 0.3 * width, y: bottom};
assert!(
abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
, "Expected to hit bottom side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
);
assert_eq!(None, b, "And only that");
let c = -0.8 * ratio; let d = -(c * origin.x) + top; let point = &line(right + 0.5, c, d); let dir = &direction(&line(left - 2., c, d), point); let (a, b) = bbox.project_ray(point, dir);
assert!(a.is_some() && b.is_some(), "Expected two intersections, a: {:?}, b: {:?}", a, b);
let expected_intersection_a = line(right, c, d);
assert!(
abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
, "Expected to hit right side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
);
let expected_intersection_b = line(origin.x, c, d);
assert!(
abs_diff_eq(line(origin.x, c, d).x, b.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(line(origin.x, c, d).y, b.clone().unwrap().y, EQ_EPSILON)
, "And then top side. found: {:?}, expected: {:?}", b.unwrap(), expected_intersection_b
);
let c = -0.8 * ratio; let d = -(c * left) + origin.y; let point = &line(left + 0.2 * width, c, d); let dir = &direction(&line(left - width, c, d), point); let (a, b) = bbox.project_ray(point, dir);
assert!(a.is_some(), "Expected one intersection");
let expected_intersection_a = line(left, c, d);
assert!(
abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
&& abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
, "Expected to hit left side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
);
assert_eq!(None, b, "And only that");
let point = &Point { x: left - width, y: origin.y};
let dir = &Point { x: 0.9 * width, y: ratio }; let (a, b) = bbox.project_ray(point, dir);
assert!(a.is_none() && b.is_none(), "Expected no intersection");
}
}
}
}
}