trackball/scope.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
use crate::Fixed;
use nalgebra::{convert, Matrix4, Point2, RealField};
use simba::scalar::SubsetOf;
/// Scope defining enclosing viewing frustum.
///
/// Implements [`Default`] and can be created with `Scope::default()`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Scope<N: Copy + RealField> {
/// Fixed quantity wrt field of view.
///
/// Default is fixed vertical field of view of π/4.
fov: Fixed<N>,
/// Clip plane distances.
///
/// Near and far clip plane distances from either target or eye whether [`Self::oim`]. Default
/// is `(1e-1, 1e+3)` measured from eye.
zcp: (N, N),
/// Object inspection mode.
///
/// Scales clip plane distances by measuring from target instead of eye. Default is `false`.
oim: bool,
/// Orthographic projection mode.
///
/// Computes scale-identical orthographic instead of perspective projection. Default is `false`.
opm: bool,
}
impl<N: Copy + RealField> Default for Scope<N> {
fn default() -> Self {
Self {
fov: Fixed::default(),
zcp: (convert(1e-1), convert(1e+3)),
oim: false,
opm: false,
}
}
}
impl<N: Copy + RealField> Scope<N> {
/// Fixed quantity wrt field of view, see [`Self::set_fov()`].
#[must_use]
pub const fn fov(&self) -> Fixed<N> {
self.fov
}
/// Sets fixed quantity wrt field of view.
///
/// Default is fixed vertical field of view of π/4.
///
/// ```
/// use nalgebra::Point2;
/// use trackball::Scope;
///
/// // Current screen size.
/// let max = Point2::new(800, 600);
/// // Default scope with fixed vertical field of view of π/4:
/// //
/// // * Increasing width increases horizontal field of view (more can be seen).
/// // * Increasing height scales scope zooming in as vertical field of view is fixed.
/// let mut scope = Scope::default();
/// // Unfix vertical field of view by fixing current unit per pixel on focus plane at distance
/// // from eye of one, that is effectively `upp` divided by `zat` to make it scale-independant:
/// //
/// // * Increasing width increases horizontal field of view (more can be seen).
/// // * Increasing height increases vertical field of view (more can be seen).
/// scope.set_fov(scope.fov().to_upp(&max.cast::<f32>()));
/// ```
pub fn set_fov(&mut self, fov: impl Into<Fixed<N>>) {
self.fov = fov.into();
}
/// Clip plane distances from eye regardless of [`Self::scale()`] wrt to distance between eye
/// and target.
///
/// Default is `(1e-1, 1e+3)` measured from eye.
#[must_use]
pub fn clip_planes(&self, zat: N) -> (N, N) {
if self.oim {
let (znear, zfar) = self.zcp;
(zat - znear, zat + zfar)
} else {
self.zcp
}
}
/// Sets clip plane distances from target or eye whether [`Self::scale()`].
///
/// Default is `(1e-1, 1e+3)` measured from eye.
pub fn set_clip_planes(&mut self, znear: N, zfar: N) {
self.zcp = (znear, zfar);
}
/// Object inspection mode.
///
/// Scales clip plane distances by measuring from target instead of eye. Default is `false`.
#[must_use]
pub const fn scale(&self) -> bool {
self.oim
}
/// Sets object inspection mode.
///
/// Scales clip plane distances by measuring from target instead of eye. Default is `false`.
pub fn set_scale(&mut self, oim: bool) {
self.oim = oim;
}
/// Orthographic projection mode.
///
/// Computes scale-identical orthographic instead of perspective projection. Default is `false`.
#[must_use]
pub const fn ortho(&self) -> bool {
self.opm
}
/// Sets orthographic projection mode.
///
/// Computes scale-identical orthographic instead of perspective projection. Default is `false`.
pub fn set_ortho(&mut self, opm: bool) {
self.opm = opm;
}
/// Projection transformation and unit per pixel on focus plane wrt distance between eye and
/// target and maximum position in screen space.
#[must_use]
pub fn projection_and_upp(&self, zat: N, max: &Point2<N>) -> (Matrix4<N>, N) {
let (znear, zfar) = self.clip_planes(zat);
if self.opm {
let (max, upp) = self.fov.max_and_upp(zat, max);
let mat = Matrix4::new_orthographic(-max.x, max.x, -max.y, max.y, znear, zfar);
(mat, upp)
} else {
let fov = self.fov.to_ver(max).into_inner();
let (max, upp) = self.fov.max_and_upp(zat, max);
let mat = Matrix4::new_perspective(max.x / max.y, fov, znear, zfar);
(mat, upp)
}
}
/// Casts components to another type, e.g., between [`f32`] and [`f64`].
#[must_use]
pub fn cast<M: Copy + RealField>(self) -> Scope<M>
where
N: SubsetOf<M>,
{
let (near, far) = self.zcp;
Scope {
fov: self.fov.cast(),
zcp: (near.to_superset(), far.to_superset()),
oim: self.oim,
opm: self.opm,
}
}
}
#[cfg(feature = "rkyv")]
impl<N: Copy + RealField> rkyv::Archive for Scope<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 Scope<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 Scope<N> {
#[inline]
fn deserialize(&self, _: &mut De) -> Result<Self, De::Error> {
Ok(rkyv::from_archived!(*self))
}
}