1use crate::utils::{abs_diff_eq, EQ_EPSILON};
2
3use super::Point;
4
5#[derive(Debug, Clone)]
9pub struct BoundingBox {
10 center: Point,
12
13 top_right: Point,
15
16 bottom_left: Point,
18}
19
20impl Default for BoundingBox {
21 fn default() -> Self {
22 Self::new_centered_square(2.0) }
24}
25
26impl BoundingBox {
27 pub fn new(center: Point, width: f64, height: f64) -> Self {
36 Self {
37 top_right: Point { x: center.x + width / 2.0, y: center.y - height / 2.0 },
38 bottom_left: Point { x: center.x - width / 2.0, y: center.y + height / 2.0 },
39 center,
40 }
41 }
42
43 pub fn new_centered(width: f64, height: f64) -> Self {
45 Self::new(Point { x: 0.0, y: 0.0 }, width, height)
46 }
47
48 pub fn new_centered_square(width: f64) -> Self {
50 Self::new_centered(width, width)
51 }
52
53 #[inline]
55 pub fn center(&self) -> &Point {
56 &self.center
57 }
58
59 #[inline]
61 pub fn top_right(&self) -> &Point {
62 &self.top_right
63 }
64
65 #[inline]
67 pub fn bottom_left(&self) -> &Point {
68 &self.bottom_left
69 }
70
71 #[inline]
73 pub fn width(&self) -> f64 {
74 self.top_right.x - self.bottom_left.x
75 }
76
77 #[inline]
79 pub fn height(&self) -> f64 {
80 self.bottom_left.y - self.top_right.y
81 }
82
83 #[inline]
85 pub fn top(&self) -> f64 {
86 self.top_right.y
87 }
88
89 #[inline]
91 pub fn bottom(&self) -> f64 {
92 self.bottom_left.y
93 }
94
95 #[inline]
97 pub fn left(&self) -> f64 {
98 self.bottom_left.x
99 }
100
101 #[inline]
103 pub fn right(&self) -> f64 {
104 self.top_right.x
105 }
106
107 pub fn corners(&self) -> [Point; 4] {
109 [
110 Point { x: self.left(), y: self.top() }, self.bottom_left().clone(),
112 Point { x: self.right(), y: self.bottom() }, self.top_right().clone(),
114 ]
115 }
116
117 #[inline]
119 pub fn is_inside(&self, point: &Point) -> bool {
120 let horizonal_ok = point.x >= self.left() && point.x <= self.right();
121 let vertical_ok = point.y >= self.top() && point.y <= self.bottom();
122
123 horizonal_ok && vertical_ok
124 }
125
126 #[inline]
128 pub fn is_exclusively_inside(&self, point: &Point) -> bool {
129 self.is_inside(point) && self.which_edge(point).is_none()
130 }
131
132 #[inline]
139 pub (crate) fn which_edge(&self, point: &Point) -> Option<usize> {
140 if abs_diff_eq(point.y, self.top(), EQ_EPSILON) {
141 Some(0)
143 } else if abs_diff_eq(point.y, self.bottom(), EQ_EPSILON) {
144 Some(2)
146 } else {
147 if abs_diff_eq(point.x, self.right(), EQ_EPSILON) {
148 Some(3)
150 } else if abs_diff_eq(point.x, self.left(), EQ_EPSILON) {
151 Some(1)
153 } else {
154 None
155 }
156 }
157 }
158
159 pub (crate) fn intersect_line(&self, a: &Point, b: &Point) -> (Option<Point>, Option<Point>) {
161 let c_x = b.x - a.x;
162 let c_y = b.y - a.y;
163 let c = c_y / c_x;
164 let d = a.y - (a.x * c);
165
166 let mut f = None;
167 let mut g = None;
168 let mut h = None;
169 let mut i = None;
170
171 if c_x.abs() > 4. * std::f64::EPSILON {
173 let right_y = (self.right() * c) + d;
175 let left_y = (self.left() * c) + d;
176
177 if right_y >= self.top()
178 && right_y <= self.bottom() {
179 f = Some(Point { x: self.right(), y: right_y });
180 }
181
182 if left_y >= self.top()
183 && left_y <= self.bottom() {
184 g = Some(Point { x: self.left(), y: left_y })
185 }
186
187 if g.is_some() && f.is_some() {
188 return (f, g);
190 }
191 } if c_y.abs() > 4. * std::f64::EPSILON {
195 if c_x.abs() < 4. * std::f64::EPSILON {
196 if a.x <= self.right()
198 && a.x >= self.left() {
199 return (
201 Some(Point { x: a.x, y: self.top() }),
202 Some(Point { x: a.x, y: self.bottom() })
203 );
204 } else {
205 return (None, None);
207 }
208 }
209
210 let top_x = (self.top() - d) / c;
212 let bottom_x = (self.bottom() - d) / c;
213
214 if top_x <= self.right()
215 && top_x >= self.left() {
216 h = Some(Point { x: top_x, y: self.top() })
217 }
218
219 if bottom_x <= self.right()
220 && bottom_x >= self.left() {
221 i = Some(Point { x: bottom_x, y: self.bottom() })
222 }
223
224 if h.is_some() && i.is_some() {
225 return (h, i);
227 }
228 } (f.or(g), h.or(i))
231 }
232
233 pub (crate) fn project_ray(&self, point: &Point, direction: &Point) -> (Option<Point>, Option<Point>) {
235 let b = Point { x: point.x + direction.x, y: point.y + direction.y };
236 let (a, b) = self.intersect_line(point, &b);
237 order_points_on_ray(point, direction, a, b)
238 }
239}
240
241pub (crate) fn order_points_on_ray(point: &Point, direction: &Point, a: Option<Point>, b: Option<Point>) -> (Option<Point>, Option<Point>) {
244 match (a,b) {
245 (None, None) => { (None, None)
247 }
248 (Some(va), Some(vb)) => { let (d, da, db) = if direction.x.abs() > direction.y.abs() {
252 (direction.x, va.x - point.x, vb.x - point.x)
254 } else {
255 (direction.y, va.y - point.y, vb.y - point.y)
256 };
257
258 match (d.signum() == da.signum(), d.signum() == db.signum()) {
259 (true, true) => {
260 if da.abs() > db.abs() {
261 (Some(vb), Some(va))
263 } else {
264 (Some(va), Some(vb))
266 }
267 },
268 (true, false) => { (Some(va), None)
270 },
271 (false, true) => { (Some(vb), None)
273 },
274 (false, false) => { (None, None)
276 }
277 }
278 },
279 (Some(va), None) => {
280 if direction.x.signum() == va.x.signum() && direction.y.signum() == va.y.signum() {
281 (Some(va), None)
283 } else {
284 (None, None)
286 }
287 },
288 (None, Some(vb)) => {
289 if direction.x.signum() == vb.x.signum() && direction.y.signum() == vb.y.signum() {
290 (Some(vb), None)
292 } else {
293 (None, None)
295 }
296 }
297 }
298}
299
300#[inline]
301pub (crate) fn next_edge(edge: usize) -> usize {
302 (edge + 1) % 4
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 fn line(x: f64, c: f64, d: f64) -> Point {
310 Point { x, y: (x * c) + d }
311 }
312 fn direction(a: &Point, b: &Point) -> Point {
314 Point { x: a.x - b.x, y: a.y - b.y }
315 }
316
317 #[test]
318 fn intersect_line_tests() {
319 let bbox = BoundingBox::new_centered_square(2.0);
321
322 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");
325 assert_eq!(b, None, "No intersection expected for a parallel line to X outside of the box");
326
327 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");
330 assert_eq!(b, None, "No intersection expected for a parallel line to Y outside of the box");
331
332 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");
335 assert_eq!(Some(Point { x: 0.0, y: bbox.bottom() }), b, "Expected intersection with bottom edge");
336
337 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");
340 assert_eq!(Some(Point { x: -1.0, y: 0.0 }), b, "Expected intersection with left edge");
341
342 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");
345 assert_eq!(Some(Point { x: bbox.left(), y: bbox.top() }), b, "Expected intersection with top left corner");
346
347 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");
350 assert_eq!(Some(Point { x: bbox.left(), y: bbox.bottom() }), b, "Expected intersection with bottom left corner");
351
352 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");
355 assert_eq!(Some(Point { x: bbox.right(), y: bbox.bottom() }), b, "Expected intersection with bottom right corner");
356
357 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");
360 assert_eq!(Some(Point { x: bbox.left(), y: bbox.bottom() }), b, "Expected intersection with bottom left corner");
361
362 let (a, b) = bbox.intersect_line(&Point { x: 0.0, y: 0.0 }, &Point { x: bbox.right(), y: bbox.top() });
364 assert_eq!(Some(Point { x: bbox.right(), y: bbox.top() }), a, "Expected intersection with top right corner");
365 assert_eq!(Some(Point { x: bbox.left(), y: bbox.bottom() }), b, "Expected intersection with left bottom corner");
366
367 let (a, b) = bbox.intersect_line(&Point { x: 0.0, y: 0.0 }, &Point { x: bbox.left(), y: bbox.bottom() });
369 assert_eq!(Some(Point { x: bbox.right(), y: bbox.top() }), a, "Expected intersection with top right corner");
370 assert_eq!(Some(Point { x: bbox.left(), y: bbox.bottom() }), b, "Expected intersection with left bottom corner");
371
372 let (a, b) = bbox.intersect_line(&Point { x: 0.5, y: 0.5 }, &Point { x: 0.4, y: 0.6 });
374 assert_eq!(Some(Point { x: 1.0, y: 0.0 }), a, "Expected intersection with middle of the right edge");
375 assert_eq!(Some(Point { x: 0.0, y: 1.0 }), b, "Expected intersection with middle of the top edge");
376
377 let (a, b) = bbox.intersect_line(&Point { x: -0.5, y: 0.5 }, &Point { x: -0.4, y: 0.6 });
379 assert_eq!(Some(Point { x: -1.0, y: 0.0 }), a, "Expected intersection with middle of the left edge");
380 assert_eq!(Some(Point { x: 0.0, y: 1.0 }), b, "Expected intersection with middle of the top edge");
381
382 let (a, b) = bbox.intersect_line(&Point { x: -0.5, y: -0.5 }, &Point { x: -0.4, y: -0.6 });
384 assert_eq!(Some(Point { x: -1.0, y: 0.0 }), a, "Expected intersection with middle of the left edge");
385 assert_eq!(Some(Point { x: 0.0, y: -1.0 }), b, "Expected intersection with middle of the bottom edge");
386
387 let (a, b) = bbox.intersect_line(&Point { x: 0.5, y: -0.5 }, &Point { x: 0.4, y: -0.6 });
389 assert_eq!(Some(Point { x: 1.0, y: 0.0 }), a, "Expected intersection with middle of the right edge");
390 assert_eq!(Some(Point { x: 0.0, y: -1.0 }), b, "Expected intersection with middle of the bottom edge");
391 }
392
393 #[test]
394 fn project_ray_tests_centered_square() {
395 let bbox = BoundingBox::new_centered_square(2.0);
397
398 let (a, b) = bbox.project_ray(&Point { x: 2.0, y: 0.0 }, &Point { x: -0.1, y: 0.0 });
400 assert_eq!(Some(Point { x: 1.0, y: 0.0 }), a, "Expected to hit right side first");
401 assert_eq!(Some(Point { x: -1.0, y: 0.0 }), b, "And then hit the left side");
402
403 let (a, b) = bbox.project_ray(&Point { x: 0.9, y: 0.0 }, &Point { x: -0.1, y: 0.0 });
405 assert_eq!(Some(Point { x: -1.0, y: 0.0 }), a, "Expected to hit left side first");
406 assert_eq!(None, b, "and only that");
407
408 let (a, b) = bbox.project_ray(&Point { x: -0.9, y: 0.0 }, &Point { x: 0.1, y: 0.0 });
410 assert_eq!(Some(Point { x: 1.0, y: 0.0 }), a, "Expected to hit right side first");
411 assert_eq!(None, b, "and only that");
412
413 let (a, b) = bbox.project_ray(&Point { x: -2.0, y: 0.0 }, &Point { x: 0.1, y: 0.0 });
415 assert_eq!(Some(Point { x: -1.0, y: 0.0 }), a, "Expected to hit left side first");
416 assert_eq!(Some(Point { x: 1.0, y: 0.0 }), b, "And then hit the right side");
417
418 let (a, b) = bbox.project_ray(&Point { x: 0.0, y: 3.0 }, &Point { x: 0.0, y: -10.0 });
420 assert_eq!(Some(Point { x: 0.0, y: 1.0 }), a, "Expected to hit top side first");
421 assert_eq!(Some(Point { x: 0.0, y: -1.0 }), b, "And then hit the bottom side");
422
423 let (a, b) = bbox.project_ray(&Point { x: 0.0, y: 0.5 }, &Point { x: 0.0, y: -10.0 });
425 assert_eq!(Some(Point { x: 0.0, y: -1.0 }), a, "Expected to hit bottom side first");
426 assert_eq!(None, b, "and only that");
427
428 let (a, b) = bbox.project_ray(&Point { x: 0.0, y: -0.5 }, &Point { x: 0.0, y: 0.2 });
430 assert_eq!(Some(Point { x: 0.0, y: 1.0 }), a, "Expected to hit top side first");
431 assert_eq!(None, b, "and only that");
432
433 let (a, b) = bbox.project_ray(&Point { x: 0.0, y: -3.0 }, &Point { x: 0.0, y: 10.0 });
435 assert_eq!(Some(Point { x: 0.0, y: -1.0 }), a, "Expected to hit bottom side first");
436 assert_eq!(Some(Point { x: 0.0, y: 1.0 }), b, "And then hit the top side");
437
438 let (a, b) = bbox.project_ray(&Point { x: 0.0, y: 0.5 }, &Point { x: 0.0, y: -10.0 });
440 assert_eq!(Some(Point { x: 0.0, y: -1.0 }), a, "Expected to hit bottom side first");
441 assert_eq!(None, b, "and only that");
442
443 let c = -0.8;
445 let d = 1.0;
446 let (a, b) = bbox.project_ray(&line(2.0, c, d), &direction(&line(-20.0, c, d), &line(2.0, c, d)));
447 assert!(a.is_some() && b.is_some(), "Expected two intersections, a: {:?}, b: {:?}", a, b);
448 assert!(
449 abs_diff_eq(line(1.0, c, d).x, a.clone().unwrap().x, EQ_EPSILON)
450 && abs_diff_eq(line(1.0, c, d).y, a.unwrap().y, EQ_EPSILON)
451 , "Expected to hit right side first"
452 );
453 assert!(
454 abs_diff_eq(line(0.0, c, d).x, b.clone().unwrap().x, EQ_EPSILON)
455 && abs_diff_eq(line(0.0, c, d).y, b.unwrap().y, EQ_EPSILON)
456 , "And then top side"
457 );
458
459 let (a, b) = bbox.project_ray(&Point { x: -0.5, y: 0.0 }, &Point { x: -1.0, y: 0.8 });
461 assert_eq!(Some(Point { x: -1.0, y: 0.4 }), a, "Expected to hit left side first");
462 assert_eq!(None, b, "And only that");
463
464 let (a, b) = bbox.project_ray(&Point { x: -10.0, y: 0.0 }, &Point { x: 1.0, y: 0.8 });
466 assert!(a.is_none() && b.is_none(), "Expected no intersection");
467 }
468
469 #[test]
470 fn project_ray_tests_non_centered_rect() {
472 let width_height_ratio = [
473 1.5, 1.1, std::f64::consts::E ];
477 let width = 1.;
478 let base_origin = Point {x: 3.1, y: 2.6};
479
480 for &ratio in width_height_ratio.iter() {
482 let height = width * ratio;
483 for i in 1..=2 {
484 for j in 1..=2 {
485 let origin = Point {
489 x: base_origin.x * -1_f64.powi(i),
490 y: base_origin.y * -1_f64.powi(j),
491 };
492
493 let bbox = BoundingBox::new(
494 origin.clone(),
495 width,
496 height
497 );
498
499 let top = origin.y + 0.5 * height;
500 let bottom = origin.y - 0.5 * height;
501 let right = origin.x + 0.5 * width;
502 let left = origin.x - 0.5 * width;
503
504 let point = &Point { x: origin.x + width, y: origin.y };
506 let dir = &Point { x: -0.1, y: 0.0 };
507 let (a, b) = bbox.project_ray(point, dir);
508 assert!(a.is_some() && b.is_some(), "Expected two intersections, a: {:?}, b: {:?}", a, b);
509 let expected_intersection_a = Point{x: right, y: origin.y};
510 assert!(
511 abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
512 && abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
513 , "Expected to hit right side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
514 );
515 let expected_intersection_b = Point{x: left, y: origin.y};
516 assert!(
517 abs_diff_eq(expected_intersection_b.x, b.clone().unwrap().x, EQ_EPSILON)
518 && abs_diff_eq(expected_intersection_b.y, b.clone().unwrap().y, EQ_EPSILON)
519 , "And then hit left side. found: {:?}, expected: {:?}", b.unwrap(), expected_intersection_b
520 );
521
522 let point = &Point { x: origin.x - width, y: origin.y };
524 let dir = &Point { x: 0.1, y: 0.0 };
525 let (a, b) = bbox.project_ray(point, dir);
526 assert!(a.is_some() && b.is_some(), "Expected two intersections, a: {:?}, b: {:?}", a, b);
527 let expected_intersection_a = Point{x: left, y: origin.y};
528 assert!(
529 abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
530 && abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
531 , "Expected to hit left side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
532 );
533 let expected_intersection_b = Point{x: right, y: origin.y};
534 assert!(
535 abs_diff_eq(expected_intersection_b.x, b.clone().unwrap().x, EQ_EPSILON)
536 && abs_diff_eq(expected_intersection_b.y, b.clone().unwrap().y, EQ_EPSILON)
537 , "And then hit right side. found: {:?}, expected: {:?}", b.unwrap(), expected_intersection_b
538 );
539
540 let point = &Point { x: origin.x + 0.4 * width, y: origin.y };
542 let dir = &Point { x: -0.1, y: 0.0 };
543 let (a, b) = bbox.project_ray(point, dir);
544 assert!(a.is_some(), "Expected one intersection");
545 let expected_intersection_a = Point{x: left, y: origin.y};
546 assert!(
547 abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
548 && abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
549 , "Expected to hit left side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
550 );
551 assert_eq!(None, b, "And only that");
552
553 let point = &Point { x: origin.x - 0.4 * width, y: origin.y };
555 let dir = &Point { x: 0.1, y: 0.0 };
556 let (a, b) = bbox.project_ray(point, dir);
557 assert!(a.is_some(), "Expected one intersection");
558 let expected_intersection_a = Point{x: right, y: origin.y};
559 assert!(
560 abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
561 && abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
562 , "Expected to hit right side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
563 );
564 assert_eq!(None, b, "And only that");
565
566 let point = &Point { x: origin.x, y: origin.y + height};
568 let dir = &Point { x: 0.0, y: -0.1 };
569 let (a, b) = bbox.project_ray(point, dir);
570 assert!(a.is_some() && b.is_some(), "Expected two intersections, a: {:?}, b: {:?}", a, b);
571 let expected_intersection_a = Point{x: origin.x, y: top};
572 assert!(
573 abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
574 && abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
575 , "Expected to hit top side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
576 );
577 let expected_intersection_b = Point{x: origin.x, y: bottom};
578 assert!(
579 abs_diff_eq(expected_intersection_b.x, b.clone().unwrap().x, EQ_EPSILON)
580 && abs_diff_eq(expected_intersection_b.y, b.clone().unwrap().y, EQ_EPSILON)
581 , "And then hit bottom side. found: {:?}, expected: {:?}", b.unwrap(), expected_intersection_b
582 );
583
584 let point = &Point { x: origin.x, y: origin.y - height};
586 let dir = &Point { x: 0.0, y: 0.1 };
587 let (a, b) = bbox.project_ray(point, dir);
588 assert!(a.is_some() && b.is_some(), "Expected two intersections, a: {:?}, b: {:?}", a, b);
589 let expected_intersection_a = Point{x: origin.x, y: bottom};
590 assert!(
591 abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
592 && abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
593 , "Expected to hit bottom side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
594 );
595 let expected_intersection_b = Point{x: origin.x, y: top};
596 assert!(
597 abs_diff_eq(expected_intersection_b.x, b.clone().unwrap().x, EQ_EPSILON)
598 && abs_diff_eq(expected_intersection_b.y, b.clone().unwrap().y, EQ_EPSILON)
599 , "And then hit top side. found: {:?}, expected: {:?}", b.unwrap(), expected_intersection_b
600 );
601
602 let point = &Point { x: origin.x, y: origin.y + 0.4 * height};
604 let dir = &Point { x: 0.0, y: -0.1 };
605 let (a, b) = bbox.project_ray(point, dir);
606 assert!(a.is_some(), "Expected one intersection");
607 let expected_intersection_a = Point{x: origin.x, y: bottom};
608 assert!(
609 abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
610 && abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
611 , "Expected to hit bottom side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
612 );
613 assert_eq!(None, b, "And only that");
614
615 let point = &Point { x: origin.x, y: origin.y - 0.4 * height};
617 let dir = &Point { x: 0.0, y: 0.1 };
618 let (a, b) = bbox.project_ray(point, dir);
619 assert!(a.is_some(), "Expected one intersection");
620 let expected_intersection_a = Point{x: origin.x, y: top};
621 assert!(
622 abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
623 && abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
624 , "Expected to hit top side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
625 );
626 assert_eq!(None, b, "And only that");
627
628 let point = &Point { x: origin.x + 0.3 * width, y: origin.y + 0.3 * height };
630 let dir = &Point { x: 0.0, y: -10.0 };
631 let (a, b) = bbox.project_ray(point, dir);
632 assert!(a.is_some(), "Expected one intersection");
633 let expected_intersection_a = Point{x: origin.x + 0.3 * width, y: bottom};
634 assert!(
635 abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
636 && abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
637 , "Expected to hit bottom side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
638 );
639 assert_eq!(None, b, "And only that");
640
641 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);
647 assert!(a.is_some() && b.is_some(), "Expected two intersections, a: {:?}, b: {:?}", a, b);
648 let expected_intersection_a = line(right, c, d);
649 assert!(
650 abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
651 && abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
652 , "Expected to hit right side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
653 );
654 let expected_intersection_b = line(origin.x, c, d);
655 assert!(
656 abs_diff_eq(line(origin.x, c, d).x, b.clone().unwrap().x, EQ_EPSILON)
657 && abs_diff_eq(line(origin.x, c, d).y, b.clone().unwrap().y, EQ_EPSILON)
658 , "And then top side. found: {:?}, expected: {:?}", b.unwrap(), expected_intersection_b
659 );
660
661 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);
667 assert!(a.is_some(), "Expected one intersection");
668 let expected_intersection_a = line(left, c, d);
669 assert!(
670 abs_diff_eq(expected_intersection_a.x, a.clone().unwrap().x, EQ_EPSILON)
671 && abs_diff_eq(expected_intersection_a.y, a.clone().unwrap().y, EQ_EPSILON)
672 , "Expected to hit left side first. found: {:?}, expected: {:?}", a.unwrap(), expected_intersection_a
673 );
674 assert_eq!(None, b, "And only that");
675
676 let point = &Point { x: left - width, y: origin.y};
678 let dir = &Point { x: 0.9 * width, y: ratio }; let (a, b) = bbox.project_ray(point, dir);
680 assert!(a.is_none() && b.is_none(), "Expected no intersection");
681 }
682 }
683 }
684 }
685}