feat: added struct CatchOwnedGradualDifficulty
This commit is contained in:
+1
-2
@@ -137,7 +137,6 @@ impl BeatmapExt for Beatmap {
|
||||
curve_bufs: CurveBuffers::default(),
|
||||
last_pos: None,
|
||||
last_time: 0.0,
|
||||
map: self,
|
||||
ticks: Vec::new(),
|
||||
with_hr: mods.hr(),
|
||||
};
|
||||
@@ -145,7 +144,7 @@ impl BeatmapExt for Beatmap {
|
||||
let mut hit_objects: Vec<_> = self
|
||||
.hit_objects
|
||||
.iter()
|
||||
.filter_map(|h| FruitOrJuice::new(h, &mut params))
|
||||
.filter_map(|h| FruitOrJuice::new(h, &mut params, self))
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ impl CatchObject {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_hr(mut self, params: &mut FruitParams<'_>) -> Self {
|
||||
pub(crate) fn with_hr(mut self, params: &mut FruitParams) -> Self {
|
||||
let mut offset_pos = self.pos;
|
||||
let time_diff = self.time - params.last_time;
|
||||
|
||||
|
||||
@@ -12,12 +12,11 @@ const LEGACY_LAST_TICK_OFFSET: f64 = 36.0;
|
||||
const BASE_SCORING_DISTANCE: f64 = 100.0;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct FruitParams<'a> {
|
||||
pub(crate) struct FruitParams {
|
||||
pub(crate) attributes: CatchDifficultyAttributes,
|
||||
pub(crate) curve_bufs: CurveBuffers,
|
||||
pub(crate) last_pos: Option<f32>,
|
||||
pub(crate) last_time: f64,
|
||||
pub(crate) map: &'a Beatmap,
|
||||
pub(crate) ticks: Vec<(Pos2, f64)>,
|
||||
pub(crate) with_hr: bool,
|
||||
}
|
||||
@@ -31,7 +30,7 @@ pub(crate) enum FruitOrJuice {
|
||||
}
|
||||
|
||||
impl FruitOrJuice {
|
||||
pub(crate) fn new(h: &HitObject, params: &mut FruitParams<'_>) -> Option<Self> {
|
||||
pub(crate) fn new(h: &HitObject, params: &mut FruitParams, map: &Beatmap) -> Option<Self> {
|
||||
match &h.kind {
|
||||
HitObjectKind::Circle => {
|
||||
let mut h = CatchObject::new((h.pos, h.start_time));
|
||||
@@ -54,17 +53,12 @@ impl FruitOrJuice {
|
||||
params.last_pos = Some(h.pos.x + control_points[control_points.len() - 1].pos.x);
|
||||
params.last_time = h.start_time;
|
||||
|
||||
let timing_point = params.map.timing_point_at(h.start_time);
|
||||
let timing_point = map.timing_point_at(h.start_time);
|
||||
|
||||
let difficulty_point = params
|
||||
.map
|
||||
.difficulty_point_at(h.start_time)
|
||||
.unwrap_or_default();
|
||||
let difficulty_point = map.difficulty_point_at(h.start_time).unwrap_or_default();
|
||||
|
||||
let vel_factor =
|
||||
BASE_SCORING_DISTANCE * params.map.slider_mult / timing_point.beat_len;
|
||||
let tick_dist_factor =
|
||||
BASE_SCORING_DISTANCE * params.map.slider_mult / params.map.tick_rate;
|
||||
let vel_factor = BASE_SCORING_DISTANCE * map.slider_mult / timing_point.beat_len;
|
||||
let tick_dist_factor = BASE_SCORING_DISTANCE * map.slider_mult / map.tick_rate;
|
||||
|
||||
let vel = vel_factor * difficulty_point.slider_vel;
|
||||
|
||||
|
||||
+110
-68
@@ -1,9 +1,9 @@
|
||||
use std::{iter, slice::Iter};
|
||||
use std::iter;
|
||||
|
||||
use crate::{
|
||||
catch::{difficulty_object::DifficultyObject, SECTION_LENGTH, STAR_SCALING_FACTOR},
|
||||
curve::CurveBuffers,
|
||||
parse::{HitObject, Pos2},
|
||||
parse::Pos2,
|
||||
Beatmap, Mods,
|
||||
};
|
||||
|
||||
@@ -50,9 +50,111 @@ use super::{
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "gradual")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CatchGradualDifficulty<'map> {
|
||||
map: &'map Beatmap,
|
||||
inner: CatchGradualDifficultyInner,
|
||||
}
|
||||
|
||||
impl<'map> CatchGradualDifficulty<'map> {
|
||||
/// Create a new difficulty attributes iterator for osu!catch maps.
|
||||
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
|
||||
let inner = CatchGradualDifficultyInner::new(map, mods);
|
||||
|
||||
Self { map, inner }
|
||||
}
|
||||
|
||||
pub(crate) fn idx(&self) -> usize {
|
||||
self.inner.idx
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for CatchGradualDifficulty<'_> {
|
||||
type Item = CatchDifficultyAttributes;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next(self.map)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gradually calculate the difficulty attributes of an osu!catch map.
|
||||
///
|
||||
/// Check [`CatchGradualDifficulty`] for more information. This struct does the same
|
||||
/// but includes an up-front allocation to avoid being bound to a lifetime.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "gradual")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CatchOwnedGradualDifficulty {
|
||||
map: Box<Beatmap>,
|
||||
inner: CatchGradualDifficultyInner,
|
||||
}
|
||||
|
||||
impl CatchOwnedGradualDifficulty {
|
||||
/// Create a new difficulty attributes iterator for osu!catch maps.
|
||||
pub fn new(map: Beatmap, mods: u32) -> Self {
|
||||
let inner = CatchGradualDifficultyInner::new(&map, mods);
|
||||
let map = Box::new(map);
|
||||
|
||||
Self { map, inner }
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn idx(&self) -> usize {
|
||||
self.inner.idx
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for CatchOwnedGradualDifficulty {
|
||||
type Item = CatchDifficultyAttributes;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next(&self.map)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct CatchObjectIter {
|
||||
last_object: Option<FruitOrJuice>,
|
||||
params: FruitParams,
|
||||
}
|
||||
|
||||
impl CatchObjectIter {
|
||||
fn new(mods: impl Mods, attributes: CatchDifficultyAttributes) -> Self {
|
||||
let params = FruitParams {
|
||||
attributes,
|
||||
curve_bufs: CurveBuffers::default(),
|
||||
last_pos: None,
|
||||
last_time: 0.0,
|
||||
ticks: Vec::new(),
|
||||
with_hr: mods.hr(),
|
||||
};
|
||||
|
||||
Self {
|
||||
last_object: None,
|
||||
params,
|
||||
}
|
||||
}
|
||||
|
||||
fn attributes(&self) -> CatchDifficultyAttributes {
|
||||
self.params.attributes.clone()
|
||||
}
|
||||
|
||||
fn next(&mut self, map: &Beatmap, idx: usize) -> Option<CatchObject> {
|
||||
if let opt @ Some(_) = self.last_object.as_mut().and_then(Iterator::next) {
|
||||
return opt;
|
||||
}
|
||||
|
||||
map.hit_objects[idx..]
|
||||
.iter()
|
||||
.find_map(|h| FruitOrJuice::new(h, &mut self.params, map))
|
||||
.and_then(|h| self.last_object.insert(h).next())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct CatchGradualDifficultyInner {
|
||||
pub(crate) idx: usize,
|
||||
clock_rate: f64,
|
||||
hit_objects: CatchObjectIter<'map>,
|
||||
hit_objects: CatchObjectIter,
|
||||
movement: Movement,
|
||||
prev: CatchObject,
|
||||
half_catcher_width: f64,
|
||||
@@ -62,9 +164,8 @@ pub struct CatchGradualDifficulty<'map> {
|
||||
strain_peak_buf: Vec<f64>,
|
||||
}
|
||||
|
||||
impl<'map> CatchGradualDifficulty<'map> {
|
||||
/// Create a new difficulty attributes iterator for osu!catch maps.
|
||||
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
|
||||
impl CatchGradualDifficultyInner {
|
||||
fn new(map: &Beatmap, mods: u32) -> Self {
|
||||
let map_attributes = map.attributes().mods(mods).build();
|
||||
|
||||
let attributes = CatchDifficultyAttributes {
|
||||
@@ -72,7 +173,7 @@ impl<'map> CatchGradualDifficulty<'map> {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let hit_objects = CatchObjectIter::new(map, mods, attributes);
|
||||
let hit_objects = CatchObjectIter::new(mods, attributes);
|
||||
|
||||
let half_catcher_width =
|
||||
(calculate_catch_width(map_attributes.cs as f32) / 2.0 / ALLOWED_CATCH_RANGE) as f64;
|
||||
@@ -104,13 +205,9 @@ impl<'map> CatchGradualDifficulty<'map> {
|
||||
&mut self.last_excess,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for CatchGradualDifficulty<'_> {
|
||||
type Item = CatchDifficultyAttributes;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let curr = self.hit_objects.next()?;
|
||||
fn next(&mut self, map: &Beatmap) -> Option<CatchDifficultyAttributes> {
|
||||
let curr = self.hit_objects.next(map, self.idx)?;
|
||||
self.idx += 1;
|
||||
|
||||
if self.idx == 1 {
|
||||
@@ -165,58 +262,3 @@ impl Iterator for CatchGradualDifficulty<'_> {
|
||||
Some(attrs)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct CatchObjectIter<'map> {
|
||||
last_object: Option<FruitOrJuice>,
|
||||
hit_objects: Iter<'map, HitObject>,
|
||||
params: FruitParams<'map>,
|
||||
}
|
||||
|
||||
impl<'map> CatchObjectIter<'map> {
|
||||
fn new(map: &'map Beatmap, mods: impl Mods, attributes: CatchDifficultyAttributes) -> Self {
|
||||
let params = FruitParams {
|
||||
attributes,
|
||||
curve_bufs: CurveBuffers::default(),
|
||||
last_pos: None,
|
||||
last_time: 0.0,
|
||||
map,
|
||||
ticks: Vec::new(),
|
||||
with_hr: mods.hr(),
|
||||
};
|
||||
|
||||
Self {
|
||||
last_object: None,
|
||||
hit_objects: map.hit_objects.iter(),
|
||||
params,
|
||||
}
|
||||
}
|
||||
|
||||
fn attributes(&self) -> CatchDifficultyAttributes {
|
||||
self.params.attributes.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for CatchObjectIter<'_> {
|
||||
type Item = CatchObject;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let opt @ Some(_) = self.last_object.as_mut().and_then(Iterator::next) {
|
||||
return opt;
|
||||
}
|
||||
|
||||
self.hit_objects
|
||||
.find_map(|h| FruitOrJuice::new(h, &mut self.params))
|
||||
.and_then(|h| self.last_object.insert(h).next())
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let min = self
|
||||
.last_object
|
||||
.as_ref()
|
||||
.map(ExactSizeIterator::len)
|
||||
.unwrap_or(0);
|
||||
|
||||
(min, None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ impl<'map> CatchGradualPerformance<'map> {
|
||||
.clone()
|
||||
.attributes(difficulty)
|
||||
.state(state)
|
||||
.passed_objects(self.difficulty.idx)
|
||||
.passed_objects(self.difficulty.idx())
|
||||
.calculate();
|
||||
|
||||
Some(performance)
|
||||
|
||||
+3
-3
@@ -17,7 +17,8 @@ pub use self::{catch_object::CatchObject, pp::*, score_state::CatchScoreState};
|
||||
|
||||
#[cfg(feature = "gradual")]
|
||||
pub use self::{
|
||||
gradual_difficulty::CatchGradualDifficulty, gradual_performance::CatchGradualPerformance,
|
||||
gradual_difficulty::{CatchGradualDifficulty, CatchOwnedGradualDifficulty},
|
||||
gradual_performance::CatchGradualPerformance,
|
||||
};
|
||||
|
||||
pub(crate) use self::fruit_or_juice::{FruitOrJuice, FruitParams};
|
||||
@@ -165,7 +166,6 @@ fn calculate_movement(params: CatchStars<'_>) -> (Movement, CatchDifficultyAttri
|
||||
curve_bufs: CurveBuffers::default(),
|
||||
last_pos: None,
|
||||
last_time: 0.0,
|
||||
map,
|
||||
ticks: Vec::new(), // using the same buffer for all sliders
|
||||
with_hr: mods.hr(),
|
||||
};
|
||||
@@ -174,7 +174,7 @@ fn calculate_movement(params: CatchStars<'_>) -> (Movement, CatchDifficultyAttri
|
||||
let mut hit_objects = map
|
||||
.hit_objects
|
||||
.iter()
|
||||
.filter_map(|h| FruitOrJuice::new(h, &mut params))
|
||||
.filter_map(|h| FruitOrJuice::new(h, &mut params, map))
|
||||
.flatten()
|
||||
.take(take);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user