From 814b845de7687f6d89801cdde9c423b19f3d23d1 Mon Sep 17 00:00:00 2001 From: Brandon Waskiewicz Date: Thu, 29 May 2014 21:42:04 -0400 Subject: [PATCH 1/2] Update line intersection *Change line<>line intersection to be ray<>line. *Update tests accordingly *Clean up and simplify intersection logic --- src/line.rs | 87 ++++++++++++++++++++------------------------------- tests/line.rs | 85 ++++++++++++++++++++++--------------------------- 2 files changed, 71 insertions(+), 101 deletions(-) diff --git a/src/line.rs b/src/line.rs index ee40211..3b397d7 100644 --- a/src/line.rs +++ b/src/line.rs @@ -17,9 +17,10 @@ use std::num::{Zero, zero, One, one}; -use num::{BaseFloat, BaseNum}; +use num::{BaseNum, BaseFloat}; use point::{Point, Point2, Point3}; use vector::{Vector, Vector2}; +use ray::{Ray2}; use intersect::Intersect; /// A generic directed line segment from `origin` to `dest`. @@ -38,71 +39,51 @@ impl, P: Point> Line

{ pub type Line2 = Line>; pub type Line3 = Line>; -/// Determines if an intersection between two line segments is found. If the segments are -/// collinear and overlapping, the intersection point that will be returned will be the first -/// intersection point found by traversing the first line segment, starting at its origin. -impl Intersect>> for (Line2, Line2) { +/// Determines if an intersection between a ray and a line segments is found. +impl Intersect>> for (Ray2, Line2) { fn intersection(&self) -> Option> { match *self { - (ref l1, ref l2) => { - let p = l1.origin; - let mut q = l2.origin; - let r = Vector2::new(l1.dest.x - l1.origin.x, l1.dest.y - l1.origin.y); - let mut s = Vector2::new(l2.dest.x - l2.origin.x, l2.dest.y - l2.origin.y); + (ref ray, ref line) => { + let p = ray.origin; + let q = line.origin; + let r = ray.direction; + let s = Vector2::new(line.dest.x - line.origin.x, line.dest.y - line.origin.y); let zero: S = Zero::zero(); - let cross = r.perp_dot(&s); - let mut q_minus_p = Vector2::new(q.x - p.x, q.y - p.y); - let mut p_minus_q = Vector2::new(p.x - q.x, p.y - q.y); - let cross_r = q_minus_p.perp_dot(&r); - let cross_s = q_minus_p.perp_dot(&s); + let cross_1 = r.perp_dot(&s); + let qmp = Vector2::new(q.x - p.x, q.y - p.y); + let cross_2 = qmp.perp_dot(&r); - if cross.is_zero() { - if cross_r.is_zero() { - // line segments are collinear + if cross_1 == zero { + if cross_2 != zero { + // parallel + return None; + } - // special case of both lines being the same single point - if r.x == zero && r.y == zero && s.x == zero && s.y == zero && p == q { - return Some(p); + // collinear + let q2mp = Vector2::new(line.dest.x - p.x, line.dest.y - p.y); + let dot_1 = qmp.dot(&r); + let dot_2 = q2mp.dot(&r); + if (dot_1 <= zero && dot_2 >= zero) || (dot_1 >= zero && dot_2 <= zero) { + return Some(p); + } + else if dot_1 >= zero && dot_2 >= zero { + if dot_1 <= dot_2 { + return Some(q); } - - // ensure l2 (q,q+s) is pointing the same direction as l1 (p,p+r) - // if it is not, then swap the two endpoints of the second segment. - // If this is not done, the algorithm below will not find an - // intersection if the two directed line segments point towards the - // opposite segment's origin. - if (r.x != zero && s.x != zero && r.x.signum() != s.x.signum()) || - (r.y != zero && s.y != zero && r.y.signum() != s.y.signum()) { - q = Point2::new(q.x + s.x, q.y + s.y); - s = Vector2::new(-s.x, -s.y); - q_minus_p = Vector2::new(q.x - p.x, q.y - p.y); - p_minus_q = Vector2::new(p.x - q.x, p.y - q.y); - } - let d1 = q_minus_p.dot(&r); - let d2 = p_minus_q.dot(&s); - let rdotr = r.dot(&r); - let sdots = s.dot(&s); - - // make sure to take into account that one or both of the segments could - // be a single point (r.r or s.s = 0) and ignore that case - if (rdotr > zero && zero <= d1 && d1 <= rdotr) || - (sdots > zero && zero <= d2 && d2 <= sdots) { - // overlapping - if (q_minus_p.x != zero && q_minus_p.x.signum() == r.x.signum()) || - (q_minus_p.y != zero && q_minus_p.y.signum() == r.y.signum()) { - return Some(q); - } - return Some(p); + else { + return Some(line.dest); } } + + // no overlap exists return None; } - let t = cross_s / cross; - let u = cross_r / cross; + let t = qmp.perp_dot(&s) / cross_1; + let u = cross_2 / cross_1; - if zero <= t && t <= One::one() && - zero <= One::one() && u <= One::one() { + if zero <= t && u >= zero && u <= One::one() { return Some(Point2::new(p.x + t*r.x, p.y + t*r.y)); } diff --git a/tests/line.rs b/tests/line.rs index 2e4022c..3ada281 100644 --- a/tests/line.rs +++ b/tests/line.rs @@ -19,65 +19,54 @@ extern crate cgmath; use cgmath::line::*; use cgmath::point::*; +use cgmath::ray::*; +use cgmath::vector::*; use cgmath::intersect::Intersect; #[test] fn test_line_intersection() { - // collinear, origins pointing towards each other, first intersection - // from l1.origin is in an endpoint in l2 - let l1 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(10.0f64, 0.0f64)); - let l2 = Line::new(Point2::new(1.5f64, 0.0f64), Point2::new(0.5f64, 0.0f64)); - assert_eq!((l1, l2).intersection(), Some(Point2::new(0.5f64, 0.0f64))); + // collinear, intersection is line dest + let r1 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(0.25, 0.0)); + let l1 = Line::new(Point2::new(1.5f32, 0.0), Point2::new(0.5, 0.0)); + assert_eq!((r1, l1).intersection(), Some(Point2::new(0.5, 0.0))); - // collinear, first intersection from p1.origin is at p1.origin itself - let l3 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(10.0f64, 0.0f64)); - let l4 = Line::new(Point2::new(-11.0f64, 0.0f64), Point2::new(1.0f64, 0.0f64)); - assert_eq!((l3, l4).intersection(), Some(Point2::new(0.0f64, 0.0f64))); + // collinear, intersection is at ray origin + let r2 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(5.0, 0.0)); + let l2 = Line::new(Point2::new(-11.0f32, 0.0), Point2::new(1.0, 0.0)); + assert_eq!((r2, l2).intersection(), Some(Point2::new(0.0, 0.0))); - // no intersection - let l5 = Line::new(Point2::new(5.0f64, 5.0f64), Point2::new(10.0f64, 6.0f64)); - let l6 = Line::new(Point2::new(5.0f64, 4.8f64), Point2::new(10.0f64, 4.1f64)); - assert_eq!((l5, l6).intersection(), None); // no intersection - - // collinear, origins pointing same direction - let l7 = Line::new(Point2::new(0.0f64, 1.0f64), Point2::new(0.0f64, 0.0f64)); - let l8 = Line::new(Point2::new(0.0f64, 0.5f64), Point2::new(0.0f64, -0.5f64)); - assert_eq!((l7, l8).intersection(), Some(Point2::new(0.0f64, 0.5f64))); + // collinear, intersection is line origin + let r3 = Ray::new(Point2::new(0.0f32, 1.0), Vector2::new(0.0, -0.25)); + let l3 = Line::new(Point2::new(0.0f32, 0.5), Point2::new(0.0, -0.5)); + assert_eq!((r3, l3).intersection(), Some(Point2::new(0.0, 0.5))); // collinear, no overlap - let l9 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(3.0f64, 0.0f64)); - let l10 = Line::new(Point2::new(10.0f64, 0.0f64), Point2::new(5.0f64, 0.0f64)); - assert_eq!((l9, l10).intersection(), None); + let r4 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(3.0, 0.0)); + let l4 = Line::new(Point2::new(-10.0f32, 0.0), Point2::new(-5.0, 0.0)); + assert_eq!((r4, l4).intersection(), None); - // intersection found - let l11 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(10.0f64, 10.0f64)); - let l12 = Line::new(Point2::new(0.0f64, 10.0f64), Point2::new(10.0f64, 0.0f64)); - assert_eq!((l11, l12).intersection(), Some(Point2::new(5.0f64, 5.0f64))); + // no intersection + let r5 = Ray::new(Point2::new(5.0f32, 5.0), Vector2::new(40.0, 8.0)); + let l5 = Line::new(Point2::new(5.0f32, 4.8), Point2::new(10.0, 4.1)); + assert_eq!((r5, l5).intersection(), None); // no intersection - // special case of both lines being the same point - let l13 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(0.0f64, 0.0f64)); - let l14 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(0.0f64, 0.0f64)); - assert_eq!((l13, l14).intersection(), Some(Point2::new(0.0f64, 0.0f64))); + // non-collinear intersection + let r6 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(10.0, 10.0)); + let l6 = Line::new(Point2::new(0.0f32, 10.0), Point2::new(10.0, 0.0)); + assert_eq!((r6, l6).intersection(), Some(Point2::new(5.0, 5.0))); - // both lines are points that are distinct - let l15 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(0.0f64, 0.0f64)); - let l16 = Line::new(Point2::new(1.0f64, 0.0f64), Point2::new(1.0f64, 0.0f64)); - assert_eq!((l15, l16).intersection(), None); + // line is a point that does not intersect + let r7 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(1.0, 1.0)); + let l7 = Line::new(Point2::new(1.0f32, 0.0), Point2::new(1.0, 0.0)); + assert_eq!((r7, l7).intersection(), None); - // one line is a point that intersects the other segment - let l15 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(10.0f64, 0.0f64)); - let l16 = Line::new(Point2::new(3.0f64, 0.0f64), Point2::new(3.0f64, 0.0f64)); - assert_eq!((l15, l16).intersection(), Some(Point2::new(3.0f64, 0.0f64))); + // line is a point that does intersect + let r8 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(1.0, 0.0)); + let l8 = Line::new(Point2::new(3.0f32, 0.0), Point2::new(3.0, 0.0)); + assert_eq!((r8, l8).intersection(), Some(Point2::new(3.0, 0.0))); - // one line is a point that is collinear but does not intersect with - // the other line - let l17 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(0.0f64, 0.0f64)); - let l18 = Line::new(Point2::new(1.0f64, 0.0f64), Point2::new(3.0f64, 0.0f64)); - assert_eq!((l17, l18).intersection(), None); - - // one line is a point that is not collinear but does not intersect - // with the other line - let l19 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(0.0f64, 0.0f64)); - let l20 = Line::new(Point2::new(1.0f64, 0.0f64), Point2::new(2.0f64, 10.0f64)); - assert_eq!((l19, l20).intersection(), None); + // line is a collinear point but no intersection + let r9 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(1.0, 0.0)); + let l9 = Line::new(Point2::new(-1.0f32, 0.0), Point2::new(-1.0, 0.0)); + assert_eq!((r9, l9).intersection(), None); } From 4f17d73f238ffe8df959bfa8d5d976452e47d639 Mon Sep 17 00:00:00 2001 From: Brandon Waskiewicz Date: Thu, 29 May 2014 23:42:23 -0400 Subject: [PATCH 2/2] Add Ray2<>Aabb2 intersection impl --- src/aabb.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- tests/aabb.rs | 16 ++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/aabb.rs b/src/aabb.rs index c4a89e7..88fe0c6 100644 --- a/src/aabb.rs +++ b/src/aabb.rs @@ -22,9 +22,11 @@ use point::{Point, Point2, Point3}; use vector::{Vector, Vector2, Vector3}; -use num::BaseNum; +use ray::{Ray2}; +use intersect::Intersect; +use num::{BaseNum, BaseFloat}; use std::fmt; -use std::num::{zero, one}; +use std::num::{zero, one, Float}; pub trait Aabb, P: Point> { /// Create a new AABB using two points as opposing corners. @@ -172,3 +174,46 @@ impl fmt::Show for Aabb3 { write!(f, "[{} - {}]", self.min, self.max) } } + +impl Intersect>> for (Ray2, Aabb2) { + fn intersection(&self) -> Option> { + match *self { + (ref ray, ref aabb) => { + + let mut tmin: S = Float::neg_infinity(); + let mut tmax: S = Float::infinity(); + + if ray.direction.x != zero() { + let tx1 = (aabb.min.x - ray.origin.x) / ray.direction.x; + let tx2 = (aabb.max.x - ray.origin.x) / ray.direction.x; + tmin = tmin.max(tx1.min(tx2)); + tmax = tmax.min(tx1.max(tx2)); + } + + if ray.direction.y != zero() { + let ty1 = (aabb.min.y - ray.origin.y) / ray.direction.y; + let ty2 = (aabb.max.y - ray.origin.y) / ray.direction.y; + tmin = tmin.max(ty1.min(ty2)); + tmax = tmax.min(ty1.max(ty2)); + } + + if tmin < zero() && tmax < zero() { + None + } + else if tmax >= tmin { + if tmin >= zero() { + Some(Point2::new(ray.origin.x + ray.direction.x * tmin, + ray.origin.y + ray.direction.y * tmin)) + } + else { + Some(Point2::new(ray.origin.x + ray.direction.x * tmax, + ray.origin.y + ray.direction.y * tmax)) + } + } + else { + None + } + } + } + } +} diff --git a/tests/aabb.rs b/tests/aabb.rs index 06ff9b9..1b61100 100644 --- a/tests/aabb.rs +++ b/tests/aabb.rs @@ -20,6 +20,8 @@ extern crate cgmath; use cgmath::aabb::*; use cgmath::point::{Point2, Point3}; use cgmath::vector::{Vector2, Vector3}; +use cgmath::ray::{Ray}; +use cgmath::intersect::Intersect; #[test] fn test_aabb() { @@ -64,3 +66,17 @@ fn test_aabb() { assert_eq!(aabb.mul_v(&Vector3::new(1i, 2i, 3i)), Aabb3::new(Point3::new(-20i, -20i, -15i), Point3::new(10i, 60i, 15i))); } + +#[test] +fn test_aabb_ray_intersect() { + let aabb = Aabb2::new(Point2::new(-5.0f32, 5.0), Point2::new(5.0, 10.0)); + let ray1 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(0.0, 1.0)); + let ray2 = Ray::new(Point2::new(-10.0f32, 0.0), Vector2::new(2.5, 1.0)); + let ray3 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(-1.0, -1.0)); + let ray4 = Ray::new(Point2::new(3.0f32, 7.0), Vector2::new(1.0, 1.0)); + + assert_eq!((ray1, aabb).intersection(), Some(Point2::new(0.0, 5.0))); + assert_eq!((ray2, aabb).intersection(), Some(Point2::new(2.5, 5.0))); + assert_eq!((ray3, aabb).intersection(), None); + assert_eq!((ray4, aabb).intersection(), Some(Point2::new(5.0, 9.0))); +}