osu: minor refactoring

This commit is contained in:
MaxOhn
2021-01-15 10:22:05 +01:00
parent b9966feb14
commit 4c551813c8
4 changed files with 77 additions and 82 deletions
@@ -26,7 +26,6 @@ impl<'p> ControlPointIter<'p> {
}
}
#[derive(Debug)]
pub(crate) enum ControlPoint {
Timing { time: f32 },
Difficulty { time: f32, speed_multiplier: f32 },
@@ -1,8 +1,6 @@
use parse::HitObject;
use std::borrow::Cow;
const NORMALIZED_RADIUS: f32 = 52.0;
pub(crate) struct DifficultyObject<'h> {
pub(crate) base: &'h HitObject,
pub(crate) prev: Option<(f32, f32)>, // (jump_dist, strain_time)
@@ -21,30 +19,20 @@ impl<'h> DifficultyObject<'h> {
prev_vals: Option<(f32, f32)>, // (jump_dist, strain_time)
prev_prev: Option<Cow<HitObject>>,
clock_rate: f32,
radius: f32,
scaling_factor: f32,
) -> Self {
let delta = (base.start_time - prev.start_time) / clock_rate;
let strain_time = delta.max(50.0);
let mut scaling_factor = NORMALIZED_RADIUS / radius;
let prev_cursor_pos = prev.pos;
if radius < 30.0 {
let small_circle_bonus = (30.0 - radius).min(5.0) / 50.0;
scaling_factor *= 1.0 + small_circle_bonus;
}
let jump_dist = if base.is_spinner() {
0.0
} else {
(base.pos * scaling_factor - prev_cursor_pos * scaling_factor).length()
(base.pos * scaling_factor - prev.pos * scaling_factor).length()
};
let angle = prev_prev.map(|prev_prev| {
let prev_prev_cursor_pos = prev_prev.pos;
let v1 = prev_prev_cursor_pos - prev.pos;
let v2 = base.pos - prev_cursor_pos;
let v1 = prev_prev.pos - prev.pos;
let v2 = base.pos - prev.pos;
let dot = v1.dot(v2);
let det = v1.x * v2.y - v1.y * v2.x;
@@ -52,8 +40,6 @@ impl<'h> DifficultyObject<'h> {
det.atan2(dot).abs()
});
// let prev = prev_diff.map(|o| (o.jump_dist, o.strain_time));
Self {
base,
prev: prev_vals,
+16 -63
View File
@@ -4,11 +4,12 @@ mod control_point_iter;
mod difficulty_object;
mod skill;
mod skill_kind;
mod slider_state;
use control_point_iter::{ControlPoint, ControlPointIter};
use difficulty_object::DifficultyObject;
use skill::Skill;
use skill_kind::SkillKind;
use slider_state::SliderState;
use parse::{Beatmap, HitObject, HitObjectKind, Mods};
use std::borrow::Cow;
@@ -16,6 +17,7 @@ use std::borrow::Cow;
const OBJECT_RADIUS: f32 = 64.0;
const SECTION_LEN: f32 = 400.0;
const DIFFICULTY_MULTIPLIER: f32 = 0.0675;
const NORMALIZED_RADIUS: f32 = 52.0;
/// Star calculation for osu!standard maps.
///
@@ -35,6 +37,12 @@ pub fn stars(map: &Beatmap, mods: impl Mods) -> DifficultyAttributes {
let section_len = SECTION_LEN * attributes.clock_rate;
let radius = OBJECT_RADIUS * (1.0 - 0.7 * (attributes.cs - 5.0) / 5.0) / 2.0;
let mut scaling_factor = NORMALIZED_RADIUS / radius;
if radius < 30.0 {
let small_circle_bonus = (30.0 - radius).min(5.0) / 50.0;
scaling_factor *= 1.0 + small_circle_bonus;
}
let mut max_combo = 0;
let mut n_circles = 0;
@@ -51,16 +59,14 @@ pub fn stars(map: &Beatmap, mods: impl Mods) -> DifficultyAttributes {
HitObjectKind::Slider {
pixel_len, repeats, ..
} => {
max_combo += count_ticks(h.start_time, *pixel_len, *repeats, &map, &mut state);
max_combo += state.count_ticks(h.start_time, *pixel_len, *repeats, &map);
let h = HitObject {
Cow::Owned(HitObject {
pos: h.pos,
start_time: h.start_time,
kind: HitObjectKind::Circle,
sound: h.sound,
};
Cow::Owned(h)
})
}
HitObjectKind::Spinner { .. } => {
max_combo += 1;
@@ -74,6 +80,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods) -> DifficultyAttributes {
let mut aim = Skill::new(SkillKind::Aim);
let mut speed = Skill::new(SkillKind::Speed);
// First object has no predecessor and thus no strain, handle distinctly
let mut current_section_end =
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
@@ -81,7 +88,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods) -> DifficultyAttributes {
let mut prev = hit_objects.next().unwrap();
let mut prev_vals = None;
// Handle first object separately to remove if-branching
// Handle second object separately to remove later if-branching
let curr = hit_objects.next().unwrap();
let h = DifficultyObject::new(
curr.as_ref(),
@@ -89,7 +96,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods) -> DifficultyAttributes {
prev_vals,
prev_prev,
attributes.clock_rate,
radius,
scaling_factor,
);
aim.process(&h);
@@ -107,7 +114,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods) -> DifficultyAttributes {
prev_vals,
prev_prev,
attributes.clock_rate,
radius,
scaling_factor,
);
while h.base.start_time > current_section_end {
@@ -157,60 +164,6 @@ pub fn stars(map: &Beatmap, mods: impl Mods) -> DifficultyAttributes {
}
}
struct SliderState<'p> {
control_points: ControlPointIter<'p>,
next_time: f32,
px_per_beat: f32,
prev_sv: f32,
}
impl<'p> SliderState<'p> {
#[inline]
fn new(map: &'p Beatmap) -> Self {
Self {
control_points: ControlPointIter::new(map),
next_time: std::f32::NEG_INFINITY,
px_per_beat: 1.0,
prev_sv: 1.0,
}
}
}
fn count_ticks(
time: f32,
pixel_len: f32,
repeats: usize,
map: &Beatmap,
state: &mut SliderState,
) -> usize {
while time >= state.next_time {
state.px_per_beat = map.sv * 100.0 * state.prev_sv;
match state.control_points.next() {
Some(ControlPoint::Timing { time }) => {
state.next_time = time;
state.prev_sv = 1.0;
}
Some(ControlPoint::Difficulty {
time,
speed_multiplier,
}) => {
state.next_time = time;
state.prev_sv = speed_multiplier;
}
None => break,
}
}
let spans = repeats as f32;
let beats = pixel_len * spans / state.px_per_beat;
let ticks = ((beats - 0.1) / spans * map.tick_rate).ceil() as usize;
ticks
.checked_sub(1)
.map_or(0, |ticks| ticks * repeats + repeats + 1)
}
#[cfg(test)]
mod tests {
use super::stars;
@@ -0,0 +1,57 @@
use super::control_point_iter::{ControlPoint, ControlPointIter};
use parse::Beatmap;
pub(crate) struct SliderState<'p> {
control_points: ControlPointIter<'p>,
next_time: f32,
px_per_beat: f32,
prev_sv: f32,
}
impl<'p> SliderState<'p> {
#[inline]
pub(crate) fn new(map: &'p Beatmap) -> Self {
Self {
control_points: ControlPointIter::new(map),
next_time: std::f32::NEG_INFINITY,
px_per_beat: 1.0,
prev_sv: 1.0,
}
}
pub(crate) fn count_ticks(
&mut self,
time: f32,
pixel_len: f32,
repeats: usize,
map: &Beatmap,
) -> usize {
while time >= self.next_time {
self.px_per_beat = map.sv * 100.0 * self.prev_sv;
match self.control_points.next() {
Some(ControlPoint::Timing { time }) => {
self.next_time = time;
self.prev_sv = 1.0;
}
Some(ControlPoint::Difficulty {
time,
speed_multiplier,
}) => {
self.next_time = time;
self.prev_sv = speed_multiplier;
}
None => break,
}
}
let spans = repeats as f32;
let beats = pixel_len * spans / self.px_per_beat;
let ticks = ((beats - 0.1) / spans * map.tick_rate).ceil() as usize;
ticks
.checked_sub(1)
.map_or(0, |ticks| ticks * repeats + repeats + 1)
}
}