From 95c3b3608f2702665de29528cc37276263e32d08 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 12 Mar 2015 22:51:35 +0300 Subject: [PATCH 01/10] Added Bound module, implemented for Point3 and Aabb3 --- src/bound.rs | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/cgmath.rs | 3 +++ 2 files changed, 63 insertions(+) create mode 100644 src/bound.rs diff --git a/src/bound.rs b/src/bound.rs new file mode 100644 index 0000000..33a20b9 --- /dev/null +++ b/src/bound.rs @@ -0,0 +1,60 @@ +// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, +// refer to the AUTHORS file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Generic spatial bounds. + +use aabb::{Aabb, Aabb3}; +use num::BaseNum; +use plane::Plane; +use point::{Point, Point3}; + +/// Spatial relation between two objects. +pub enum Relation { + /// Completely inside + In, + /// Crosses the boundary + Cross, + /// Completely outside + Out, +} + +/// Generic bound. +pub trait Bound { + /// Classify the spatial relation with a plane + fn relate(&self, &Plane) -> Relation; +} + +impl Bound for Point3 { + fn relate(&self, plane: &Plane) -> Relation { + let dist = self.dot(&plane.n); + if dist > plane.d { + Relation::In + }else if dist < plane.d { + Relation::Out + }else { + Relation::Cross + } + } +} + +impl Bound for Aabb3 { + fn relate(&self, plane: &Plane) -> Relation { + match (self.min.relate(plane), self.max.relate(plane)) { + (Relation::In, Relation::In) => Relation::In, + (Relation::Out, Relation::Out) => Relation::Out, + (_, _) => Relation::Cross, + } + } +} diff --git a/src/cgmath.rs b/src/cgmath.rs index c8335da..c0c7df8 100644 --- a/src/cgmath.rs +++ b/src/cgmath.rs @@ -16,6 +16,7 @@ #![crate_type = "rlib"] #![crate_type = "dylib"] #![feature(old_impl_check, plugin, core, std_misc, custom_derive)] +#![plugin(rand_macros)] //! Computer graphics-centric math. //! @@ -52,6 +53,7 @@ pub use transform::*; pub use projection::*; pub use aabb::*; +pub use bound::*; pub use cylinder::Cylinder; pub use frustum::{Frustum, FrustumPoints}; pub use intersect::Intersect; @@ -80,6 +82,7 @@ mod transform; mod projection; mod aabb; +mod bound; mod cylinder; mod frustum; mod intersect; From e71887a8481cbbe7163e626a84716c92af64e6e8 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 12 Mar 2015 22:53:33 +0300 Subject: [PATCH 02/10] Renamed the main entry point to be lib.rs --- src/{cgmath.rs => lib.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{cgmath.rs => lib.rs} (100%) diff --git a/src/cgmath.rs b/src/lib.rs similarity index 100% rename from src/cgmath.rs rename to src/lib.rs From 2722815d848e7c55293313970ee369e4a452b68e Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 12 Mar 2015 23:02:39 +0300 Subject: [PATCH 03/10] Implemented Bound for Sphere --- src/bound.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/bound.rs b/src/bound.rs index 33a20b9..753ae1a 100644 --- a/src/bound.rs +++ b/src/bound.rs @@ -19,6 +19,7 @@ use aabb::{Aabb, Aabb3}; use num::BaseNum; use plane::Plane; use point::{Point, Point3}; +use sphere::Sphere; /// Spatial relation between two objects. pub enum Relation { @@ -58,3 +59,16 @@ impl Bound for Aabb3 { } } } + +impl Bound for Sphere { + fn relate(&self, plane: &Plane) -> Relation { + let dist = self.center.dot(&plane.n) - plane.d; + if dist > self.radius { + Relation::In + }else if dist < - self.radius { + Relation::Out + }else { + Relation::Cross + } + } +} From d3d7241c86e89d0c3de8bf9b0580da79858ac484 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 13 Mar 2015 11:32:23 +0300 Subject: [PATCH 04/10] Moved bound implementations into the corresponding modules --- src/aabb.rs | 13 +++++++++++ src/bound.rs | 58 +++++++++----------------------------------------- src/point.rs | 15 +++++++++++++ src/sphere.rs | 17 ++++++++++++++- tests/bound.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 49 deletions(-) create mode 100644 tests/bound.rs diff --git a/src/aabb.rs b/src/aabb.rs index 5cb8620..aab695a 100644 --- a/src/aabb.rs +++ b/src/aabb.rs @@ -20,11 +20,13 @@ //! dimension) where the slope of every line is either 0 or undefined. These //! are useful for very cheap collision detection. +use bound::*; use point::{Point, Point2, Point3}; use vector::{Vector, Vector2, Vector3}; use ray::{Ray2}; use intersect::Intersect; use num::{zero, one, BaseNum, BaseFloat}; +use plane::Plane; use std::fmt; use std::num::Float; @@ -217,3 +219,14 @@ impl Intersect>> for (Ray2, Aabb2) { } } } + +impl Bound for Aabb3 { + fn relate(&self, plane: &Plane) -> Relation { + //TODO: match all 8 corners + match (self.min.relate(plane), self.max.relate(plane)) { + (Relation::In, Relation::In) => Relation::In, + (Relation::Out, Relation::Out) => Relation::Out, + (_, _) => Relation::Cross, + } + } +} diff --git a/src/bound.rs b/src/bound.rs index 753ae1a..cc5ff56 100644 --- a/src/bound.rs +++ b/src/bound.rs @@ -15,60 +15,22 @@ //! Generic spatial bounds. -use aabb::{Aabb, Aabb3}; -use num::BaseNum; use plane::Plane; -use point::{Point, Point3}; -use sphere::Sphere; /// Spatial relation between two objects. +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)] +#[repr(u8)] pub enum Relation { - /// Completely inside - In, - /// Crosses the boundary - Cross, - /// Completely outside - Out, + /// Completely inside + In, + /// Crosses the boundary + Cross, + /// Completely outside + Out, } /// Generic bound. pub trait Bound { - /// Classify the spatial relation with a plane - fn relate(&self, &Plane) -> Relation; -} - -impl Bound for Point3 { - fn relate(&self, plane: &Plane) -> Relation { - let dist = self.dot(&plane.n); - if dist > plane.d { - Relation::In - }else if dist < plane.d { - Relation::Out - }else { - Relation::Cross - } - } -} - -impl Bound for Aabb3 { - fn relate(&self, plane: &Plane) -> Relation { - match (self.min.relate(plane), self.max.relate(plane)) { - (Relation::In, Relation::In) => Relation::In, - (Relation::Out, Relation::Out) => Relation::Out, - (_, _) => Relation::Cross, - } - } -} - -impl Bound for Sphere { - fn relate(&self, plane: &Plane) -> Relation { - let dist = self.center.dot(&plane.n) - plane.d; - if dist > self.radius { - Relation::In - }else if dist < - self.radius { - Relation::Out - }else { - Relation::Cross - } - } + /// Classify the spatial relation with a plane + fn relate(&self, &Plane) -> Relation; } diff --git a/src/point.rs b/src/point.rs index 4b8d6e3..6e2bf0d 100644 --- a/src/point.rs +++ b/src/point.rs @@ -23,7 +23,9 @@ use std::ops::*; use approx::ApproxEq; use array::{Array1, FixedArray}; +use bound::*; use num::{BaseNum, BaseFloat, one, zero}; +use plane::Plane; use vector::*; /// A point in 2-dimensional space. @@ -445,3 +447,16 @@ impl fmt::Debug for Point3 { write!(f, "[{:?}, {:?}, {:?}]", self.x, self.y, self.z) } } + +impl Bound for Point3 { + fn relate(&self, plane: &Plane) -> Relation { + let dist = self.dot(&plane.n); + if dist > plane.d { + Relation::In + }else if dist < plane.d { + Relation::Out + }else { + Relation::Cross + } + } +} diff --git a/src/sphere.rs b/src/sphere.rs index 46474e9..519be4f 100644 --- a/src/sphere.rs +++ b/src/sphere.rs @@ -15,9 +15,11 @@ //! Bounding sphere +use bound::*; use intersect::Intersect; -use num::{BaseFloat, zero}; +use num::{BaseNum, BaseFloat, zero}; use point::{Point, Point3}; +use plane::Plane; use ray::Ray3; use vector::Vector; @@ -42,3 +44,16 @@ impl Intersect>> for (Sphere, Ray3) { } } } + +impl Bound for Sphere { + fn relate(&self, plane: &Plane) -> Relation { + let dist = self.center.dot(&plane.n) - plane.d; + if dist > self.radius { + Relation::In + }else if dist < - self.radius { + Relation::Out + }else { + Relation::Cross + } + } +} diff --git a/tests/bound.rs b/tests/bound.rs new file mode 100644 index 0000000..2716a5c --- /dev/null +++ b/tests/bound.rs @@ -0,0 +1,51 @@ +// Copyright 2013-2015 The CGMath Developers. For a full listing of the authors, +// refer to the AUTHORS file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate cgmath; + +use cgmath::{Aabb3, Bound, Plane, Point, Point3, Relation, Sphere, Vector, vec3}; + +#[test] +fn point() { + let point = Point3::new(1f32, 2.0, 3.0); + let normal = vec3(0f32, -0.8, -0.36); + let plane = Plane::from_point_normal(point, normal); + + assert_eq!(point.relate(&plane), Relation::Cross); + assert_eq!(point.add_v(&normal).relate(&plane), Relation::In); + assert_eq!(point.add_v(&normal.mul_s(-1.0)).relate(&plane), Relation::Out); +} + +#[test] +fn sphere() { + let point = Point3::new(1f32, 2.0, 3.0); + let sphere = Sphere { center: point, radius: 1.0 }; + let normal = vec3(0f32, 0.0, 1.0); + + assert_eq!(sphere.relate( + &Plane::from_point_normal(point, normal) + ), Relation::Cross); + assert_eq!(sphere.relate( + &Plane::from_point_normal(point.add_v(&normal.mul_s(-3.0)), normal), + ), Relation::In); + assert_eq!(sphere.relate( + &Plane::from_point_normal(point.add_v(&normal.mul_s(3.0)), normal), + ), Relation::Out); +} + +#[test] +fn aabb() { + //TODO +} From 4d71e8aad9d88f70c5b5bdc84e4a1c394a054082 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 13 Mar 2015 11:41:31 +0300 Subject: [PATCH 05/10] Moved the bound tests around. Added one for Aabb3 --- tests/aabb.rs | 16 ++++++++++++++-- tests/bound.rs | 51 ------------------------------------------------- tests/point.rs | 16 ++++++++++++++-- tests/sphere.rs | 17 +++++++++++++++++ 4 files changed, 45 insertions(+), 55 deletions(-) delete mode 100644 tests/bound.rs diff --git a/tests/aabb.rs b/tests/aabb.rs index 3bc0870..9c228e9 100644 --- a/tests/aabb.rs +++ b/tests/aabb.rs @@ -19,9 +19,10 @@ use cgmath::{Aabb, Aabb2, Aabb3}; use cgmath::{Point2, Point3}; use cgmath::{Vector2, Vector3}; use cgmath::{Ray, Intersect}; +use cgmath::{Plane, Bound, Relation}; #[test] -fn test_aabb() { +fn test_general() { let aabb = Aabb2::new(Point2::new(-20is, 30is), Point2::new(10is, -10is)); assert_eq!(aabb.min(), &Point2::new(-20is, -10is)); assert_eq!(aabb.max(), &Point2::new(10is, 30is)); @@ -65,7 +66,7 @@ fn test_aabb() { } #[test] -fn test_aabb_ray_intersect() { +fn test_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)); @@ -77,3 +78,14 @@ fn test_aabb_ray_intersect() { assert_eq!((ray3, aabb).intersection(), None); assert_eq!((ray4, aabb).intersection(), Some(Point2::new(5.0, 9.0))); } + +#[test] +fn test_bound() { + let aabb = Aabb3::new(Point3::new(-5.0f32, 5.0, 0.0), Point3::new(5.0, 10.0, 1.0)); + let plane1 = Plane::from_point_normal(Point3::new(0f32, 0.0, 0.0), Vector3::new(0f32, 0.0, 1.0)); + let plane2 = Plane::from_point_normal(Point3::new(-5.0f32, 4.0, 0.0), Vector3::new(0f32, 1.0, 0.0)); + let plane3 = Plane::from_point_normal(Point3::new(6.0f32, 0.0, 0.0), Vector3::new(1f32, 0.0, 0.0)); + assert_eq!(aabb.relate(&plane1), Relation::Cross); + assert_eq!(aabb.relate(&plane2), Relation::In); + assert_eq!(aabb.relate(&plane3), Relation::Out); +} diff --git a/tests/bound.rs b/tests/bound.rs deleted file mode 100644 index 2716a5c..0000000 --- a/tests/bound.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2013-2015 The CGMath Developers. For a full listing of the authors, -// refer to the AUTHORS file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -extern crate cgmath; - -use cgmath::{Aabb3, Bound, Plane, Point, Point3, Relation, Sphere, Vector, vec3}; - -#[test] -fn point() { - let point = Point3::new(1f32, 2.0, 3.0); - let normal = vec3(0f32, -0.8, -0.36); - let plane = Plane::from_point_normal(point, normal); - - assert_eq!(point.relate(&plane), Relation::Cross); - assert_eq!(point.add_v(&normal).relate(&plane), Relation::In); - assert_eq!(point.add_v(&normal.mul_s(-1.0)).relate(&plane), Relation::Out); -} - -#[test] -fn sphere() { - let point = Point3::new(1f32, 2.0, 3.0); - let sphere = Sphere { center: point, radius: 1.0 }; - let normal = vec3(0f32, 0.0, 1.0); - - assert_eq!(sphere.relate( - &Plane::from_point_normal(point, normal) - ), Relation::Cross); - assert_eq!(sphere.relate( - &Plane::from_point_normal(point.add_v(&normal.mul_s(-3.0)), normal), - ), Relation::In); - assert_eq!(sphere.relate( - &Plane::from_point_normal(point.add_v(&normal.mul_s(3.0)), normal), - ), Relation::Out); -} - -#[test] -fn aabb() { - //TODO -} diff --git a/tests/point.rs b/tests/point.rs index 93f8047..f67a5c0 100644 --- a/tests/point.rs +++ b/tests/point.rs @@ -16,11 +16,23 @@ extern crate cgmath; -use cgmath::Point3; -use cgmath::ApproxEq; +use cgmath::{Point, Point3, Vector, Vector3}; +use cgmath::{Bound, Relation, Plane}; +use cgmath::{ApproxEq}; #[test] fn test_homogeneous() { let p = Point3::new(1.0f64, 2.0f64, 3.0f64); assert!(p.approx_eq( &Point3::from_homogeneous( &p.to_homogeneous() ) )); } + +#[test] +fn test_bound() { + let point = Point3::new(1f32, 2.0, 3.0); + let normal = Vector3::new(0f32, -0.8, -0.36); + let plane = Plane::from_point_normal(point, normal); + + assert_eq!(point.relate(&plane), Relation::Cross); + assert_eq!(point.add_v(&normal).relate(&plane), Relation::In); + assert_eq!(point.add_v(&normal.mul_s(-1.0)).relate(&plane), Relation::Out); +} diff --git a/tests/sphere.rs b/tests/sphere.rs index b54ab83..4ab9af0 100644 --- a/tests/sphere.rs +++ b/tests/sphere.rs @@ -31,3 +31,20 @@ fn test_intersection() { assert_eq!((sphere,r2).intersection(), Some(Point3::new(1f64, 0f64, 0f64))); assert_eq!((sphere,r3).intersection(), None); } + +#[test] +fn test_bound() { + let point = Point3::new(1f32, 2.0, 3.0); + let sphere = Sphere { center: point, radius: 1.0 }; + let normal = vec3(0f32, 0.0, 1.0); + + assert_eq!(sphere.relate( + &Plane::from_point_normal(point, normal) + ), Relation::Cross); + assert_eq!(sphere.relate( + &Plane::from_point_normal(point.add_v(&normal.mul_s(-3.0)), normal), + ), Relation::In); + assert_eq!(sphere.relate( + &Plane::from_point_normal(point.add_v(&normal.mul_s(3.0)), normal), + ), Relation::Out); +} From 896c357ea8e0e023b1a38426604da30b7ff83db3 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 13 Mar 2015 23:49:22 +0300 Subject: [PATCH 06/10] Implemented Aabb to_corners() --- src/aabb.rs | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/aabb.rs b/src/aabb.rs index aab695a..cf47d80 100644 --- a/src/aabb.rs +++ b/src/aabb.rs @@ -103,6 +103,15 @@ impl Aabb2 { p1.y.partial_max(p2.y)), } } + + /// Compute corners. + #[inline] + pub fn to_corners(&self) -> [Point2; 4] { + [self.min, + Point2::new(self.max.x, self.min.y), + Point2::new(self.min.x, self.max.y), + self.max] + } } impl Aabb, Point2> for Aabb2 { @@ -150,6 +159,19 @@ impl Aabb3 { p1.z.partial_max(p2.z)), } } + + /// Compute corners. + #[inline] + pub fn to_corners(&self) -> [Point3; 8] { + [self.min, + Point3::new(self.max.x, self.min.y, self.min.z), + Point3::new(self.min.x, self.max.y, self.min.z), + Point3::new(self.max.x, self.max.y, self.min.z), + Point3::new(self.min.x, self.min.y, self.max.z), + Point3::new(self.max.x, self.min.y, self.max.z), + Point3::new(self.min.x, self.max.y, self.max.z), + self.max] + } } impl Aabb, Point3> for Aabb3 { @@ -222,11 +244,13 @@ impl Intersect>> for (Ray2, Aabb2) { impl Bound for Aabb3 { fn relate(&self, plane: &Plane) -> Relation { - //TODO: match all 8 corners - match (self.min.relate(plane), self.max.relate(plane)) { - (Relation::In, Relation::In) => Relation::In, - (Relation::Out, Relation::Out) => Relation::Out, - (_, _) => Relation::Cross, + let corners = self.to_corners(); + let first = corners[0].relate(plane); + for p in corners[1..].iter() { + if p.relate(plane) != first { + return Relation::Cross; + } } + first } } From 715f3b7157a8ba438304808b3352f4e535829a30 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 14 Mar 2015 00:07:15 +0300 Subject: [PATCH 07/10] Added tests for aabb to_corners(). Fixed test warnings. --- tests/aabb.rs | 82 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/tests/aabb.rs b/tests/aabb.rs index 9c228e9..b21d238 100644 --- a/tests/aabb.rs +++ b/tests/aabb.rs @@ -8,7 +8,7 @@ // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, +// distributed under the License is distributed on an "AS IS" BASisize, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. @@ -23,46 +23,46 @@ use cgmath::{Plane, Bound, Relation}; #[test] fn test_general() { - let aabb = Aabb2::new(Point2::new(-20is, 30is), Point2::new(10is, -10is)); - assert_eq!(aabb.min(), &Point2::new(-20is, -10is)); - assert_eq!(aabb.max(), &Point2::new(10is, 30is)); - assert_eq!(aabb.dim(), Vector2::new(30is, 40is)); - assert_eq!(aabb.volume(), 30is * 40is); - assert_eq!(aabb.center(), Point2::new(-5is, 10is)); + let aabb = Aabb2::new(Point2::new(-20isize, 30isize), Point2::new(10isize, -10isize)); + assert_eq!(aabb.min(), &Point2::new(-20isize, -10isize)); + assert_eq!(aabb.max(), &Point2::new(10isize, 30isize)); + assert_eq!(aabb.dim(), Vector2::new(30isize, 40isize)); + assert_eq!(aabb.volume(), 30isize * 40isize); + assert_eq!(aabb.center(), Point2::new(-5isize, 10isize)); - assert!(aabb.contains(&Point2::new(0is, 0is))); - assert!(!aabb.contains(&Point2::new(-50is, -50is))); - assert!(!aabb.contains(&Point2::new(50is, 50is))); + assert!(aabb.contains(&Point2::new(0isize, 0isize))); + assert!(!aabb.contains(&Point2::new(-50isize, -50isize))); + assert!(!aabb.contains(&Point2::new(50isize, 50isize))); - assert_eq!(aabb.grow(&Point2::new(0is, 0is)), aabb); - assert_eq!(aabb.grow(&Point2::new(100is, 100is)), - Aabb2::new(Point2::new(-20is, -10is), Point2::new(100is, 100is))); - assert_eq!(aabb.grow(&Point2::new(-100is, -100is)), - Aabb2::new(Point2::new(-100is, -100is), Point2::new(10is, 30is))); + assert_eq!(aabb.grow(&Point2::new(0isize, 0isize)), aabb); + assert_eq!(aabb.grow(&Point2::new(100isize, 100isize)), + Aabb2::new(Point2::new(-20isize, -10isize), Point2::new(100isize, 100isize))); + assert_eq!(aabb.grow(&Point2::new(-100isize, -100isize)), + Aabb2::new(Point2::new(-100isize, -100isize), Point2::new(10isize, 30isize))); - let aabb = Aabb3::new(Point3::new(-20is, 30is, 5is), Point3::new(10is, -10is, -5is)); - assert_eq!(aabb.min(), &Point3::new(-20is, -10is, -5is)); - assert_eq!(aabb.max(), &Point3::new(10is, 30is, 5is)); - assert_eq!(aabb.dim(), Vector3::new(30is, 40is, 10is)); - assert_eq!(aabb.volume(), 30is * 40is * 10is); - assert_eq!(aabb.center(), Point3::new(-5is, 10is, 0is)); + let aabb = Aabb3::new(Point3::new(-20isize, 30isize, 5isize), Point3::new(10isize, -10isize, -5isize)); + assert_eq!(aabb.min(), &Point3::new(-20isize, -10isize, -5isize)); + assert_eq!(aabb.max(), &Point3::new(10isize, 30isize, 5isize)); + assert_eq!(aabb.dim(), Vector3::new(30isize, 40isize, 10isize)); + assert_eq!(aabb.volume(), 30isize * 40isize * 10isize); + assert_eq!(aabb.center(), Point3::new(-5isize, 10isize, 0isize)); - assert!(aabb.contains(&Point3::new(0is, 0is, 0is))); - assert!(!aabb.contains(&Point3::new(-100is, 0is, 0is))); - assert!(!aabb.contains(&Point3::new(100is, 0is, 0is))); - assert!(aabb.contains(&Point3::new(9is, 29is, -1is))); - assert!(!aabb.contains(&Point3::new(10is, 30is, 5is))); - assert!(aabb.contains(&Point3::new(-20is, -10is, -5is))); - assert!(!aabb.contains(&Point3::new(-21is, -11is, -6is))); + assert!(aabb.contains(&Point3::new(0isize, 0isize, 0isize))); + assert!(!aabb.contains(&Point3::new(-100isize, 0isize, 0isize))); + assert!(!aabb.contains(&Point3::new(100isize, 0isize, 0isize))); + assert!(aabb.contains(&Point3::new(9isize, 29isize, -1isize))); + assert!(!aabb.contains(&Point3::new(10isize, 30isize, 5isize))); + assert!(aabb.contains(&Point3::new(-20isize, -10isize, -5isize))); + assert!(!aabb.contains(&Point3::new(-21isize, -11isize, -6isize))); - assert_eq!(aabb.add_v(&Vector3::new(1is, 2is, 3is)), - Aabb3::new(Point3::new(-19is, 32is, 8is), Point3::new(11is, -8is, -2is))); + assert_eq!(aabb.add_v(&Vector3::new(1isize, 2isize, 3isize)), + Aabb3::new(Point3::new(-19isize, 32isize, 8isize), Point3::new(11isize, -8isize, -2isize))); - assert_eq!(aabb.mul_s(2is), - Aabb3::new(Point3::new(-40is, -20is, -10is), Point3::new(20is, 60is, 10is))); + assert_eq!(aabb.mul_s(2isize), + Aabb3::new(Point3::new(-40isize, -20isize, -10isize), Point3::new(20isize, 60isize, 10isize))); - assert_eq!(aabb.mul_v(&Vector3::new(1is, 2is, 3is)), - Aabb3::new(Point3::new(-20is, -20is, -15is), Point3::new(10is, 60is, 15is))); + assert_eq!(aabb.mul_v(&Vector3::new(1isize, 2isize, 3isize)), + Aabb3::new(Point3::new(-20isize, -20isize, -15isize), Point3::new(10isize, 60isize, 15isize))); } #[test] @@ -79,6 +79,20 @@ fn test_ray_intersect() { assert_eq!((ray4, aabb).intersection(), Some(Point2::new(5.0, 9.0))); } +#[test] +fn test_corners() { + let corners = Aabb2::new(Point2::new(-5.0f32, 5.0), Point2::new(5.0, 10.0)) + .to_corners(); + assert!(corners.contains(&Point2::new(-5f32, 10.0))); + assert!(corners.contains(&Point2::new(5f32, 5.0))); + + let corners = Aabb3::new(Point3::new(-20isize, 30isize, 5isize), Point3::new(10isize, -10isize, -5isize)) + .to_corners(); + assert!(corners.contains(&Point3::new(-20isize, 30isize, -5isize))); + assert!(corners.contains(&Point3::new(10isize, 30isize, 5isize))); + assert!(corners.contains(&Point3::new(10isize, -10isize, 5isize))); +} + #[test] fn test_bound() { let aabb = Aabb3::new(Point3::new(-5.0f32, 5.0, 0.0), Point3::new(5.0, 10.0, 1.0)); From fa3aacafcfdc0c8c20b48d02869d1aec378258ef Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 14 Mar 2015 01:31:29 +0300 Subject: [PATCH 08/10] Improved Plane, fixed Frustum computation from Matrix4, implemented Frustum contains() tests. --- src/frustum.rs | 47 ++++++++++++++++++++++++++++++++++++----------- src/plane.rs | 21 +++++++++++++++++---- src/projection.rs | 4 ++-- tests/frustum.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 tests/frustum.rs diff --git a/src/frustum.rs b/src/frustum.rs index 579bb1d..9cefe1a 100644 --- a/src/frustum.rs +++ b/src/frustum.rs @@ -16,14 +16,15 @@ //! View frustum for visibility determination use array::Array2; +use bound::*; use matrix::Matrix4; use num::BaseFloat; use plane::Plane; use point::Point3; use vector::{Vector, EuclideanVector}; -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)] -pub struct Frustum { +#[derive(Copy, Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] +pub struct Frustum { pub left: Plane, pub right: Plane, pub bottom: Plane, @@ -34,7 +35,7 @@ pub struct Frustum { impl Frustum { - /// Constructs a frustum + /// Construct a frustum. pub fn new(left: Plane, right: Plane, bottom: Plane, top: Plane, near: Plane, far: Plane) -> Frustum { @@ -48,14 +49,38 @@ Frustum { } } - /// Extracts frustum planes from a projection matrix - pub fn from_matrix4(mat: Matrix4) -> Frustum { - Frustum::new(Plane::from_vector4(mat.row(3).add_v(&mat.row(0)).normalize()), - Plane::from_vector4(mat.row(3).sub_v(&mat.row(0)).normalize()), - Plane::from_vector4(mat.row(3).add_v(&mat.row(1)).normalize()), - Plane::from_vector4(mat.row(3).sub_v(&mat.row(1)).normalize()), - Plane::from_vector4(mat.row(3).add_v(&mat.row(2)).normalize()), - Plane::from_vector4(mat.row(3).sub_v(&mat.row(2)).normalize())) + /// Extract frustum planes from a projection matrix. + pub fn from_matrix4(mat: Matrix4) -> Option> { + Some(Frustum::new( + match Plane::from_vector4_alt(mat.row(3).add_v(&mat.row(0))).normalize() + { Some(p) => p, None => return None }, + match Plane::from_vector4_alt(mat.row(3).sub_v(&mat.row(0))).normalize() + { Some(p) => p, None => return None }, + match Plane::from_vector4_alt(mat.row(3).add_v(&mat.row(1))).normalize() + { Some(p) => p, None => return None }, + match Plane::from_vector4_alt(mat.row(3).sub_v(&mat.row(1))).normalize() + { Some(p) => p, None => return None }, + match Plane::from_vector4_alt(mat.row(3).add_v(&mat.row(2))).normalize() + { Some(p) => p, None => return None }, + match Plane::from_vector4_alt(mat.row(3).sub_v(&mat.row(2))).normalize() + { Some(p) => p, None => return None } + )) + } + + /// Find the spatial relation of a bound inside this frustum. + pub fn contains>(&self, bound: &B) -> Relation { + [&self.left, &self.right, &self.top, &self.bottom, &self.near, &self.far] + .iter().fold(Relation::In, |cur, p| { + let r = bound.relate(p); + // if we see Cross, then the result is Cross + // if we see In, then we keep the old result + // otherwise, take the current result + if cur == Relation::Cross || r == Relation::In { + cur + }else { + r + } + }) } } diff --git a/src/plane.rs b/src/plane.rs index ada76f4..909045e 100644 --- a/src/plane.rs +++ b/src/plane.rs @@ -17,7 +17,7 @@ use std::fmt; use approx::ApproxEq; use intersect::Intersect; -use num::{BaseFloat, Zero, zero}; +use num::{BaseFloat, one, Zero, zero}; use point::{Point, Point3}; use ray::Ray3; use vector::{Vector3, Vector4}; @@ -65,9 +65,13 @@ impl Plane { /// Construct a plane from the components of a four-dimensional vector pub fn from_vector4(v: Vector4) -> Plane { - match v { - Vector4 { x, y, z, w } => Plane { n: Vector3::new(x, y, z), d: w }, - } + Plane { n: Vector3::new(v.x, v.y, v.z), d: v.w } + } + + /// Construct a plane from the components of a four-dimensional vector + /// Assuming alternative representation: `A*x + B*y + C*z + D = 0` + pub fn from_vector4_alt(v: Vector4) -> Plane { + Plane { n: Vector3::new(v.x, v.y, v.z), d: -v.w } } /// Constructs a plane that passes through the the three points `a`, `b` and `c` @@ -94,6 +98,15 @@ impl Plane { pub fn from_point_normal(p: Point3, n: Vector3) -> Plane { Plane { n: n, d: p.dot(&n) } } + + /// Normalize a plane. + pub fn normalize(&self) -> Option> { + if self.n.approx_eq(&zero()) { None } + else { + let denom = one::() / self.n.length(); + Some(Plane::new(self.n.mul_s(denom), self.d*denom)) + } + } } impl Intersect>> for (Plane, Ray3) { diff --git a/src/projection.rs b/src/projection.rs index 7dc9753..733197c 100644 --- a/src/projection.rs +++ b/src/projection.rs @@ -97,7 +97,7 @@ impl> PerspectiveFov { impl> Projection for PerspectiveFov { fn to_frustum(&self) -> Frustum { // TODO: Could this be faster? - Frustum::from_matrix4(self.to_matrix4()) + Frustum::from_matrix4(self.to_matrix4()).unwrap() } } @@ -153,7 +153,7 @@ pub struct Perspective { impl Projection for Perspective { fn to_frustum(&self) -> Frustum { // TODO: Could this be faster? - Frustum::from_matrix4(self.to_matrix4()) + Frustum::from_matrix4(self.to_matrix4()).unwrap() } } diff --git a/tests/frustum.rs b/tests/frustum.rs new file mode 100644 index 0000000..ba2a9e7 --- /dev/null +++ b/tests/frustum.rs @@ -0,0 +1,40 @@ +// Copyright 2015 The CGMath Developers. For a full listing of the authors, +// refer to the AUTHORS file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate cgmath; + +use cgmath::{PerspectiveFov, Point3, Projection, Relation, Sphere, rad}; + +#[test] +fn test_contains() { + let frustum = PerspectiveFov { + fovy: rad(1f32), + aspect: 1f32, + near: 1f32, + far: 10f32, + }.to_frustum(); + assert_eq!(frustum.contains(&Sphere { + center: Point3::new(0f32, 0f32, -5f32), + radius: 1f32, + }), Relation::In); + assert_eq!(frustum.contains(&Sphere { + center: Point3::new(0f32, 3f32, -5f32), + radius: 1f32, + }), Relation::Cross); + assert_eq!(frustum.contains(&Sphere { + center: Point3::new(0f32, 0f32, 5f32), + radius: 1f32, + }), Relation::Out); +} From 6691dce4209a6c769f5a9ac53aab9f1481430f47 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 14 Mar 2015 23:41:15 +0300 Subject: [PATCH 09/10] Added Bound::relate_clip_space with default implementation --- src/aabb.rs | 8 ++++---- src/bound.rs | 22 ++++++++++++++++------ src/frustum.rs | 2 +- src/point.rs | 15 +++++++++++++-- src/sphere.rs | 6 +++--- tests/aabb.rs | 8 ++++---- tests/point.rs | 6 +++--- tests/sphere.rs | 6 +++--- 8 files changed, 47 insertions(+), 26 deletions(-) diff --git a/src/aabb.rs b/src/aabb.rs index cf47d80..b0d1846 100644 --- a/src/aabb.rs +++ b/src/aabb.rs @@ -242,12 +242,12 @@ impl Intersect>> for (Ray2, Aabb2) { } } -impl Bound for Aabb3 { - fn relate(&self, plane: &Plane) -> Relation { +impl Bound for Aabb3 { + fn relate_plane(&self, plane: &Plane) -> Relation { let corners = self.to_corners(); - let first = corners[0].relate(plane); + let first = corners[0].relate_plane(plane); for p in corners[1..].iter() { - if p.relate(plane) != first { + if p.relate_plane(plane) != first { return Relation::Cross; } } diff --git a/src/bound.rs b/src/bound.rs index cc5ff56..4154ebf 100644 --- a/src/bound.rs +++ b/src/bound.rs @@ -15,22 +15,32 @@ //! Generic spatial bounds. +use matrix::Matrix4; +use num::BaseFloat; use plane::Plane; /// Spatial relation between two objects. #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)] #[repr(u8)] pub enum Relation { - /// Completely inside + /// Completely inside. In, - /// Crosses the boundary + /// Crosses the boundary. Cross, - /// Completely outside + /// Completely outside. Out, } /// Generic bound. -pub trait Bound { - /// Classify the spatial relation with a plane - fn relate(&self, &Plane) -> Relation; +pub trait Bound: Sized { + /// Classify the spatial relation with a plane. + fn relate_plane(&self, &Plane) -> Relation; + /// Classify the relation with a projection matrix. + fn relate_clip_space(&self, projection: &Matrix4) -> Relation { + use frustum::Frustum; + match Frustum::from_matrix4(*projection) { + Some(f) => f.contains(self), + None => Relation::Cross, + } + } } diff --git a/src/frustum.rs b/src/frustum.rs index 9cefe1a..2dc6c1d 100644 --- a/src/frustum.rs +++ b/src/frustum.rs @@ -71,7 +71,7 @@ Frustum { pub fn contains>(&self, bound: &B) -> Relation { [&self.left, &self.right, &self.top, &self.bottom, &self.near, &self.far] .iter().fold(Relation::In, |cur, p| { - let r = bound.relate(p); + let r = bound.relate_plane(p); // if we see Cross, then the result is Cross // if we see In, then we keep the old result // otherwise, take the current result diff --git a/src/point.rs b/src/point.rs index 6e2bf0d..ab854dd 100644 --- a/src/point.rs +++ b/src/point.rs @@ -24,6 +24,7 @@ use std::ops::*; use approx::ApproxEq; use array::{Array1, FixedArray}; use bound::*; +use matrix::{Matrix, Matrix4}; use num::{BaseNum, BaseFloat, one, zero}; use plane::Plane; use vector::*; @@ -448,8 +449,8 @@ impl fmt::Debug for Point3 { } } -impl Bound for Point3 { - fn relate(&self, plane: &Plane) -> Relation { +impl Bound for Point3 { + fn relate_plane(&self, plane: &Plane) -> Relation { let dist = self.dot(&plane.n); if dist > plane.d { Relation::In @@ -459,4 +460,14 @@ impl Bound for Point3 { Relation::Cross } } + + fn relate_clip_space(&self, projection: &Matrix4) -> Relation { + use std::cmp::Ordering::*; + let p = projection.mul_v(&self.to_homogeneous()); + match (p.x.abs().partial_cmp(&p.w), p.y.abs().partial_cmp(&p.w), p.z.abs().partial_cmp(&p.w)) { + (Some(Less), Some(Less), Some(Less)) => Relation::In, + (Some(Greater), _, _) | (_, Some(Greater), _) | (_, _, Some(Greater)) => Relation::Out, + _ => Relation::Cross, + } + } } diff --git a/src/sphere.rs b/src/sphere.rs index 519be4f..61565ac 100644 --- a/src/sphere.rs +++ b/src/sphere.rs @@ -17,7 +17,7 @@ use bound::*; use intersect::Intersect; -use num::{BaseNum, BaseFloat, zero}; +use num::{BaseFloat, zero}; use point::{Point, Point3}; use plane::Plane; use ray::Ray3; @@ -45,8 +45,8 @@ impl Intersect>> for (Sphere, Ray3) { } } -impl Bound for Sphere { - fn relate(&self, plane: &Plane) -> Relation { +impl Bound for Sphere { + fn relate_plane(&self, plane: &Plane) -> Relation { let dist = self.center.dot(&plane.n) - plane.d; if dist > self.radius { Relation::In diff --git a/tests/aabb.rs b/tests/aabb.rs index b21d238..41cf0db 100644 --- a/tests/aabb.rs +++ b/tests/aabb.rs @@ -8,7 +8,7 @@ // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASisize, +// distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. @@ -99,7 +99,7 @@ fn test_bound() { let plane1 = Plane::from_point_normal(Point3::new(0f32, 0.0, 0.0), Vector3::new(0f32, 0.0, 1.0)); let plane2 = Plane::from_point_normal(Point3::new(-5.0f32, 4.0, 0.0), Vector3::new(0f32, 1.0, 0.0)); let plane3 = Plane::from_point_normal(Point3::new(6.0f32, 0.0, 0.0), Vector3::new(1f32, 0.0, 0.0)); - assert_eq!(aabb.relate(&plane1), Relation::Cross); - assert_eq!(aabb.relate(&plane2), Relation::In); - assert_eq!(aabb.relate(&plane3), Relation::Out); + assert_eq!(aabb.relate_plane(&plane1), Relation::Cross); + assert_eq!(aabb.relate_plane(&plane2), Relation::In); + assert_eq!(aabb.relate_plane(&plane3), Relation::Out); } diff --git a/tests/point.rs b/tests/point.rs index f67a5c0..5f65f3c 100644 --- a/tests/point.rs +++ b/tests/point.rs @@ -32,7 +32,7 @@ fn test_bound() { let normal = Vector3::new(0f32, -0.8, -0.36); let plane = Plane::from_point_normal(point, normal); - assert_eq!(point.relate(&plane), Relation::Cross); - assert_eq!(point.add_v(&normal).relate(&plane), Relation::In); - assert_eq!(point.add_v(&normal.mul_s(-1.0)).relate(&plane), Relation::Out); + assert_eq!(point.relate_plane(&plane), Relation::Cross); + assert_eq!(point.add_v(&normal).relate_plane(&plane), Relation::In); + assert_eq!(point.add_v(&normal.mul_s(-1.0)).relate_plane(&plane), Relation::Out); } diff --git a/tests/sphere.rs b/tests/sphere.rs index 4ab9af0..a3f61eb 100644 --- a/tests/sphere.rs +++ b/tests/sphere.rs @@ -38,13 +38,13 @@ fn test_bound() { let sphere = Sphere { center: point, radius: 1.0 }; let normal = vec3(0f32, 0.0, 1.0); - assert_eq!(sphere.relate( + assert_eq!(sphere.relate_plane( &Plane::from_point_normal(point, normal) ), Relation::Cross); - assert_eq!(sphere.relate( + assert_eq!(sphere.relate_plane( &Plane::from_point_normal(point.add_v(&normal.mul_s(-3.0)), normal), ), Relation::In); - assert_eq!(sphere.relate( + assert_eq!(sphere.relate_plane( &Plane::from_point_normal(point.add_v(&normal.mul_s(3.0)), normal), ), Relation::Out); } From e0d85820e3bb16918f988780f34c41773c63d843 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 15 Mar 2015 23:21:07 +0300 Subject: [PATCH 10/10] Fixed the frustum/bound relation. --- src/frustum.rs | 13 +++++-------- src/lib.rs | 1 - 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/frustum.rs b/src/frustum.rs index 2dc6c1d..e9e4468 100644 --- a/src/frustum.rs +++ b/src/frustum.rs @@ -71,15 +71,12 @@ Frustum { pub fn contains>(&self, bound: &B) -> Relation { [&self.left, &self.right, &self.top, &self.bottom, &self.near, &self.far] .iter().fold(Relation::In, |cur, p| { + use std::cmp::max; let r = bound.relate_plane(p); - // if we see Cross, then the result is Cross - // if we see In, then we keep the old result - // otherwise, take the current result - if cur == Relation::Cross || r == Relation::In { - cur - }else { - r - } + // If any of the planes are `Out`, the bound is outside. + // Otherwise, if any are `Cross`, the bound is crossing. + // Otherwise, the bound is fully inside. + max(cur, r) }) } } diff --git a/src/lib.rs b/src/lib.rs index c0c7df8..f1eb8ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,6 @@ #![crate_type = "rlib"] #![crate_type = "dylib"] #![feature(old_impl_check, plugin, core, std_misc, custom_derive)] -#![plugin(rand_macros)] //! Computer graphics-centric math. //!