2013-06-16 07:34:09 +00:00
|
|
|
// Copyright 2013 The Lmath 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.
|
|
|
|
|
2013-07-14 01:44:50 +00:00
|
|
|
//! Three-dimensional plane type
|
2013-07-13 13:37:38 +00:00
|
|
|
|
2013-07-14 01:44:50 +00:00
|
|
|
use math::{Vec3, Vec4, Mat3};
|
|
|
|
use math::{Point, Point3};
|
|
|
|
use math::Ray3;
|
2013-06-16 07:34:09 +00:00
|
|
|
|
|
|
|
/// A plane formed from the equation: `Ax + Bx + Cx + D = 0`
|
|
|
|
///
|
|
|
|
/// # Fields
|
|
|
|
///
|
2013-07-11 23:18:05 +00:00
|
|
|
/// - `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
|
2013-06-29 06:38:55 +00:00
|
|
|
#[deriving(Clone, Eq)]
|
2013-07-09 06:43:16 +00:00
|
|
|
pub struct Plane3<T> {
|
2013-07-11 23:18:05 +00:00
|
|
|
normal: Vec3<T>,
|
|
|
|
distance: T,
|
2013-06-16 07:34:09 +00:00
|
|
|
}
|
|
|
|
|
2013-07-12 01:22:14 +00:00
|
|
|
impl_approx!(Plane3 { normal, distance })
|
|
|
|
|
2013-07-09 06:43:16 +00:00
|
|
|
impl<T:Clone + Float> Plane3<T> {
|
2013-06-16 07:34:09 +00:00
|
|
|
/// # 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
|
2013-07-09 06:43:16 +00:00
|
|
|
pub fn from_abcd(a: T, b: T, c: T, d: T) -> Plane3<T> {
|
|
|
|
Plane3 {
|
2013-07-11 23:18:05 +00:00
|
|
|
normal: Vec3::new(a, b, c),
|
|
|
|
distance: d,
|
2013-06-16 07:34:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-11 23:18:05 +00:00
|
|
|
/// Construct a plane from a normal vector and a scalar distance
|
|
|
|
pub fn from_nd(normal: Vec3<T>, distance: T) -> Plane3<T> {
|
|
|
|
Plane3 { normal: normal, distance: distance }
|
2013-06-16 07:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Construct a plane from the components of a four-dimensional vector
|
2013-07-09 06:43:16 +00:00
|
|
|
pub fn from_vec4(vec: Vec4<T>) -> Plane3<T> {
|
|
|
|
Plane3::from_abcd(vec.x.clone(), vec.y.clone(), vec.z.clone(), vec.w.clone())
|
2013-06-16 07:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Compute the distance from the plane to the point
|
|
|
|
pub fn distance(&self, pos: &Point3<T>) -> T {
|
2013-07-12 03:41:03 +00:00
|
|
|
self.normal.dot(pos.as_vec3()) + self.distance
|
2013-06-16 07:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Computes the point at which `ray` intersects the plane
|
|
|
|
pub fn intersection_r(&self, _ray: &Ray3<T>) -> Point3<T> {
|
|
|
|
fail!(~"not yet implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns `true` if the ray intersects the plane
|
|
|
|
pub fn intersects(&self, _ray: &Ray3<T>) -> bool {
|
|
|
|
fail!(~"not yet implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns `true` if `pos` is located behind the plane - otherwise it returns `false`
|
|
|
|
pub fn contains(&self, pos: &Point3<T>) -> bool {
|
|
|
|
self.distance(pos) < zero!(T)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-09 06:43:16 +00:00
|
|
|
impl<T:Clone + Float> Plane3<T> {
|
2013-06-16 07:34:09 +00:00
|
|
|
/// Constructs a plane that passes through the the three points `a`, `b` and `c`
|
|
|
|
pub fn from_3p(a: Point3<T>,
|
|
|
|
b: Point3<T>,
|
2013-07-09 06:43:16 +00:00
|
|
|
c: Point3<T>) -> Option<Plane3<T>> {
|
2013-06-16 07:34:09 +00:00
|
|
|
// create two vectors that run parallel to the plane
|
2013-07-11 04:49:14 +00:00
|
|
|
let v0 = (b - a);
|
|
|
|
let v1 = (c - a);
|
2013-06-16 07:34:09 +00:00
|
|
|
// find the vector that is perpendicular to v1 and v2
|
2013-07-11 23:18:05 +00:00
|
|
|
let mut normal = v0.cross(&v1);
|
2013-06-16 07:34:09 +00:00
|
|
|
|
2013-07-11 23:18:05 +00:00
|
|
|
if normal.approx_eq(&Vec3::zero()) {
|
2013-06-16 07:34:09 +00:00
|
|
|
None
|
|
|
|
} else {
|
|
|
|
// compute the normal and the distance to the plane
|
2013-07-11 23:18:05 +00:00
|
|
|
normal.normalize_self();
|
2013-07-12 03:41:03 +00:00
|
|
|
let distance = -a.as_vec3().dot(&normal);
|
2013-06-16 07:34:09 +00:00
|
|
|
|
2013-07-11 23:18:05 +00:00
|
|
|
Some(Plane3::from_nd(normal, distance))
|
2013-06-16 07:34:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Computes the ray created from the two-plane intersection of `self` and `other`
|
|
|
|
///
|
|
|
|
/// # Return value
|
|
|
|
///
|
|
|
|
/// - `Some(r)`: The ray `r` where the planes intersect.
|
|
|
|
/// - `None`: No valid intersection was found. The planes are probably parallel.
|
2013-07-09 06:43:16 +00:00
|
|
|
pub fn intersection_2pl(&self, other: &Plane3<T>) -> Option<Ray3<T>> {
|
2013-07-11 23:18:05 +00:00
|
|
|
let dir = self.normal.cross(&other.normal);
|
2013-06-16 07:34:09 +00:00
|
|
|
|
2013-07-11 23:18:05 +00:00
|
|
|
if dir.approx_eq(&Vec3::zero::<T>()) {
|
2013-06-16 07:34:09 +00:00
|
|
|
None // the planes are parallel
|
|
|
|
} else {
|
|
|
|
// The end-point of the ray is at the three-plane intersection between
|
|
|
|
// `self`, `other`, and a tempory plane positioned at the origin
|
2013-07-11 23:18:05 +00:00
|
|
|
do Plane3::from_nd(dir.clone(), zero!(T)).intersection_3pl(self, other).map |origin| {
|
2013-06-16 07:34:09 +00:00
|
|
|
Ray3 {
|
2013-07-11 23:18:05 +00:00
|
|
|
origin: origin.clone(),
|
|
|
|
direction: dir.clone(),
|
2013-06-16 07:34:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Computes the three-plane intersection between `self`, `other_a` and `other_b`.
|
|
|
|
///
|
|
|
|
/// # Return value
|
|
|
|
///
|
|
|
|
/// - `Some(p)`: The position vector `p` where the planes intersect.
|
|
|
|
/// - `None`: No valid intersection was found. The normals of the three
|
|
|
|
/// planes are probably coplanar.
|
2013-07-09 06:43:16 +00:00
|
|
|
pub fn intersection_3pl(&self, other_a: &Plane3<T>, other_b: &Plane3<T>) -> Option<Point3<T>> {
|
2013-07-11 23:18:05 +00:00
|
|
|
let mx = Mat3::new(self.normal.x.clone(), other_a.normal.x.clone(), other_b.normal.x.clone(),
|
|
|
|
self.normal.y.clone(), other_a.normal.y.clone(), other_b.normal.y.clone(),
|
|
|
|
self.normal.z.clone(), other_a.normal.z.clone(), other_b.normal.z.clone());
|
2013-06-16 07:34:09 +00:00
|
|
|
do mx.inverse().map |m| {
|
2013-07-11 23:18:05 +00:00
|
|
|
Point3::origin() + m.mul_v(&Vec3::new(self.distance.clone(),
|
|
|
|
other_a.distance.clone(),
|
|
|
|
other_b.distance.clone()))
|
2013-06-16 07:34:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-09 06:43:16 +00:00
|
|
|
impl<T> ToStr for Plane3<T> {
|
2013-06-16 07:34:09 +00:00
|
|
|
pub fn to_str(&self) -> ~str {
|
2013-07-11 23:18:05 +00:00
|
|
|
fmt!("%?x + %?y + %?z + %? = 0",
|
|
|
|
self.normal.x,
|
|
|
|
self.normal.y,
|
|
|
|
self.normal.z,
|
|
|
|
self.distance)
|
2013-06-16 07:34:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2013-07-14 01:44:50 +00:00
|
|
|
use math::plane::*;
|
|
|
|
use math::point::*;
|
2013-06-16 07:34:09 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_from_3p() {
|
2013-07-09 06:43:16 +00:00
|
|
|
assert_eq!(Plane3::from_3p(Point3::new(5f, 0f, 5f),
|
|
|
|
Point3::new(5f, 5f, 5f),
|
|
|
|
Point3::new(5f, 0f, -1f)), Some(Plane3::from_abcd(-1f, 0f, 0f, 5f)));
|
2013-06-16 07:34:09 +00:00
|
|
|
|
2013-07-09 06:43:16 +00:00
|
|
|
assert_eq!(Plane3::from_3p(Point3::new(0f, 5f, -5f),
|
|
|
|
Point3::new(0f, 5f, 0f),
|
|
|
|
Point3::new(0f, 5f, 5f)), None); // The points are parallel
|
2013-06-16 07:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_plane_intersection_3pl() {
|
2013-07-09 06:43:16 +00:00
|
|
|
let p0 = Plane3::from_abcd(1.0, 0.0, 0.0, 1.0);
|
|
|
|
let p1 = Plane3::from_abcd(0.0, -1.0, 0.0, 2.0);
|
|
|
|
let p2 = Plane3::from_abcd(0.0, 0.0, 1.0, 1.0);
|
2013-06-16 07:34:09 +00:00
|
|
|
|
2013-07-08 08:17:36 +00:00
|
|
|
assert_eq!(p0.intersection_3pl(&p1, &p2), Some(Point3::new(1.0, -2.0, 1.0)));
|
2013-06-16 07:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_to_str() {
|
2013-07-09 06:43:16 +00:00
|
|
|
assert_eq!(Plane3::from_abcd(1.0, 2.0, 3.0, 4.0).to_str(), ~"1x + 2y + 3z + 4 = 0");
|
2013-06-16 07:34:09 +00:00
|
|
|
}
|
|
|
|
}
|