refactored beatmap types into a new module

This commit is contained in:
MaxOhn
2022-06-27 07:38:19 +02:00
parent 5fdec291ec
commit c2e96ce95b
11 changed files with 344 additions and 336 deletions
+9
View File
@@ -2,6 +2,15 @@
- __Additions__: - __Additions__:
- Added the `ControlPoint` and `ControlPointerIter` types to the public interface, aswell as the `Beatmap::control_points` method - Added the `ControlPoint` and `ControlPointerIter` types to the public interface, aswell as the `Beatmap::control_points` method
- __Breaking changes:__
- Moved some types to a different module. The following types can now be found in `rosu_pp::beatmap`:
- `Beatmap`
- `BeatmapAttributes`
- `ControlPoint`
- `ControlPointIter`
- `DifficultyPoint`
- `GameMode`
- `TimingPoint`
# v0.5.2 (2022-06-14) # v0.5.2 (2022-06-14)
@@ -1,9 +1,56 @@
use crate::{ use std::{cmp::Ordering, iter::Copied, slice::Iter};
parse::{DifficultyPoint, TimingPoint},
Beatmap,
};
use std::{iter::Copied, slice::Iter}; use crate::Beatmap;
/// New rhythm speed change.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct TimingPoint {
/// The beat length for this timing section
pub beat_len: f64,
/// The start time of this timing section
pub time: f64,
}
impl PartialOrd for TimingPoint {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.time.partial_cmp(&other.time)
}
}
/// [`TimingPoint`] that depends on a previous one.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DifficultyPoint {
/// The start time for the current speed multiplier
pub time: f64,
/// The speed multiplier until the next timing point
pub speed_multiplier: f64,
}
impl PartialOrd for DifficultyPoint {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.time.partial_cmp(&other.time)
}
}
/// Control point for a [`Beatmap`].
#[derive(Copy, Clone, Debug)]
pub enum ControlPoint {
/// A timing point containing the current beat length.
Timing(TimingPoint),
/// A difficulty point containing the current speed multiplier.
Difficulty(DifficultyPoint),
}
impl ControlPoint {
/// Provides the timestamp of the control point.
#[inline]
pub fn time(&self) -> f64 {
match self {
Self::Timing(point) => point.time,
Self::Difficulty(point) => point.time,
}
}
}
/// Iterator for a [`Beatmap`]'s timing- and difficulty points sorted by timestamp /// Iterator for a [`Beatmap`]'s timing- and difficulty points sorted by timestamp
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@@ -31,26 +78,6 @@ impl<'p> ControlPointIter<'p> {
} }
} }
/// Control point for a [`Beatmap`].
#[derive(Copy, Clone, Debug)]
pub enum ControlPoint {
/// A timing point containing the current beat length.
Timing(TimingPoint),
/// A difficulty point containing the current speed multiplier.
Difficulty(DifficultyPoint),
}
impl ControlPoint {
/// Provides the timestamp of the control point.
#[inline]
pub fn time(&self) -> f64 {
match self {
Self::Timing(point) => point.time,
Self::Difficulty(point) => point.time,
}
}
}
impl<'p> Iterator for ControlPointIter<'p> { impl<'p> Iterator for ControlPointIter<'p> {
type Item = ControlPoint; type Item = ControlPoint;
@@ -80,8 +107,8 @@ impl<'p> Iterator for ControlPointIter<'p> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{ use crate::{
parse::{DifficultyPoint, TimingPoint}, beatmap::{ControlPoint, ControlPointIter, DifficultyPoint, TimingPoint},
Beatmap, ControlPoint, ControlPointIter, Beatmap,
}; };
#[test] #[test]
+79
View File
@@ -0,0 +1,79 @@
use crate::parse::HitObject;
pub use self::{
attributes::BeatmapAttributes,
control_points::{ControlPoint, ControlPointIter, DifficultyPoint, TimingPoint},
mode::GameMode,
};
mod attributes;
mod control_points;
mod mode;
/// The main beatmap struct containing all data relevant
/// for difficulty and performance calculation
#[derive(Clone, Default, Debug)]
pub struct Beatmap {
/// The game mode.
pub mode: GameMode,
/// The version of the .osu file.
pub version: u8,
/// The amount of circles.
pub n_circles: u32,
/// The amount of sliders.
pub n_sliders: u32,
/// The amount of spinners.
pub n_spinners: u32,
/// The approach rate.
pub ar: f32,
/// The overall difficulty.
pub od: f32,
/// The circle size.
pub cs: f32,
/// The health drain rate.
pub hp: f32,
/// Base slider velocity in pixels per beat
pub slider_mult: f64,
/// Amount of slider ticks per beat.
pub tick_rate: f64,
/// All hitobjects of the beatmap.
pub hit_objects: Vec<HitObject>,
/// Store the sounds for all objects in their own Vec to minimize the struct size.
/// Hitsounds are only used in osu!taiko in which they represent color.
pub sounds: Vec<u8>,
/// Timing points that indicate a new timing section.
pub timing_points: Vec<TimingPoint>,
/// Timing point for the current timing section.
pub difficulty_points: Vec<DifficultyPoint>,
/// The stack leniency that is used to calculate
/// the stack offset for stacked positions.
pub stack_leniency: f32,
}
impl Beatmap {
/// Extract a beatmap's attributes into their own type.
#[inline]
pub fn attributes(&self) -> BeatmapAttributes {
BeatmapAttributes::new(self.ar, self.od, self.cs, self.hp)
}
/// The beats per minute of the map.
#[inline]
pub fn bpm(&self) -> f64 {
match self.timing_points.first() {
Some(point) => point.beat_len.recip() * 1000.0 * 60.0,
None => 0.0,
}
}
/// Create an iterator over the map's timing- and difficulty points sorted by timestamp.
#[inline]
pub fn control_points(&self) -> ControlPointIter<'_> {
ControlPointIter::new(self)
}
}
+20
View File
@@ -0,0 +1,20 @@
/// The mode of a beatmap.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[allow(clippy::upper_case_acronyms)]
pub enum GameMode {
/// osu!standard
STD = 0,
/// osu!taiko
TKO = 1,
/// osu!catch
CTB = 2,
/// osu!mania
MNA = 3,
}
impl Default for GameMode {
#[inline]
fn default() -> Self {
Self::STD
}
}
+2 -5
View File
@@ -1,4 +1,4 @@
use crate::{Beatmap, ControlPoint, ControlPointIter}; use crate::beatmap::{Beatmap, ControlPoint, ControlPointIter};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct SliderState<'p> { pub(crate) struct SliderState<'p> {
@@ -45,10 +45,7 @@ impl<'p> SliderState<'p> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{ use crate::beatmap::{Beatmap, DifficultyPoint, TimingPoint};
parse::{DifficultyPoint, TimingPoint},
Beatmap,
};
use super::SliderState; use super::SliderState;
+6 -5
View File
@@ -191,9 +191,13 @@ pub mod osu;
/// Everything about osu!taiko. /// Everything about osu!taiko.
pub mod taiko; pub mod taiko;
/// Beatmap parsing and the contained types. /// Beatmap parsing
pub mod parse; pub mod parse;
/// Beatmap and contained types
pub mod beatmap;
pub use beatmap::{Beatmap, GameMode};
mod gradual; mod gradual;
pub use gradual::{GradualDifficultyAttributes, GradualPerformanceAttributes, ScoreState}; pub use gradual::{GradualDifficultyAttributes, GradualPerformanceAttributes, ScoreState};
@@ -206,16 +210,13 @@ pub use stars::AnyStars;
mod curve; mod curve;
mod mods; mod mods;
mod control_point_iter;
pub use control_point_iter::{ControlPoint, ControlPointIter};
pub use catch::{CatchPP, CatchStars}; pub use catch::{CatchPP, CatchStars};
pub use mania::{ManiaPP, ManiaStars}; pub use mania::{ManiaPP, ManiaStars};
pub use osu::{OsuPP, OsuStars}; pub use osu::{OsuPP, OsuStars};
pub use taiko::{TaikoPP, TaikoStars}; pub use taiko::{TaikoPP, TaikoStars};
pub use mods::Mods; pub use mods::Mods;
pub use parse::{Beatmap, BeatmapAttributes, GameMode, ParseError, ParseResult}; pub use parse::{ParseError, ParseResult};
/// Provides some additional methods on [`Beatmap`](crate::Beatmap). /// Provides some additional methods on [`Beatmap`](crate::Beatmap).
pub trait BeatmapExt { pub trait BeatmapExt {
+2 -5
View File
@@ -1,4 +1,4 @@
use crate::{Beatmap, ControlPoint, ControlPointIter}; use crate::beatmap::{Beatmap, ControlPoint, ControlPointIter};
pub(crate) struct SliderState<'p> { pub(crate) struct SliderState<'p> {
control_points: ControlPointIter<'p>, control_points: ControlPointIter<'p>,
@@ -44,10 +44,7 @@ impl<'p> SliderState<'p> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{ use crate::beatmap::{Beatmap, DifficultyPoint, TimingPoint};
parse::{DifficultyPoint, TimingPoint},
Beatmap,
};
use super::SliderState; use super::SliderState;
-31
View File
@@ -1,31 +0,0 @@
use std::cmp::Ordering;
/// New rhythm speed change.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct TimingPoint {
/// The beat length for this timing section
pub beat_len: f64,
/// The start time of this timing section
pub time: f64,
}
impl PartialOrd for TimingPoint {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.time.partial_cmp(&other.time)
}
}
/// [`TimingPoint`](crate::parse::TimingPoint) that depends on a previous one.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DifficultyPoint {
/// The start time for the current speed multiplier
pub time: f64,
/// The speed multiplier until the next timing point
pub speed_multiplier: f64,
}
impl PartialOrd for DifficultyPoint {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.time.partial_cmp(&other.time)
}
}
+3 -3
View File
@@ -1,7 +1,7 @@
use super::Pos2;
use std::cmp::Ordering; use std::cmp::Ordering;
use super::{PathControlPoint, Pos2};
/// "Intermediate" hitobject created through parsing. /// "Intermediate" hitobject created through parsing.
/// Each mode will handle them differently. /// Each mode will handle them differently.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@@ -65,7 +65,7 @@ pub enum HitObjectKind {
/// The amount of repeat points of the slider. /// The amount of repeat points of the slider.
repeats: usize, repeats: usize,
/// The control points of the slider. /// The control points of the slider.
control_points: Vec<super::PathControlPoint>, control_points: Vec<PathControlPoint>,
}, },
/// A spinner object. /// A spinner object.
Spinner { Spinner {
+5 -96
View File
@@ -1,5 +1,3 @@
mod attributes;
mod control_point;
mod error; mod error;
mod hitobject; mod hitobject;
mod hitsound; mod hitsound;
@@ -7,8 +5,6 @@ mod pos2;
mod reader; mod reader;
mod sort; mod sort;
pub use attributes::BeatmapAttributes;
pub use control_point::{DifficultyPoint, TimingPoint};
pub use error::{ParseError, ParseResult}; pub use error::{ParseError, ParseResult};
pub use hitobject::{HitObject, HitObjectKind}; pub use hitobject::{HitObject, HitObjectKind};
pub use hitsound::HitSound; pub use hitsound::HitSound;
@@ -32,7 +28,7 @@ use std::path::Path;
#[cfg(feature = "async_std")] #[cfg(feature = "async_std")]
use async_std::{fs::File, io::Read as AsyncRead, path::Path}; use async_std::{fs::File, io::Read as AsyncRead, path::Path};
use crate::control_point_iter::ControlPointIter; use crate::beatmap::{Beatmap, DifficultyPoint, GameMode, TimingPoint};
fn sort_unstable<T: PartialOrd>(slice: &mut [T]) { fn sort_unstable<T: PartialOrd>(slice: &mut [T]) {
slice.sort_unstable_by(|p1, p2| p1.partial_cmp(p2).unwrap_or(Ordering::Equal)); slice.sort_unstable_by(|p1, p2| p1.partial_cmp(p2).unwrap_or(Ordering::Equal));
@@ -595,72 +591,6 @@ macro_rules! from_path {
}; };
} }
/// The mode of a beatmap.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[allow(clippy::upper_case_acronyms)]
pub enum GameMode {
/// osu!standard
STD = 0,
/// osu!taiko
TKO = 1,
/// osu!catch
CTB = 2,
/// osu!mania
MNA = 3,
}
impl Default for GameMode {
#[inline]
fn default() -> Self {
Self::STD
}
}
/// The main beatmap struct containing all data relevant
/// for difficulty and pp calculation
#[derive(Clone, Default, Debug)]
pub struct Beatmap {
/// The game mode.
pub mode: GameMode,
/// The version of the .osu file.
pub version: u8,
/// The amount of circles.
pub n_circles: u32,
/// The amount of sliders.
pub n_sliders: u32,
/// The amount of spinners.
pub n_spinners: u32,
/// The approach rate.
pub ar: f32,
/// The overall difficulty.
pub od: f32,
/// The circle size.
pub cs: f32,
/// The health drain rate.
pub hp: f32,
/// Base slider velocity in pixels per beat
pub slider_mult: f64,
/// Amount of slider ticks per beat.
pub tick_rate: f64,
/// All hitobjects of the beatmap.
pub hit_objects: Vec<HitObject>,
/// Store the sounds for all objects in their own Vec to minimize the struct size.
/// Hitsounds are only used in osu!taiko in which they represent color.
pub sounds: Vec<u8>,
/// Timing points that indicate a new timing section.
pub timing_points: Vec<TimingPoint>,
/// Timing point for the current timing section.
pub difficulty_points: Vec<DifficultyPoint>,
/// The stack leniency that is used to calculate
/// the stack offset for stacked positions.
pub stack_leniency: f32,
}
impl Beatmap { impl Beatmap {
const CIRCLE_FLAG: u8 = 1 << 0; const CIRCLE_FLAG: u8 = 1 << 0;
const SLIDER_FLAG: u8 = 1 << 1; const SLIDER_FLAG: u8 = 1 << 1;
@@ -668,27 +598,6 @@ impl Beatmap {
const SPINNER_FLAG: u8 = 1 << 3; const SPINNER_FLAG: u8 = 1 << 3;
// const COMBO_OFFSET_FLAG: u8 = (1 << 4) | (1 << 5) | (1 << 6); // const COMBO_OFFSET_FLAG: u8 = (1 << 4) | (1 << 5) | (1 << 6);
const HOLD_FLAG: u8 = 1 << 7; const HOLD_FLAG: u8 = 1 << 7;
/// Extract a beatmap's attributes into their own type.
#[inline]
pub fn attributes(&self) -> BeatmapAttributes {
BeatmapAttributes::new(self.ar, self.od, self.cs, self.hp)
}
/// The beats per minute of the map.
#[inline]
pub fn bpm(&self) -> f64 {
match self.timing_points.first() {
Some(point) => point.beat_len.recip() * 1000.0 * 60.0,
None => 0.0,
}
}
/// Create an iterator over the map's timing- and difficulty points sorted by timestamp.
#[inline]
pub fn control_points(&self) -> ControlPointIter<'_> {
ControlPointIter::new(self)
}
} }
mod slider_parsing { mod slider_parsing {
@@ -927,11 +836,11 @@ mod tests {
println!("map_id: {}", map_id); println!("map_id: {}", map_id);
let map = match Beatmap::from_path(format!("./maps/{}.osu", map_id)).await { let map = match Beatmap::from_path(format!("./maps/{}.osu", map_id)).await {
Ok(map) => map, Ok(map) => beatmap,
Err(why) => panic!("Error while parsing map: {}", why), Err(why) => panic!("Error while parsing map: {}", why),
}; };
print_info(map); print_info(beatmap);
println!("---"); println!("---");
} }
}); });
@@ -945,11 +854,11 @@ mod tests {
println!("map_id: {}", map_id); println!("map_id: {}", map_id);
let map = match Beatmap::from_path(format!("./maps/{}.osu", map_id)).await { let map = match Beatmap::from_path(format!("./maps/{}.osu", map_id)).await {
Ok(map) => map, Ok(map) => beatmap,
Err(why) => panic!("Error while parsing map: {}", why), Err(why) => panic!("Error while parsing map: {}", why),
}; };
print_info(map); print_info(beatmap);
println!("---"); println!("---");
} }
}); });