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")]
|
#![cfg(feature = "fruits")]
|
||||||
|
|
||||||
mod catch_object;
|
mod catch_object;
|
||||||
mod control_point_iter;
|
|
||||||
mod difficulty_object;
|
mod difficulty_object;
|
||||||
mod movement;
|
mod movement;
|
||||||
mod pp;
|
mod pp;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use super::control_point_iter::{ControlPoint, ControlPointIter};
|
use crate::{Beatmap, ControlPoint, ControlPointIter};
|
||||||
use crate::Beatmap;
|
|
||||||
|
|
||||||
pub(crate) struct SliderState<'p> {
|
pub(crate) struct SliderState<'p> {
|
||||||
control_points: ControlPointIter<'p>,
|
control_points: ControlPointIter<'p>,
|
||||||
|
|||||||
@@ -114,6 +114,12 @@ mod curve;
|
|||||||
mod math_util;
|
mod math_util;
|
||||||
mod mods;
|
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")]
|
#[cfg(feature = "fruits")]
|
||||||
pub use fruits::FruitsPP;
|
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;
|
use super::super::DifficultyAttributes;
|
||||||
|
|
||||||
mod control_point_iter;
|
|
||||||
mod difficulty_object;
|
mod difficulty_object;
|
||||||
mod osu_object;
|
mod osu_object;
|
||||||
mod skill;
|
mod skill;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use super::control_point_iter::{ControlPoint, ControlPointIter};
|
use crate::{Beatmap, ControlPoint, ControlPointIter};
|
||||||
use crate::Beatmap;
|
|
||||||
|
|
||||||
pub(crate) struct SliderState<'p> {
|
pub(crate) struct SliderState<'p> {
|
||||||
control_points: ControlPointIter<'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;
|
use super::super::DifficultyAttributes;
|
||||||
|
|
||||||
mod control_point_iter;
|
// mod control_point_iter;
|
||||||
mod difficulty_object;
|
mod difficulty_object;
|
||||||
mod skill;
|
mod skill;
|
||||||
mod skill_kind;
|
mod skill_kind;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use super::control_point_iter::{ControlPoint, ControlPointIter};
|
use crate::{Beatmap, ControlPoint, ControlPointIter};
|
||||||
|
|
||||||
use crate::Beatmap;
|
|
||||||
|
|
||||||
pub(crate) struct SliderState<'p> {
|
pub(crate) struct SliderState<'p> {
|
||||||
control_points: ControlPointIter<'p>,
|
control_points: ControlPointIter<'p>,
|
||||||
@@ -35,12 +33,9 @@ impl<'p> SliderState<'p> {
|
|||||||
self.next_time = time;
|
self.next_time = time;
|
||||||
self.prev_sv = 1.0;
|
self.prev_sv = 1.0;
|
||||||
}
|
}
|
||||||
Some(ControlPoint::Difficulty {
|
Some(ControlPoint::Difficulty { time, speed_mult }) => {
|
||||||
time,
|
|
||||||
speed_multiplier,
|
|
||||||
}) => {
|
|
||||||
self.next_time = time;
|
self.next_time = time;
|
||||||
self.prev_sv = speed_multiplier;
|
self.prev_sv = speed_mult;
|
||||||
}
|
}
|
||||||
None => break,
|
None => break,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::cmp::Ordering;
|
|||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct TimingPoint {
|
pub struct TimingPoint {
|
||||||
pub beat_len: f32,
|
pub beat_len: f32,
|
||||||
pub bpm: f32,
|
pub bpm: f32, // TODO: Remove
|
||||||
pub time: f32,
|
pub time: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user