osu & fruits: refactored ControlPointIter & added unit test
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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,7 +1,6 @@
|
||||
#![cfg(feature = "fruits")]
|
||||
|
||||
mod catch_object;
|
||||
mod control_point_iter;
|
||||
mod difficulty_object;
|
||||
mod movement;
|
||||
mod pp;
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
use super::super::DifficultyAttributes;
|
||||
|
||||
mod control_point_iter;
|
||||
mod difficulty_object;
|
||||
mod osu_object;
|
||||
mod skill;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user