osu & fruits: refactored ControlPointIter & added unit test

This commit is contained in:
MaxOhn
2021-01-22 15:42:48 +01:00
parent 89d5f7362c
commit adade9a84a
12 changed files with 167 additions and 231 deletions
+154
View File
@@ -0,0 +1,154 @@
#![cfg(any(feature = "osu", feature = "fruits"))]
use crate::{Beatmap, DifficultyPoint, TimingPoint};
use std::slice::Iter;
macro_rules! next_tuple {
($iter:expr, ($first:ident, $second:ident)) => {
$iter.next().map(|e| (e.$first, e.$second))
};
}
pub(crate) struct ControlPointIter<'p> {
timing_points: Iter<'p, TimingPoint>,
difficulty_points: Iter<'p, DifficultyPoint>,
next_timing: Option<(f32, f32)>,
next_difficulty: Option<(f32, f32)>,
}
impl<'p> ControlPointIter<'p> {
#[inline]
pub(crate) fn new(map: &'p Beatmap) -> Self {
let mut timing_points = map.timing_points.iter();
let mut difficulty_points = map.difficulty_points.iter();
Self {
next_timing: next_tuple!(timing_points, (time, beat_len)),
next_difficulty: next_tuple!(difficulty_points, (time, speed_multiplier)),
timing_points,
difficulty_points,
}
}
}
pub(crate) enum ControlPoint {
Timing {
time: f32,
#[cfg(not(feature = "no_sliders_no_leniency"))]
beat_len: f32,
},
Difficulty {
time: f32,
speed_mult: f32,
},
}
#[cfg(not(feature = "no_sliders_no_leniency"))]
impl ControlPoint {
#[inline]
pub(crate) fn time(&self) -> f32 {
match self {
Self::Timing { time, .. } => *time,
Self::Difficulty { time, .. } => *time,
}
}
}
impl<'p> Iterator for ControlPointIter<'p> {
type Item = ControlPoint;
fn next(&mut self) -> Option<Self::Item> {
match (self.next_timing, self.next_difficulty) {
(Some((time, _beat_len)), Some((d, _))) if time <= d => {
self.next_timing = next_tuple!(self.timing_points, (time, beat_len));
#[cfg(not(feature = "no_sliders_no_leniency"))]
{
Some(ControlPoint::Timing {
time,
beat_len: _beat_len,
})
}
#[cfg(feature = "no_sliders_no_leniency")]
{
Some(ControlPoint::Timing { time })
}
}
(_, Some((time, speed_mult))) => {
self.next_difficulty =
next_tuple!(self.difficulty_points, (time, speed_multiplier));
Some(ControlPoint::Difficulty { time, speed_mult })
}
(Some((time, _beat_len)), None) => {
self.next_timing = next_tuple!(self.timing_points, (time, beat_len));
#[cfg(not(feature = "no_sliders_no_leniency"))]
{
Some(ControlPoint::Timing {
time,
beat_len: _beat_len,
})
}
#[cfg(feature = "no_sliders_no_leniency")]
{
Some(ControlPoint::Timing { time })
}
}
(None, None) => None,
}
}
}
#[cfg(test)]
mod test {
use crate::{Beatmap, ControlPoint, ControlPointIter, DifficultyPoint, TimingPoint};
#[test]
fn control_point_iter() {
let map = Beatmap {
timing_points: vec![
TimingPoint {
time: 1.0,
beat_len: 10.0,
bpm: 0.0,
},
TimingPoint {
time: 3.0,
beat_len: 10.0,
bpm: 0.0,
},
TimingPoint {
time: 4.0,
beat_len: 10.0,
bpm: 0.0,
},
],
difficulty_points: vec![
DifficultyPoint {
time: 2.0,
speed_multiplier: 10.0,
},
DifficultyPoint {
time: 5.0,
speed_multiplier: 10.0,
},
],
..Default::default()
};
let mut iter = ControlPointIter::new(&map);
assert!(matches!(iter.next(), Some(ControlPoint::Timing { .. })));
assert!(matches!(iter.next(), Some(ControlPoint::Difficulty { .. })));
assert!(matches!(iter.next(), Some(ControlPoint::Timing { .. })));
assert!(matches!(iter.next(), Some(ControlPoint::Timing { .. })));
assert!(matches!(iter.next(), Some(ControlPoint::Difficulty { .. })));
assert!(matches!(iter.next(), None));
}
}
-74
View File
@@ -1,74 +0,0 @@
use crate::{Beatmap, DifficultyPoint, TimingPoint};
use std::slice::Iter;
macro_rules! next_tuple {
($iter:expr, ($first:ident, $second:ident)) => {
$iter.next().map(|e| (e.$first, e.$second))
};
}
pub(crate) struct ControlPointIter<'p> {
timing_points: Iter<'p, TimingPoint>,
difficulty_points: Iter<'p, DifficultyPoint>,
next_timing: Option<(f32, f32)>,
next_difficulty: Option<(f32, f32)>,
}
impl<'p> ControlPointIter<'p> {
#[inline]
pub(crate) fn new(map: &'p Beatmap) -> Self {
let mut timing_points = map.timing_points.iter();
let mut difficulty_points = map.difficulty_points.iter();
Self {
next_timing: next_tuple!(timing_points, (time, beat_len)),
next_difficulty: next_tuple!(difficulty_points, (time, speed_multiplier)),
timing_points,
difficulty_points,
}
}
}
pub(crate) enum ControlPoint {
Timing { time: f32, beat_len: f32 },
Difficulty { time: f32, speed_mult: f32 },
}
impl ControlPoint {
#[inline]
pub(crate) fn time(&self) -> f32 {
match self {
Self::Timing { time, .. } => *time,
Self::Difficulty { time, .. } => *time,
}
}
}
impl<'p> Iterator for ControlPointIter<'p> {
type Item = ControlPoint;
fn next(&mut self) -> Option<Self::Item> {
match (self.next_timing, self.next_difficulty) {
(Some((time, beat_len)), Some((d, _))) if time <= d => {
self.next_timing = next_tuple!(self.timing_points, (time, beat_len));
Some(ControlPoint::Timing { time, beat_len })
}
(_, Some((time, speed_mult))) => {
self.next_difficulty =
next_tuple!(self.difficulty_points, (time, speed_multiplier));
Some(ControlPoint::Difficulty { time, speed_mult })
}
(Some((time, beat_len)), None) => {
self.next_timing = next_tuple!(self.timing_points, (time, beat_len));
Some(ControlPoint::Timing { time, beat_len })
}
(None, None) => None,
}
}
}
-1
View File
@@ -1,7 +1,6 @@
#![cfg(feature = "fruits")]
mod catch_object;
mod control_point_iter;
mod difficulty_object;
mod movement;
mod pp;
+1 -2
View File
@@ -1,5 +1,4 @@
use super::control_point_iter::{ControlPoint, ControlPointIter};
use crate::Beatmap;
use crate::{Beatmap, ControlPoint, ControlPointIter};
pub(crate) struct SliderState<'p> {
control_points: ControlPointIter<'p>,
+6
View File
@@ -114,6 +114,12 @@ mod curve;
mod math_util;
mod mods;
#[cfg(any(feature = "osu", feature = "fruits"))]
pub(crate) mod control_point_iter;
#[cfg(any(feature = "osu", feature = "fruits"))]
pub(crate) use control_point_iter::{ControlPoint, ControlPointIter};
#[cfg(feature = "fruits")]
pub use fruits::FruitsPP;
@@ -1,74 +0,0 @@
use crate::{Beatmap, DifficultyPoint, TimingPoint};
use std::slice::Iter;
macro_rules! next_tuple {
($iter:expr, ($first:ident, $second:ident)) => {
$iter.next().map(|e| (e.$first, e.$second))
};
}
pub(crate) struct ControlPointIter<'p> {
timing_points: Iter<'p, TimingPoint>,
difficulty_points: Iter<'p, DifficultyPoint>,
next_timing: Option<(f32, f32)>,
next_difficulty: Option<(f32, f32)>,
}
impl<'p> ControlPointIter<'p> {
#[inline]
pub(crate) fn new(map: &'p Beatmap) -> Self {
let mut timing_points = map.timing_points.iter();
let mut difficulty_points = map.difficulty_points.iter();
Self {
next_timing: next_tuple!(timing_points, (time, beat_len)),
next_difficulty: next_tuple!(difficulty_points, (time, speed_multiplier)),
timing_points,
difficulty_points,
}
}
}
pub(crate) enum ControlPoint {
Timing { time: f32, beat_len: f32 },
Difficulty { time: f32, speed_mult: f32 },
}
impl ControlPoint {
#[inline]
pub(crate) fn time(&self) -> f32 {
match self {
Self::Timing { time, .. } => *time,
Self::Difficulty { time, .. } => *time,
}
}
}
impl<'p> Iterator for ControlPointIter<'p> {
type Item = ControlPoint;
fn next(&mut self) -> Option<Self::Item> {
match (self.next_timing, self.next_difficulty) {
(Some((time, beat_len)), Some((d, _))) if time <= d => {
self.next_timing = next_tuple!(self.timing_points, (time, beat_len));
Some(ControlPoint::Timing { time, beat_len })
}
(_, Some((time, speed_mult))) => {
self.next_difficulty =
next_tuple!(self.difficulty_points, (time, speed_multiplier));
Some(ControlPoint::Difficulty { time, speed_mult })
}
(Some((time, beat_len)), None) => {
self.next_timing = next_tuple!(self.timing_points, (time, beat_len));
Some(ControlPoint::Timing { time, beat_len })
}
(None, None) => None,
}
}
}
-1
View File
@@ -6,7 +6,6 @@
use super::super::DifficultyAttributes;
mod control_point_iter;
mod difficulty_object;
mod osu_object;
mod skill;
+1 -2
View File
@@ -1,5 +1,4 @@
use super::control_point_iter::{ControlPoint, ControlPointIter};
use crate::Beatmap;
use crate::{Beatmap, ControlPoint, ControlPointIter};
pub(crate) struct SliderState<'p> {
control_points: ControlPointIter<'p>,
@@ -1,67 +0,0 @@
use crate::{Beatmap, DifficultyPoint, TimingPoint};
use std::slice::Iter;
macro_rules! next_tuple {
($iter:expr, ($first:ident, $second:ident)) => {
$iter.next().map(|e| (e.$first, e.$second))
};
}
pub(crate) struct ControlPointIter<'p> {
timing_points: Iter<'p, TimingPoint>,
difficulty_points: Iter<'p, DifficultyPoint>,
next_timing: Option<f32>,
next_difficulty: Option<(f32, f32)>,
}
impl<'p> ControlPointIter<'p> {
#[inline]
pub(crate) fn new(map: &'p Beatmap) -> Self {
let mut timing_points = map.timing_points.iter();
let mut difficulty_points = map.difficulty_points.iter();
Self {
next_timing: timing_points.next().map(|t| t.time),
next_difficulty: next_tuple!(difficulty_points, (time, speed_multiplier)),
timing_points,
difficulty_points,
}
}
}
pub(crate) enum ControlPoint {
Timing { time: f32 },
Difficulty { time: f32, speed_multiplier: f32 },
}
impl<'p> Iterator for ControlPointIter<'p> {
type Item = ControlPoint;
fn next(&mut self) -> Option<Self::Item> {
match (self.next_timing, self.next_difficulty) {
(Some(time), Some((d, _))) if time <= d => {
self.next_timing = self.timing_points.next().map(|t| t.time);
Some(ControlPoint::Timing { time })
}
(_, Some((time, speed_multiplier))) => {
self.next_difficulty =
next_tuple!(self.difficulty_points, (time, speed_multiplier));
Some(ControlPoint::Difficulty {
time,
speed_multiplier,
})
}
(Some(time), _) => {
self.next_timing = self.timing_points.next().map(|t| t.time);
Some(ControlPoint::Timing { time })
}
(None, None) => None,
}
}
}
@@ -6,7 +6,7 @@
use super::super::DifficultyAttributes;
mod control_point_iter;
// mod control_point_iter;
mod difficulty_object;
mod skill;
mod skill_kind;
@@ -1,6 +1,4 @@
use super::control_point_iter::{ControlPoint, ControlPointIter};
use crate::Beatmap;
use crate::{Beatmap, ControlPoint, ControlPointIter};
pub(crate) struct SliderState<'p> {
control_points: ControlPointIter<'p>,
@@ -35,12 +33,9 @@ impl<'p> SliderState<'p> {
self.next_time = time;
self.prev_sv = 1.0;
}
Some(ControlPoint::Difficulty {
time,
speed_multiplier,
}) => {
Some(ControlPoint::Difficulty { time, speed_mult }) => {
self.next_time = time;
self.prev_sv = speed_multiplier;
self.prev_sv = speed_mult;
}
None => break,
}
+1 -1
View File
@@ -4,7 +4,7 @@ use std::cmp::Ordering;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct TimingPoint {
pub beat_len: f32,
pub bpm: f32,
pub bpm: f32, // TODO: Remove
pub time: f32,
}