pbrt/shared/src/utils/transform.rs
Wito Wiala 731a37abae More fixes to allow for spirv compilation. Namely,
issues with array initialization with functions ([object::default()]),
initializing arrays from a function (implemented a simple gpu version
using manual MaybeUninit pointers),
changing enums with distinct types to structs or changing the selection
logic, changing pointer subtraction in light samplers to a scan (this
will come back to bite me in the ass), and ignoring the data module,
since SPIR-V cant use pointers in statics.
2026-02-20 21:11:17 +00:00

2138 lines
84 KiB
Rust

use core::iter::{Product, Sum};
use core::ops::{Add, Div, Index, IndexMut, Mul};
use num_traits::Float as NumFloat;
use super::math::{SquareMatrix, radians, safe_acos};
use super::quaternion::Quaternion;
use crate::core::color::{RGB, XYZ};
use crate::core::geometry::{
Bounds3f, Frame, Normal, Normal3f, Point, Point3f, Point3fi, Ray, Vector, Vector3f, Vector3fi,
VectorLike,
};
use crate::core::interaction::{
Interaction, InteractionBase, InteractionTrait, MediumInteraction, SurfaceInteraction,
};
use crate::utils::gpu_array_from_fn;
use crate::{Float, gamma};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct TransformGeneric<T: NumFloat> {
m: SquareMatrix<T, 4>,
m_inv: SquareMatrix<T, 4>,
}
impl<T: NumFloat + Sum + Product> TransformGeneric<T> {
pub fn new(m: SquareMatrix<T, 4>, m_inv: SquareMatrix<T, 4>) -> Self {
Self { m, m_inv }
}
pub fn from_matrix(m: SquareMatrix<T, 4>) -> Option<Self> {
let inv = m.inverse()?;
Some(Self { m, m_inv: inv })
}
pub fn from_flat(flat: &[T; 16]) -> Option<Self> {
let m: SquareMatrix<T, 4> = SquareMatrix::from(flat);
let inv = m.inverse()?;
Some(Self { m, m_inv: inv })
}
pub fn identity() -> Self {
let m: SquareMatrix<T, 4> = SquareMatrix::identity();
Self { m, m_inv: m }
}
pub fn is_identity(&self) -> bool {
self.m.is_identity()
}
pub fn inverse(&self) -> Self {
Self {
m: self.m_inv,
m_inv: self.m,
}
}
pub fn apply_inverse(&self, p: Point<T, 3>) -> Point<T, 3> {
*self * p
}
pub fn apply_inverse_vector(&self, v: Vector<T, 3>) -> Vector<T, 3> {
let x = v.x();
let y = v.y();
let z = v.z();
Vector::<T, 3>::new(
self.m_inv[0][0] * x + self.m_inv[0][1] * y + self.m_inv[0][2] * z,
self.m_inv[1][0] * x + self.m_inv[1][1] * y + self.m_inv[1][2] * z,
self.m_inv[2][0] * x + self.m_inv[2][1] * y + self.m_inv[2][2] * z,
)
}
pub fn apply_inverse_normal(&self, n: Normal<T, 3>) -> Normal<T, 3> {
*self * n
}
pub fn swaps_handedness(&self) -> bool {
let s = SquareMatrix::<T, 3>::new([
[self.m[0][0], self.m[0][1], self.m[0][2]],
[self.m[1][0], self.m[1][1], self.m[1][2]],
[self.m[2][0], self.m[2][1], self.m[2][2]],
]);
s.determinant() < T::zero()
}
pub fn get_matrix(&self) -> SquareMatrix<T, 4> {
self.m
}
}
impl<T: NumFloat + Sum + Product> Default for TransformGeneric<T> {
fn default() -> Self {
Self::identity()
}
}
pub type Transform = TransformGeneric<Float>;
impl TransformGeneric<Float> {
pub fn apply_to_point(&self, p: Point3f) -> Point3f {
let x = p.x();
let y = p.y();
let z = p.z();
let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z + self.m[0][3];
let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z + self.m[1][3];
let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z + self.m[2][3];
let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z + self.m[3][3];
if wp == 1. {
Point3f::new(xp, yp, zp)
} else {
Point3f::new(xp / wp, yp / wp, zp / wp)
}
}
pub fn apply_to_vector(&self, p: Vector3f) -> Vector3f {
let x = p.x();
let y = p.y();
let z = p.z();
let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z;
let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z;
let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z;
let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z;
if wp == 1. {
Vector3f::new(xp, yp, zp)
} else {
Vector3f::new(xp / wp, yp / wp, zp / wp)
}
}
pub fn apply_to_normal(&self, p: Normal3f) -> Normal3f {
let x = p.x();
let y = p.y();
let z = p.z();
let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z;
let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z;
let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z;
let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z;
if wp == 1. {
Normal3f::new(xp, yp, zp)
} else {
Normal3f::new(xp / wp, yp / wp, zp / wp)
}
}
pub fn apply_to_bounds(&self, b: Bounds3f) -> Bounds3f {
let mut new_bounds = Bounds3f::default();
for i in 0..8 {
let corner = self.apply_to_point(b.corner(i));
new_bounds = new_bounds.union_point(corner);
}
new_bounds
}
pub fn apply_to_ray(&self, r: &Ray, t_max: &mut Option<Float>) -> Ray {
let norm_squared = r.d.norm_squared();
let mut o = Point3fi::new_from_point(r.o);
if norm_squared > 0. {
let dt = r.d.abs().dot(o.error()) / norm_squared;
let offset = Vector3fi::new_from_vector(r.d * dt);
o = o + offset;
if let Some(t) = t_max.as_mut() {
*t -= dt;
}
}
Ray::new(o.into(), r.d, Some(r.time), &*r.medium)
}
pub fn apply_to_interval(&self, pi: &Point3fi) -> Point3fi {
let p = pi.midpoint();
let x = p.x();
let y = p.y();
let z = p.z();
let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z;
let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z;
let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z;
let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z;
let mut p_error = Vector3f::default();
if pi.is_exact() {
p_error[0] = gamma(3)
* ((self.m[0][0] * x).abs()
+ (self.m[0][1] * y).abs()
+ (self.m[0][2] + z).abs()
+ self.m[0][3].abs());
p_error[1] = gamma(3)
* ((self.m[1][0] * x).abs()
+ (self.m[1][1] * y).abs()
+ (self.m[1][2] + z).abs()
+ self.m[1][3].abs());
p_error[2] = gamma(3)
* ((self.m[2][0] * x).abs()
+ (self.m[2][1] * y).abs()
+ (self.m[2][2] + z).abs()
+ self.m[2][3].abs());
}
if wp == 1. {
Point3fi::new_with_error(Point([xp, yp, zp]), p_error)
} else {
Point3fi::new_with_error(Point([xp / wp, yp / wp, zp / wp]), p_error)
}
}
pub fn apply_to_vector_interval(&self, vi: &Vector3fi) -> Vector3fi {
let v = vi.midpoint();
let x = v.x();
let y = v.y();
let z = v.z();
// Transform the midpoint of the vector interval
let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z;
let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z;
let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z;
let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z;
let mut v_error = Vector3f::default();
// Propagate the error, ignoring the translational part of the matrix
if vi.is_exact() {
v_error[0] = gamma(3)
* ((self.m[0][0] * x).abs() + (self.m[0][1] * y).abs() + (self.m[0][2] * z).abs());
v_error[1] = gamma(3)
* ((self.m[1][0] * x).abs() + (self.m[1][1] * y).abs() + (self.m[1][2] * z).abs());
v_error[2] = gamma(3)
* ((self.m[2][0] * x).abs() + (self.m[2][1] * y).abs() + (self.m[2][2] * z).abs());
}
if wp == 1. {
Vector3fi::new_with_error(Vector3f::new(xp, yp, zp), v_error)
} else {
Vector3fi::new_with_error(Vector3f::new(xp / wp, yp / wp, zp / wp), v_error)
}
}
pub fn apply_to_interaction(&self, inter: &Interaction) -> Interaction {
match inter {
Interaction::Surface(si) => {
let mut ret = si.clone();
ret.common.pi = self.apply_to_interval(&si.common.pi);
let n = self.apply_to_normal(si.common.n);
ret.common.wo = self.apply_to_vector(si.common.wo).normalize();
ret.dpdu = self.apply_to_vector(si.dpdu);
ret.dpdv = self.apply_to_vector(si.dpdv);
ret.dndu = self.apply_to_normal(si.dndu);
ret.dndv = self.apply_to_normal(si.dndv);
ret.dpdx = self.apply_to_vector(si.dpdx);
ret.dpdy = self.apply_to_vector(si.dpdy);
let shading_n = self.apply_to_normal(si.shading.n);
ret.shading.n = shading_n.normalize();
ret.shading.dpdu = self.apply_to_vector(si.shading.dpdu);
ret.shading.dpdv = self.apply_to_vector(si.shading.dpdv);
ret.shading.dndu = self.apply_to_normal(si.shading.dndu);
ret.shading.dndv = self.apply_to_normal(si.shading.dndv);
ret.common.n = n.normalize().face_forward(ret.shading.n);
Interaction::Surface(ret)
}
Interaction::Medium(mi) => {
let mut ret = mi.clone();
ret.common.pi = self.apply_to_interval(&mi.common.pi);
ret.common.n = self.apply_to_normal(mi.common.n).normalize();
ret.common.wo = self.apply_to_vector(mi.common.wo).normalize();
Interaction::Medium(ret)
}
Interaction::Simple(sim) => {
let mut ret = sim.clone();
ret.common.pi = self.apply_to_interval(&sim.common.pi);
if sim.common.n != Default::default() {
ret.common.n = self.apply_to_normal(sim.common.n).normalize();
}
if sim.common.wo != Default::default() {
ret.common.wo = self.apply_to_vector(sim.common.wo).normalize();
}
Interaction::Simple(ret)
}
}
}
pub fn apply_inverse_interval(&self, p: &Point3fi) -> Point3fi {
let x = Float::from(p.x());
let y = Float::from(p.y());
let z = Float::from(p.z());
let m_inv = &self.m_inv;
// Compute transformed coordinates from point
let xp = (m_inv[0][0] * x + m_inv[0][1] * y) + (m_inv[0][2] * z + m_inv[0][3]);
let yp = (m_inv[1][0] * x + m_inv[1][1] * y) + (m_inv[1][2] * z + m_inv[1][3]);
let zp = (m_inv[2][0] * x + m_inv[2][1] * y) + (m_inv[2][2] * z + m_inv[2][3]);
let wp = (m_inv[3][0] * x + m_inv[3][1] * y) + (m_inv[3][2] * z + m_inv[3][3]);
// Compute absolute error for transformed point
let g3 = gamma(3);
let p_out_error = if p.is_exact() {
Vector3f::new(
g3 * (m_inv[0][0] * x).abs() + (m_inv[0][1] * y).abs() + (m_inv[0][2] * z).abs(),
g3 * (m_inv[1][0] * x).abs() + (m_inv[1][1] * y).abs() + (m_inv[1][2] * z).abs(),
g3 * (m_inv[2][0] * x).abs() + (m_inv[2][1] * y).abs() + (m_inv[2][2] * z).abs(),
)
} else {
let p_in_error = p.error();
let g3_plus_1 = g3 + 1.0;
Vector3f::new(
g3_plus_1
* (m_inv[0][0].abs() * p_in_error.x()
+ m_inv[0][1].abs() * p_in_error.y()
+ m_inv[0][2].abs() * p_in_error.z())
+ g3 * ((m_inv[0][0] * x).abs()
+ (m_inv[0][1] * y).abs()
+ (m_inv[0][2] * z).abs()
+ m_inv[0][3].abs()),
g3_plus_1
* (m_inv[1][0].abs() * p_in_error.x()
+ m_inv[1][1].abs() * p_in_error.y()
+ m_inv[1][2].abs() * p_in_error.z())
+ g3 * ((m_inv[1][0] * x).abs()
+ (m_inv[1][1] * y).abs()
+ (m_inv[1][2] * z).abs()
+ m_inv[1][3].abs()),
g3_plus_1
* (m_inv[2][0].abs() * p_in_error.x()
+ m_inv[2][1].abs() * p_in_error.y()
+ m_inv[2][2].abs() * p_in_error.z())
+ g3 * ((m_inv[2][0] * x).abs()
+ (m_inv[2][1] * y).abs()
+ (m_inv[2][2] * z).abs()
+ m_inv[2][3].abs()),
)
};
if wp == 1.0 {
Point3fi::new_with_error(Point3f::new(xp, yp, zp), p_out_error)
} else {
Point3fi::new_with_error(Point3f::new(xp / wp, yp / wp, zp / wp), p_out_error)
}
}
pub fn apply_inverse_ray(&self, r: &Ray, t_max: Option<Float>) -> (Ray, Float) {
let mut o = self.apply_inverse_interval(&Point3fi::new_from_point(r.o));
let d = self.apply_inverse_vector(r.d);
// Offset ray origin to edge of error bounds and compute _tMax_
let mut t = 0.;
let length_squared = d.norm_squared();
if length_squared > 0. {
let o_error = Vector3f::new(o.x().width() / 2., o.y().width() / 2., o.z().width() / 2.);
let dt = d.abs().dot(o_error) / length_squared;
o = o + Vector3fi::from(d * dt);
if let Some(t_max) = t_max {
t = t_max - dt;
}
}
(Ray::new(Point3f::from(o), d, Some(r.time), &*r.medium), t)
}
pub fn to_quaternion(self) -> Quaternion {
let trace = self.m.trace();
let mut quat = Quaternion::default();
if trace > 0. {
let mut s = (trace + 1.).sqrt();
quat.w = 0.5 * s;
s = 0.5 / s;
quat.v[0] = (self.m[2][1] - self.m[1][2]) * s;
quat.v[1] = (self.m[0][2] - self.m[2][0]) * s;
quat.v[2] = (self.m[1][0] - self.m[0][1]) * s;
} else {
let nxt = [1, 2, 0];
let mut q = [0.; 3];
let mut i = 0;
if self.m[1][1] > self.m[0][0] {
i = 1;
}
if self.m[2][2] > self.m[i][i] {
i = 2;
}
let j = nxt[i];
let k = nxt[j];
let mut s = (self.m[i][i] - (self.m[j][j] + self.m[k][k]) + 1.).sqrt();
q[i] = 0.5 * s;
if s != 0. {
s = 0.5 / s;
}
quat.w = (self.m[k][j] - self.m[j][k]) * s;
q[j] = (self.m[j][i] + self.m[i][j]) * s;
q[k] = (self.m[k][i] + self.m[i][k]) * s;
quat.v[0] = q[0];
quat.v[1] = q[1];
quat.v[2] = q[2];
}
quat
}
pub fn translate(delta: Vector3f) -> Self {
TransformGeneric {
m: SquareMatrix::new([
[1.0, 0.0, 0.0, delta.x()],
[0.0, 1.0, 0.0, delta.y()],
[0.0, 0.0, 1.0, delta.z()],
[0.0, 0.0, 0.0, 1.0],
]),
m_inv: SquareMatrix::new([
[1.0, 0.0, 0.0, -delta.x()],
[0.0, 1.0, 0.0, -delta.y()],
[0.0, 0.0, 1.0, -delta.z()],
[0.0, 0.0, 0.0, 1.0],
]),
}
}
pub fn scale(x: Float, y: Float, z: Float) -> Self {
let m = SquareMatrix::new([
[x, 0., 0., 0.],
[0., y, 0., 0.],
[0., 0., z, 0.],
[0., 0., 0., 1.],
]);
let m_inv = SquareMatrix::new([
[1. / x, 0., 0., 0.],
[0., 1. / y, 0., 0.],
[0., 0., 1. / z, 0.],
[0., 0., 0., 1.],
]);
Self { m, m_inv }
}
pub fn perspective(fov: Float, n: Float, f: Float) -> Option<TransformGeneric<Float>> {
let persp: SquareMatrix<Float, 4> = SquareMatrix::new([
[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., f / (f - n), -f * n / (f - n)],
[0., 0., 1., 0.],
]);
let inv_tan_ang = 1. / (radians(fov) / 2.).tan();
let persp_transform = TransformGeneric::from_matrix(persp)?;
Some(TransformGeneric::scale(inv_tan_ang, inv_tan_ang, 1.) * persp_transform)
}
pub fn orthographic(z_near: Float, z_far: Float) -> Self {
Self::scale(1., 1., 1. / (z_far - z_near)) * Self::translate(Vector3f::new(0., 0., -z_near))
}
pub fn rotate_around_axis(theta: Float, axis: impl Into<Vector3f>) -> Self {
let sin_theta = theta.to_radians().sin();
let cos_theta = theta.to_radians().cos();
TransformGeneric::rotate(sin_theta, cos_theta, axis)
}
pub fn rotate(sin_theta: Float, cos_theta: Float, axis: impl Into<Vector3f>) -> Self {
let vec_axis: Vector3f = axis.into();
let a = vec_axis.normalize();
let mut m: SquareMatrix<Float, 4> = SquareMatrix::default();
m[0][0] = a.x() * a.x() + (1. - a.x() * a.x()) * cos_theta;
m[0][1] = a.x() * a.y() * (1. - cos_theta) - a.z() * sin_theta;
m[0][2] = a.x() * a.z() * (1. - cos_theta) + a.y() * sin_theta;
m[0][3] = 0.;
m[1][0] = a.x() * a.y() * (1. - cos_theta) + a.z() * sin_theta;
m[1][1] = a.y() * a.y() + (1. - a.y() * a.y()) * cos_theta;
m[1][2] = a.y() * a.z() * (1. - cos_theta) - a.x() * sin_theta;
m[1][3] = 0.;
m[2][0] = a.x() * a.z() * (1. - cos_theta) - a.y() * sin_theta;
m[2][1] = a.y() * a.z() * (1. - cos_theta) + a.x() * sin_theta;
m[2][2] = a.z() * a.z() + (1. - a.z() * a.z()) * cos_theta;
m[2][3] = 0.;
TransformGeneric::new(m, m.transpose())
}
pub fn rotate_from_to(from: Vector3f, to: Vector3f) -> Self {
let refl;
if from.x().abs() < 0.72 && to.x().abs() < 0.72 {
refl = Vector3f::new(1., 0., 0.);
} else if from.y().abs() < 0.72 && to.y().abs() < 0.72 {
refl = Vector3f::new(0., 1., 0.);
} else {
refl = Vector3f::new(0., 0., 1.);
}
let u = refl - from;
let v = refl - to;
let uu = u.dot(u);
let vv = v.dot(v);
let uv = u.dot(v);
let mut r: SquareMatrix<Float, 4> = SquareMatrix::default();
for i in 0..3 {
for j in 0..3 {
let k = if i == j { 1. } else { 0. };
r[i][j] = k - 2. / uu * u[i] * u[j] - 2. * vv * v[i] * v[j]
+ 4. * uv / (uu * vv) * v[i] * u[j];
}
}
TransformGeneric::new(r, r.transpose())
}
pub fn rotate_x(theta: Float) -> Self {
let sin_theta = theta.to_radians().sin();
let cos_theta = theta.to_radians().cos();
let m = SquareMatrix::new([
[1., 0., 0., 0.],
[0., cos_theta, -sin_theta, 0.],
[0., sin_theta, cos_theta, 0.],
[0., 0., 0., 1.],
]);
Self {
m,
m_inv: m.transpose(),
}
}
pub fn rotate_y(theta: Float) -> Self {
let sin_theta = theta.to_radians().sin();
let cos_theta = theta.to_radians().cos();
let m = SquareMatrix::new([
[cos_theta, 0., sin_theta, 0.],
[0., 1., 0., 0.],
[-sin_theta, 0., cos_theta, 0.],
[0., 0., 0., 1.],
]);
Self {
m,
m_inv: m.transpose(),
}
}
pub fn rotate_z(theta: Float) -> Self {
let sin_theta = theta.to_radians().sin();
let cos_theta = theta.to_radians().cos();
let m = SquareMatrix::new([
[cos_theta, -sin_theta, 0., 0.],
[sin_theta, cos_theta, 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.],
]);
Self {
m,
m_inv: m.transpose(),
}
}
pub fn decompose(&self) -> (Vector3f, SquareMatrix<Float, 4>, SquareMatrix<Float, 4>) {
let t = Vector3f::new(self.m[0][3], self.m[1][3], self.m[2][3]);
let mut m = self.m;
for i in 0..3 {
m[i][3] = 0.;
m[3][i] = m[i][3];
}
m[3][3] = 1.;
let mut norm: Float;
let mut r = m;
let mut count = 0;
loop {
let rit = r
.transpose()
.inverse()
.expect("Transform is not decomposable");
let rnext = (r + rit) / 2.;
norm = 0.0;
for i in 0..3 {
let n = (r[i][0] - rnext[i][0]).abs()
+ (r[i][1] - rnext[i][1]).abs()
+ (r[i][2] - rnext[i][2]).abs();
norm = norm.max(n);
}
r = rnext;
count += 1;
if count > 100 || norm < 0.0001 {
break;
}
}
let s = r
.transpose()
.inverse()
.expect("Part of decomposition is singular")
* m;
(t, r, s)
}
pub fn has_scale(&self, tolerance: Option<Float>) -> bool {
let la2 = self
.apply_to_vector(Vector3f::new(1., 0., 0.))
.norm_squared();
let lb2 = self
.apply_to_vector(Vector3f::new(0., 1., 0.))
.norm_squared();
let lc2 = self
.apply_to_vector(Vector3f::new(0., 0., 1.))
.norm_squared();
let tol = tolerance.unwrap_or(1e-3);
(la2 - 1.).abs() > tol || (lb2 - 1.).abs() > tol || (lc2 - 1.).abs() > tol
}
}
impl<T: num_traits::Float> PartialEq for TransformGeneric<T> {
fn eq(&self, other: &Self) -> bool {
self.m == other.m && self.m_inv == other.m_inv
}
}
impl<T: NumFloat> Mul for TransformGeneric<T> {
type Output = TransformGeneric<T>;
fn mul(self, rhs: TransformGeneric<T>) -> Self::Output {
TransformGeneric {
m: self.m * rhs.m,
m_inv: rhs.m_inv * self.m_inv,
}
}
}
impl<'b, T> Mul<&'b TransformGeneric<T>> for &TransformGeneric<T>
where
T: NumFloat,
{
type Output = TransformGeneric<T>;
fn mul(self, rhs: &'b TransformGeneric<T>) -> Self::Output {
TransformGeneric {
m: self.m * rhs.m,
m_inv: rhs.m_inv * self.m_inv,
}
}
}
impl Mul<TransformGeneric<Float>> for Float {
type Output = TransformGeneric<Float>;
fn mul(self, rhs: TransformGeneric<Float>) -> Self::Output {
TransformGeneric {
m: rhs.m * self,
m_inv: rhs.m_inv * self,
}
}
}
impl<T> Mul<Point<T, 3>> for TransformGeneric<T>
where
T: NumFloat,
{
type Output = Point<T, 3>;
fn mul(self, p: Point<T, 3>) -> Self::Output {
let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3];
let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3];
let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3];
let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3];
if wp == T::one() {
Point([xp, yp, zp])
} else {
Point([xp / wp, yp / wp, zp / wp])
}
}
}
impl<T> Mul<Vector<T, 3>> for TransformGeneric<T>
where
T: NumFloat,
{
type Output = Vector<T, 3>;
fn mul(self, p: Vector<T, 3>) -> Self::Output {
let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3];
let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3];
let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3];
let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3];
if wp == T::one() {
Vector([xp, yp, zp])
} else {
Vector([xp / wp, yp / wp, zp / wp])
}
}
}
impl<T> Mul<Normal<T, 3>> for TransformGeneric<T>
where
T: NumFloat,
{
type Output = Normal<T, 3>;
fn mul(self, p: Normal<T, 3>) -> Self::Output {
let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3];
let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3];
let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3];
let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3];
if wp == T::one() {
Normal([xp, yp, zp])
} else {
Normal([xp / wp, yp / wp, zp / wp])
}
}
}
impl From<Frame> for TransformGeneric<Float> {
fn from(frame: Frame) -> Self {
let values: &[Float; 16] = &[
frame.x.x(),
frame.x.y(),
frame.x.z(),
0.,
frame.y.x(),
frame.y.y(),
frame.y.z(),
0.,
frame.z.x(),
frame.z.y(),
frame.z.z(),
0.,
0.,
0.,
0.,
1.,
];
Self::from_flat(values).expect("Transform must be inversible")
}
}
impl From<Quaternion> for TransformGeneric<Float> {
fn from(q: Quaternion) -> Self {
let xx = q.v.x() * q.v.x();
let yy = q.v.y() * q.v.y();
let zz = q.v.z() * q.v.z();
let xy = q.v.x() * q.v.y();
let xz = q.v.x() * q.v.z();
let yz = q.v.y() * q.v.z();
let wx = q.v.x() * q.w;
let wy = q.v.y() * q.w;
let wz = q.v.z() * q.w;
let mut m = [[0.0; 4]; 4];
m[0][0] = 1. - 2. * (yy + zz);
m[0][1] = 2. * (xy - wz);
m[0][2] = 2. * (xz + wy);
m[1][0] = 2. * (xy + wz);
m[1][1] = 1. - 2. * (xx + zz);
m[1][2] = 2. * (yz - wx);
m[2][0] = 2. * (xz - wy);
m[2][1] = 2. * (yz + wx);
m[2][2] = 1. - 2. * (xx + yy);
m[3][3] = 1.;
let m_sq = SquareMatrix::new(m);
// For a pure rotation, the inverse is the transpose.
let m_inv_sq = m_sq.transpose();
TransformGeneric::new(m_sq, m_inv_sq)
}
}
#[repr(C)]
#[derive(Default, Debug, Copy, Clone)]
pub struct DerivativeTerm {
kc: Float,
kx: Float,
ky: Float,
kz: Float,
}
impl DerivativeTerm {
pub fn new(kc: Float, kx: Float, ky: Float, kz: Float) -> Self {
Self { kc, kx, ky, kz }
}
pub fn from_matrix(m: [Float; 4]) -> Self {
Self {
kc: m[0],
kx: m[1],
ky: m[2],
kz: m[3],
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct AnimatedTransform {
pub start_transform: Transform,
pub end_transform: Transform,
pub start_time: Float,
pub end_time: Float,
actually_animated: bool,
t: [Vector3f; 2],
r: [Quaternion; 2],
s: [SquareMatrix<Float, 4>; 2],
has_rotation: bool,
c1: [DerivativeTerm; 3],
c2: [DerivativeTerm; 3],
c3: [DerivativeTerm; 3],
c4: [DerivativeTerm; 3],
c5: [DerivativeTerm; 3],
}
impl Default for AnimatedTransform {
fn default() -> Self {
Self {
start_transform: Transform::default(),
end_transform: Transform::default(),
start_time: 0.0,
end_time: 0.0,
actually_animated: false,
t: gpu_array_from_fn(|_| Vector3f::default()),
r: gpu_array_from_fn(|_| Quaternion::default()),
s: gpu_array_from_fn(|_| SquareMatrix::default()),
has_rotation: false,
c1: gpu_array_from_fn(|_| DerivativeTerm::default()),
c2: gpu_array_from_fn(|_| DerivativeTerm::default()),
c3: gpu_array_from_fn(|_| DerivativeTerm::default()),
c4: gpu_array_from_fn(|_| DerivativeTerm::default()),
c5: gpu_array_from_fn(|_| DerivativeTerm::default()),
}
}
}
impl AnimatedTransform {
pub fn from_transform(t: &Transform) -> Self {
Self::new(t, 0., t, 1.)
}
pub fn new(
start_transform: &Transform,
start_time: Float,
end_transform: &Transform,
end_time: Float,
) -> Self {
let actually_animated = start_transform != end_transform;
if !actually_animated {
return Self::default();
}
let (t0, r_temp, s0) = start_transform.decompose();
let mut rm = r_temp;
let scale_transform =
TransformGeneric::from_matrix(rm).expect("Scaling matrix should invertible");
let r0 = scale_transform.to_quaternion();
let (t1, r_temp, s1) = end_transform.decompose();
rm = r_temp;
let scale_transform =
TransformGeneric::from_matrix(rm).expect("Scaling matrix should invertible");
let mut r1 = scale_transform.to_quaternion();
if r0.dot(r1) < 0. {
r1 = -r1;
}
let has_rotation = r0.dot(r1) < 0.9995;
let t = [t0, t1];
let r = [r0, r1];
let s = [s0, s1];
let (c1, c2, c3, c4, c5) = if has_rotation {
let mut c1: [DerivativeTerm; 3] = gpu_array_from_fn(|_| DerivativeTerm::default());
let mut c2: [DerivativeTerm; 3] = gpu_array_from_fn(|_| DerivativeTerm::default());
let mut c3: [DerivativeTerm; 3] = gpu_array_from_fn(|_| DerivativeTerm::default());
let mut c4: [DerivativeTerm; 3] = gpu_array_from_fn(|_| DerivativeTerm::default());
let mut c5: [DerivativeTerm; 3] = gpu_array_from_fn(|_| DerivativeTerm::default());
let cos_theta = r0.dot(r1);
let theta = safe_acos(cos_theta);
let qperp: Quaternion = (r0 - r1 * cos_theta).normalize();
let t0x = t0.x();
let t0y = t0.y();
let t0z = t0.z();
let t1x = t1.x();
let t1y = t1.y();
let t1z = t1.z();
let q0x = r0.v.x();
let q0y = r0.v.y();
let q0z = r0.v.z();
let q0w = r0.w;
let qperpx = qperp.v.x();
let qperpy = qperp.v.y();
let qperpz = qperp.v.z();
let qperpw = qperp.w;
let s000 = s0[0][0];
let s001 = s0[0][1];
let s002 = s0[0][2];
let s010 = s0[1][0];
let s011 = s0[1][1];
let s012 = s0[1][2];
let s020 = s0[2][0];
let s021 = s0[2][1];
let s022 = s0[2][2];
let s100 = s1[0][0];
let s101 = s1[0][1];
let s102 = s1[0][2];
let s110 = s1[1][0];
let s111 = s1[1][1];
let s112 = s1[1][2];
let s120 = s1[2][0];
let s121 = s1[2][1];
let s122 = s1[2][2];
c1[0] = DerivativeTerm::new(
-t0x + t1x,
(-1. + q0y * q0y + q0z * q0z + qperpy * qperpy + qperpz * qperpz) * s000
+ q0w * q0z * s010
- qperpx * qperpy * s010
+ qperpw * qperpz * s010
- q0w * q0y * s020
- qperpw * qperpy * s020
- qperpx * qperpz * s020
+ s100
- q0y * q0y * s100
- q0z * q0z * s100
- qperpy * qperpy * s100
- qperpz * qperpz * s100
- q0w * q0z * s110
+ qperpx * qperpy * s110
- qperpw * qperpz * s110
+ q0w * q0y * s120
+ qperpw * qperpy * s120
+ qperpx * qperpz * s120
+ q0x * (-(q0y * s010) - q0z * s020 + q0y * s110 + q0z * s120),
(-1. + q0y * q0y + q0z * q0z + qperpy * qperpy + qperpz * qperpz) * s001
+ q0w * q0z * s011
- qperpx * qperpy * s011
+ qperpw * qperpz * s011
- q0w * q0y * s021
- qperpw * qperpy * s021
- qperpx * qperpz * s021
+ s101
- q0y * q0y * s101
- q0z * q0z * s101
- qperpy * qperpy * s101
- qperpz * qperpz * s101
- q0w * q0z * s111
+ qperpx * qperpy * s111
- qperpw * qperpz * s111
+ q0w * q0y * s121
+ qperpw * qperpy * s121
+ qperpx * qperpz * s121
+ q0x * (-(q0y * s011) - q0z * s021 + q0y * s111 + q0z * s121),
(-1. + q0y * q0y + q0z * q0z + qperpy * qperpy + qperpz * qperpz) * s002
+ q0w * q0z * s012
- qperpx * qperpy * s012
+ qperpw * qperpz * s012
- q0w * q0y * s022
- qperpw * qperpy * s022
- qperpx * qperpz * s022
+ s102
- q0y * q0y * s102
- q0z * q0z * s102
- qperpy * qperpy * s102
- qperpz * qperpz * s102
- q0w * q0z * s112
+ qperpx * qperpy * s112
- qperpw * qperpz * s112
+ q0w * q0y * s122
+ qperpw * qperpy * s122
+ qperpx * qperpz * s122
+ q0x * (-(q0y * s012) - q0z * s022 + q0y * s112 + q0z * s122),
);
c2[0] = DerivativeTerm::new(
0.,
-(qperpy * qperpy * s000) - qperpz * qperpz * s000 + qperpx * qperpy * s010
- qperpw * qperpz * s010
+ qperpw * qperpy * s020
+ qperpx * qperpz * s020
+ q0y * q0y * (s000 - s100)
+ q0z * q0z * (s000 - s100)
+ qperpy * qperpy * s100
+ qperpz * qperpz * s100
- qperpx * qperpy * s110
+ qperpw * qperpz * s110
- qperpw * qperpy * s120
- qperpx * qperpz * s120
+ 2. * q0x * qperpy * s010 * theta
- 2. * q0w * qperpz * s010 * theta
+ 2. * q0w * qperpy * s020 * theta
+ 2. * q0x * qperpz * s020 * theta
+ q0y
* (q0x * (-s010 + s110)
+ q0w * (-s020 + s120)
+ 2. * (-2. * qperpy * s000 + qperpx * s010 + qperpw * s020) * theta)
+ q0z
* (q0w * (s010 - s110) + q0x * (-s020 + s120)
- 2. * (2. * qperpz * s000 + qperpw * s010 - qperpx * s020) * theta),
-(qperpy * qperpy * s001) - qperpz * qperpz * s001 + qperpx * qperpy * s011
- qperpw * qperpz * s011
+ qperpw * qperpy * s021
+ qperpx * qperpz * s021
+ q0y * q0y * (s001 - s101)
+ q0z * q0z * (s001 - s101)
+ qperpy * qperpy * s101
+ qperpz * qperpz * s101
- qperpx * qperpy * s111
+ qperpw * qperpz * s111
- qperpw * qperpy * s121
- qperpx * qperpz * s121
+ 2. * q0x * qperpy * s011 * theta
- 2. * q0w * qperpz * s011 * theta
+ 2. * q0w * qperpy * s021 * theta
+ 2. * q0x * qperpz * s021 * theta
+ q0y
* (q0x * (-s011 + s111)
+ q0w * (-s021 + s121)
+ 2. * (-2. * qperpy * s001 + qperpx * s011 + qperpw * s021) * theta)
+ q0z
* (q0w * (s011 - s111) + q0x * (-s021 + s121)
- 2. * (2. * qperpz * s001 + qperpw * s011 - qperpx * s021) * theta),
-(qperpy * qperpy * s002) - qperpz * qperpz * s002 + qperpx * qperpy * s012
- qperpw * qperpz * s012
+ qperpw * qperpy * s022
+ qperpx * qperpz * s022
+ q0y * q0y * (s002 - s102)
+ q0z * q0z * (s002 - s102)
+ qperpy * qperpy * s102
+ qperpz * qperpz * s102
- qperpx * qperpy * s112
+ qperpw * qperpz * s112
- qperpw * qperpy * s122
- qperpx * qperpz * s122
+ 2. * q0x * qperpy * s012 * theta
- 2. * q0w * qperpz * s012 * theta
+ 2. * q0w * qperpy * s022 * theta
+ 2. * q0x * qperpz * s022 * theta
+ q0y
* (q0x * (-s012 + s112)
+ q0w * (-s022 + s122)
+ 2. * (-2. * qperpy * s002 + qperpx * s012 + qperpw * s022) * theta)
+ q0z
* (q0w * (s012 - s112) + q0x * (-s022 + s122)
- 2. * (2. * qperpz * s002 + qperpw * s012 - qperpx * s022) * theta),
);
c3[0] = DerivativeTerm::new(
0.,
-2. * (q0x * qperpy * s010 - q0w * qperpz * s010
+ q0w * qperpy * s020
+ q0x * qperpz * s020
- q0x * qperpy * s110
+ q0w * qperpz * s110
- q0w * qperpy * s120
- q0x * qperpz * s120
+ q0y
* (-2. * qperpy * s000
+ qperpx * s010
+ qperpw * s020
+ 2. * qperpy * s100
- qperpx * s110
- qperpw * s120)
+ q0z
* (-2. * qperpz * s000 - qperpw * s010
+ qperpx * s020
+ 2. * qperpz * s100
+ qperpw * s110
- qperpx * s120))
* theta,
-2. * (q0x * qperpy * s011 - q0w * qperpz * s011
+ q0w * qperpy * s021
+ q0x * qperpz * s021
- q0x * qperpy * s111
+ q0w * qperpz * s111
- q0w * qperpy * s121
- q0x * qperpz * s121
+ q0y
* (-2. * qperpy * s001
+ qperpx * s011
+ qperpw * s021
+ 2. * qperpy * s101
- qperpx * s111
- qperpw * s121)
+ q0z
* (-2. * qperpz * s001 - qperpw * s011
+ qperpx * s021
+ 2. * qperpz * s101
+ qperpw * s111
- qperpx * s121))
* theta,
-2. * (q0x * qperpy * s012 - q0w * qperpz * s012
+ q0w * qperpy * s022
+ q0x * qperpz * s022
- q0x * qperpy * s112
+ q0w * qperpz * s112
- q0w * qperpy * s122
- q0x * qperpz * s122
+ q0y
* (-2. * qperpy * s002
+ qperpx * s012
+ qperpw * s022
+ 2. * qperpy * s102
- qperpx * s112
- qperpw * s122)
+ q0z
* (-2. * qperpz * s002 - qperpw * s012
+ qperpx * s022
+ 2. * qperpz * s102
+ qperpw * s112
- qperpx * s122))
* theta,
);
c4[0] = DerivativeTerm::new(
0.,
-(q0x * qperpy * s010) + q0w * qperpz * s010
- q0w * qperpy * s020
- q0x * qperpz * s020
+ q0x * qperpy * s110
- q0w * qperpz * s110
+ q0w * qperpy * s120
+ q0x * qperpz * s120
+ 2. * q0y * q0y * s000 * theta
+ 2. * q0z * q0z * s000 * theta
- 2. * qperpy * qperpy * s000 * theta
- 2. * qperpz * qperpz * s000 * theta
+ 2. * qperpx * qperpy * s010 * theta
- 2. * qperpw * qperpz * s010 * theta
+ 2. * qperpw * qperpy * s020 * theta
+ 2. * qperpx * qperpz * s020 * theta
+ q0y
* (-(qperpx * s010) - qperpw * s020
+ 2. * qperpy * (s000 - s100)
+ qperpx * s110
+ qperpw * s120
- 2. * q0x * s010 * theta
- 2. * q0w * s020 * theta)
+ q0z
* (2. * qperpz * s000 + qperpw * s010
- qperpx * s020
- 2. * qperpz * s100
- qperpw * s110
+ qperpx * s120
+ 2. * q0w * s010 * theta
- 2. * q0x * s020 * theta),
-(q0x * qperpy * s011) + q0w * qperpz * s011
- q0w * qperpy * s021
- q0x * qperpz * s021
+ q0x * qperpy * s111
- q0w * qperpz * s111
+ q0w * qperpy * s121
+ q0x * qperpz * s121
+ 2. * q0y * q0y * s001 * theta
+ 2. * q0z * q0z * s001 * theta
- 2. * qperpy * qperpy * s001 * theta
- 2. * qperpz * qperpz * s001 * theta
+ 2. * qperpx * qperpy * s011 * theta
- 2. * qperpw * qperpz * s011 * theta
+ 2. * qperpw * qperpy * s021 * theta
+ 2. * qperpx * qperpz * s021 * theta
+ q0y
* (-(qperpx * s011) - qperpw * s021
+ 2. * qperpy * (s001 - s101)
+ qperpx * s111
+ qperpw * s121
- 2. * q0x * s011 * theta
- 2. * q0w * s021 * theta)
+ q0z
* (2. * qperpz * s001 + qperpw * s011
- qperpx * s021
- 2. * qperpz * s101
- qperpw * s111
+ qperpx * s121
+ 2. * q0w * s011 * theta
- 2. * q0x * s021 * theta),
-(q0x * qperpy * s012) + q0w * qperpz * s012
- q0w * qperpy * s022
- q0x * qperpz * s022
+ q0x * qperpy * s112
- q0w * qperpz * s112
+ q0w * qperpy * s122
+ q0x * qperpz * s122
+ 2. * q0y * q0y * s002 * theta
+ 2. * q0z * q0z * s002 * theta
- 2. * qperpy * qperpy * s002 * theta
- 2. * qperpz * qperpz * s002 * theta
+ 2. * qperpx * qperpy * s012 * theta
- 2. * qperpw * qperpz * s012 * theta
+ 2. * qperpw * qperpy * s022 * theta
+ 2. * qperpx * qperpz * s022 * theta
+ q0y
* (-(qperpx * s012) - qperpw * s022
+ 2. * qperpy * (s002 - s102)
+ qperpx * s112
+ qperpw * s122
- 2. * q0x * s012 * theta
- 2. * q0w * s022 * theta)
+ q0z
* (2. * qperpz * s002 + qperpw * s012
- qperpx * s022
- 2. * qperpz * s102
- qperpw * s112
+ qperpx * s122
+ 2. * q0w * s012 * theta
- 2. * q0x * s022 * theta),
);
c5[0] = DerivativeTerm::new(
0.,
2. * (qperpy * qperpy * s000 + qperpz * qperpz * s000 - qperpx * qperpy * s010
+ qperpw * qperpz * s010
- qperpw * qperpy * s020
- qperpx * qperpz * s020
- qperpy * qperpy * s100
- qperpz * qperpz * s100
+ q0y * q0y * (-s000 + s100)
+ q0z * q0z * (-s000 + s100)
+ qperpx * qperpy * s110
- qperpw * qperpz * s110
+ q0y * (q0x * (s010 - s110) + q0w * (s020 - s120))
+ qperpw * qperpy * s120
+ qperpx * qperpz * s120
+ q0z * (-(q0w * s010) + q0x * s020 + q0w * s110 - q0x * s120))
* theta,
2. * (qperpy * qperpy * s001 + qperpz * qperpz * s001 - qperpx * qperpy * s011
+ qperpw * qperpz * s011
- qperpw * qperpy * s021
- qperpx * qperpz * s021
- qperpy * qperpy * s101
- qperpz * qperpz * s101
+ q0y * q0y * (-s001 + s101)
+ q0z * q0z * (-s001 + s101)
+ qperpx * qperpy * s111
- qperpw * qperpz * s111
+ q0y * (q0x * (s011 - s111) + q0w * (s021 - s121))
+ qperpw * qperpy * s121
+ qperpx * qperpz * s121
+ q0z * (-(q0w * s011) + q0x * s021 + q0w * s111 - q0x * s121))
* theta,
2. * (qperpy * qperpy * s002 + qperpz * qperpz * s002 - qperpx * qperpy * s012
+ qperpw * qperpz * s012
- qperpw * qperpy * s022
- qperpx * qperpz * s022
- qperpy * qperpy * s102
- qperpz * qperpz * s102
+ q0y * q0y * (-s002 + s102)
+ q0z * q0z * (-s002 + s102)
+ qperpx * qperpy * s112
- qperpw * qperpz * s112
+ q0y * (q0x * (s012 - s112) + q0w * (s022 - s122))
+ qperpw * qperpy * s122
+ qperpx * qperpz * s122
+ q0z * (-(q0w * s012) + q0x * s022 + q0w * s112 - q0x * s122))
* theta,
);
c1[1] = DerivativeTerm::new(
-t0y + t1y,
-(qperpx * qperpy * s000) - qperpw * qperpz * s000 - s010
+ q0z * q0z * s010
+ qperpx * qperpx * s010
+ qperpz * qperpz * s010
- q0y * q0z * s020
+ qperpw * qperpx * s020
- qperpy * qperpz * s020
+ qperpx * qperpy * s100
+ qperpw * qperpz * s100
+ q0w * q0z * (-s000 + s100)
+ q0x * q0x * (s010 - s110)
+ s110
- q0z * q0z * s110
- qperpx * qperpx * s110
- qperpz * qperpz * s110
+ q0x * (q0y * (-s000 + s100) + q0w * (s020 - s120))
+ q0y * q0z * s120
- qperpw * qperpx * s120
+ qperpy * qperpz * s120,
-(qperpx * qperpy * s001) - qperpw * qperpz * s001 - s011
+ q0z * q0z * s011
+ qperpx * qperpx * s011
+ qperpz * qperpz * s011
- q0y * q0z * s021
+ qperpw * qperpx * s021
- qperpy * qperpz * s021
+ qperpx * qperpy * s101
+ qperpw * qperpz * s101
+ q0w * q0z * (-s001 + s101)
+ q0x * q0x * (s011 - s111)
+ s111
- q0z * q0z * s111
- qperpx * qperpx * s111
- qperpz * qperpz * s111
+ q0x * (q0y * (-s001 + s101) + q0w * (s021 - s121))
+ q0y * q0z * s121
- qperpw * qperpx * s121
+ qperpy * qperpz * s121,
-(qperpx * qperpy * s002) - qperpw * qperpz * s002 - s012
+ q0z * q0z * s012
+ qperpx * qperpx * s012
+ qperpz * qperpz * s012
- q0y * q0z * s022
+ qperpw * qperpx * s022
- qperpy * qperpz * s022
+ qperpx * qperpy * s102
+ qperpw * qperpz * s102
+ q0w * q0z * (-s002 + s102)
+ q0x * q0x * (s012 - s112)
+ s112
- q0z * q0z * s112
- qperpx * qperpx * s112
- qperpz * qperpz * s112
+ q0x * (q0y * (-s002 + s102) + q0w * (s022 - s122))
+ q0y * q0z * s122
- qperpw * qperpx * s122
+ qperpy * qperpz * s122,
);
c2[1] = DerivativeTerm::new(
0.,
qperpx * qperpy * s000 + qperpw * qperpz * s000 + q0z * q0z * s010
- qperpx * qperpx * s010
- qperpz * qperpz * s010
- q0y * q0z * s020
- qperpw * qperpx * s020
+ qperpy * qperpz * s020
- qperpx * qperpy * s100
- qperpw * qperpz * s100
+ q0x * q0x * (s010 - s110)
- q0z * q0z * s110
+ qperpx * qperpx * s110
+ qperpz * qperpz * s110
+ q0y * q0z * s120
+ qperpw * qperpx * s120
- qperpy * qperpz * s120
+ 2. * q0z * qperpw * s000 * theta
+ 2. * q0y * qperpx * s000 * theta
- 4. * q0z * qperpz * s010 * theta
+ 2. * q0z * qperpy * s020 * theta
+ 2. * q0y * qperpz * s020 * theta
+ q0x
* (q0w * s020 + q0y * (-s000 + s100) - q0w * s120
+ 2. * qperpy * s000 * theta
- 4. * qperpx * s010 * theta
- 2. * qperpw * s020 * theta)
+ q0w
* (-(q0z * s000) + q0z * s100 + 2. * qperpz * s000 * theta
- 2. * qperpx * s020 * theta),
qperpx * qperpy * s001 + qperpw * qperpz * s001 + q0z * q0z * s011
- qperpx * qperpx * s011
- qperpz * qperpz * s011
- q0y * q0z * s021
- qperpw * qperpx * s021
+ qperpy * qperpz * s021
- qperpx * qperpy * s101
- qperpw * qperpz * s101
+ q0x * q0x * (s011 - s111)
- q0z * q0z * s111
+ qperpx * qperpx * s111
+ qperpz * qperpz * s111
+ q0y * q0z * s121
+ qperpw * qperpx * s121
- qperpy * qperpz * s121
+ 2. * q0z * qperpw * s001 * theta
+ 2. * q0y * qperpx * s001 * theta
- 4. * q0z * qperpz * s011 * theta
+ 2. * q0z * qperpy * s021 * theta
+ 2. * q0y * qperpz * s021 * theta
+ q0x
* (q0w * s021 + q0y * (-s001 + s101) - q0w * s121
+ 2. * qperpy * s001 * theta
- 4. * qperpx * s011 * theta
- 2. * qperpw * s021 * theta)
+ q0w
* (-(q0z * s001) + q0z * s101 + 2. * qperpz * s001 * theta
- 2. * qperpx * s021 * theta),
qperpx * qperpy * s002 + qperpw * qperpz * s002 + q0z * q0z * s012
- qperpx * qperpx * s012
- qperpz * qperpz * s012
- q0y * q0z * s022
- qperpw * qperpx * s022
+ qperpy * qperpz * s022
- qperpx * qperpy * s102
- qperpw * qperpz * s102
+ q0x * q0x * (s012 - s112)
- q0z * q0z * s112
+ qperpx * qperpx * s112
+ qperpz * qperpz * s112
+ q0y * q0z * s122
+ qperpw * qperpx * s122
- qperpy * qperpz * s122
+ 2. * q0z * qperpw * s002 * theta
+ 2. * q0y * qperpx * s002 * theta
- 4. * q0z * qperpz * s012 * theta
+ 2. * q0z * qperpy * s022 * theta
+ 2. * q0y * qperpz * s022 * theta
+ q0x
* (q0w * s022 + q0y * (-s002 + s102) - q0w * s122
+ 2. * qperpy * s002 * theta
- 4. * qperpx * s012 * theta
- 2. * qperpw * s022 * theta)
+ q0w
* (-(q0z * s002) + q0z * s102 + 2. * qperpz * s002 * theta
- 2. * qperpx * s022 * theta),
);
c3[1] = DerivativeTerm::new(
0.,
2. * (-(q0x * qperpy * s000) - q0w * qperpz * s000
+ 2. * q0x * qperpx * s010
+ q0x * qperpw * s020
+ q0w * qperpx * s020
+ q0x * qperpy * s100
+ q0w * qperpz * s100
- 2. * q0x * qperpx * s110
- q0x * qperpw * s120
- q0w * qperpx * s120
+ q0z
* (2. * qperpz * s010 - qperpy * s020 + qperpw * (-s000 + s100)
- 2. * qperpz * s110
+ qperpy * s120)
+ q0y * (-(qperpx * s000) - qperpz * s020 + qperpx * s100 + qperpz * s120))
* theta,
2. * (-(q0x * qperpy * s001) - q0w * qperpz * s001
+ 2. * q0x * qperpx * s011
+ q0x * qperpw * s021
+ q0w * qperpx * s021
+ q0x * qperpy * s101
+ q0w * qperpz * s101
- 2. * q0x * qperpx * s111
- q0x * qperpw * s121
- q0w * qperpx * s121
+ q0z
* (2. * qperpz * s011 - qperpy * s021 + qperpw * (-s001 + s101)
- 2. * qperpz * s111
+ qperpy * s121)
+ q0y * (-(qperpx * s001) - qperpz * s021 + qperpx * s101 + qperpz * s121))
* theta,
2. * (-(q0x * qperpy * s002) - q0w * qperpz * s002
+ 2. * q0x * qperpx * s012
+ q0x * qperpw * s022
+ q0w * qperpx * s022
+ q0x * qperpy * s102
+ q0w * qperpz * s102
- 2. * q0x * qperpx * s112
- q0x * qperpw * s122
- q0w * qperpx * s122
+ q0z
* (2. * qperpz * s012 - qperpy * s022 + qperpw * (-s002 + s102)
- 2. * qperpz * s112
+ qperpy * s122)
+ q0y * (-(qperpx * s002) - qperpz * s022 + qperpx * s102 + qperpz * s122))
* theta,
);
c4[1] = DerivativeTerm::new(
0.,
-(q0x * qperpy * s000) - q0w * qperpz * s000
+ 2. * q0x * qperpx * s010
+ q0x * qperpw * s020
+ q0w * qperpx * s020
+ q0x * qperpy * s100
+ q0w * qperpz * s100
- 2. * q0x * qperpx * s110
- q0x * qperpw * s120
- q0w * qperpx * s120
+ 2. * qperpx * qperpy * s000 * theta
+ 2. * qperpw * qperpz * s000 * theta
+ 2. * q0x * q0x * s010 * theta
+ 2. * q0z * q0z * s010 * theta
- 2. * qperpx * qperpx * s010 * theta
- 2. * qperpz * qperpz * s010 * theta
+ 2. * q0w * q0x * s020 * theta
- 2. * qperpw * qperpx * s020 * theta
+ 2. * qperpy * qperpz * s020 * theta
+ q0y
* (-(qperpx * s000) - qperpz * s020 + qperpx * s100 + qperpz * s120
- 2. * q0x * s000 * theta)
+ q0z
* (2. * qperpz * s010 - qperpy * s020 + qperpw * (-s000 + s100)
- 2. * qperpz * s110
+ qperpy * s120
- 2. * q0w * s000 * theta
- 2. * q0y * s020 * theta),
-(q0x * qperpy * s001) - q0w * qperpz * s001
+ 2. * q0x * qperpx * s011
+ q0x * qperpw * s021
+ q0w * qperpx * s021
+ q0x * qperpy * s101
+ q0w * qperpz * s101
- 2. * q0x * qperpx * s111
- q0x * qperpw * s121
- q0w * qperpx * s121
+ 2. * qperpx * qperpy * s001 * theta
+ 2. * qperpw * qperpz * s001 * theta
+ 2. * q0x * q0x * s011 * theta
+ 2. * q0z * q0z * s011 * theta
- 2. * qperpx * qperpx * s011 * theta
- 2. * qperpz * qperpz * s011 * theta
+ 2. * q0w * q0x * s021 * theta
- 2. * qperpw * qperpx * s021 * theta
+ 2. * qperpy * qperpz * s021 * theta
+ q0y
* (-(qperpx * s001) - qperpz * s021 + qperpx * s101 + qperpz * s121
- 2. * q0x * s001 * theta)
+ q0z
* (2. * qperpz * s011 - qperpy * s021 + qperpw * (-s001 + s101)
- 2. * qperpz * s111
+ qperpy * s121
- 2. * q0w * s001 * theta
- 2. * q0y * s021 * theta),
-(q0x * qperpy * s002) - q0w * qperpz * s002
+ 2. * q0x * qperpx * s012
+ q0x * qperpw * s022
+ q0w * qperpx * s022
+ q0x * qperpy * s102
+ q0w * qperpz * s102
- 2. * q0x * qperpx * s112
- q0x * qperpw * s122
- q0w * qperpx * s122
+ 2. * qperpx * qperpy * s002 * theta
+ 2. * qperpw * qperpz * s002 * theta
+ 2. * q0x * q0x * s012 * theta
+ 2. * q0z * q0z * s012 * theta
- 2. * qperpx * qperpx * s012 * theta
- 2. * qperpz * qperpz * s012 * theta
+ 2. * q0w * q0x * s022 * theta
- 2. * qperpw * qperpx * s022 * theta
+ 2. * qperpy * qperpz * s022 * theta
+ q0y
* (-(qperpx * s002) - qperpz * s022 + qperpx * s102 + qperpz * s122
- 2. * q0x * s002 * theta)
+ q0z
* (2. * qperpz * s012 - qperpy * s022 + qperpw * (-s002 + s102)
- 2. * qperpz * s112
+ qperpy * s122
- 2. * q0w * s002 * theta
- 2. * q0y * s022 * theta),
);
c5[1] = DerivativeTerm::new(
0.,
-2. * (qperpx * qperpy * s000 + qperpw * qperpz * s000 + q0z * q0z * s010
- qperpx * qperpx * s010
- qperpz * qperpz * s010
- q0y * q0z * s020
- qperpw * qperpx * s020
+ qperpy * qperpz * s020
- qperpx * qperpy * s100
- qperpw * qperpz * s100
+ q0w * q0z * (-s000 + s100)
+ q0x * q0x * (s010 - s110)
- q0z * q0z * s110
+ qperpx * qperpx * s110
+ qperpz * qperpz * s110
+ q0x * (q0y * (-s000 + s100) + q0w * (s020 - s120))
+ q0y * q0z * s120
+ qperpw * qperpx * s120
- qperpy * qperpz * s120)
* theta,
-2. * (qperpx * qperpy * s001 + qperpw * qperpz * s001 + q0z * q0z * s011
- qperpx * qperpx * s011
- qperpz * qperpz * s011
- q0y * q0z * s021
- qperpw * qperpx * s021
+ qperpy * qperpz * s021
- qperpx * qperpy * s101
- qperpw * qperpz * s101
+ q0w * q0z * (-s001 + s101)
+ q0x * q0x * (s011 - s111)
- q0z * q0z * s111
+ qperpx * qperpx * s111
+ qperpz * qperpz * s111
+ q0x * (q0y * (-s001 + s101) + q0w * (s021 - s121))
+ q0y * q0z * s121
+ qperpw * qperpx * s121
- qperpy * qperpz * s121)
* theta,
-2. * (qperpx * qperpy * s002 + qperpw * qperpz * s002 + q0z * q0z * s012
- qperpx * qperpx * s012
- qperpz * qperpz * s012
- q0y * q0z * s022
- qperpw * qperpx * s022
+ qperpy * qperpz * s022
- qperpx * qperpy * s102
- qperpw * qperpz * s102
+ q0w * q0z * (-s002 + s102)
+ q0x * q0x * (s012 - s112)
- q0z * q0z * s112
+ qperpx * qperpx * s112
+ qperpz * qperpz * s112
+ q0x * (q0y * (-s002 + s102) + q0w * (s022 - s122))
+ q0y * q0z * s122
+ qperpw * qperpx * s122
- qperpy * qperpz * s122)
* theta,
);
c1[2] = DerivativeTerm::new(
-t0z + t1z,
qperpw * qperpy * s000
- qperpx * qperpz * s000
- q0y * q0z * s010
- qperpw * qperpx * s010
- qperpy * qperpz * s010
- s020
+ q0y * q0y * s020
+ qperpx * qperpx * s020
+ qperpy * qperpy * s020
- qperpw * qperpy * s100
+ qperpx * qperpz * s100
+ q0x * q0z * (-s000 + s100)
+ q0y * q0z * s110
+ qperpw * qperpx * s110
+ qperpy * qperpz * s110
+ q0w * (q0y * (s000 - s100) + q0x * (-s010 + s110))
+ q0x * q0x * (s020 - s120)
+ s120
- q0y * q0y * s120
- qperpx * qperpx * s120
- qperpy * qperpy * s120,
qperpw * qperpy * s001
- qperpx * qperpz * s001
- q0y * q0z * s011
- qperpw * qperpx * s011
- qperpy * qperpz * s011
- s021
+ q0y * q0y * s021
+ qperpx * qperpx * s021
+ qperpy * qperpy * s021
- qperpw * qperpy * s101
+ qperpx * qperpz * s101
+ q0x * q0z * (-s001 + s101)
+ q0y * q0z * s111
+ qperpw * qperpx * s111
+ qperpy * qperpz * s111
+ q0w * (q0y * (s001 - s101) + q0x * (-s011 + s111))
+ q0x * q0x * (s021 - s121)
+ s121
- q0y * q0y * s121
- qperpx * qperpx * s121
- qperpy * qperpy * s121,
qperpw * qperpy * s002
- qperpx * qperpz * s002
- q0y * q0z * s012
- qperpw * qperpx * s012
- qperpy * qperpz * s012
- s022
+ q0y * q0y * s022
+ qperpx * qperpx * s022
+ qperpy * qperpy * s022
- qperpw * qperpy * s102
+ qperpx * qperpz * s102
+ q0x * q0z * (-s002 + s102)
+ q0y * q0z * s112
+ qperpw * qperpx * s112
+ qperpy * qperpz * s112
+ q0w * (q0y * (s002 - s102) + q0x * (-s012 + s112))
+ q0x * q0x * (s022 - s122)
+ s122
- q0y * q0y * s122
- qperpx * qperpx * s122
- qperpy * qperpy * s122,
);
c2[2] = DerivativeTerm::new(
0.,
q0w * q0y * s000 - q0x * q0z * s000 - qperpw * qperpy * s000
+ qperpx * qperpz * s000
- q0w * q0x * s010
- q0y * q0z * s010
+ qperpw * qperpx * s010
+ qperpy * qperpz * s010
+ q0x * q0x * s020
+ q0y * q0y * s020
- qperpx * qperpx * s020
- qperpy * qperpy * s020
- q0w * q0y * s100
+ q0x * q0z * s100
+ qperpw * qperpy * s100
- qperpx * qperpz * s100
+ q0w * q0x * s110
+ q0y * q0z * s110
- qperpw * qperpx * s110
- qperpy * qperpz * s110
- q0x * q0x * s120
- q0y * q0y * s120
+ qperpx * qperpx * s120
+ qperpy * qperpy * s120
- 2. * q0y * qperpw * s000 * theta
+ 2. * q0z * qperpx * s000 * theta
- 2. * q0w * qperpy * s000 * theta
+ 2. * q0x * qperpz * s000 * theta
+ 2. * q0x * qperpw * s010 * theta
+ 2. * q0w * qperpx * s010 * theta
+ 2. * q0z * qperpy * s010 * theta
+ 2. * q0y * qperpz * s010 * theta
- 4. * q0x * qperpx * s020 * theta
- 4. * q0y * qperpy * s020 * theta,
q0w * q0y * s001 - q0x * q0z * s001 - qperpw * qperpy * s001
+ qperpx * qperpz * s001
- q0w * q0x * s011
- q0y * q0z * s011
+ qperpw * qperpx * s011
+ qperpy * qperpz * s011
+ q0x * q0x * s021
+ q0y * q0y * s021
- qperpx * qperpx * s021
- qperpy * qperpy * s021
- q0w * q0y * s101
+ q0x * q0z * s101
+ qperpw * qperpy * s101
- qperpx * qperpz * s101
+ q0w * q0x * s111
+ q0y * q0z * s111
- qperpw * qperpx * s111
- qperpy * qperpz * s111
- q0x * q0x * s121
- q0y * q0y * s121
+ qperpx * qperpx * s121
+ qperpy * qperpy * s121
- 2. * q0y * qperpw * s001 * theta
+ 2. * q0z * qperpx * s001 * theta
- 2. * q0w * qperpy * s001 * theta
+ 2. * q0x * qperpz * s001 * theta
+ 2. * q0x * qperpw * s011 * theta
+ 2. * q0w * qperpx * s011 * theta
+ 2. * q0z * qperpy * s011 * theta
+ 2. * q0y * qperpz * s011 * theta
- 4. * q0x * qperpx * s021 * theta
- 4. * q0y * qperpy * s021 * theta,
q0w * q0y * s002 - q0x * q0z * s002 - qperpw * qperpy * s002
+ qperpx * qperpz * s002
- q0w * q0x * s012
- q0y * q0z * s012
+ qperpw * qperpx * s012
+ qperpy * qperpz * s012
+ q0x * q0x * s022
+ q0y * q0y * s022
- qperpx * qperpx * s022
- qperpy * qperpy * s022
- q0w * q0y * s102
+ q0x * q0z * s102
+ qperpw * qperpy * s102
- qperpx * qperpz * s102
+ q0w * q0x * s112
+ q0y * q0z * s112
- qperpw * qperpx * s112
- qperpy * qperpz * s112
- q0x * q0x * s122
- q0y * q0y * s122
+ qperpx * qperpx * s122
+ qperpy * qperpy * s122
- 2. * q0y * qperpw * s002 * theta
+ 2. * q0z * qperpx * s002 * theta
- 2. * q0w * qperpy * s002 * theta
+ 2. * q0x * qperpz * s002 * theta
+ 2. * q0x * qperpw * s012 * theta
+ 2. * q0w * qperpx * s012 * theta
+ 2. * q0z * qperpy * s012 * theta
+ 2. * q0y * qperpz * s012 * theta
- 4. * q0x * qperpx * s022 * theta
- 4. * q0y * qperpy * s022 * theta,
);
c3[2] = DerivativeTerm::new(
0.,
-2. * (-(q0w * qperpy * s000)
+ q0x * qperpz * s000
+ q0x * qperpw * s010
+ q0w * qperpx * s010
- 2. * q0x * qperpx * s020
+ q0w * qperpy * s100
- q0x * qperpz * s100
- q0x * qperpw * s110
- q0w * qperpx * s110
+ q0z * (qperpx * s000 + qperpy * s010 - qperpx * s100 - qperpy * s110)
+ 2. * q0x * qperpx * s120
+ q0y
* (qperpz * s010 - 2. * qperpy * s020 + qperpw * (-s000 + s100)
- qperpz * s110
+ 2. * qperpy * s120))
* theta,
-2. * (-(q0w * qperpy * s001)
+ q0x * qperpz * s001
+ q0x * qperpw * s011
+ q0w * qperpx * s011
- 2. * q0x * qperpx * s021
+ q0w * qperpy * s101
- q0x * qperpz * s101
- q0x * qperpw * s111
- q0w * qperpx * s111
+ q0z * (qperpx * s001 + qperpy * s011 - qperpx * s101 - qperpy * s111)
+ 2. * q0x * qperpx * s121
+ q0y
* (qperpz * s011 - 2. * qperpy * s021 + qperpw * (-s001 + s101)
- qperpz * s111
+ 2. * qperpy * s121))
* theta,
-2. * (-(q0w * qperpy * s002)
+ q0x * qperpz * s002
+ q0x * qperpw * s012
+ q0w * qperpx * s012
- 2. * q0x * qperpx * s022
+ q0w * qperpy * s102
- q0x * qperpz * s102
- q0x * qperpw * s112
- q0w * qperpx * s112
+ q0z * (qperpx * s002 + qperpy * s012 - qperpx * s102 - qperpy * s112)
+ 2. * q0x * qperpx * s122
+ q0y
* (qperpz * s012 - 2. * qperpy * s022 + qperpw * (-s002 + s102)
- qperpz * s112
+ 2. * qperpy * s122))
* theta,
);
c4[2] = DerivativeTerm::new(
0.,
q0w * qperpy * s000
- q0x * qperpz * s000
- q0x * qperpw * s010
- q0w * qperpx * s010
+ 2. * q0x * qperpx * s020
- q0w * qperpy * s100
+ q0x * qperpz * s100
+ q0x * qperpw * s110
+ q0w * qperpx * s110
- 2. * q0x * qperpx * s120
- 2. * qperpw * qperpy * s000 * theta
+ 2. * qperpx * qperpz * s000 * theta
- 2. * q0w * q0x * s010 * theta
+ 2. * qperpw * qperpx * s010 * theta
+ 2. * qperpy * qperpz * s010 * theta
+ 2. * q0x * q0x * s020 * theta
+ 2. * q0y * q0y * s020 * theta
- 2. * qperpx * qperpx * s020 * theta
- 2. * qperpy * qperpy * s020 * theta
+ q0z
* (-(qperpx * s000) - qperpy * s010 + qperpx * s100 + qperpy * s110
- 2. * q0x * s000 * theta)
+ q0y
* (-(qperpz * s010)
+ 2. * qperpy * s020
+ qperpw * (s000 - s100)
+ qperpz * s110
- 2. * qperpy * s120
+ 2. * q0w * s000 * theta
- 2. * q0z * s010 * theta),
q0w * qperpy * s001
- q0x * qperpz * s001
- q0x * qperpw * s011
- q0w * qperpx * s011
+ 2. * q0x * qperpx * s021
- q0w * qperpy * s101
+ q0x * qperpz * s101
+ q0x * qperpw * s111
+ q0w * qperpx * s111
- 2. * q0x * qperpx * s121
- 2. * qperpw * qperpy * s001 * theta
+ 2. * qperpx * qperpz * s001 * theta
- 2. * q0w * q0x * s011 * theta
+ 2. * qperpw * qperpx * s011 * theta
+ 2. * qperpy * qperpz * s011 * theta
+ 2. * q0x * q0x * s021 * theta
+ 2. * q0y * q0y * s021 * theta
- 2. * qperpx * qperpx * s021 * theta
- 2. * qperpy * qperpy * s021 * theta
+ q0z
* (-(qperpx * s001) - qperpy * s011 + qperpx * s101 + qperpy * s111
- 2. * q0x * s001 * theta)
+ q0y
* (-(qperpz * s011)
+ 2. * qperpy * s021
+ qperpw * (s001 - s101)
+ qperpz * s111
- 2. * qperpy * s121
+ 2. * q0w * s001 * theta
- 2. * q0z * s011 * theta),
q0w * qperpy * s002
- q0x * qperpz * s002
- q0x * qperpw * s012
- q0w * qperpx * s012
+ 2. * q0x * qperpx * s022
- q0w * qperpy * s102
+ q0x * qperpz * s102
+ q0x * qperpw * s112
+ q0w * qperpx * s112
- 2. * q0x * qperpx * s122
- 2. * qperpw * qperpy * s002 * theta
+ 2. * qperpx * qperpz * s002 * theta
- 2. * q0w * q0x * s012 * theta
+ 2. * qperpw * qperpx * s012 * theta
+ 2. * qperpy * qperpz * s012 * theta
+ 2. * q0x * q0x * s022 * theta
+ 2. * q0y * q0y * s022 * theta
- 2. * qperpx * qperpx * s022 * theta
- 2. * qperpy * qperpy * s022 * theta
+ q0z
* (-(qperpx * s002) - qperpy * s012 + qperpx * s102 + qperpy * s112
- 2. * q0x * s002 * theta)
+ q0y
* (-(qperpz * s012)
+ 2. * qperpy * s022
+ qperpw * (s002 - s102)
+ qperpz * s112
- 2. * qperpy * s122
+ 2. * q0w * s002 * theta
- 2. * q0z * s012 * theta),
);
c5[2] = DerivativeTerm::new(
0.,
2. * (qperpw * qperpy * s000 - qperpx * qperpz * s000 + q0y * q0z * s010
- qperpw * qperpx * s010
- qperpy * qperpz * s010
- q0y * q0y * s020
+ qperpx * qperpx * s020
+ qperpy * qperpy * s020
+ q0x * q0z * (s000 - s100)
- qperpw * qperpy * s100
+ qperpx * qperpz * s100
+ q0w * (q0y * (-s000 + s100) + q0x * (s010 - s110))
- q0y * q0z * s110
+ qperpw * qperpx * s110
+ qperpy * qperpz * s110
+ q0y * q0y * s120
- qperpx * qperpx * s120
- qperpy * qperpy * s120
+ q0x * q0x * (-s020 + s120))
* theta,
2. * (qperpw * qperpy * s001 - qperpx * qperpz * s001 + q0y * q0z * s011
- qperpw * qperpx * s011
- qperpy * qperpz * s011
- q0y * q0y * s021
+ qperpx * qperpx * s021
+ qperpy * qperpy * s021
+ q0x * q0z * (s001 - s101)
- qperpw * qperpy * s101
+ qperpx * qperpz * s101
+ q0w * (q0y * (-s001 + s101) + q0x * (s011 - s111))
- q0y * q0z * s111
+ qperpw * qperpx * s111
+ qperpy * qperpz * s111
+ q0y * q0y * s121
- qperpx * qperpx * s121
- qperpy * qperpy * s121
+ q0x * q0x * (-s021 + s121))
* theta,
2. * (qperpw * qperpy * s002 - qperpx * qperpz * s002 + q0y * q0z * s012
- qperpw * qperpx * s012
- qperpy * qperpz * s012
- q0y * q0y * s022
+ qperpx * qperpx * s022
+ qperpy * qperpy * s022
+ q0x * q0z * (s002 - s102)
- qperpw * qperpy * s102
+ qperpx * qperpz * s102
+ q0w * (q0y * (-s002 + s102) + q0x * (s012 - s112))
- q0y * q0z * s112
+ qperpw * qperpx * s112
+ qperpy * qperpz * s112
+ q0y * q0y * s122
- qperpx * qperpx * s122
- qperpy * qperpy * s122
+ q0x * q0x * (-s022 + s122))
* theta,
);
(c1, c2, c3, c4, c5)
} else {
(
gpu_array_from_fn(|_| DerivativeTerm::default()),
gpu_array_from_fn(|_| DerivativeTerm::default()),
gpu_array_from_fn(|_| DerivativeTerm::default()),
gpu_array_from_fn(|_| DerivativeTerm::default()),
gpu_array_from_fn(|_| DerivativeTerm::default()),
)
};
AnimatedTransform {
start_transform: *start_transform,
end_transform: *end_transform,
start_time,
end_time,
actually_animated,
t,
r,
s,
has_rotation,
c1,
c2,
c3,
c4,
c5,
}
}
pub fn apply_point(&self, p: Point3f, time: Float) -> Point3f {
if !self.actually_animated || time <= self.start_time {
return self.start_transform.apply_to_point(p);
} else if time >= self.end_time {
return self.end_transform.apply_to_point(p);
}
let t = self.interpolate(time);
t.apply_to_point(p)
}
pub fn apply_vector(&self, v: Vector3f, time: Float) -> Vector3f {
if !self.actually_animated || time <= self.start_time {
return self.start_transform.apply_to_vector(v);
} else if time >= self.end_time {
return self.end_transform.apply_to_vector(v);
}
let t = self.interpolate(time);
t.apply_to_vector(v)
}
pub fn apply_ray(&self, r: &Ray, t_max: &mut Option<Float>) -> Ray {
if !self.actually_animated || r.time <= self.start_time {
return self.start_transform.apply_to_ray(r, t_max);
} else if r.time >= self.end_time {
return self.end_transform.apply_to_ray(r, t_max);
}
let t = self.interpolate(r.time);
t.apply_to_ray(r, t_max)
}
pub fn apply_interaction(&self, si: &Interaction) -> Interaction {
if !self.actually_animated {
return self.start_transform.apply_to_interaction(si);
}
let t = self.interpolate(si.time());
t.apply_to_interaction(si)
}
pub fn interpolate(&self, time: Float) -> TransformGeneric<Float> {
if !self.actually_animated || time <= self.start_time {
return self.start_transform;
}
if time >= self.end_time {
return self.end_transform;
}
let dt = (time - self.start_time) / (self.end_time - self.start_time);
let trans = (1.0 - dt) * self.t[0] + dt * self.t[1];
let rotate = Quaternion::slerp(dt, self.r[0], self.r[1]);
let scale = (1.0 - dt) * self.s[0] + dt * self.s[1];
let scale_transform =
TransformGeneric::from_matrix(scale).expect("Scale matrix is not inversible");
TransformGeneric::translate(trans) * TransformGeneric::from(rotate) * scale_transform
}
pub fn apply_inverse_point(&self, p: Point3f, time: Float) -> Point3f {
if !self.actually_animated {
return self.start_transform.apply_inverse(p);
}
self.interpolate(time).apply_inverse(p)
}
pub fn apply_inverse_vector(&self, v: Vector3f, time: Float) -> Vector3f {
if !self.actually_animated {
return self.start_transform.apply_inverse_vector(v);
}
self.interpolate(time).apply_inverse_vector(v)
}
pub fn apply_inverse_normal(&self, n: Normal3f, time: Float) -> Normal3f {
if !self.actually_animated {
return self.start_transform.apply_inverse_normal(n);
}
self.interpolate(time).apply_inverse_normal(n)
}
pub fn motion_bounds(&self, b: &Bounds3f) -> Bounds3f {
if !self.actually_animated {
return self.start_transform.apply_to_bounds(*b);
}
self.start_transform
.apply_to_bounds(*b)
.union(self.end_transform.apply_to_bounds(*b))
}
pub fn is_animated(&self) -> bool {
self.actually_animated
}
}
pub fn look_at(
pos: impl Into<Point3f>,
look: impl Into<Point3f>,
up: impl Into<Point3f>,
) -> Option<TransformGeneric<Float>> {
let mut world_from_camera: SquareMatrix<Float, 4> = SquareMatrix::default();
// Initialize fourth column of viewing matrix
let pos: Point3f = pos.into();
let look: Point3f = look.into();
let up: Point3f = up.into();
world_from_camera[0][3] = pos.x();
world_from_camera[1][3] = pos.y();
world_from_camera[2][3] = pos.z();
world_from_camera[3][3] = 1.;
// Initialize first three columns of viewing matrix
let dir = (look - pos).normalize();
if Vector3f::from(up).normalize().cross(dir).norm() == 0. {
panic!(
"LookAt: \"up\" vector ({}, {}, {}) and viewing direction ({}, {}, {}) passed to LookAt are pointing in the same direction.",
up.x(),
up.y(),
up.z(),
dir.x(),
dir.y(),
dir.z()
);
}
let right = Vector3f::from(up).normalize().cross(dir).normalize();
let new_up = dir.cross(right);
world_from_camera[0][0] = right.x();
world_from_camera[1][0] = right.y();
world_from_camera[2][0] = right.z();
world_from_camera[3][0] = 0.;
world_from_camera[0][1] = new_up.x();
world_from_camera[1][1] = new_up.y();
world_from_camera[2][1] = new_up.z();
world_from_camera[3][1] = 0.;
world_from_camera[0][2] = dir.x();
world_from_camera[1][2] = dir.y();
world_from_camera[2][2] = dir.z();
world_from_camera[3][2] = 0.;
let camera_from_world = world_from_camera.inverse()?;
Some(TransformGeneric::new(camera_from_world, world_from_camera))
}