rosu_pp/model/
hit_object.rs1use 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#[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 pub const fn is_circle(&self) -> bool {
27 matches!(&self.kind, HitObjectKind::Circle)
28 }
29
30 pub const fn is_slider(&self) -> bool {
32 matches!(&self.kind, HitObjectKind::Slider(_))
33 }
34
35 pub const fn is_spinner(&self) -> bool {
37 matches!(&self.kind, HitObjectKind::Spinner(_))
38 }
39
40 pub const fn is_hold_note(&self) -> bool {
42 matches!(&self.kind, HitObjectKind::Hold(_))
43 }
44
45 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#[derive(Clone, Debug, PartialEq)]
67pub enum HitObjectKind {
68 Circle,
69 Slider(Slider),
70 Spinner(Spinner),
71 Hold(HoldNote),
72}
73
74#[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 pub const fn span_count(&self) -> usize {
86 self.repeats + 1
87 }
88
89 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 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#[derive(Copy, Clone, Debug, PartialEq)]
130pub struct Spinner {
131 pub duration: f64,
132}
133
134#[derive(Copy, Clone, Debug, PartialEq)]
136pub struct HoldNote {
137 pub duration: f64,
138}