wrapping angle interp

This commit is contained in:
Robin Appelman 2022-05-16 20:37:44 +02:00
commit 4eb601187b
4 changed files with 107 additions and 12 deletions

View file

@ -1,3 +1,4 @@
use crate::wrapping::Wrapping;
use crate::DemoInfo; use crate::DemoInfo;
use splines::Spline; use splines::Spline;
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
@ -138,8 +139,8 @@ impl DebugToggle {
pub struct DemoCamera { pub struct DemoCamera {
demo: DemoInfo, demo: DemoInfo,
positions: Spline<f32, Vec3>, positions: Spline<f32, Vec3>,
pitch: Spline<f32, f32>, pitch: Spline<f32, Wrapping<-180, 180>>,
yaw: Spline<f32, f32>, yaw: Spline<f32, Wrapping<-180, 180>>,
playing: bool, playing: bool,
start_tick: f64, start_tick: f64,
playback_start_time: f64, playback_start_time: f64,
@ -269,8 +270,8 @@ impl DemoCamera {
.clamped_sample(tick as f32) .clamped_sample(tick as f32)
.unwrap_or(vec3(0.0, 0.0, 0.0)), .unwrap_or(vec3(0.0, 0.0, 0.0)),
angles: [ angles: [
self.pitch.clamped_sample(tick as f32).unwrap_or_default(), self.pitch.clamped_sample(tick as f32).unwrap_or_default().0,
self.yaw.clamped_sample(tick as f32).unwrap_or_default(), self.yaw.clamped_sample(tick as f32).unwrap_or_default().0,
], ],
} }
} }

View file

