rosu_map/section/hit_objects/slider/path.rs
1use crate::{section::general::GameMode, util::Pos};
2
3use super::{
4 curve::{BorrowedCurve, Curve, CurveBuffers},
5 path_type::PathType,
6};
7
8/// The path of a [`HitObjectSlider`].
9///
10/// [`HitObjectSlider`]: crate::section::hit_objects::HitObjectSlider
11#[derive(Clone, Debug)]
12pub struct SliderPath {
13 mode: GameMode,
14 control_points: Vec<PathControlPoint>,
15 expected_dist: Option<f64>,
16 curve: Option<Curve>,
17}
18
19impl SliderPath {
20 /// Creates a new [`SliderPath`].
21 ///
22 /// The contained [`Curve`] will not necessarily be calculated yet, only
23 /// when accessing it with methods such as [`SliderPath::curve`].
24 pub const fn new(
25 mode: GameMode,
26 control_points: Vec<PathControlPoint>,
27 expected_dist: Option<f64>,
28 ) -> Self {
29 Self {
30 mode,
31 control_points,
32 expected_dist,
33 curve: None,
34 }
35 }
36
37 /// Returns an immutable reference to the control points.
38 pub fn control_points(&self) -> &[PathControlPoint] {
39 &self.control_points
40 }
41
42 /// Returns the expected distance.
43 pub const fn expected_dist(&self) -> Option<f64> {
44 self.expected_dist
45 }
46
47 /// Returns a reference to the [`Curve`].
48 ///
49 /// If the curve has not yet been accessed, it needs to be calculated
50 /// first.
51 ///
52 /// In case curves of multiple slider paths are being calculated, it is
53 /// recommended to initialize [`CurveBuffers`] and pass a mutable reference
54 /// of it to [`SliderPath::curve_with_bufs`] so the buffers are re-used for
55 /// all sliders.
56 ///
57 /// Alternatively, to avoid storing the curve altogether because it will be
58 /// accessed only once, using [`SliderPath::borrowed_curve`] should be
59 /// preferred.
60 pub fn curve(&mut self) -> &Curve {
61 if let Some(ref curve) = self.curve {
62 curve
63 } else {
64 let curve = self.calculate_curve();
65
66 self.curve.insert(curve)
67 }
68 }
69
70 /// Returns a reference to the [`Curve`].
71 ///
72 /// If the curve has not yet been accessed, it needs to be calculated
73 /// first.
74 ///
75 /// In case the curve will be accessed only once, using
76 /// [`SliderPath::borrowed_curve`] should be preferred.
77 pub fn curve_with_bufs(&mut self, bufs: &mut CurveBuffers) -> &Curve {
78 if let Some(ref curve) = self.curve {
79 curve
80 } else {
81 let curve = self.calculate_curve_with_bufs(bufs);
82
83 self.curve.insert(curve)
84 }
85 }
86
87 /// Returns a [`BorrowedCurve`].
88 ///
89 /// If the curve has been calculated before, the returned curve will borrow
90 /// from it. Otherwise, it will be calculated and returned **without**
91 /// storing it by borrowing from the given [`CurveBuffers`].
92 ///
93 /// This should be preferred over [`SliderPath::curve_with_bufs`] if the
94 /// curve will be accessed only once.
95 pub fn borrowed_curve<'a, 'b: 'a>(&'a self, bufs: &'b mut CurveBuffers) -> BorrowedCurve<'a> {
96 if let Some(ref curve) = self.curve {
97 curve.as_borrowed_curve()
98 } else {
99 BorrowedCurve::new(self.mode, &self.control_points, self.expected_dist, bufs)
100 }
101 }
102
103 /// Returns a mutable reference to the control points.
104 ///
105 /// Note that calling this method will invalidate the stored curve
106 /// so it must be recalculated on its next access.
107 pub fn control_points_mut(&mut self) -> &mut Vec<PathControlPoint> {
108 self.clear_curve();
109
110 &mut self.control_points
111 }
112
113 /// Returns a mutable reference to the expected distance.
114 ///
115 /// Note that calling this method will invalidate the stored curve
116 /// so it must be recalculated on its next access.
117 pub fn expected_dist_mut(&mut self) -> &mut Option<f64> {
118 self.clear_curve();
119
120 &mut self.expected_dist
121 }
122
123 /// Remove the stored curve so that it has to be re-calculated when
124 /// accessing it the next time.
125 pub fn clear_curve(&mut self) {
126 self.curve = None;
127 }
128
129 fn calculate_curve(&self) -> Curve {
130 self.calculate_curve_with_bufs(&mut CurveBuffers::default())
131 }
132
133 fn calculate_curve_with_bufs(&self, bufs: &mut CurveBuffers) -> Curve {
134 Curve::new(self.mode, &self.control_points, self.expected_dist, bufs)
135 }
136}
137
138impl PartialEq for SliderPath {
139 fn eq(&self, other: &Self) -> bool {
140 self.control_points == other.control_points
141 }
142}
143
144/// A positional control point of a curve.
145#[derive(Copy, Clone, Debug, Default, PartialEq)]
146pub struct PathControlPoint {
147 pub pos: Pos,
148 pub path_type: Option<PathType>,
149}
150
151impl PathControlPoint {
152 /// Initialize a new [`PathControlPoint`].
153 pub const fn new(pos: Pos) -> Self {
154 Self {
155 pos,
156 path_type: None,
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn borrowed_curve() {
167 let mut bufs = CurveBuffers::default();
168 let mut path = SliderPath::new(GameMode::Osu, Vec::new(), None);
169
170 // freshly calculate the curve; lifetime will depend on `bufs`
171 let borrowed_curve = path.borrowed_curve(&mut bufs);
172
173 // access to let the borrow checker know it will be used
174 let _ = borrowed_curve.dist();
175
176 // calculate **and store** the curve
177 let _ = path.curve_with_bufs(&mut bufs);
178
179 // access the stored curve; lifetime will depend on `path`
180 let borrowed_curve = path.borrowed_curve(&mut bufs);
181
182 // access to let the borrow checker know it will be used
183 let _ = borrowed_curve.dist();
184 }
185}