Merge pull request #82 from brandonw/add-intersection-impls
Update intersection impls
This commit is contained in:
commit
a010ddee54
4 changed files with 134 additions and 103 deletions
49
src/aabb.rs
49
src/aabb.rs
|
@ -22,9 +22,11 @@
|
||||||
|
|
||||||
use point::{Point, Point2, Point3};
|
use point::{Point, Point2, Point3};
|
||||||
use vector::{Vector, Vector2, Vector3};
|
use vector::{Vector, Vector2, Vector3};
|
||||||
use num::BaseNum;
|
use ray::{Ray2};
|
||||||
|
use intersect::Intersect;
|
||||||
|
use num::{BaseNum, BaseFloat};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::num::{zero, one};
|
use std::num::{zero, one, Float};
|
||||||
|
|
||||||
pub trait Aabb<S: BaseNum, V: Vector<S>, P: Point<S, V>> {
|
pub trait Aabb<S: BaseNum, V: Vector<S>, P: Point<S, V>> {
|
||||||
/// Create a new AABB using two points as opposing corners.
|
/// Create a new AABB using two points as opposing corners.
|
||||||
|
@ -172,3 +174,46 @@ impl<S: BaseNum> fmt::Show for Aabb3<S> {
|
||||||
write!(f, "[{} - {}]", self.min, self.max)
|
write!(f, "[{} - {}]", self.min, self.max)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S: BaseFloat> Intersect<Option<Point2<S>>> for (Ray2<S>, Aabb2<S>) {
|
||||||
|
fn intersection(&self) -> Option<Point2<S>> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
95
src/line.rs
95
src/line.rs
|
@ -17,9 +17,10 @@
|
||||||
|
|
||||||
use std::num::{Zero, zero, One, one};
|
use std::num::{Zero, zero, One, one};
|
||||||
|
|
||||||
use num::{BaseFloat, BaseNum};
|
use num::{BaseNum, BaseFloat};
|
||||||
use point::{Point, Point2, Point3};
|
use point::{Point, Point2, Point3};
|
||||||
use vector::{Vector, Vector2};
|
use vector::{Vector, Vector2};
|
||||||
|
use ray::{Ray2};
|
||||||
use intersect::Intersect;
|
use intersect::Intersect;
|
||||||
|
|
||||||
/// A generic directed line segment from `origin` to `dest`.
|
/// A generic directed line segment from `origin` to `dest`.
|
||||||
|
@ -38,71 +39,51 @@ impl<S: BaseNum, V: Vector<S>, P: Point<S, V>> Line<P> {
|
||||||
pub type Line2<S> = Line<Point2<S>>;
|
pub type Line2<S> = Line<Point2<S>>;
|
||||||
pub type Line3<S> = Line<Point3<S>>;
|
pub type Line3<S> = Line<Point3<S>>;
|
||||||
|
|
||||||
/// Determines if an intersection between two line segments is found. If the segments are
|
/// Determines if an intersection between a ray and a line segments is found.
|
||||||
/// collinear and overlapping, the intersection point that will be returned will be the first
|
impl<S: BaseFloat> Intersect<Option<Point2<S>>> for (Ray2<S>, Line2<S>) {
|
||||||
/// intersection point found by traversing the first line segment, starting at its origin.
|
|
||||||
impl<S: BaseFloat> Intersect<Option<Point2<S>>> for (Line2<S>, Line2<S>) {
|
|
||||||
fn intersection(&self) -> Option<Point2<S>> {
|
fn intersection(&self) -> Option<Point2<S>> {
|
||||||
match *self {
|
match *self {
|
||||||
(ref l1, ref l2) => {
|
(ref ray, ref line) => {
|
||||||
let p = l1.origin;
|
let p = ray.origin;
|
||||||
let mut q = l2.origin;
|
let q = line.origin;
|
||||||
let r = Vector2::new(l1.dest.x - l1.origin.x, l1.dest.y - l1.origin.y);
|
let r = ray.direction;
|
||||||
let mut s = Vector2::new(l2.dest.x - l2.origin.x, l2.dest.y - l2.origin.y);
|
let s = Vector2::new(line.dest.x - line.origin.x, line.dest.y - line.origin.y);
|
||||||
let zero: S = Zero::zero();
|
let zero: S = Zero::zero();
|
||||||
|
|
||||||
let cross = r.perp_dot(&s);
|
let cross_1 = r.perp_dot(&s);
|
||||||
let mut q_minus_p = Vector2::new(q.x - p.x, q.y - p.y);
|
let qmp = 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_2 = qmp.perp_dot(&r);
|
||||||
let cross_r = q_minus_p.perp_dot(&r);
|
|
||||||
let cross_s = q_minus_p.perp_dot(&s);
|
|
||||||
|
|
||||||
if cross.is_zero() {
|
if cross_1 == zero {
|
||||||
if cross_r.is_zero() {
|
if cross_2 != zero {
|
||||||
// line segments are collinear
|
// parallel
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let t = cross_s / cross;
|
// collinear
|
||||||
let u = cross_r / cross;
|
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);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Some(line.dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if zero <= t && t <= One::one() &&
|
// no overlap exists
|
||||||
zero <= One::one() && u <= One::one() {
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let t = qmp.perp_dot(&s) / cross_1;
|
||||||
|
let u = cross_2 / cross_1;
|
||||||
|
|
||||||
|
if zero <= t && u >= zero && u <= One::one() {
|
||||||
return Some(Point2::new(p.x + t*r.x, p.y + t*r.y));
|
return Some(Point2::new(p.x + t*r.x, p.y + t*r.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ extern crate cgmath;
|
||||||
use cgmath::aabb::*;
|
use cgmath::aabb::*;
|
||||||
use cgmath::point::{Point2, Point3};
|
use cgmath::point::{Point2, Point3};
|
||||||
use cgmath::vector::{Vector2, Vector3};
|
use cgmath::vector::{Vector2, Vector3};
|
||||||
|
use cgmath::ray::{Ray};
|
||||||
|
use cgmath::intersect::Intersect;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_aabb() {
|
fn test_aabb() {
|
||||||
|
@ -64,3 +66,17 @@ fn test_aabb() {
|
||||||
assert_eq!(aabb.mul_v(&Vector3::new(1i, 2i, 3i)),
|
assert_eq!(aabb.mul_v(&Vector3::new(1i, 2i, 3i)),
|
||||||
Aabb3::new(Point3::new(-20i, -20i, -15i), Point3::new(10i, 60i, 15i)));
|
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)));
|
||||||
|
}
|
||||||
|
|
|
@ -19,65 +19,54 @@ extern crate cgmath;
|
||||||
|
|
||||||
use cgmath::line::*;
|
use cgmath::line::*;
|
||||||
use cgmath::point::*;
|
use cgmath::point::*;
|
||||||
|
use cgmath::ray::*;
|
||||||
|
use cgmath::vector::*;
|
||||||
use cgmath::intersect::Intersect;
|
use cgmath::intersect::Intersect;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_line_intersection() {
|
fn test_line_intersection() {
|
||||||
// collinear, origins pointing towards each other, first intersection
|
// collinear, intersection is line dest
|
||||||
// from l1.origin is in an endpoint in l2
|
let r1 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(0.25, 0.0));
|
||||||
let l1 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(10.0f64, 0.0f64));
|
let l1 = Line::new(Point2::new(1.5f32, 0.0), Point2::new(0.5, 0.0));
|
||||||
let l2 = Line::new(Point2::new(1.5f64, 0.0f64), Point2::new(0.5f64, 0.0f64));
|
assert_eq!((r1, l1).intersection(), Some(Point2::new(0.5, 0.0)));
|
||||||
assert_eq!((l1, l2).intersection(), Some(Point2::new(0.5f64, 0.0f64)));
|
|
||||||
|
|
||||||
// collinear, first intersection from p1.origin is at p1.origin itself
|
// collinear, intersection is at ray origin
|
||||||
let l3 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(10.0f64, 0.0f64));
|
let r2 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(5.0, 0.0));
|
||||||
let l4 = Line::new(Point2::new(-11.0f64, 0.0f64), Point2::new(1.0f64, 0.0f64));
|
let l2 = Line::new(Point2::new(-11.0f32, 0.0), Point2::new(1.0, 0.0));
|
||||||
assert_eq!((l3, l4).intersection(), Some(Point2::new(0.0f64, 0.0f64)));
|
assert_eq!((r2, l2).intersection(), Some(Point2::new(0.0, 0.0)));
|
||||||
|
|
||||||
// no intersection
|
// collinear, intersection is line origin
|
||||||
let l5 = Line::new(Point2::new(5.0f64, 5.0f64), Point2::new(10.0f64, 6.0f64));
|
let r3 = Ray::new(Point2::new(0.0f32, 1.0), Vector2::new(0.0, -0.25));
|
||||||
let l6 = Line::new(Point2::new(5.0f64, 4.8f64), Point2::new(10.0f64, 4.1f64));
|
let l3 = Line::new(Point2::new(0.0f32, 0.5), Point2::new(0.0, -0.5));
|
||||||
assert_eq!((l5, l6).intersection(), None); // no intersection
|
assert_eq!((r3, l3).intersection(), Some(Point2::new(0.0, 0.5)));
|
||||||
|
|
||||||
// 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, no overlap
|
// collinear, no overlap
|
||||||
let l9 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(3.0f64, 0.0f64));
|
let r4 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(3.0, 0.0));
|
||||||
let l10 = Line::new(Point2::new(10.0f64, 0.0f64), Point2::new(5.0f64, 0.0f64));
|
let l4 = Line::new(Point2::new(-10.0f32, 0.0), Point2::new(-5.0, 0.0));
|
||||||
assert_eq!((l9, l10).intersection(), None);
|
assert_eq!((r4, l4).intersection(), None);
|
||||||
|
|
||||||
// intersection found
|
// no intersection
|
||||||
let l11 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(10.0f64, 10.0f64));
|
let r5 = Ray::new(Point2::new(5.0f32, 5.0), Vector2::new(40.0, 8.0));
|
||||||
let l12 = Line::new(Point2::new(0.0f64, 10.0f64), Point2::new(10.0f64, 0.0f64));
|
let l5 = Line::new(Point2::new(5.0f32, 4.8), Point2::new(10.0, 4.1));
|
||||||
assert_eq!((l11, l12).intersection(), Some(Point2::new(5.0f64, 5.0f64)));
|
assert_eq!((r5, l5).intersection(), None); // no intersection
|
||||||
|
|
||||||
// special case of both lines being the same point
|
// non-collinear intersection
|
||||||
let l13 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(0.0f64, 0.0f64));
|
let r6 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(10.0, 10.0));
|
||||||
let l14 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(0.0f64, 0.0f64));
|
let l6 = Line::new(Point2::new(0.0f32, 10.0), Point2::new(10.0, 0.0));
|
||||||
assert_eq!((l13, l14).intersection(), Some(Point2::new(0.0f64, 0.0f64)));
|
assert_eq!((r6, l6).intersection(), Some(Point2::new(5.0, 5.0)));
|
||||||
|
|
||||||
// both lines are points that are distinct
|
// line is a point that does not intersect
|
||||||
let l15 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(0.0f64, 0.0f64));
|
let r7 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(1.0, 1.0));
|
||||||
let l16 = Line::new(Point2::new(1.0f64, 0.0f64), Point2::new(1.0f64, 0.0f64));
|
let l7 = Line::new(Point2::new(1.0f32, 0.0), Point2::new(1.0, 0.0));
|
||||||
assert_eq!((l15, l16).intersection(), None);
|
assert_eq!((r7, l7).intersection(), None);
|
||||||
|
|
||||||
// one line is a point that intersects the other segment
|
// line is a point that does intersect
|
||||||
let l15 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(10.0f64, 0.0f64));
|
let r8 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(1.0, 0.0));
|
||||||
let l16 = Line::new(Point2::new(3.0f64, 0.0f64), Point2::new(3.0f64, 0.0f64));
|
let l8 = Line::new(Point2::new(3.0f32, 0.0), Point2::new(3.0, 0.0));
|
||||||
assert_eq!((l15, l16).intersection(), Some(Point2::new(3.0f64, 0.0f64)));
|
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
|
// line is a collinear point but no intersection
|
||||||
// the other line
|
let r9 = Ray::new(Point2::new(0.0f32, 0.0), Vector2::new(1.0, 0.0));
|
||||||
let l17 = Line::new(Point2::new(0.0f64, 0.0f64), Point2::new(0.0f64, 0.0f64));
|
let l9 = Line::new(Point2::new(-1.0f32, 0.0), Point2::new(-1.0, 0.0));
|
||||||
let l18 = Line::new(Point2::new(1.0f64, 0.0f64), Point2::new(3.0f64, 0.0f64));
|
assert_eq!((r9, l9).intersection(), None);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue