// 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::*; use num::{BaseNum, BaseFloat, zero, one}; use point::{Point, Point3}; use quaternion::*; use ray::Ray; use rotation::{Rotation, Rotation3}; use std::marker::PhantomFn; 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>: Sized + PhantomFn { /// 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. #[derive(Copy, Clone, RustcEncodable, RustcDecodable)] pub struct Decomposed { pub scale: S, pub rot: R, pub disp: V, } impl< S: BaseFloat, V: Vector, 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< S: BaseFloat + 'static, R: Rotation3 > 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< S: BaseFloat + 'static, R: Rotation3 > Transform3 for Decomposed, R> {} impl< S: BaseFloat, R: fmt::Debug + Rotation3 > fmt::Debug 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. #[derive(Copy, Clone, RustcEncodable, RustcDecodable)] 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 {} /// A trait that allows extracting components (rotation, translation, scale) /// from an arbitrary transformation/ pub trait ToComponents, P: Point> { /// Associated rotation type type Rotation; /// Extract translation component fn to_translation(&self) -> V; /// Extract rotation component fn to_rotation(&self) -> Self::Rotation; /// Extract scale component fn to_scale(&self) -> V; } pub trait ToComponents3: ToComponents, Point3> where Self::Rotation: ToMatrix3 {} impl< S: BaseFloat, V: Vector + Clone, P: Point, R: Rotation + Clone, > ToComponents for Decomposed { type Rotation = R; fn to_translation(&self) -> V { self.disp.clone() } fn to_rotation(&self) -> R { self.rot.clone() } fn to_scale(&self) -> V { Vector::from_value(self.scale) } } impl< S: BaseFloat, R: Rotation, Point3> + Clone + ToMatrix3, > ToComponents3 for Decomposed, R> {} impl< S: BaseFloat + 'static, > ToComponents, Point3> for AffineMatrix3 { type Rotation = Quaternion; fn to_translation(&self) -> Vector3 { Vector3::new(self.mat.w.x, self.mat.w.y, self.mat.w.z) } fn to_rotation(&self) -> Quaternion { Matrix3::new( self.mat.x.x, self.mat.x.y, self.mat.x.z, self.mat.y.x, self.mat.y.y, self.mat.y.z, self.mat.z.x, self.mat.z.y, self.mat.z.z, ).to_quaternion() } fn to_scale(&self) -> Vector3 { Vector3::new(self.mat.x.x, self.mat.y.y, self.mat.z.z) } } impl< S: BaseFloat + 'static, > ToComponents3 for AffineMatrix3 {}