// Copyright 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. use std::fmt; use approx::ApproxEq; use matrix::{Matrix, Matrix4, ToMatrix4}; use num::{BaseNum, BaseFloat, zero, one}; use point::{Point, Point3}; use ray::Ray; use rotation::{Rotation, Rotation3}; use vector::{Vector, Vector3}; /// A trait representing an [affine /// transformation](https://en.wikipedia.org/wiki/Affine_transformation) that /// can be applied to points or vectors. An affine transformation is one which pub trait Transform, P: Point> { /// Create an identity transformation. That is, a transformation which /// does nothing. fn identity() -> Self; /// Create a transformation that rotates a vector to look at `center` from /// `eye`, using `up` for orientation. fn look_at(eye: &P, center: &P, up: &V) -> Self; /// Transform a vector using this transform. fn transform_vector(&self, vec: &V) -> V; /// Transform a point using this transform. fn transform_point(&self, point: &P) -> P; /// Transform a ray using this transform. #[inline] fn transform_ray(&self, ray: &Ray) -> Ray { Ray::new(self.transform_point(&ray.origin), self.transform_vector(&ray.direction)) } /// Transform a vector as a point using this transform. #[inline] fn transform_as_point(&self, vec: &V) -> V { self.transform_point(&Point::from_vec(vec)).to_vec() } /// Combine this transform with another, yielding a new transformation /// which has the effects of both. fn concat(&self, other: &Self) -> Self; /// Create a transform that "un-does" this one. fn invert(&self) -> Option; /// Combine this transform with another, in-place. #[inline] fn concat_self(&mut self, other: &Self) { *self = Transform::concat(self, other); } /// Invert this transform in-place, failing if the transformation is not /// invertible. #[inline] fn invert_self(&mut self) { *self = self.invert().unwrap() } } /// A generic transformation consisting of a rotation, /// displacement vector and scale amount. #[deriving(Copy, Clone, Encodable, Decodable)] pub struct Decomposed { pub scale: S, pub rot: R, pub disp: V, } impl, P: Point, R: Rotation> Transform for Decomposed { #[inline] fn identity() -> Decomposed { Decomposed { scale: one(), rot: Rotation::identity(), disp: zero(), } } #[inline] fn look_at(eye: &P, center: &P, up: &V) -> Decomposed { let origin: P = Point::origin(); let rot: R = Rotation::look_at(¢er.sub_p(eye), up); let disp: V = rot.rotate_vector(&origin.sub_p(eye)); Decomposed { scale: one(), rot: rot, disp: disp, } } #[inline] fn transform_vector(&self, vec: &V) -> V { self.rot.rotate_vector(&vec.mul_s(self.scale.clone())) } #[inline] fn transform_point(&self, point: &P) -> P { self.rot.rotate_point(&point.mul_s(self.scale.clone())).add_v(&self.disp) } fn concat(&self, other: &Decomposed) -> Decomposed { Decomposed { scale: self.scale * other.scale, rot: self.rot.concat(&other.rot), disp: self.transform_as_point(&other.disp), } } fn invert(&self) -> Option> { if self.scale.approx_eq(&zero()) { None } else { let s = one::() / self.scale; let r = self.rot.invert(); let d = r.rotate_vector(&self.disp).mul_s(-s); Some(Decomposed { scale: s, rot: r, disp: d, }) } } } pub trait Transform3: Transform, Point3>+ ToMatrix4 {} impl> ToMatrix4 for Decomposed, R> { fn to_matrix4(&self) -> Matrix4 { let mut m = self.rot.to_matrix3().mul_s(self.scale.clone()).to_matrix4(); m.w = self.disp.extend(one()); m } } impl> Transform3 for Decomposed,R> where S: 'static {} impl> fmt::Show for Decomposed,R> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "(scale({}), rot({}), disp{})", self.scale, self.rot, self.disp) } } /// A homogeneous transformation matrix. #[deriving(Copy, Clone, Encodable, Decodable)] pub struct AffineMatrix3 { pub mat: Matrix4, } impl Transform, Point3> for AffineMatrix3 { #[inline] fn identity() -> AffineMatrix3 { AffineMatrix3 { mat: Matrix4::identity() } } #[inline] fn look_at(eye: &Point3, center: &Point3, up: &Vector3) -> AffineMatrix3 { AffineMatrix3 { mat: Matrix4::look_at(eye, center, up) } } #[inline] fn transform_vector(&self, vec: &Vector3) -> Vector3 { self.mat.mul_v(&vec.extend(zero())).truncate() } #[inline] fn transform_point(&self, point: &Point3) -> Point3 { Point3::from_homogeneous(&self.mat.mul_v(&point.to_homogeneous())) } #[inline] fn concat(&self, other: &AffineMatrix3) -> AffineMatrix3 { AffineMatrix3 { mat: self.mat.mul_m(&other.mat) } } #[inline] fn invert(&self) -> Option> { self.mat.invert().map(|m| AffineMatrix3{ mat: m }) } } impl ToMatrix4 for AffineMatrix3 { #[inline] fn to_matrix4(&self) -> Matrix4 { self.mat.clone() } } impl Transform3 for AffineMatrix3 where S: 'static {}