use crate::camera::camera::Projection;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ViewPreset {
Front,
Back,
Left,
Right,
Top,
Bottom,
Isometric,
}
impl ViewPreset {
pub fn all() -> &'static [ViewPreset] {
&[
Self::Front,
Self::Back,
Self::Left,
Self::Right,
Self::Top,
Self::Bottom,
Self::Isometric,
]
}
pub fn name(self) -> &'static str {
match self {
Self::Front => "Front",
Self::Back => "Back",
Self::Left => "Left",
Self::Right => "Right",
Self::Top => "Top",
Self::Bottom => "Bottom",
Self::Isometric => "Isometric",
}
}
pub fn orientation(self) -> glam::Quat {
use std::f32::consts::PI;
match self {
Self::Front => glam::Quat::IDENTITY,
Self::Back => glam::Quat::from_rotation_y(PI),
Self::Left => glam::Quat::from_rotation_y(-std::f32::consts::FRAC_PI_2),
Self::Right => glam::Quat::from_rotation_y(std::f32::consts::FRAC_PI_2),
Self::Top => glam::Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2),
Self::Bottom => glam::Quat::from_rotation_x(std::f32::consts::FRAC_PI_2),
Self::Isometric => {
let iso_pitch = (1.0_f32 / 2.0_f32.sqrt()).atan();
glam::Quat::from_rotation_y(std::f32::consts::FRAC_PI_4)
* glam::Quat::from_rotation_x(iso_pitch)
}
}
}
pub fn preferred_projection(self) -> Option<Projection> {
match self {
Self::Isometric => None,
_ => Some(Projection::Orthographic),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_all_orientations_are_unit_quaternions() {
for preset in ViewPreset::all() {
let q = preset.orientation();
let len = q.length();
assert!(
(len - 1.0).abs() < 1e-5,
"{:?}: quaternion length = {len}, expected 1.0",
preset
);
}
}
#[test]
fn test_front_looks_along_negative_z() {
let q = ViewPreset::Front.orientation();
let forward = q * glam::Vec3::NEG_Z;
assert!(
(forward - glam::Vec3::NEG_Z).length() < 1e-5,
"front forward = {forward:?}, expected -Z"
);
}
#[test]
fn test_opposite_presets_are_180_apart() {
let pairs = [
(ViewPreset::Front, ViewPreset::Back),
(ViewPreset::Left, ViewPreset::Right),
(ViewPreset::Top, ViewPreset::Bottom),
];
for (a, b) in &pairs {
let qa = a.orientation();
let qb = b.orientation();
let angle = qa.angle_between(qb);
assert!(
(angle - std::f32::consts::PI).abs() < 0.01,
"{:?}/{:?}: angle = {angle:.4}, expected π",
a,
b
);
}
}
#[test]
fn test_all_presets_unique() {
let all = ViewPreset::all();
for i in 0..all.len() {
for j in (i + 1)..all.len() {
let qi = all[i].orientation();
let qj = all[j].orientation();
let angle = qi.angle_between(qj);
assert!(
angle > 0.01,
"{:?} and {:?} have nearly identical orientations (angle={angle})",
all[i],
all[j]
);
}
}
}
}