trackball/
bound.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
use crate::{Clamp, Frame, Plane};
use core::fmt::Debug;
use nalgebra::{Isometry3, Point3, RealField, UnitQuaternion, Vector3};

/// Orthogonal boundary conditions implementing [`Clamp`].
///
/// Implements [`Default`] and can be created with `Bound::default()`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Bound<N: Copy + RealField> {
	/// Isometry in world space of bound inversely transforming target and eye positions.
	pub transform: Isometry3<N>,
	/// Minimum components of target position in world space. Default splats `N::MIN`.
	pub min_target: Point3<N>,
	/// Maximum components of target position in world space. Default splats `N::MAX`.
	pub max_target: Point3<N>,
	/// Minimum components of eye position in world space. Default splats `N::MIN`.
	pub min_eye: Point3<N>,
	/// Maximum components of eye position in world space. Default splats `N::MAX`.
	pub max_eye: Point3<N>,
	/// Minimum components of up axis in world space following yaw. Default splats `N::MIN`.
	pub min_up: Point3<N>,
	/// Maximum components of up axis in world space following yaw. Default splats `N::MAX`.
	pub max_up: Point3<N>,
	/// Minimum distance of eye from target. Default is `N::zero()`.
	pub min_distance: N,
	/// Maximum distance of eye from target. Default is `N::MAX`.
	pub max_distance: N,
	/// Epsilon allowing clamped [`Delta`] to more likely pass revalidation.
	///
	/// Default is [`AbsDiffEq::default_epsilon()`]`.sqrt()`.
	///
	/// [`Delta`]: crate::Delta
	/// [`AbsDiffEq::default_epsilon()`]: approx::AbsDiffEq::default_epsilon()
	pub hysteresis: N,
}

impl<N: Copy + RealField> Default for Bound<N> {
	fn default() -> Self {
		let min = N::min_value().unwrap();
		let max = N::max_value().unwrap();
		Self {
			transform: Isometry3::default(),
			hysteresis: N::default_epsilon().sqrt(),
			min_target: Point3::new(min, min, min),
			max_target: Point3::new(max, max, max),
			min_eye: Point3::new(min, min, min),
			max_eye: Point3::new(max, max, max),
			min_up: Point3::new(min, min, min),
			max_up: Point3::new(max, max, max),
			min_distance: N::zero(),
			max_distance: max,
		}
	}
}

impl<N: Copy + RealField> Clamp<N> for Bound<N> {
	/// Using lower loop limit for flat boundary conditions.
	fn loops(&self) -> usize {
		10
	}
	/// Find any boundary plane exceeded by target position.
	fn target(&self, frame: &Frame<N>) -> Option<Plane<N>> {
		let target = self.transform.inverse() * frame.target();
		let axes = [Vector3::x_axis(), Vector3::y_axis(), Vector3::z_axis()];
		for (distance, axis) in axes.into_iter().enumerate() {
			let min_plane = Plane::new(axis, self.min_target[distance]);
			if min_plane.distance_from(&target) > self.hysteresis {
				return Some(min_plane);
			}
			let max_plane = Plane::new(axis, self.max_target[distance]);
			if max_plane.distance_from(&target) < -self.hysteresis {
				return Some(max_plane);
			}
		}
		None
	}
	/// Find any boundary plane exceeded by eye position.
	fn eye(&self, frame: &Frame<N>) -> Option<Plane<N>> {
		let distance = frame.distance();
		if (self.min_distance - distance) > self.hysteresis {
			return Some(Plane::new(frame.roll_axis(), self.min_distance));
		}
		if (self.max_distance - distance) < -self.hysteresis {
			return Some(Plane::new(frame.roll_axis(), self.max_distance));
		}
		let eye = self.transform.inverse() * frame.eye();
		let axes = [Vector3::x_axis(), Vector3::y_axis(), Vector3::z_axis()];
		for (distance, axis) in axes.into_iter().enumerate() {
			let min_plane = Plane::new(axis, self.min_eye[distance]);
			if min_plane.distance_from(&eye) > self.hysteresis {
				return Some(min_plane);
			}
			let max_plane = Plane::new(axis, self.max_eye[distance]);
			if max_plane.distance_from(&eye) < -self.hysteresis {
				return Some(max_plane);
			}
		}
		None
	}
	/// Find any boundary plane exceeded by up position.
	fn up(&self, frame: &Frame<N>) -> Option<Plane<N>> {
		let roll_axis = frame.roll_axis();
		let yaw = UnitQuaternion::from_axis_angle(
			&frame.local_yaw_axis(),
			roll_axis.x.atan2(roll_axis.z),
		);
		let up = yaw * frame.yaw_axis();
		let axes = [Vector3::x_axis(), Vector3::y_axis(), Vector3::z_axis()];
		for (distance, axis) in axes.into_iter().enumerate() {
			let min_plane = Plane::new(yaw.inverse() * axis, self.min_up[distance]);
			if min_plane.distance_from(&Point3::from(up.into_inner())) > self.hysteresis {
				return Some(min_plane);
			}
			let max_plane = Plane::new(yaw.inverse() * axis, self.max_up[distance]);
			if max_plane.distance_from(&Point3::from(up.into_inner())) < -self.hysteresis {
				return Some(max_plane);
			}
		}
		None
	}
}

#[cfg(feature = "rkyv")]
impl<N: Copy + RealField> rkyv::Archive for Bound<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 Bound<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 Bound<N> {
	#[inline]
	fn deserialize(&self, _: &mut De) -> Result<Self, De::Error> {
		Ok(rkyv::from_archived!(*self))
	}
}