From 9d99347bbfa768d6a8cb45e7174dcfa738f9bfc9 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 16 Jun 2013 17:34:09 +1000 Subject: [PATCH] Add types from cgmath --- src/frustum.rs | 120 ++++++++++++++++++++++++ src/lmath.rs | 6 +- src/plane.rs | 187 +++++++++++++++++++++++++++++++++++++ src/point.rs | 105 +++++++++++++++++++++ src/projection.rs | 229 ++++++++++++++++++++++++++++++++++++++++++++++ src/ray.rs | 47 ++++++++++ 6 files changed, 693 insertions(+), 1 deletion(-) create mode 100644 src/frustum.rs create mode 100644 src/plane.rs create mode 100644 src/point.rs create mode 100644 src/ray.rs diff --git a/src/frustum.rs b/src/frustum.rs new file mode 100644 index 0000000..77f8b28 --- /dev/null +++ b/src/frustum.rs @@ -0,0 +1,120 @@ +// 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. + +use mat::Mat4; +use plane::Plane; +use point::Point3; + +mod num_macros; + +#[deriving(Eq)] +pub struct Frustum { + left: Plane, + right: Plane, + bottom: Plane, + top: Plane, + near: Plane, + far: Plane, +} + +#[deriving(Eq)] +pub struct FrustumPoints { + near_top_left: Point3, + near_top_right: Point3, + near_bottom_left: Point3, + near_bottom_right: Point3, + far_top_left: Point3, + far_top_right: Point3, + far_bottom_left: Point3, + far_bottom_right: Point3, +} + +impl Frustum { + /// Constructs a frustum + pub fn from_planes(left: Plane, right: Plane, + bottom: Plane, top: Plane, + near: Plane, far: Plane) -> Frustum { + Frustum { + left: left, + right: right, + bottom: bottom, + top: top, + near: near, + far: far, + } + } + + /// Extracts frustum planes from a projection matrix + pub fn from_matrix(mat: Mat4) -> Frustum { + Frustum { + left: Plane::from_vec4(mat.row(3).add_v(&mat.row(0)).normalize()), + right: Plane::from_vec4(mat.row(3).sub_v(&mat.row(0)).normalize()), + bottom: Plane::from_vec4(mat.row(3).add_v(&mat.row(1)).normalize()), + top: Plane::from_vec4(mat.row(3).sub_v(&mat.row(1)).normalize()), + near: Plane::from_vec4(mat.row(3).add_v(&mat.row(2)).normalize()), + far: Plane::from_vec4(mat.row(3).sub_v(&mat.row(2)).normalize()), + } + } + + pub fn base() -> Frustum { + Frustum { + left: Plane::from_abcd( one!(T), zero!(T), zero!(T), one!(T)), + right: Plane::from_abcd(-one!(T), zero!(T), zero!(T), one!(T)), + bottom: Plane::from_abcd( zero!(T), one!(T), zero!(T), one!(T)), + top: Plane::from_abcd( zero!(T), -one!(T), zero!(T), one!(T)), + near: Plane::from_abcd( zero!(T), zero!(T), -one!(T), one!(T)), + far: Plane::from_abcd( zero!(T), zero!(T), one!(T), one!(T)), + } + } +} + +impl> Frustum { + /// Computes where the frustum planes intersect to form corners and returns + /// a struct containing the eight resulting position vectors. + pub fn to_points(&self) -> FrustumPoints { + FrustumPoints { + near_top_left: self.near.intersection_3pl(&self.top, &self.left).unwrap(), + near_top_right: self.near.intersection_3pl(&self.top, &self.right).unwrap(), + near_bottom_left: self.near.intersection_3pl(&self.bottom, &self.left).unwrap(), + near_bottom_right: self.near.intersection_3pl(&self.bottom, &self.right).unwrap(), + far_top_left: self.far.intersection_3pl(&self.top, &self.left).unwrap(), + far_top_right: self.far.intersection_3pl(&self.top, &self.right).unwrap(), + far_bottom_left: self.far.intersection_3pl(&self.bottom, &self.left).unwrap(), + far_bottom_right: self.far.intersection_3pl(&self.bottom, &self.right).unwrap(), + } + } +} + +impl> ApproxEq for Frustum { + #[inline] + pub fn approx_epsilon() -> T { + ApproxEq::approx_epsilon::() + } + + #[inline] + pub fn approx_eq(&self, other: &Frustum) -> bool { + self.approx_eq_eps(other, &ApproxEq::approx_epsilon::()) + } + + #[inline] + pub fn approx_eq_eps(&self, other: &Frustum, epsilon: &T) -> bool { + self.left.approx_eq_eps(&other.left, epsilon) && + self.right.approx_eq_eps(&other.right, epsilon) && + self.bottom.approx_eq_eps(&other.bottom, epsilon) && + self.top.approx_eq_eps(&other.top, epsilon) && + self.near.approx_eq_eps(&other.near, epsilon) && + self.far.approx_eq_eps(&other.far, epsilon) + } +} \ No newline at end of file diff --git a/src/lmath.rs b/src/lmath.rs index 6667d36..81b78d8 100644 --- a/src/lmath.rs +++ b/src/lmath.rs @@ -19,7 +19,7 @@ author = "Brendan Zabarauskas", url = "https://github.com/bjz/lmath-rs")]; -#[comment = "A generic linear algebra library."]; +#[comment = "A mathematics library for computer graphics."]; #[license = "ASL2"]; #[crate_type = "lib"]; @@ -29,4 +29,8 @@ pub mod mat; pub mod quat; pub mod vec; +pub mod frustum; +pub mod plane; +pub mod point; pub mod projection; +pub mod ray; diff --git a/src/plane.rs b/src/plane.rs new file mode 100644 index 0000000..c820db3 --- /dev/null +++ b/src/plane.rs @@ -0,0 +1,187 @@ +// 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. + +pub use dim::Dimensional; +use mat::Mat3; +use point::Point3; +use ray::Ray3; +use vec::{Vec3, Vec4}; + +mod num_macros; +mod dim_macros; + +/// A plane formed from the equation: `Ax + Bx + Cx + D = 0` +/// +/// # Fields +/// +/// - `n`: 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 +#[deriving(Eq)] +pub struct Plane { + norm: Vec3, + dist: T, +} + +impl_dimensional!(Plane, T, 4) +impl_dimensional_fns!(Plane, T, 4) +impl_swap!(Plane) +impl_approx!(Plane) + +impl Plane { + /// # 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: T, b: T, c: T, d: T) -> Plane { + Plane { + norm: Vec3::new(a, b, c), + dist: d, + } + } + + /// Construct a plane from a normal vector `n` and a distance `d` + pub fn from_nd(norm: Vec3, dist: T) -> Plane { + Plane { norm: norm, dist: dist } + } + + /// Construct a plane from the components of a four-dimensional vector + pub fn from_vec4(vec: Vec4) -> Plane { + Plane::from_abcd(vec.x, vec.y, vec.z, vec.w) + } + + /// Compute the distance from the plane to the point + pub fn distance(&self, pos: &Point3) -> T { + self.norm.dot(&**pos) + self.dist + } + + /// Computes the point at which `ray` intersects the plane + pub fn intersection_r(&self, _ray: &Ray3) -> Point3 { + fail!(~"not yet implemented") + } + + /// Returns `true` if the ray intersects the plane + pub fn intersects(&self, _ray: &Ray3) -> bool { + fail!(~"not yet implemented") + } + + /// Returns `true` if `pos` is located behind the plane - otherwise it returns `false` + pub fn contains(&self, pos: &Point3) -> bool { + self.distance(pos) < zero!(T) + } +} + +impl> Plane { + /// Constructs a plane that passes through the the three points `a`, `b` and `c` + pub fn from_3p(a: Point3, + b: Point3, + c: Point3) -> Option> { + // create two vectors that run parallel to the plane + let v0 = (*b).sub_v(&*a); + let v1 = (*c).sub_v(&*a); + // find the vector that is perpendicular to v1 and v2 + let mut norm = v0.cross(&v1); + + if norm.approx_eq(&Vec3::zero()) { + None + } else { + // compute the normal and the distance to the plane + norm.normalize_self(); + let dist = -a.dot(&norm); + + Some(Plane::from_nd(norm, dist)) + } + } + + /// 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. + pub fn intersection_2pl(&self, other: &Plane) -> Option> { + let ray_dir = self.norm.cross(&other.norm); + + if ray_dir.approx_eq(&Vec3::zero::()) { + 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 + do Plane::from_nd(ray_dir, zero!(T)).intersection_3pl(self, other).map |ray_pos| { + Ray3 { + pos: *ray_pos, + dir: ray_dir, + } + } + } + } + + /// 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. + pub fn intersection_3pl(&self, other_a: &Plane, other_b: &Plane) -> Option> { + let mx = Mat3::new(self.norm.x, other_a.norm.x, other_b.norm.x, + self.norm.y, other_a.norm.y, other_b.norm.y, + self.norm.z, other_a.norm.z, other_b.norm.z); + do mx.inverse().map |m| { + Point3(m.mul_v(&Vec3::new(self.dist, other_a.dist, other_b.dist))) + } + } +} + +impl ToStr for Plane { + pub fn to_str(&self) -> ~str { + fmt!("%?x + %?y + %?z + %? = 0", self.norm.x, self.norm.y, self.norm.z, self.dist) + } +} + +#[cfg(test)] +mod tests { + use plane::*; + use point::*; + + #[test] + fn test_from_3p() { + assert_eq!(Plane::from_3p(Point3::new(5f, 0f, 5f), + Point3::new(5f, 5f, 5f), + Point3::new(5f, 0f, -1f)), Some(Plane::from_abcd(-1f, 0f, 0f, 5f))); + + assert_eq!(Plane::from_3p(Point3::new(0f, 5f, -5f), + Point3::new(0f, 5f, 0f), + Point3::new(0f, 5f, 5f)), None); // The points are parallel + } + + #[test] + fn test_plane_intersection_3pl() { + let p0 = Plane::from_abcd(1.0, 0.0, 0.0, 1.0); + let p1 = Plane::from_abcd(0.0, -1.0, 0.0, 2.0); + let p2 = Plane::from_abcd(0.0, 0.0, 1.0, 1.0); + + assert_eq!(p0.intersection_3pl(&p1, &p2).unwrap(), Point3::new(1.0, -2.0, 1.0)); + } + + #[test] + fn test_to_str() { + assert_eq!(Plane::from_abcd(1.0, 2.0, 3.0, 4.0).to_str(), ~"1x + 2y + 3z + 4 = 0"); + } +} diff --git a/src/point.rs b/src/point.rs new file mode 100644 index 0000000..f8a3a43 --- /dev/null +++ b/src/point.rs @@ -0,0 +1,105 @@ +// 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. + +use dim::Dimensional; +use vec::{Vec2, Vec3}; + +mod dim_macros; + +/// A geometric point +pub trait Point: Eq + ApproxEq + ToStr { + pub fn translate(&self, offset: &V) -> Self; + pub fn distance(&self, other: &Self) -> T; +} + +/// A two-dimensional point +#[deriving(Eq)] +pub struct Point2(Vec2); + +impl_dimensional!(Point2, T, 2) +impl_dimensional_fns!(Point2, T, 2) +impl_approx!(Point2) + +impl Point2 { + pub fn new(x: T, y: T) -> Point2 { + Point2(Vec2::new(x, y)) + } +} + +impl Point> for Point2 { + pub fn translate(&self, offset: &Vec2) -> Point2 { + Point2(self.add_v(offset)) + } + + pub fn distance(&self, other: &Point2) -> T { + (**self).distance(&**other) + } +} + +impl ToStr for Point2 { + pub fn to_str(&self) -> ~str { + fmt!("[%?, %?]", self.x, self.y) + } +} + +/// A three-dimensional point +#[deriving(Eq)] +pub struct Point3(Vec3); + +impl_dimensional!(Point3, T, 3) +impl_dimensional_fns!(Point3, T, 3) +impl_approx!(Point3) + +impl Point3 { + pub fn new(x: T, y: T, z: T) -> Point3 { + Point3(Vec3::new(x, y, z)) + } +} + +impl Point> for Point3 { + pub fn translate(&self, offset: &Vec3) -> Point3 { + Point3(self.add_v(offset)) + } + + pub fn distance(&self, other: &Point3) -> T { + (**self).distance(&**other) + } +} + +impl ToStr for Point3 { + pub fn to_str(&self) -> ~str { + fmt!("[%?, %?, %?]", self.x, self.y, self.z) + } +} + +#[cfg(test)] +mod test_point2 { + use point::*; + + #[test] + fn test_to_str() { + assert_eq!(Point2::new(1, 2).to_str(), ~"[1, 2]"); + } +} + +#[cfg(test)] +mod test_point3 { + use point::*; + + #[test] + fn test_to_str() { + assert_eq!(Point3::new(1, 2, 3).to_str(), ~"[1, 2, 3]"); + } +} diff --git a/src/projection.rs b/src/projection.rs index 4c68973..7aa3f0f 100644 --- a/src/projection.rs +++ b/src/projection.rs @@ -13,7 +13,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use frustum::Frustum; use mat::Mat4; +use plane::Plane; mod num_macros; @@ -97,3 +99,230 @@ pub fn ortho(left: T, right: T, bottom: T, top: T, near: T, far: c2r0, c2r1, c2r2, c2r3, c3r0, c3r1, c3r2, c3r3) } + +pub trait Projection { + pub fn if_valid(&self, f: &fn() -> U) -> Result; + pub fn to_mat4(&self) -> Result, ~str>; + pub fn to_frustum(&self) -> Result, ~str>; +} + +/// A symmetrical perspective projection based on a field-of-view angle +#[deriving(Eq)] +pub struct PerspectiveFOV { + fovy: T, //radians + aspect: T, + near: T, + far: T, +} + +impl PerspectiveFOV { + pub fn to_perspective(&self) -> Result, ~str> { + do self.if_valid { + let angle = self.fovy / two!(T); + let ymax = self.near * angle.tan(); + let xmax = ymax * self.aspect; + + Perspective { + left: -xmax, + right: xmax, + bottom: -ymax, + top: ymax, + near: self.near, + far: self.far, + } + } + } +} + +impl Projection for PerspectiveFOV { + pub fn if_valid(&self, f: &fn() -> U) -> Result { + let frac_pi_2: T = Real::frac_pi_2(); + cond! ( + (self.fovy < zero!(T)) { Err(fmt!("The vertical field of view cannot be below zero, found: %?", self.fovy)) } + (self.fovy > frac_pi_2) { Err(fmt!("The vertical field of view cannot be greater than a half turn, found: %?", self.fovy)) } + (self.aspect < zero!(T)) { Err(fmt!("The aspect ratio cannot be below zero, found: %?", self.aspect)) } + (self.near < zero!(T)) { Err(fmt!("The near plane distance cannot be below zero, found: %?", self.near)) } + (self.far < zero!(T)) { Err(fmt!("The far plane distance cannot be below zero, found: %?", self.far)) } + (self.far < self.near) { Err(fmt!("The far plane cannot be closer than the near plane, found: far: %?, near: %?", self.far, self.near)) } + _ { Ok(f()) } + ) + } + + pub fn to_mat4(&self) -> Result, ~str> { + do self.to_perspective().chain |proj| { proj.to_mat4() } + } + + pub fn to_frustum(&self) -> Result, ~str> { + do self.to_perspective().chain |proj| { proj.to_frustum() } + } +} + +/// A perspective projection with arbitrary left/right/bottom/top distances +#[deriving(Eq)] +pub struct Perspective { + left: T, + right: T, + bottom: T, + top: T, + near: T, + far: T, +} + +impl Projection for Perspective { + pub fn if_valid(&self, f: &fn() -> U) -> Result { + cond! ( + (self.left > self.right) { Err(fmt!("`left` cannot be greater than `right`, found: left: %? right: %?", self.left, self.right)) } + (self.bottom > self.top) { Err(fmt!("`bottom` cannot be greater than `top`, found: bottom: %? top: %?", self.bottom, self.top)) } + (self.near > self.far) { Err(fmt!("`near` cannot be greater than `far`, found: near: %? far: %?", self.near, self.far)) } + _ { Ok(f()) } + ) + } + + pub fn to_mat4(&self) -> Result, ~str> { + do self.if_valid { + let c0r0 = (two!(T) * self.near) / (self.right - self.left); + let c0r1 = zero!(T); + let c0r2 = zero!(T); + let c0r3 = zero!(T); + + let c1r0 = zero!(T); + let c1r1 = (two!(T) * self.near) / (self.top - self.bottom); + let c1r2 = zero!(T); + let c1r3 = zero!(T); + + let c2r0 = (self.right + self.left) / (self.right - self.left); + let c2r1 = (self.top + self.bottom) / (self.top - self.bottom); + let c2r2 = -(self.far + self.near) / (self.far - self.near); + let c2r3 = -one!(T); + + let c3r0 = zero!(T); + let c3r1 = zero!(T); + let c3r2 = -(two!(T) * self.far * self.near) / (self.far - self.near); + let c3r3 = zero!(T); + + Mat4::new(c0r0, c0r1, c0r2, c0r3, + c1r0, c1r1, c1r2, c1r3, + c2r0, c2r1, c2r2, c2r3, + c3r0, c3r1, c3r2, c3r3) + } + } + + pub fn to_frustum(&self) -> Result, ~str> { + do self.if_valid { + /* + + <---- l --->|<--- r -----> + +-----------+-----------+ ^ + \ | / | + \ | / | + \ + | + / | + \ / Nl | Nr \ / | + \/ | \/ | + left \ | / right f + plane \ | / plane | + ⟨Nl,Dl⟩ \ | / ⟨Nr,Dr⟩ | + \θl|θr/ | + \ | / | + \|/ | + + v + + θl = tan⁻¹(l/f) + + + + / Nl + / + / + \ | / + \θl| / + \ | / + \|/ θl + +- - - - + \ + + Nl = ⟨cos(θl), 0, sin(θl)⟩ + + left plane = ⟨Nl, 0⟩ + = ⟨cos(θl), 0, sin(θl), 0⟩ + + */ + + let theta_l = (self.left / self.far).atan(); + let theta_r = (self.right / self.far).atan(); + let theta_b = (self.bottom / self.far).atan(); + let theta_t = (self.top / self.far).atan(); + + Frustum { + left: Plane::from_abcd(theta_l.cos(), zero!(T), theta_l.sin(), zero!(T)), + right: Plane::from_abcd(theta_r.cos(), zero!(T), theta_r.sin(), zero!(T)), + bottom: Plane::from_abcd(zero!(T), theta_b.cos(), theta_b.sin(), zero!(T)), + top: Plane::from_abcd(zero!(T), theta_t.cos(), theta_t.sin(), zero!(T)), + near: Plane::from_abcd(zero!(T), zero!(T), -one!(T), -self.near), + far: Plane::from_abcd(zero!(T), zero!(T), one!(T), self.far), + } + } + } +} + +/// An orthographic projection with arbitrary left/right/bottom/top distances +#[deriving(Eq)] +pub struct Ortho { + left: T, + right: T, + bottom: T, + top: T, + near: T, + far: T, +} + +impl Projection for Ortho { + pub fn if_valid(&self, f: &fn() -> U) -> Result { + cond! ( + (self.left > self.right) { Err(fmt!("`left` cannot be greater than `right`, found: left: %? right: %?", self.left, self.right)) } + (self.bottom > self.top) { Err(fmt!("`bottom` cannot be greater than `top`, found: bottom: %? top: %?", self.bottom, self.top)) } + (self.near > self.far) { Err(fmt!("`near` cannot be greater than `far`, found: near: %? far: %?", self.near, self.far)) } + _ { Ok(f()) } + ) + } + + pub fn to_mat4(&self) -> Result, ~str> { + do self.if_valid { + let c0r0 = two!(T) / (self.right - self.left); + let c0r1 = zero!(T); + let c0r2 = zero!(T); + let c0r3 = zero!(T); + + let c1r0 = zero!(T); + let c1r1 = two!(T) / (self.top - self.bottom); + let c1r2 = zero!(T); + let c1r3 = zero!(T); + + let c2r0 = zero!(T); + let c2r1 = zero!(T); + let c2r2 = -two!(T) / (self.far - self.near); + let c2r3 = -one!(T); + + let c3r0 = -(self.right + self.left) / (self.right - self.left); + let c3r1 = -(self.top + self.bottom) / (self.top - self.bottom); + let c3r2 = -(self.far + self.near) / (self.far - self.near); + let c3r3 = one!(T); + + Mat4::new(c0r0, c0r1, c0r2, c0r3, + c1r0, c1r1, c1r2, c1r3, + c2r0, c2r1, c2r2, c2r3, + c3r0, c3r1, c3r2, c3r3) + } + } + + pub fn to_frustum(&self) -> Result, ~str> { + do self.if_valid { + Frustum { + left: Plane::from_abcd(one!(T), zero!(T), zero!(T), self.left), + right: Plane::from_abcd(-one!(T), zero!(T), zero!(T), self.right), + bottom: Plane::from_abcd(zero!(T), one!(T), zero!(T), self.bottom), + top: Plane::from_abcd(zero!(T), -one!(T), zero!(T), self.top), + near: Plane::from_abcd(zero!(T), zero!(T), -one!(T), self.near), + far: Plane::from_abcd(zero!(T), zero!(T), one!(T), self.far), + } + } + } +} diff --git a/src/ray.rs b/src/ray.rs new file mode 100644 index 0000000..12a3a76 --- /dev/null +++ b/src/ray.rs @@ -0,0 +1,47 @@ +// 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. + +use vec::Vec3; +use point::Point3; + +/// A three-dimensional ray +/// +/// # Fields +/// +/// - `pos`: the endpoint of the ray +/// - `dir`: the direction vector +#[deriving(Eq)] +pub struct Ray3 { + pos: Point3, + dir: Vec3, +} + +impl> ApproxEq for Ray3 { + #[inline] + pub fn approx_epsilon() -> T { + ApproxEq::approx_epsilon::() + } + + #[inline] + pub fn approx_eq(&self, other: &Ray3) -> bool { + self.approx_eq_eps(other, &ApproxEq::approx_epsilon::()) + } + + #[inline] + pub fn approx_eq_eps(&self, other: &Ray3, epsilon: &T) -> bool { + self.pos.approx_eq_eps(&other.pos, epsilon) && + self.dir.approx_eq_eps(&other.dir, epsilon) + } +} \ No newline at end of file