Skip to main content

rosu_pp/model/
hit_object.rs

1use std::{borrow::Cow, cmp::Ordering};
2
3use rosu_map::section::{
4    general::GameMode,
5    hit_objects::{BorrowedCurve, Curve, CurveBuffers},
6};
7
8pub use rosu_map::{
9    section::hit_objects::{PathControlPoint, PathType, SplineType, hit_samples::HitSoundType},
10    util::Pos,
11};
12
13use crate::model::mods::Reflection;
14
15/// All hitobject related data required for difficulty and performance
16/// calculation except for the [`HitSoundType`].
17#[derive(Clone, Debug, PartialEq)]
18pub struct HitObject {
19    pub pos: Pos,
20    pub start_time: f64,
21    pub kind: HitObjectKind,
22}
23
24impl HitObject {
25    /// Whether the hitobject is a circle.
26    pub const fn is_circle(&self) -> bool {
27        matches!(&self.kind, HitObjectKind::Circle)
28    }
29
30    /// Whether the hitobject is a slider.
31    pub const fn is_slider(&self) -> bool {
32        matches!(&self.kind, HitObjectKind::Slider(_))
33    }
34
35    /// Whether the hitobject is a spinner.
36    pub const fn is_spinner(&self) -> bool {
37        matches!(&self.kind, HitObjectKind::Spinner(_))
38    }
39
40    /// Whether the hitobject is a hold note.
41    pub const fn is_hold_note(&self) -> bool {
42        matches!(&self.kind, HitObjectKind::Hold(_))
43    }
44
45    /// The end time of the object.
46    ///
47    /// Note that this will not return the correct value for sliders.
48    pub(crate) fn end_time(&self) -> f64 {
49        match &self.kind {
50            HitObjectKind::Circle | HitObjectKind::Slider { .. } => self.start_time,
51            HitObjectKind::Spinner(Spinner { duration })
52            | HitObjectKind::Hold(HoldNote { duration }) => self.start_time + *duration,
53        }
54    }
55}
56
57impl PartialOrd for HitObject {
58    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
59        self.start_time.partial_cmp(&other.start_time)
60    }
61}
62
63/// Additional data for a [`HitObject`].
64///
65/// Note that each mode handles hit objects differently.
66#[derive(Clone, Debug, PartialEq)]
67pub enum HitObjectKind {
68    Circle,
69    Slider(Slider),
70    Spinner(Spinner),
71    Hold(HoldNote),
72}
73
74/// A slider.
75#[derive(Clone, Debug, PartialEq)]
76pub struct Slider {
77    pub expected_dist: Option<f64>,
78    pub repeats: usize,
79    pub control_points: Box<[PathControlPoint]>,
80    pub node_sounds: Box<[HitSoundType]>,
81}
82
83impl Slider {
84    /// The amount of spans of the slider.
85    pub const fn span_count(&self) -> usize {
86        self.repeats + 1
87    }
88
89    /// Creates the [`Curve`] of a [`Slider`].
90    ///
91    /// Applies the [`Reflection`] onto control points before creating the
92    /// curve.
93    pub(crate) fn curve(
94        &self,
95        mode: GameMode,
96        reflection: Reflection,
97        bufs: &mut CurveBuffers,
98    ) -> Curve {
99        fn reflect<F: Fn(Pos) -> Pos>(points: &mut Cow<'_, [PathControlPoint]>, f: F) {
100            points
101                .to_mut()
102                .iter_mut()
103                .for_each(|point| point.pos = f(point.pos));
104        }
105
106        let mut points = Cow::Borrowed(self.control_points.as_ref());
107
108        match reflection {
109            Reflection::None => {}
110            Reflection::Vertical => reflect(&mut points, |pos| Pos::new(pos.x, -pos.y)),
111            Reflection::Horizontal => reflect(&mut points, |pos| Pos::new(-pos.x, pos.y)),
112            Reflection::Both => reflect(&mut points, |pos| Pos::new(-pos.x, -pos.y)),
113        }
114
115        Curve::new(mode, points.as_ref(), self.expected_dist, bufs)
116    }
117
118    /// Creates the [`BorrowedCurve`] of a [`Slider`].
119    pub(crate) fn borrowed_curve<'bufs>(
120        &self,
121        mode: GameMode,
122        bufs: &'bufs mut CurveBuffers,
123    ) -> BorrowedCurve<'bufs> {
124        BorrowedCurve::new(mode, &self.control_points, self.expected_dist, bufs)
125    }
126}
127
128/// A spinner.
129#[derive(Copy, Clone, Debug, PartialEq)]
130pub struct Spinner {
131    pub duration: f64,
132}
133
134/// A hold note.
135#[derive(Copy, Clone, Debug, PartialEq)]
136pub struct HoldNote {
137    pub duration: f64,
138}