use approx::{AbsDiffEq, RelativeEq, UlpsEq};
use nalgebra::{Isometry3, Point3, RealField, Unit, UnitQuaternion, Vector3};
use simba::scalar::SubsetOf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Frame<N: Copy + RealField> {
pos: Point3<N>,
rot: UnitQuaternion<N>,
zat: N,
}
impl<N: Copy + RealField> Frame<N> {
#[must_use]
pub fn look_at(target: Point3<N>, eye: &Point3<N>, up: &Vector3<N>) -> Self {
let dir = target - eye;
Self {
pos: target,
rot: UnitQuaternion::face_towards(&-dir, up),
zat: dir.norm(),
}
}
#[must_use]
pub fn eye(&self) -> Point3<N> {
self.pos + self.rot * Vector3::z_axis().into_inner() * self.zat
}
pub fn set_eye(&mut self, eye: &Point3<N>, up: &Vector3<N>) {
*self = Self::look_at(self.pos, eye, up);
}
#[must_use]
pub const fn target(&self) -> &Point3<N> {
&self.pos
}
pub fn set_target(&mut self, pos: Point3<N>) {
let eye = self.eye();
let (new_dir, zat) = Unit::new_and_get(pos - eye);
let old_dir = Unit::new_normalize(self.pos - eye);
let rot = UnitQuaternion::rotation_between_axis(&old_dir, &new_dir)
.unwrap_or_else(|| UnitQuaternion::from_axis_angle(&self.yaw_axis(), N::pi()));
let rot = rot * self.rot;
*self = Self { pos, rot, zat }
}
#[must_use]
pub const fn distance(&self) -> N {
self.zat
}
pub fn set_distance(&mut self, zat: N) {
self.zat = zat;
}
pub fn scale(&mut self, rat: N) {
self.zat *= rat;
}
pub fn local_scale_around(&mut self, rat: N, pos: &Point3<N>) {
self.local_slide(&(pos - pos * rat));
self.scale(rat);
}
pub fn scale_around(&mut self, rat: N, pos: &Point3<N>) {
let pos = pos - self.pos.coords;
self.slide(&(pos - pos * rat));
self.scale(rat);
}
pub fn local_slide(&mut self, vec: &Vector3<N>) {
self.pos += self.rot * vec;
}
pub fn slide(&mut self, vec: &Vector3<N>) {
self.pos += vec;
}
pub fn local_orbit(&mut self, rot: &UnitQuaternion<N>) {
self.rot *= rot;
}
pub fn local_orbit_around(&mut self, rot: &UnitQuaternion<N>, pos: &Point3<N>) {
self.local_slide(&(pos - rot * pos));
self.local_orbit(rot);
}
pub fn orbit(&mut self, rot: &UnitQuaternion<N>) {
self.rot = rot * self.rot;
}
pub fn orbit_around(&mut self, rot: &UnitQuaternion<N>, pos: &Point3<N>) {
let pos = pos - self.pos.coords;
self.slide(&(pos - rot * pos));
self.orbit(rot);
}
pub fn look_around(&mut self, pitch: N, yaw: N, yaw_axis: &Unit<Vector3<N>>) {
let pitch = UnitQuaternion::from_axis_angle(&self.pitch_axis(), pitch);
let yaw = UnitQuaternion::from_axis_angle(yaw_axis, yaw);
self.orbit_around(&(yaw * pitch), &self.eye());
}
#[allow(clippy::unused_self)]
#[must_use]
pub fn local_pitch_axis(&self) -> Unit<Vector3<N>> {
Vector3::x_axis()
}
#[allow(clippy::unused_self)]
#[must_use]
pub fn local_yaw_axis(&self) -> Unit<Vector3<N>> {
Vector3::y_axis()
}
#[allow(clippy::unused_self)]
#[must_use]
pub fn local_roll_axis(&self) -> Unit<Vector3<N>> {
Vector3::z_axis()
}
#[must_use]
pub fn pitch_axis(&self) -> Unit<Vector3<N>> {
self.rot * self.local_pitch_axis()
}
#[must_use]
pub fn yaw_axis(&self) -> Unit<Vector3<N>> {
self.rot * self.local_yaw_axis()
}
#[must_use]
pub fn roll_axis(&self) -> Unit<Vector3<N>> {
self.rot * self.local_roll_axis()
}
#[must_use]
pub fn try_lerp_slerp(&self, other: &Self, t: N, epsilon: N) -> Option<Self> {
Some(Self {
pos: self.pos.lerp(&other.pos, t),
rot: self.rot.try_slerp(&other.rot, t, epsilon)?,
zat: self.zat * (N::one() - t) + other.zat * t,
})
}
pub fn renormalize(&mut self) -> N {
self.rot.renormalize()
}
#[must_use]
pub fn view(&self) -> Isometry3<N> {
Isometry3::from_parts(
self.eye().into(),
self.rot,
)
}
#[must_use]
pub fn inverse_view(&self) -> Isometry3<N> {
let rot = self.rot.inverse();
let eye = rot * self.pos + Vector3::z_axis().into_inner() * self.zat;
Isometry3::from_parts((-eye.coords).into(), rot)
}
#[must_use]
pub fn cast<M: Copy + RealField>(self) -> Frame<M>
where
N: SubsetOf<M>,
{
Frame {
pos: self.pos.cast(),
rot: self.rot.cast(),
zat: self.zat.to_superset(),
}
}
}
impl<N: Copy + RealField + AbsDiffEq> AbsDiffEq for Frame<N>
where
N::Epsilon: Copy,
{
type Epsilon = N::Epsilon;
fn default_epsilon() -> N::Epsilon {
N::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: N::Epsilon) -> bool {
self.pos.abs_diff_eq(&other.pos, epsilon)
&& self.rot.abs_diff_eq(&other.rot, epsilon)
&& self.zat.abs_diff_eq(&other.zat, epsilon)
}
}
impl<N: Copy + RealField + RelativeEq> RelativeEq for Frame<N>
where
N::Epsilon: Copy,
{
fn default_max_relative() -> N::Epsilon {
N::default_max_relative()
}
fn relative_eq(&self, other: &Self, epsilon: N::Epsilon, max_relative: N::Epsilon) -> bool {
self.pos.relative_eq(&other.pos, epsilon, max_relative)
&& self.rot.relative_eq(&other.rot, epsilon, max_relative)
&& self.zat.relative_eq(&other.zat, epsilon, max_relative)
}
}
impl<N: Copy + RealField + UlpsEq> UlpsEq for Frame<N>
where
N::Epsilon: Copy,
{
fn default_max_ulps() -> u32 {
N::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: N::Epsilon, max_ulps: u32) -> bool {
self.pos.ulps_eq(&other.pos, epsilon, max_ulps)
&& self.rot.ulps_eq(&other.rot, epsilon, max_ulps)
&& self.zat.ulps_eq(&other.zat, epsilon, max_ulps)
}
}
#[cfg(feature = "rkyv")]
impl<N: Copy + RealField> rkyv::Archive for Frame<N> {
type Archived = Self;
type Resolver = ();
#[inline]
#[allow(unsafe_code)]
unsafe fn resolve(&self, _: usize, (): Self::Resolver, out: *mut Self::Archived) {
out.write(rkyv::to_archived!(*self as Self));
}
}
#[cfg(feature = "rkyv")]
impl<Ser: rkyv::Fallible + ?Sized, N: Copy + RealField> rkyv::Serialize<Ser> for Frame<N> {
#[inline]
fn serialize(&self, _: &mut Ser) -> Result<Self::Resolver, Ser::Error> {
Ok(())
}
}
#[cfg(feature = "rkyv")]
impl<De: rkyv::Fallible + ?Sized, N: Copy + RealField> rkyv::Deserialize<Self, De> for Frame<N> {
#[inline]
fn deserialize(&self, _: &mut De) -> Result<Self, De::Error> {
Ok(rkyv::from_archived!(*self))
}
}