@ -1,4 +1,5 @@
use crate::bsp::{map_coords, UNIT_SCALE}; use crate::bsp::{map_coords, UNIT_SCALE};
use crate::wrapping::Wrapping;
use crate::Error; use crate::Error;
use splines::{Interpolation, Key}; use splines::{Interpolation, Key};
use std::fs; use std::fs;
@ -44,8 +45,8 @@ impl DemoInfo {
#[derive(Default)] #[derive(Default)]
pub struct Positions { pub struct Positions {
pub positions: Vec<Key<f32, Vec3>>, pub positions: Vec<Key<f32, Vec3>>,
pub pitch: Vec<Key<f32, f32>>, pub pitch: Vec<Key<f32, Wrapping<-180, 180>>>,
pub yaw: Vec<Key<f32, f32>>, pub yaw: Vec<Key<f32, Wrapping<-180, 180>>>,
} }
struct PovAnalyzer { struct PovAnalyzer {
@ -113,14 +114,14 @@ impl MessageHandler for PovAnalyzer {
NON_LOCAL_PITCH_ANGLES => { NON_LOCAL_PITCH_ANGLES => {
self.positions.pitch.push(Key::new( self.positions.pitch.push(Key::new(
tick as f32, tick as f32,
f32::try_from(&prop.value).unwrap_or_default(), Wrapping(f32::try_from(&prop.value).unwrap_or_default()),
Interpolation::Linear, Interpolation::Linear,
)); ));
} }
NON_LOCAL_YAW_ANGLES => { NON_LOCAL_YAW_ANGLES => {
self.positions.yaw.push(Key::new( self.positions.yaw.push(Key::new(
tick as f32, tick as f32,
f32::try_from(&prop.value).unwrap_or_default(), Wrapping(f32::try_from(&prop.value).unwrap_or_default()),
Interpolation::Linear, Interpolation::Linear,
)); ));
} }
@ -161,13 +162,13 @@ impl MessageHandler for PovAnalyzer {
if self.is_pov { if self.is_pov {
self.positions.pitch.push(Key::new( self.positions.pitch.push(Key::new(
tick as f32, tick as f32,
meta.view_angles[0].local_angles.y, Wrapping(meta.view_angles[0].local_angles.y),
Interpolation::CatmullRom, Interpolation::Linear,
)); ));
self.positions.yaw.push(Key::new( self.positions.yaw.push(Key::new(
tick as f32, tick as f32,
meta.view_angles[0].local_angles.x, Wrapping(meta.view_angles[0].local_angles.x),
Interpolation::CatmullRom, Interpolation::Linear,
)); ));
let pos = map_coords(meta.view_angles[0].origin); let pos = map_coords(meta.view_angles[0].origin);
self.positions.positions.push(Key::new( self.positions.positions.push(Key::new(

View file

@ -4,6 +4,7 @@ mod demo;
mod loader; mod loader;
mod renderer; mod renderer;
mod ui; mod ui;
mod wrapping;
use clap::Parser; use clap::Parser;
use std::fs; use std::fs;

92
src/wrapping.rs Normal file
View file

@ -0,0 +1,92 @@
use splines::Interpolate;
#[derive(Clone, Copy, Default, Debug)]
pub struct Wrapping<const MIN: i32, const MAX: i32>(pub f32);
/// Map input numbers such that the difference between the output % MAX is the wrapping difference between the input
fn unwrap<const MIN: i32, const MAX: i32>(a: f32, b: f32) -> (f32, f32) {
let offset = (MAX - MIN) as f32;
if a - b < MIN as f32 {
(a + offset, b)
} else if b - a < MIN as f32 {
(a, b + offset)
} else {
(a, b)
}
}
#[test]
fn test_unwrap() {
assert_eq!((101.0, 99.0), unwrap::<0, 100>(1.0, 99.0));
assert_eq!((99.0, 101.0), unwrap::<0, 100>(99.0, 1.0));
assert_eq!((120.0, 99.0), unwrap::<-100, 100>(-80.0, 99.0));
}
fn wrap<const MIN: i32, const MAX: i32>(num: f32) -> f32 {
let offset = (MAX - MIN) as f32;
if num > MAX as f32 {
num - offset
} else if num < MIN as f32 {
num + offset
} else {
num
}
}
impl<const MIN: i32, const MAX: i32> Interpolate<f32> for Wrapping<MIN, MAX> {
fn step(t: f32, threshold: f32, a: Self, b: Self) -> Self {
if t < threshold {
a
} else {
b
}
}
fn lerp(t: f32, a: Self, b: Self) -> Self {
let (a, b) = unwrap::<MIN, MAX>(a.0, b.0);
let c = f32::lerp(t, a, b);
Wrapping(wrap::<MIN, MAX>(c))
}
fn cosine(t: f32, a: Self, b: Self) -> Self {
let (a, b) = unwrap::<MIN, MAX>(a.0, b.0);
let c = f32::cosine(t, a, b);
Wrapping(wrap::<MIN, MAX>(c))
}
fn cubic_hermite(
_t: f32,
_x: (f32, Self),
_a: (f32, Self),
_b: (f32, Self),
_y: (f32, Self),
) -> Self {
todo!();
}
fn quadratic_bezier(_t: f32, _a: Self, _u: Self, _b: Self) -> Self {
todo!();
}
fn cubic_bezier(_t: f32, _a: Self, _u: Self, _v: Self, _b: Self) -> Self {
todo!();
}
fn cubic_bezier_mirrored(_t: f32, _a: Self, _u: Self, _v: Self, _b: Self) -> Self {
todo!()
}
}
#[test]
fn test_wrapping_interp() {
use splines::{Interpolation, Key, Spline};
let spline = Spline::from_vec(vec![
Key::new(0.0, Wrapping::<-180, 180>(160.0), Interpolation::Linear),
Key::new(10.0, Wrapping::<-180, 180>(-160.0), Interpolation::Linear),
]);
assert_eq!(168.0, spline.sample(2.0).unwrap().0);
assert_eq!(180.0, spline.sample(5.0).unwrap().0);
assert_eq!(-172.0, spline.sample(7.0).unwrap().0);
}