From 774551728b93c551c1150d2df11f3d978dbd3a0e Mon Sep 17 00:00:00 2001 From: Rouven Spreckels Date: Tue, 20 Feb 2024 17:56:17 +0100 Subject: [PATCH] Implement `track` operation. --- src/clamp.rs | 32 ++++++++++++++++++++++++++++++++ src/delta.rs | 11 +++++++++++ src/frame.rs | 12 +++++++++--- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/clamp.rs b/src/clamp.rs index 66c492c..84c0b4c 100644 --- a/src/clamp.rs +++ b/src/clamp.rs @@ -122,6 +122,38 @@ pub trait Clamp: Send + Sync + Debug + 'static { } (min_delta != *delta).then_some((min_delta, loops)) } + Delta::Track { vec: _ } => { + let old_frame = frame; + let old_target = frame.target(); + let old_rot_inverse = frame.view().rotation.inverse(); + let mut min_delta = *delta; + let mut loops = 0; + loop { + let frame = min_delta.transform(old_frame); + let mut bound = false; + if let Some(plane) = self.target(&frame) { + bound = true; + let new_target = plane.project_point(frame.target()); + let vec = old_rot_inverse * (new_target - old_target); + min_delta = Delta::Track { vec }; + } + let frame = min_delta.transform(old_frame); + if let Some(_plane) = self.up(&frame) { + bound = true; + // TODO Implement gliding. + min_delta = Delta::Frame; + } + if bound { + if loops == self.loops() { + break; + } + loops += 1; + } else { + break; + } + } + (min_delta != *delta).then_some((min_delta, loops)) + } &Delta::Orbit { rot: _, pos } => { if pos != Point3::origin() { return Some((Delta::Frame, 0)); diff --git a/src/delta.rs b/src/delta.rs index 74d6b2c..fe534f1 100644 --- a/src/delta.rs +++ b/src/delta.rs @@ -19,6 +19,13 @@ pub enum Delta { /// Yaw axis. yaw_axis: Unit>, }, + /// Tracks target which slides by vector in world space. + /// + /// Preserves eye position inclusive its roll attitude. + Track { + /// Vector in world space of a sliding target to track. + vec: Vector3, + }, /// Orbits eye by rotation in camera space around point in camera space. /// /// See [`Frame::local_orbit_around()`]. @@ -58,6 +65,7 @@ impl Delta { yaw, ref yaw_axis, } => frame.look_around(*pitch, *yaw, yaw_axis), + Self::Track { vec } => frame.set_target(frame.target() + vec), Self::Orbit { rot, pos } => frame.local_orbit_around(rot, pos), Self::Slide { vec } => frame.local_slide(vec), Self::Scale { rat, pos } => frame.local_scale_around(*rat, pos), @@ -80,6 +88,7 @@ impl Delta { yaw: -yaw, yaw_axis, }, + Self::Track { vec } => Self::Track { vec: -vec }, Self::Orbit { rot, pos } => Self::Orbit { rot: rot.inverse(), pos, @@ -111,6 +120,7 @@ impl Delta { yaw: yaw * t, yaw_axis, }, + Self::Track { vec } => Self::Track { vec: vec * t }, Self::Orbit { rot, pos } => Self::Orbit { rot: rot.powf(t), pos, @@ -139,6 +149,7 @@ impl Delta { yaw: yaw.to_superset(), yaw_axis: yaw_axis.cast(), }, + Self::Track { vec } => Delta::Track { vec: vec.cast() }, Self::Orbit { rot, pos } => Delta::Orbit { rot: rot.cast(), pos: pos.cast(), diff --git a/src/frame.rs b/src/frame.rs index a2cd2df..5e5e829 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -40,10 +40,16 @@ impl Frame { &self.pos } /// Sets target position in world space preserving eye position inclusive its roll attitude. - pub fn set_target(&mut self, target: Point3) { + /// + /// Allows to track a moving object. + pub fn set_target(&mut self, pos: Point3) { let eye = self.eye(); - self.pos = target; - self.zat = (self.pos - eye).norm(); + 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 } } /// Distance between eye and target. #[must_use]