Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pitch smoothing #98

Merged
merged 4 commits into from
Jul 28, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 67 additions & 21 deletions src/modules/tas_studio/editor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3508,23 +3508,45 @@ impl Editor {
}

let frames = &self.branch().frames;
let smoothed = smoothed_yaws(

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 smoothed_yaws = smoothed_views(
self.smooth_window_s,
self.smooth_small_window_s,
self.smooth_small_window_multiplier,
&frames[..frames.len()],
yaws,
);

let smoothed_pitches = smoothed_views(
self.smooth_window_s,
self.smooth_small_window_s,
self.smooth_small_window_multiplier,
&frames[..frames.len()],
pitches,
);

let mut line = "target_yaw_override".to_string();
// Skip the first frame because it is the initial frame before the start of the TAS.
for yaw in &smoothed[1..] {
for yaw in &smoothed_yaws[1..] {
let yaw = yaw.to_degrees();
write!(&mut line, " {yaw}").unwrap();
}

let op = Operation::Insert { line_idx: 0, line };
self.apply_operation(op)?;

let mut line = "pitch_override".to_string();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the pitch was constant then it probably doesn't need to be smoothed? Also do you generally want to smooth both at once?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure how I would change existing functions to stop working for stream of constant pitch. It just seems a lot easier to mirror it and just works. I see what you mean about second point where someone might just want to smooth the yaw. However, they can delete it easily. Also, adding another option to smooth pitch might be inconvenient as if soneone wants smoothing, just smooth it.

for pitch in &smoothed_pitches[1..] {
let pitch = pitch.to_degrees();
write!(&mut line, " {pitch}").unwrap();
}

let op = Operation::Insert { line_idx: 1, line };
self.apply_operation(op)?;

Ok(())
}

Expand Down Expand Up @@ -3561,21 +3583,23 @@ impl Editor {
));
}

let mut smoothed = smoothed_yaws(
let yaws = frames.iter().map(|f| f.state.prev_frame_input.yaw);
let mut smoothed_yaws = smoothed_views(
self.smooth_window_s,
self.smooth_small_window_s,
self.smooth_small_window_multiplier,
&frames[start..=end],
yaws,
);

// Skip the first frame because it is the initial frame before the start of the TAS.
let first = start.max(1);
if start == 0 {
smoothed.remove(0);
smoothed_yaws.remove(0);
}

// Convert to degrees for .hltas.
for yaw in &mut smoothed {
for yaw in &mut smoothed_yaws {
*yaw = yaw.to_degrees();
}

Expand All @@ -3585,7 +3609,7 @@ impl Editor {

if repeat == 0 {
let mut line = "target_yaw_override".to_string();
for yaw in smoothed {
for yaw in smoothed_yaws {
write!(&mut line, " {yaw}").unwrap();
}

Expand All @@ -3594,7 +3618,7 @@ impl Editor {
self.apply_operation(op)?;
Ok(())
} else {
let target_yaw_override = Line::TargetYawOverride(smoothed);
let target_yaw_override = Line::TargetYawOverride(smoothed_yaws);

// We need to insert the line in the middle of a frame bulk, so split it.
let mut line = self.branch().branch.script.lines[line_idx].clone();
Expand Down Expand Up @@ -3779,21 +3803,41 @@ impl Editor {
),
);

// Compute and insert the smoothed TargetYawOverride line.
let mut smoothed = smoothed_yaws(
// Compute and insert the smoothed TargetYawOverride and PitchOverride line.
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 mut smoothed_yaws = smoothed_views(
self.smooth_window_s,
self.smooth_small_window_s,
self.smooth_small_window_multiplier,
&branch.frames,
yaws,
);
let mut smoothed_pitches = smoothed_views(
self.smooth_window_s,
self.smooth_small_window_s,
self.smooth_small_window_multiplier,
&branch.frames,
pitches,
);

// First yaw corresponds to the initial frame, which is not controlled by the TAS.
smoothed.remove(0);
for yaw in &mut smoothed {
smoothed_yaws.remove(0);
for yaw in &mut smoothed_yaws {
*yaw = yaw.to_degrees();
}
let line = Line::TargetYawOverride(smoothed);

let line = Line::TargetYawOverride(smoothed_yaws);
smoothed_script.lines.insert(2, line);

smoothed_pitches.remove(0);
for pitch in &mut smoothed_pitches {
*pitch = pitch.to_degrees();
}
let line = Line::PitchOverride(smoothed_pitches);
smoothed_script.lines.insert(3, line);

// Remove all lines disabling vectorial strafing.
let mut i = 0;
while i < smoothed_script.lines.len() {
Expand Down Expand Up @@ -4555,18 +4599,18 @@ fn unwrap_angles(xs: impl Iterator<Item = f32>) -> impl Iterator<Item = f32> {
})
}

fn smoothed_yaws(
khanghugo marked this conversation as resolved.
Show resolved Hide resolved
fn smoothed_views(
window_size: f32,
small_window_size: f32,
small_window_multiplier: f32,
frames: &[Frame],
views: impl Iterator<Item = f32>,
) -> Vec<f32> {
if frames.is_empty() {
return vec![];
}

let yaws = frames.iter().map(|f| f.state.prev_frame_input.yaw);
let unwrapped: Vec<f32> = unwrap_angles(yaws).collect();
let unwrapped: Vec<f32> = unwrap_angles(views).collect();
let mut rv = Vec::with_capacity(unwrapped.len());

fn frame_time(frame: &Frame) -> f32 {
Expand All @@ -4580,18 +4624,19 @@ fn smoothed_yaws(
));

// 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() {
let mut total_yaw = 0.;
let mut total_view = 0.;
let mut total_weight = 0.;

let mut process_frame =
|(mut rem_win_size, mut rem_small_win_size), (mut frame_time, yaw): (f32, f32)| {
|(mut rem_win_size, mut rem_small_win_size), (mut frame_time, view): (f32, f32)| {
// If there's any small window zone left to cover, do so.
if rem_small_win_size > 0. {
let dt = frame_time.min(rem_small_win_size);
let weight = dt * small_window_multiplier;

total_yaw += yaw * weight;
total_view += view * weight;
total_weight += weight;

rem_win_size -= dt;
Expand All @@ -4614,7 +4659,7 @@ fn smoothed_yaws(
let dt = frame_time.min(rem_win_size);
let weight = dt;

total_yaw += yaw * weight;
total_view += view * weight;
total_weight += weight;

rem_win_size -= dt;
Expand Down Expand Up @@ -4657,7 +4702,7 @@ fn smoothed_yaws(
.chain(repeat_last.clone())
.try_fold((rem_win_size, rem_small_win_size), &mut process_frame);

rv.push(total_yaw / total_weight);
rv.push(total_view / total_weight);
}

rv
Expand Down Expand Up @@ -4923,7 +4968,8 @@ mod tests {
})
.collect();

let smoothed = smoothed_yaws(1., small_window_size, 4., &frames);
let yaws = frames.iter().map(|f| f.state.prev_frame_input.yaw);
let smoothed = smoothed_views(1., small_window_size, 4., &frames, yaws);
expect.assert_debug_eq(&smoothed);
}

Expand Down
Loading