diff --git a/src/modules/tas_optimizer/optimizer.rs b/src/modules/tas_optimizer/optimizer.rs index 706601c..e35d7cd 100644 --- a/src/modules/tas_optimizer/optimizer.rs +++ b/src/modules/tas_optimizer/optimizer.rs @@ -432,6 +432,7 @@ impl Optimizer { Line::RenderYawOverride(_) => (), Line::PitchOverride(_) => (), Line::RenderPitchOverride(_) => (), + Line::IgnoreSmoothing => (), } } diff --git a/src/modules/tas_optimizer/simulator.rs b/src/modules/tas_optimizer/simulator.rs index 5665791..d8d724a 100644 --- a/src/modules/tas_optimizer/simulator.rs +++ b/src/modules/tas_optimizer/simulator.rs @@ -127,6 +127,7 @@ impl<'a, T: Trace> Iterator for Simulator<'a, T> { Line::RenderYawOverride(_) => (), Line::PitchOverride(_) => (), Line::RenderPitchOverride(_) => (), + Line::IgnoreSmoothing => (), } // Advance to the next line for non-frame-bulks. diff --git a/src/modules/tas_studio/editor/mod.rs b/src/modules/tas_studio/editor/mod.rs index bd7ff7e..48085f8 100644 --- a/src/modules/tas_studio/editor/mod.rs +++ b/src/modules/tas_studio/editor/mod.rs @@ -17,6 +17,7 @@ use hltas::types::{ use hltas::HLTAS; use itertools::Itertools; use thiserror::Error; +use utils::get_ignore_smoothing_frames; use self::db::{Action, ActionKind, Branch, Db}; use self::operation::{Key, Operation}; @@ -3512,12 +3513,15 @@ impl Editor { let yaws = frames.iter().map(|f| f.state.prev_frame_input.yaw); let pitches = frames.iter().map(|f| f.state.prev_frame_input.pitch); + let ignore_smoothing_frames = get_ignore_smoothing_frames(&self.script(), frames.len()); + let smoothed_yaws = smoothed_views( self.smooth_window_s, self.smooth_small_window_s, self.smooth_small_window_multiplier, &frames[..frames.len()], yaws, + ignore_smoothing_frames.as_slice(), ); let smoothed_pitches = smoothed_views( @@ -3526,6 +3530,7 @@ impl Editor { self.smooth_small_window_multiplier, &frames[..frames.len()], pitches, + ignore_smoothing_frames.as_slice(), ); let mut line = "target_yaw_override".to_string(); @@ -3583,6 +3588,8 @@ impl Editor { )); } + let ignore_smoothing_frames = get_ignore_smoothing_frames(&self.script(), frames.len()); + let yaws = frames.iter().map(|f| f.state.prev_frame_input.yaw); let mut smoothed_yaws = smoothed_views( self.smooth_window_s, @@ -3590,6 +3597,7 @@ impl Editor { self.smooth_small_window_multiplier, &frames[start..=end], yaws, + &ignore_smoothing_frames.as_slice(), ); // Skip the first frame because it is the initial frame before the start of the TAS. @@ -3807,12 +3815,16 @@ impl Editor { let yaws = branch.frames.iter().map(|f| f.state.prev_frame_input.yaw); let pitches = branch.frames.iter().map(|f| f.state.prev_frame_input.pitch); + let ignore_smoothing_frames = + get_ignore_smoothing_frames(&smoothed_script, branch.frames.len()); + let mut smoothed_yaws = smoothed_views( self.smooth_window_s, self.smooth_small_window_s, self.smooth_small_window_multiplier, &branch.frames, yaws, + &ignore_smoothing_frames.as_slice(), ); let mut smoothed_pitches = smoothed_views( self.smooth_window_s, @@ -3820,6 +3832,7 @@ impl Editor { self.smooth_small_window_multiplier, &branch.frames, pitches, + &ignore_smoothing_frames.as_slice(), ); // First yaw corresponds to the initial frame, which is not controlled by the TAS. @@ -4581,10 +4594,11 @@ fn forward(pitch: f32, yaw: f32) -> Vec3 { } fn unwrap_angles(xs: impl Iterator) -> impl Iterator { - use std::f32::consts::PI; + // does things in f64 to avoid imprecision + use std::f64::consts::PI; xs.scan((0., 0.), |(prev, offset), curr| { - let mut diff = curr - *prev + *offset; + let mut diff = curr as f64 - *prev as f64 + *offset as f64; while diff >= PI { diff -= 2. * PI; *offset -= 2. * PI; @@ -4595,7 +4609,7 @@ fn unwrap_angles(xs: impl Iterator) -> impl Iterator { } *prev += diff; - Some(*prev) + Some(*prev as f32) }) } @@ -4605,6 +4619,7 @@ fn smoothed_views( small_window_multiplier: f32, frames: &[Frame], views: impl Iterator, + ignore_smoothing_frames: &[bool], ) -> Vec { if frames.is_empty() { return vec![]; @@ -4626,6 +4641,11 @@ fn smoothed_views( // The smoothing window is centered at the center of each yaw. // For pitch smoothing, every frame has both pitch and yaw so iterate over this is ok. for i in 0..unwrapped.len() { + if ignore_smoothing_frames[i] { + rv.push(unwrapped[i]); + continue; + } + let mut total_view = 0.; let mut total_weight = 0.; @@ -4950,6 +4970,7 @@ mod tests { input: impl IntoIterator, small_window_size: f32, expect: Expect, + ignore_smoothing_frames: &[bool], ) { let frames: Vec = input .into_iter() @@ -4969,7 +4990,14 @@ mod tests { .collect(); let yaws = frames.iter().map(|f| f.state.prev_frame_input.yaw); - let smoothed = smoothed_views(1., small_window_size, 4., &frames, yaws); + let smoothed = smoothed_views( + 1., + small_window_size, + 4., + &frames, + yaws, + ignore_smoothing_frames, + ); expect.assert_debug_eq(&smoothed); } @@ -4985,6 +5013,7 @@ mod tests { 2.0, ] "#]], + vec![false; 3].as_slice(), ); } @@ -5008,6 +5037,7 @@ mod tests { -0.75, ] "#]], + vec![false; 5].as_slice(), ); } @@ -5031,6 +5061,7 @@ mod tests { -0.75, ] "#]], + vec![false; 5].as_slice(), ); } @@ -5054,6 +5085,7 @@ mod tests { -0.9, ] "#]], + vec![false; 5].as_slice(), ); } @@ -5079,6 +5111,7 @@ mod tests { -0.9, ] "#]], + vec![false; 6].as_slice(), ); } @@ -5102,6 +5135,79 @@ mod tests { -0.9, ] "#]], + vec![false; 5].as_slice(), + ); + } + + #[test] + fn test_smoothing_ignore1() { + check_smoothing( + [ + (0.25, -1.), + (0.25, -1.), + (0.4, 1.), + (0.25, -1.), + (0.25, -1.), + ], + 0.5, + expect![[r#" + [ + -1.0, + -0.4, + 0.28000003, + -0.4, + -0.9, + ] + "#]], + &[true, false, false, false, false], + ); + } + + #[test] + fn test_smoothing_ignore2() { + check_smoothing( + [ + (0.25, -1.), + (0.25, -1.), + (0.4, 1.), + (0.25, -1.), + (0.25, -1.), + ], + 0.5, + expect![[r#" + [ + -1.0, + -0.4, + 0.28000003, + -0.4, + -1.0, + ] + "#]], + &[true, false, false, false, true], + ); + } + + #[test] + fn test_smoothing_ignore3() { + check_smoothing( + [ + (0.25, -1.), + (0.25, -1.), + (0.4, 1.), + (0.25, -1.), + (0.25, -1.), + ], + 0.5, + expect![[r#" + [ + -1.0, + -0.4, + 1.0, + -0.4, + -1.0, + ] + "#]], + &[true, false, true, false, true], ); } diff --git a/src/modules/tas_studio/editor/utils.rs b/src/modules/tas_studio/editor/utils.rs index 46a37be..65d9f0c 100644 --- a/src/modules/tas_studio/editor/utils.rs +++ b/src/modules/tas_studio/editor/utils.rs @@ -1,5 +1,6 @@ use std::iter; use std::num::NonZeroU32; +use std::ops::Range; use hltas::types::{AutoMovement, FrameBulk, Line, StrafeDir, StrafeSettings, StrafeType}; use hltas::HLTAS; @@ -304,3 +305,45 @@ pub fn join_lines(prev: &mut Line, next: &Line) { prev_bulk.frame_count = NonZeroU32::new(temp.get() + next_bulk.frame_count.get()).unwrap(); } + +pub fn get_ignore_smoothing_frames(hltas: &HLTAS, frame_count: usize) -> Vec { + let line_first_frame_iter = line_first_frame_idx_and_frame_count(hltas).collect::>(); + + let mut numbers = vec![]; + let mut get_next = false; + + for (idx, line) in hltas.lines.iter().enumerate() { + // if matches `ignore_smoothing`, takes the immediate line or at the least calculate + // its affecting frame bulk + if matches!(line, Line::IgnoreSmoothing) && !get_next { + get_next = true; + numbers.push(idx); + continue; + } + + // taking a framebulk immediately after `ignore_smoothing` + if matches!(line, Line::FrameBulk(_)) && get_next { + get_next = false; + numbers.push(idx); + continue; + } + } + + let ranges = numbers + // by the end of this, we have + // is line idx -> framebulk idx -> is line idx -> framebulk idx + .chunks(2) + // chunking by 2 will have us range of (start..end) based on line idx and bulk idx + .map(|arr| { + // .nth `line_first_frame_iter` to get the actual frame number + // need to add 1 always to the line number just because + (line_first_frame_iter[arr[0] + 1])..(line_first_frame_iter[arr[1] + 1]) + }) + .collect::>>(); + + let contains = |idx| ranges.iter().any(|range| range.contains(&idx)); + + (0..frame_count) + .map(|frame_idx| if contains(frame_idx) { true } else { false }) + .collect::>() +}