From 8519e0fb8ec52d85ba0cd9b0f66a8b44d627cbca Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Wed, 18 Sep 2013 11:36:41 +1000 Subject: [PATCH] Add Plane::from_points constructor and impl ApproxEq for Plane --- src/cgmath/plane.rs | 96 +++++++++++++++++++++++++++++++++++++-------- src/cgmath/point.rs | 6 +++ 2 files changed, 85 insertions(+), 17 deletions(-) diff --git a/src/cgmath/plane.rs b/src/cgmath/plane.rs index 1f1cde5..4d3d3b1 100644 --- a/src/cgmath/plane.rs +++ b/src/cgmath/plane.rs @@ -14,25 +14,67 @@ // limitations under the License. use intersect::Intersect; -use point::Point3; +use point::{Point, Point3}; use ray::Ray3; -use vector::Vec3; +use vector::{Vector, EuclideanVector, Vec3}; use std::fmt; -/// A 3-dimendional plane formed from the equation: `Ax + Bx + Cx + D = 0` +/// A 3-dimendional plane formed from the equation: `a*x + b*y + c*z - d = 0`. /// /// # Fields /// -/// - `normal`: the normal of the plane where: -/// - `normal.x`: corresponds to `A` in the plane equation -/// - `normal.y`: corresponds to `B` in the plane equation -/// - `normal.z`: corresponds to `C` in the plane equation -/// - `distance`: the distance value, corresponding to `D` in the plane equation +/// - `n`: a unit vector representing the normal of the plane where: +/// - `n.x`: corresponds to `A` in the plane equation +/// - `n.y`: corresponds to `B` in the plane equation +/// - `n.z`: corresponds to `C` in the plane equation +/// - `d`: the distance value, corresponding to `D` in the plane equation +/// +/// # Notes +/// +/// The `a*x + b*y + c*z - d = 0` form is preferred over the other common +/// alternative, `a*x + b*y + c*z + d = 0`, because it tends to avoid +/// superfluous negations (see _Real Time Collision Detection_, p. 55). #[deriving(Clone, Eq)] pub struct Plane { - normal: Vec3, - distance: S, + n: Vec3, + d: S, +} + +impl Plane { + /// Construct a plane from a normal vector and a scalar distance + pub fn new(n: Vec3, d: S) -> Plane { + Plane { n: n, d: d } + } + + /// # Arguments + /// + /// - `a`: the `x` component of the normal + /// - `b`: the `y` component of the normal + /// - `c`: the `z` component of the normal + /// - `d`: the plane's distance value + pub fn from_abcd(a: S, b: S, c: S, d: S) -> Plane { + Plane { n: Vec3::new(a, b, c), d: d } + } + + /// Constructs a plane that passes through the the three points `a`, `b` and `c` + pub fn from_points(a: Point3, b: Point3, c: Point3) -> Option> { + // create two vectors that run parallel to the plane + let v0 = b.sub_p(&a); + let v1 = c.sub_p(&a); + + // find the normal vector that is perpendicular to v1 and v2 + let mut n = v0.cross(&v1); + + if n.approx_eq(&Vec3::zero()) { None } + else { + // compute the normal and the distance to the plane + n.normalize_self(); + let d = -a.dot(&n); + + Some(Plane::new(n, d)) + } + } } impl Intersect>> for (Plane, Ray3) { @@ -53,12 +95,32 @@ impl Intersect>> for (Plane, Plane, Plane) { } } -impl ToStr for Plane { - fn to_str(&self) -> ~str { - format!("{:f}x + {:f}y + {:f}z + {:f} = 0", - self.normal.x, - self.normal.y, - self.normal.z, - self.distance) +impl ApproxEq for Plane { + #[inline] + fn approx_epsilon() -> S { + // TODO: fix this after static methods are fixed in rustc + fail!(~"Doesn't work!"); + } + + #[inline] + fn approx_eq(&self, other: &Plane) -> bool { + self.n.approx_eq(&other.n) && + self.d.approx_eq(&other.d) + } + + #[inline] + fn approx_eq_eps(&self, other: &Plane, approx_epsilon: &S) -> bool { + self.n.approx_eq_eps(&other.n, approx_epsilon) && + self.d.approx_eq_eps(&other.d, approx_epsilon) + } +} + +impl ToStr for Plane { + fn to_str(&self) -> ~str { + format!("{:f}x + {:f}y + {:f}z - {:f} = 0", + self.n.x, + self.n.y, + self.n.z, + self.d) } } diff --git a/src/cgmath/point.rs b/src/cgmath/point.rs index ec68b56..854201a 100644 --- a/src/cgmath/point.rs +++ b/src/cgmath/point.rs @@ -74,6 +74,12 @@ pub trait Point #[inline] fn rem_self_s(&mut self, s: S) { self.each_mut(|_, x| *x = x.rem(&s)) } #[inline] fn add_self_v(&mut self, other: &V) { self.each_mut(|i, x| *x = x.add(other.i(i))) } + + /// This is a weird one, but its useful for plane calculations + #[inline] + fn dot(&self, v: &V) -> S { + build::(|i| self.i(i).mul(v.i(i))).comp_add() + } } array!(impl Point2 -> [S, ..2] _2)