adjusted slider handling for fruits
This commit is contained in:
+89
-57
@@ -25,6 +25,7 @@ const ALLOWED_CATCH_RANGE: f32 = 0.8;
|
|||||||
const CATCHER_SIZE: f32 = 106.75;
|
const CATCHER_SIZE: f32 = 106.75;
|
||||||
|
|
||||||
const LEGACY_LAST_TICK_OFFSET: f32 = 36.0;
|
const LEGACY_LAST_TICK_OFFSET: f32 = 36.0;
|
||||||
|
const BASE_SCORING_DISTANCE: f32 = 100.0;
|
||||||
|
|
||||||
/// Star calculation for osu!ctb maps
|
/// Star calculation for osu!ctb maps
|
||||||
///
|
///
|
||||||
@@ -73,65 +74,67 @@ pub fn stars(
|
|||||||
control_points,
|
control_points,
|
||||||
} => {
|
} => {
|
||||||
// HR business
|
// HR business
|
||||||
last_pos.replace(
|
*last_pos = Some(h.pos.x + control_points[control_points.len() - 1].pos.x);
|
||||||
h.pos.x + control_points[control_points.len() - 1].pos.x
|
|
||||||
- control_points[0].pos.x, // TODO: Remove subtract
|
|
||||||
);
|
|
||||||
*last_time = h.start_time;
|
*last_time = h.start_time;
|
||||||
|
|
||||||
// Responsible for timing point values
|
// Responsible for timing point values
|
||||||
slider_state.update(h.start_time);
|
slider_state.update(h.start_time);
|
||||||
|
|
||||||
let mut tick_distance = 100.0 * map.slider_mult / map.tick_rate;
|
let span_count = *repeats + 1;
|
||||||
|
|
||||||
|
let mut tick_dist = 100.0 * map.slider_mult / map.tick_rate;
|
||||||
|
|
||||||
if map.version >= 8 {
|
if map.version >= 8 {
|
||||||
tick_distance /=
|
tick_dist /=
|
||||||
(100.0 / slider_state.speed_mult).max(10.0).min(1000.0) / 100.0;
|
(100.0 / slider_state.slider_velocity).max(10.0).min(1000.0) / 100.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let duration = *repeats as f32 * slider_state.beat_len * pixel_len
|
// Build the curve w.r.t. the control points
|
||||||
/ (map.slider_mult * slider_state.speed_mult)
|
|
||||||
/ 100.0;
|
|
||||||
|
|
||||||
// Build the curve w.r.t. the curve points
|
|
||||||
let curve = Curve::new(control_points, *pixel_len, &mut curve_bufs);
|
let curve = Curve::new(control_points, *pixel_len, &mut curve_bufs);
|
||||||
|
|
||||||
let mut current_distance = tick_distance;
|
let velocity =
|
||||||
let time_add = duration * (tick_distance / (*pixel_len * *repeats as f32));
|
(BASE_SCORING_DISTANCE * map.slider_mult * slider_state.slider_velocity)
|
||||||
|
/ slider_state.beat_len;
|
||||||
|
|
||||||
let target = *pixel_len - tick_distance / 8.0;
|
let end_time = h.start_time + span_count as f32 * curve.dist() / velocity;
|
||||||
ticks.reserve((target / tick_distance) as usize);
|
let duration = end_time - h.start_time;
|
||||||
|
|
||||||
|
let mut curr_dist = tick_dist;
|
||||||
|
let time_add = duration * (tick_dist / (*pixel_len * span_count as f32));
|
||||||
|
|
||||||
|
let target = *pixel_len - tick_dist / 8.0;
|
||||||
|
ticks.reserve((target / tick_dist) as usize);
|
||||||
|
|
||||||
// Tick of the first span
|
// Tick of the first span
|
||||||
if current_distance < target {
|
if curr_dist < target {
|
||||||
for tick_idx in 1.. {
|
for tick_idx in 1.. {
|
||||||
let progress = current_distance / *pixel_len;
|
let progress = curr_dist / *pixel_len;
|
||||||
let pos = curve.position_at(progress);
|
let pos = curve.position_at(progress);
|
||||||
let time = h.start_time + time_add * tick_idx as f32;
|
let time = h.start_time + time_add * tick_idx as f32;
|
||||||
ticks.push((pos, time));
|
ticks.push((pos, time));
|
||||||
current_distance += tick_distance;
|
curr_dist += tick_dist;
|
||||||
|
|
||||||
if current_distance >= target {
|
if curr_dist >= target {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tiny_droplets +=
|
tiny_droplets +=
|
||||||
tiny_droplet_count(h.start_time, time_add, duration, *repeats, &ticks);
|
tiny_droplet_count(h.start_time, time_add, duration, span_count, &ticks);
|
||||||
|
|
||||||
let mut slider_objects = Vec::with_capacity(repeats * (ticks.len() + 1));
|
let mut slider_objects = Vec::with_capacity(span_count * (ticks.len() + 1));
|
||||||
slider_objects.push((h.pos, h.start_time));
|
slider_objects.push((h.pos, h.start_time));
|
||||||
|
|
||||||
// Other spans
|
// Other spans
|
||||||
if *repeats <= 1 {
|
if span_count <= 1 {
|
||||||
slider_objects.append(&mut ticks); // automatically empties buffer for next slider
|
slider_objects.append(&mut ticks); // automatically empties buffer for next slider
|
||||||
} else {
|
} else {
|
||||||
slider_objects.extend(&ticks);
|
slider_objects.extend(&ticks);
|
||||||
|
|
||||||
for repeat_id in 1..*repeats {
|
for span_idx in 1..span_count {
|
||||||
let dist = (repeat_id % 2) as f32 * *pixel_len;
|
let dist = (span_idx % 2) as f32 * *pixel_len;
|
||||||
let time_offset = (duration / *repeats as f32) * repeat_id as f32;
|
let time_offset = (duration / span_count as f32) * span_idx as f32;
|
||||||
let progress = dist / *pixel_len;
|
let progress = dist / *pixel_len;
|
||||||
let pos = curve.position_at(progress);
|
let pos = curve.position_at(progress);
|
||||||
|
|
||||||
@@ -139,7 +142,7 @@ pub fn stars(
|
|||||||
slider_objects.push((pos, h.start_time + time_offset));
|
slider_objects.push((pos, h.start_time + time_offset));
|
||||||
|
|
||||||
// Actual ticks
|
// Actual ticks
|
||||||
if repeat_id & 1 == 1 {
|
if span_idx & 1 == 1 {
|
||||||
slider_objects.extend(ticks.iter().rev().enumerate().map(
|
slider_objects.extend(ticks.iter().rev().enumerate().map(
|
||||||
|(i, (pos, time))| (*pos, *time + time_add * 2.0 * (i + 1) as f32),
|
|(i, (pos, time))| (*pos, *time + time_add * 2.0 * (i + 1) as f32),
|
||||||
));
|
));
|
||||||
@@ -152,12 +155,11 @@ pub fn stars(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Slider tail
|
// Slider tail
|
||||||
// let dist_end = (*repeats % 2) as f32 * *pixel_len;
|
|
||||||
let pos = curve.position_at(1.0); // TODO: what if reversing odd amount?
|
let pos = curve.position_at(1.0); // TODO: what if reversing odd amount?
|
||||||
slider_objects.push((pos, h.start_time + duration));
|
slider_objects.push((pos, h.start_time + duration));
|
||||||
|
|
||||||
fruits += 1 + *repeats;
|
fruits += span_count; // TODO: +1?
|
||||||
droplets += slider_objects.len() - 1 - *repeats;
|
droplets += slider_objects.len() - 1 - span_count; // TODO: -1?
|
||||||
|
|
||||||
let iter = slider_objects.into_iter().map(CatchObject::new);
|
let iter = slider_objects.into_iter().map(CatchObject::new);
|
||||||
|
|
||||||
@@ -308,62 +310,64 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
|||||||
control_points,
|
control_points,
|
||||||
} => {
|
} => {
|
||||||
// HR business
|
// HR business
|
||||||
last_pos.replace(
|
*last_pos = Some(h.pos.x + control_points[control_points.len() - 1].pos.x);
|
||||||
h.pos.x + control_points[control_points.len() - 1].pos.x
|
|
||||||
- control_points[0].pos.x, // TODO: Remove subtract
|
|
||||||
);
|
|
||||||
*last_time = h.start_time;
|
*last_time = h.start_time;
|
||||||
|
|
||||||
// Responsible for timing point values
|
// Responsible for timing point values
|
||||||
slider_state.update(h.start_time);
|
slider_state.update(h.start_time);
|
||||||
|
|
||||||
let mut tick_distance = 100.0 * map.slider_mult / map.tick_rate;
|
let span_count = *repeats + 1;
|
||||||
|
|
||||||
|
let mut tick_dist = 100.0 * map.slider_mult / map.tick_rate;
|
||||||
|
|
||||||
if map.version >= 8 {
|
if map.version >= 8 {
|
||||||
tick_distance /=
|
tick_dist /=
|
||||||
(100.0 / slider_state.speed_mult).max(10.0).min(1000.0) / 100.0;
|
(100.0 / slider_state.slider_velocity).max(10.0).min(1000.0) / 100.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let duration = *repeats as f32 * slider_state.beat_len * pixel_len
|
// Build the curve w.r.t. the control points
|
||||||
/ (map.slider_mult * slider_state.speed_mult)
|
|
||||||
/ 100.0;
|
|
||||||
|
|
||||||
// Build the curve w.r.t. the curve points
|
|
||||||
let curve = Curve::new(control_points, *pixel_len, &mut curve_bufs);
|
let curve = Curve::new(control_points, *pixel_len, &mut curve_bufs);
|
||||||
|
|
||||||
let mut current_distance = tick_distance;
|
let velocity =
|
||||||
let time_add = duration * (tick_distance / (*pixel_len * *repeats as f32));
|
(BASE_SCORING_DISTANCE * map.slider_mult * slider_state.slider_velocity)
|
||||||
|
/ slider_state.beat_len;
|
||||||
|
|
||||||
let target = *pixel_len - tick_distance / 8.0;
|
let end_time = h.start_time + span_count as f32 * curve.dist() / velocity;
|
||||||
ticks.reserve((target / tick_distance) as usize);
|
let duration = end_time - h.start_time;
|
||||||
|
|
||||||
|
let mut curr_dist = tick_dist;
|
||||||
|
let time_add = duration * (tick_dist / (*pixel_len * span_count as f32));
|
||||||
|
|
||||||
|
let target = *pixel_len - tick_dist / 8.0;
|
||||||
|
ticks.reserve((target / tick_dist) as usize);
|
||||||
|
|
||||||
// Tick of the first span
|
// Tick of the first span
|
||||||
if current_distance < target {
|
if curr_dist < target {
|
||||||
for tick_idx in 1.. {
|
for tick_idx in 1.. {
|
||||||
let progress = current_distance / *pixel_len;
|
let progress = curr_dist / *pixel_len;
|
||||||
let pos = curve.position_at(progress);
|
let pos = curve.position_at(progress);
|
||||||
let time = h.start_time + time_add * tick_idx as f32;
|
let time = h.start_time + time_add * tick_idx as f32;
|
||||||
ticks.push((pos, time));
|
ticks.push((pos, time));
|
||||||
current_distance += tick_distance;
|
curr_dist += tick_dist;
|
||||||
|
|
||||||
if current_distance >= target {
|
if curr_dist >= target {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut slider_objects = Vec::with_capacity(repeats * (ticks.len() + 1));
|
let mut slider_objects = Vec::with_capacity(span_count * (ticks.len() + 1));
|
||||||
slider_objects.push((h.pos, h.start_time));
|
slider_objects.push((h.pos, h.start_time));
|
||||||
|
|
||||||
// Other spans
|
// Other spans
|
||||||
if *repeats <= 1 {
|
if span_count <= 1 {
|
||||||
slider_objects.append(&mut ticks); // automatically empties buffer for next slider
|
slider_objects.append(&mut ticks); // automatically empties buffer for next slider
|
||||||
} else {
|
} else {
|
||||||
slider_objects.extend(&ticks);
|
slider_objects.extend(&ticks);
|
||||||
|
|
||||||
for repeat_id in 1..*repeats {
|
for span_idx in 1..span_count {
|
||||||
let dist = (repeat_id % 2) as f32 * *pixel_len;
|
let dist = (span_idx % 2) as f32 * *pixel_len;
|
||||||
let time_offset = (duration / *repeats as f32) * repeat_id as f32;
|
let time_offset = (duration / span_count as f32) * span_idx as f32;
|
||||||
let progress = dist / *pixel_len;
|
let progress = dist / *pixel_len;
|
||||||
let pos = curve.position_at(progress);
|
let pos = curve.position_at(progress);
|
||||||
|
|
||||||
@@ -371,7 +375,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
|||||||
slider_objects.push((pos, h.start_time + time_offset));
|
slider_objects.push((pos, h.start_time + time_offset));
|
||||||
|
|
||||||
// Actual ticks
|
// Actual ticks
|
||||||
if repeat_id & 1 == 1 {
|
if span_idx & 1 == 1 {
|
||||||
slider_objects.extend(ticks.iter().copied().rev());
|
slider_objects.extend(ticks.iter().copied().rev());
|
||||||
} else {
|
} else {
|
||||||
slider_objects.extend(ticks.iter().copied());
|
slider_objects.extend(ticks.iter().copied());
|
||||||
@@ -382,7 +386,6 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Slider tail
|
// Slider tail
|
||||||
// let dist_end = (*repeats % 2) as f32 * *pixel_len;
|
|
||||||
let pos = curve.position_at(1.0); // TODO: what if reversing odd amount?
|
let pos = curve.position_at(1.0); // TODO: what if reversing odd amount?
|
||||||
slider_objects.push((pos, h.start_time + duration));
|
slider_objects.push((pos, h.start_time + duration));
|
||||||
|
|
||||||
@@ -631,3 +634,32 @@ impl PerformanceAttributes {
|
|||||||
self.pp
|
self.pp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn custom_fruits() {
|
||||||
|
use std::{fs::File, time::Instant};
|
||||||
|
|
||||||
|
use crate::{Beatmap, FruitsPP};
|
||||||
|
|
||||||
|
let path = "./maps/2753127.osu";
|
||||||
|
let file = File::open(path).unwrap();
|
||||||
|
let map = Beatmap::parse(file).unwrap();
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
let result = FruitsPP::new(&map).mods(0).calculate();
|
||||||
|
|
||||||
|
let iters = 100;
|
||||||
|
let accum = start.elapsed();
|
||||||
|
|
||||||
|
// * Tiny benchmark for pp calculation
|
||||||
|
// let mut accum = accum;
|
||||||
|
|
||||||
|
// for _ in 0..iters {
|
||||||
|
// let start = Instant::now();
|
||||||
|
// let _result = OsuPP::new(&map).mods(0).calculate();
|
||||||
|
// accum += start.elapsed();
|
||||||
|
// }
|
||||||
|
|
||||||
|
println!("{:#?}", result);
|
||||||
|
println!("Calculation average: {:?}", accum / iters);
|
||||||
|
}
|
||||||
|
|||||||
+10
-12
@@ -4,7 +4,7 @@ pub(crate) struct SliderState<'p> {
|
|||||||
control_points: ControlPointIter<'p>,
|
control_points: ControlPointIter<'p>,
|
||||||
next: Option<ControlPoint>,
|
next: Option<ControlPoint>,
|
||||||
pub(crate) beat_len: f32,
|
pub(crate) beat_len: f32,
|
||||||
pub(crate) speed_mult: f32,
|
pub(crate) slider_velocity: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'p> SliderState<'p> {
|
impl<'p> SliderState<'p> {
|
||||||
@@ -12,12 +12,11 @@ impl<'p> SliderState<'p> {
|
|||||||
pub(crate) fn new(map: &'p Beatmap) -> Self {
|
pub(crate) fn new(map: &'p Beatmap) -> Self {
|
||||||
let mut control_points = ControlPointIter::new(map);
|
let mut control_points = ControlPointIter::new(map);
|
||||||
|
|
||||||
let (beat_len, speed_mult) = match control_points.next() {
|
let (beat_len, slider_velocity) = match control_points.next() {
|
||||||
Some(ControlPoint::Timing { beat_len, .. }) => (beat_len, 1.0),
|
Some(ControlPoint::Timing { beat_len, .. }) => (beat_len, 1.0),
|
||||||
Some(ControlPoint::Difficulty {
|
Some(ControlPoint::Difficulty {
|
||||||
slider_velocity: speed_mult,
|
slider_velocity, ..
|
||||||
..
|
}) => (1000.0, slider_velocity),
|
||||||
}) => (1000.0, speed_mult),
|
|
||||||
None => (1000.0, 1.0),
|
None => (1000.0, 1.0),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ impl<'p> SliderState<'p> {
|
|||||||
next: control_points.next(),
|
next: control_points.next(),
|
||||||
control_points,
|
control_points,
|
||||||
beat_len,
|
beat_len,
|
||||||
speed_mult,
|
slider_velocity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,12 +34,11 @@ impl<'p> SliderState<'p> {
|
|||||||
match next {
|
match next {
|
||||||
ControlPoint::Timing { beat_len, .. } => {
|
ControlPoint::Timing { beat_len, .. } => {
|
||||||
self.beat_len = *beat_len;
|
self.beat_len = *beat_len;
|
||||||
self.speed_mult = 1.0;
|
self.slider_velocity = 1.0;
|
||||||
}
|
}
|
||||||
ControlPoint::Difficulty {
|
ControlPoint::Difficulty {
|
||||||
slider_velocity: speed_mult,
|
slider_velocity, ..
|
||||||
..
|
} => self.slider_velocity = *slider_velocity,
|
||||||
} => self.speed_mult = *speed_mult,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.next = self.control_points.next();
|
self.next = self.control_points.next();
|
||||||
@@ -94,10 +92,10 @@ mod test {
|
|||||||
|
|
||||||
state.update(3.0);
|
state.update(3.0);
|
||||||
assert_eq!(state.beat_len, 20.0);
|
assert_eq!(state.beat_len, 20.0);
|
||||||
assert_eq!(state.speed_mult, 1.0);
|
assert_eq!(state.slider_velocity, 1.0);
|
||||||
|
|
||||||
state.update(5.0);
|
state.update(5.0);
|
||||||
assert_eq!(state.beat_len, 30.0);
|
assert_eq!(state.beat_len, 30.0);
|
||||||
assert_eq!(state.speed_mult, 45.0);
|
assert_eq!(state.slider_velocity, 45.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user