trackball/
bound.rs

1use crate::{Clamp, Frame, Plane};
2use core::fmt::Debug;
3use nalgebra::{Isometry3, Point3, RealField, UnitQuaternion, Vector3};
4
5/// Orthogonal boundary conditions implementing [`Clamp`].
6///
7/// Implements [`Default`] and can be created with `Bound::default()`.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct Bound<N: Copy + RealField> {
11	/// Isometry in world space of bound inversely transforming target and eye positions.
12	pub transform: Isometry3<N>,
13	/// Minimum components of target position in world space. Default splats `N::MIN`.
14	pub min_target: Point3<N>,
15	/// Maximum components of target position in world space. Default splats `N::MAX`.
16	pub max_target: Point3<N>,
17	/// Minimum components of eye position in world space. Default splats `N::MIN`.
18	pub min_eye: Point3<N>,
19	/// Maximum components of eye position in world space. Default splats `N::MAX`.
20	pub max_eye: Point3<N>,
21	/// Minimum components of up axis in world space following yaw. Default splats `N::MIN`.
22	pub min_up: Point3<N>,
23	/// Maximum components of up axis in world space following yaw. Default splats `N::MAX`.
24	pub max_up: Point3<N>,
25	/// Minimum distance of eye from target. Default is `N::zero()`.
26	pub min_distance: N,
27	/// Maximum distance of eye from target. Default is `N::MAX`.
28	pub max_distance: N,
29	/// Epsilon allowing clamped [`Delta`] to more likely pass revalidation.
30	///
31	/// Default is [`AbsDiffEq::default_epsilon()`]`.sqrt()`.
32	///
33	/// [`Delta`]: crate::Delta
34	/// [`AbsDiffEq::default_epsilon()`]: approx::AbsDiffEq::default_epsilon()
35	pub hysteresis: N,
36}
37
38impl<N: Copy + RealField> Default for Bound<N> {
39	fn default() -> Self {
40		let min = N::min_value().unwrap();
41		let max = N::max_value().unwrap();
42		Self {
43			transform: Isometry3::default(),
44			hysteresis: N::default_epsilon().sqrt(),
45			min_target: Point3::new(min, min, min),
46			max_target: Point3::new(max, max, max),
47			min_eye: Point3::new(min, min, min),
48			max_eye: Point3::new(max, max, max),
49			min_up: Point3::new(min, min, min),
50			max_up: Point3::new(max, max, max),
51			min_distance: N::zero(),
52			max_distance: max,
53		}
54	}
55}
56
57impl<N: Copy + RealField> Clamp<N> for Bound<N> {
58	/// Using lower loop limit for flat boundary conditions.
59	fn loops(&self) -> usize {
60		10
61	}
62	/// Find any boundary plane exceeded by target position.
63	fn target(&self, frame: &Frame<N>) -> Option<Plane<N>> {
64		let target = self.transform.inverse() * frame.target();
65		let axes = [Vector3::x_axis(), Vector3::y_axis(), Vector3::z_axis()];
66		for (distance, axis) in axes.into_iter().enumerate() {
67			let min_plane = Plane::new(axis, self.min_target[distance]);
68			if min_plane.distance_from(&target) > self.hysteresis {
69				return Some(min_plane);
70			}
71			let max_plane = Plane::new(axis, self.max_target[distance]);
72			if max_plane.distance_from(&target) < -self.hysteresis {
73				return Some(max_plane);
74			}
75		}
76		None
77	}
78	/// Find any boundary plane exceeded by eye position.
79	fn eye(&self, frame: &Frame<N>) -> Option<Plane<N>> {
80		let distance = frame.distance();
81		if (self.min_distance - distance) > self.hysteresis {
82			return Some(Plane::new(frame.roll_axis(), self.min_distance));
83		}
84		if (self.max_distance - distance) < -self.hysteresis {
85			return Some(Plane::new(frame.roll_axis(), self.max_distance));
86		}
87		let eye = self.transform.inverse() * frame.eye();
88		let axes = [Vector3::x_axis(), Vector3::y_axis(), Vector3::z_axis()];
89		for (distance, axis) in axes.into_iter().enumerate() {
90			let min_plane = Plane::new(axis, self.min_eye[distance]);
91			if min_plane.distance_from(&eye) > self.hysteresis {
92				return Some(min_plane);
93			}
94			let max_plane = Plane::new(axis, self.max_eye[distance]);
95			if max_plane.distance_from(&eye) < -self.hysteresis {
96				return Some(max_plane);
97			}
98		}
99		None
100	}
101	/// Find any boundary plane exceeded by up position.
102	fn up(&self, frame: &Frame<N>) -> Option<Plane<N>> {
103		let roll_axis = frame.roll_axis();
104		let yaw = UnitQuaternion::from_axis_angle(
105			&frame.local_yaw_axis(),
106			roll_axis.x.atan2(roll_axis.z),
107		);
108		let up = yaw * frame.yaw_axis();
109		let axes = [Vector3::x_axis(), Vector3::y_axis(), Vector3::z_axis()];
110		for (distance, axis) in axes.into_iter().enumerate() {
111			let min_plane = Plane::new(yaw.inverse() * axis, self.min_up[distance]);
112			if min_plane.distance_from(&Point3::from(up.into_inner())) > self.hysteresis {
113				return Some(min_plane);
114			}
115			let max_plane = Plane::new(yaw.inverse() * axis, self.max_up[distance]);
116			if max_plane.distance_from(&Point3::from(up.into_inner())) < -self.hysteresis {
117				return Some(max_plane);
118			}
119		}
120		None
121	}
122}
123
124#[cfg(feature = "rkyv")]
125impl<N: Copy + RealField> rkyv::Archive for Bound<N> {
126	type Archived = Self;
127	type Resolver = ();
128
129	#[inline]
130	#[allow(unsafe_code)]
131	unsafe fn resolve(&self, _: usize, (): Self::Resolver, out: *mut Self::Archived) {
132		unsafe {
133			out.write(rkyv::to_archived!(*self as Self));
134		}
135	}
136}
137
138#[cfg(feature = "rkyv")]
139impl<Ser: rkyv::Fallible + ?Sized, N: Copy + RealField> rkyv::Serialize<Ser> for Bound<N> {
140	#[inline]
141	fn serialize(&self, _: &mut Ser) -> Result<Self::Resolver, Ser::Error> {
142		Ok(())
143	}
144}
145
146#[cfg(feature = "rkyv")]
147impl<De: rkyv::Fallible + ?Sized, N: Copy + RealField> rkyv::Deserialize<Self, De> for Bound<N> {
148	#[inline]
149	fn deserialize(&self, _: &mut De) -> Result<Self, De::Error> {
150		Ok(rkyv::from_archived!(*self))
151	}
152}