rosu_map/section/hit_objects/slider/
event.rs

1/// The type of a [`SliderEvent`].
2#[derive(Copy, Clone, Debug, PartialEq, Eq)]
3pub enum SliderEventType {
4    Head,
5    Tick,
6    Repeat,
7    LastTick,
8    Tail,
9}
10
11/// A [`HitObjectSlider`] event as generated by [`SliderEventsIter`].
12///
13/// [`HitObjectSlider`]: crate::section::hit_objects::HitObjectSlider
14#[derive(Clone, Debug, PartialEq)]
15pub struct SliderEvent {
16    pub kind: SliderEventType,
17    pub span_idx: i32,
18    pub span_start_time: f64,
19    pub time: f64,
20    pub path_progress: f64,
21}
22
23/// Iterator over [`SliderEvent`]s for a [`HitObjectSlider`].
24///
25/// [`HitObjectSlider`]: crate::section::hit_objects::HitObjectSlider
26#[derive(Debug, PartialEq)]
27pub struct SliderEventsIter<'ticks_buf> {
28    start_time: f64,
29    span_duration: f64,
30    min_dist_from_end: f64,
31    tick_dist: f64,
32    len: f64,
33    span_count: i32,
34    ticks: &'ticks_buf mut Vec<SliderEvent>,
35    state: SliderEventsIterState,
36}
37
38impl<'ticks_buf> SliderEventsIter<'ticks_buf> {
39    const MAX_LEN: f64 = 100_000.0;
40    const TAIL_LENIENCY: f64 = -36.0;
41
42    /// Create a new [`SliderEventsIter`].
43    ///
44    /// The `ticks` buffer is used to store pending ticks internally and is
45    /// passed by reference so that it can be re-used for multiple sliders.
46    pub fn new(
47        start_time: f64,
48        span_duration: f64,
49        velocity: f64,
50        mut tick_dist: f64,
51        total_dist: f64,
52        span_count: i32,
53        ticks: &'ticks_buf mut Vec<SliderEvent>,
54    ) -> Self {
55        let len = Self::MAX_LEN.min(total_dist);
56        tick_dist = tick_dist.clamp(0.0, len);
57        ticks.clear();
58
59        Self {
60            start_time,
61            span_duration,
62            min_dist_from_end: velocity * 10.0,
63            tick_dist,
64            len,
65            span_count,
66            ticks,
67            state: SliderEventsIterState::Head,
68        }
69    }
70}
71
72impl Iterator for SliderEventsIter<'_> {
73    type Item = SliderEvent;
74
75    fn next(&mut self) -> Option<Self::Item> {
76        loop {
77            match self.state {
78                SliderEventsIterState::Head => {
79                    self.state = SliderEventsIterState::Ticks { span: 0 };
80
81                    return Some(SliderEvent {
82                        kind: SliderEventType::Head,
83                        span_idx: 0,
84                        span_start_time: self.start_time,
85                        time: self.start_time,
86                        path_progress: 0.0,
87                    });
88                }
89                SliderEventsIterState::Ticks { ref mut span } => {
90                    if let Some(event) = self.ticks.pop() {
91                        return Some(event);
92                    }
93
94                    if *span == self.span_count {
95                        self.state = SliderEventsIterState::LastTick;
96                    } else {
97                        let curr_span = *span;
98                        *span += 1;
99                        generate_ticks(self, curr_span);
100                    }
101                }
102                SliderEventsIterState::LastTick => {
103                    let total_duration = f64::from(self.span_count) * self.span_duration;
104                    let final_span_idx = self.span_count - 1;
105                    let final_span_start_time =
106                        self.start_time + f64::from(final_span_idx) * self.span_duration;
107                    let last_tick_time = (self.start_time + total_duration / 2.0)
108                        .max((final_span_start_time + self.span_duration) + Self::TAIL_LENIENCY);
109                    let mut last_tick_progress =
110                        (last_tick_time - final_span_start_time) / self.span_duration;
111
112                    if self.span_count % 2 == 0 {
113                        last_tick_progress = 1.0 - last_tick_progress;
114                    }
115
116                    self.state = SliderEventsIterState::Tail;
117
118                    return Some(SliderEvent {
119                        kind: SliderEventType::LastTick,
120                        span_idx: final_span_idx,
121                        span_start_time: final_span_start_time,
122                        time: last_tick_time,
123                        path_progress: last_tick_progress,
124                    });
125                }
126                SliderEventsIterState::Tail => {
127                    let total_duration = f64::from(self.span_count) * self.span_duration;
128                    let final_span_idx = self.span_count - 1;
129
130                    self.state = SliderEventsIterState::Done;
131
132                    return Some(SliderEvent {
133                        kind: SliderEventType::Tail,
134                        span_idx: final_span_idx,
135                        span_start_time: self.start_time
136                            + f64::from(self.span_count - 1) * self.span_duration,
137                        time: self.start_time + total_duration,
138                        path_progress: f64::from(self.span_count % 2),
139                    });
140                }
141                SliderEventsIterState::Done => return None,
142            }
143        }
144    }
145
146    fn size_hint(&self) -> (usize, Option<usize>) {
147        match self.state {
148            SliderEventsIterState::Head => (3, None),
149            SliderEventsIterState::Ticks { .. } => (2 + self.ticks.len(), None),
150            SliderEventsIterState::LastTick => (2, Some(2)),
151            SliderEventsIterState::Tail => (1, Some(1)),
152            SliderEventsIterState::Done => (0, Some(0)),
153        }
154    }
155}
156
157#[derive(Copy, Clone, Debug, PartialEq, Eq)]
158enum SliderEventsIterState {
159    Head,
160    Ticks { span: i32 },
161    LastTick,
162    Tail,
163    Done,
164}
165
166fn generate_ticks(iter: &mut SliderEventsIter<'_>, span: i32) {
167    let reversed = span % 2 == 1;
168    let span_start_time = iter.start_time + f64::from(span) * iter.span_duration;
169    let with_repeat = span < iter.span_count - 1;
170
171    if reversed && with_repeat {
172        let repeat = new_repeat_point(span, span_start_time, iter.span_duration);
173        iter.ticks.push(repeat);
174    }
175
176    let mut d = iter.tick_dist;
177
178    if d > 0.0 {
179        while d <= iter.len {
180            if d >= iter.len - iter.min_dist_from_end {
181                break;
182            }
183
184            let path_progress = d / iter.len;
185
186            let time_progres = if reversed {
187                1.0 - path_progress
188            } else {
189                path_progress
190            };
191
192            let tick = SliderEvent {
193                kind: SliderEventType::Tick,
194                span_idx: span,
195                span_start_time,
196                time: span_start_time + time_progres * iter.span_duration,
197                path_progress,
198            };
199
200            iter.ticks.push(tick);
201            d += iter.tick_dist;
202        }
203    }
204
205    // We pop from the back so we want to double-reverse
206    if !reversed {
207        if with_repeat {
208            let repeat = new_repeat_point(span, span_start_time, iter.span_duration);
209            iter.ticks.push(repeat);
210        }
211
212        iter.ticks.reverse();
213    }
214}
215
216fn new_repeat_point(span: i32, span_start_time: f64, span_duration: f64) -> SliderEvent {
217    SliderEvent {
218        kind: SliderEventType::Repeat,
219        span_idx: span,
220        span_start_time,
221        time: span_start_time + span_duration,
222        path_progress: f64::from((span + 1) % 2),
223    }
224}
225
226#[cfg(test)]
227mod tests {
228    use super::*;
229
230    const START_TIME: f64 = 0.0;
231    const SPAN_DURATION: f64 = 1000.0;
232
233    #[test]
234    fn single_span() {
235        let mut buf = Vec::new();
236
237        let events: Vec<_> = SliderEventsIter::new(
238            START_TIME,
239            SPAN_DURATION,
240            1.0,
241            SPAN_DURATION / 2.0,
242            SPAN_DURATION,
243            1,
244            &mut buf,
245        )
246        .collect();
247
248        assert_eq!(events[0].kind, SliderEventType::Head);
249        assert!((events[0].time - START_TIME).abs() < f64::EPSILON);
250
251        assert_eq!(events[1].kind, SliderEventType::Tick);
252        assert!((events[1].time - (SPAN_DURATION / 2.0)).abs() < f64::EPSILON);
253
254        assert_eq!(events[3].kind, SliderEventType::Tail);
255        assert!((events[3].time - SPAN_DURATION).abs() < f64::EPSILON);
256    }
257
258    #[test]
259    fn repeat() {
260        let mut buf = Vec::new();
261
262        let events: Vec<_> = SliderEventsIter::new(
263            START_TIME,
264            SPAN_DURATION,
265            1.0,
266            SPAN_DURATION / 2.0,
267            SPAN_DURATION,
268            2,
269            &mut buf,
270        )
271        .collect();
272
273        assert_eq!(events[0].kind, SliderEventType::Head);
274        assert!((events[0].time - START_TIME).abs() < f64::EPSILON);
275
276        assert_eq!(events[1].kind, SliderEventType::Tick);
277        assert!((events[1].time - (SPAN_DURATION / 2.0)).abs() < f64::EPSILON);
278
279        assert_eq!(events[2].kind, SliderEventType::Repeat);
280        assert!((events[2].time - SPAN_DURATION).abs() < f64::EPSILON);
281
282        assert_eq!(events[3].kind, SliderEventType::Tick);
283        assert!((events[3].time - (SPAN_DURATION + SPAN_DURATION / 2.0)).abs() < f64::EPSILON);
284
285        assert_eq!(events[5].kind, SliderEventType::Tail);
286        assert!((events[5].time - (2.0 * SPAN_DURATION)).abs() < f64::EPSILON);
287    }
288
289    #[test]
290    fn non_even_ticks() {
291        let mut buf = Vec::new();
292
293        let events: Vec<_> = SliderEventsIter::new(
294            START_TIME,
295            SPAN_DURATION,
296            1.0,
297            300.0,
298            SPAN_DURATION,
299            2,
300            &mut buf,
301        )
302        .collect();
303
304        assert_eq!(events[0].kind, SliderEventType::Head);
305        assert!((events[0].time - START_TIME).abs() < f64::EPSILON);
306
307        assert_eq!(events[1].kind, SliderEventType::Tick);
308        assert!((events[1].time - 300.0).abs() < f64::EPSILON);
309
310        assert_eq!(events[2].kind, SliderEventType::Tick);
311        assert!((events[2].time - 600.0).abs() < f64::EPSILON);
312
313        assert_eq!(events[3].kind, SliderEventType::Tick);
314        assert!((events[3].time - 900.0).abs() < f64::EPSILON);
315
316        assert_eq!(events[4].kind, SliderEventType::Repeat);
317        assert!((events[4].time - SPAN_DURATION).abs() < f64::EPSILON);
318
319        assert_eq!(events[5].kind, SliderEventType::Tick);
320        assert!((events[5].time - 1100.0).abs() < f64::EPSILON);
321
322        assert_eq!(events[6].kind, SliderEventType::Tick);
323        assert!((events[6].time - 1400.0).abs() < f64::EPSILON);
324
325        assert_eq!(events[7].kind, SliderEventType::Tick);
326        assert!((events[7].time - 1700.0).abs() < f64::EPSILON);
327
328        assert_eq!(events[9].kind, SliderEventType::Tail);
329        assert!((events[9].time - (2.0 * SPAN_DURATION)).abs() < f64::EPSILON);
330    }
331
332    #[test]
333    fn last_tick_offset() {
334        let mut buf = Vec::new();
335
336        let last_tick = SliderEventsIter::new(
337            START_TIME,
338            SPAN_DURATION,
339            1.0,
340            SPAN_DURATION / 2.0,
341            SPAN_DURATION,
342            1,
343            &mut buf,
344        )
345        .nth(2)
346        .unwrap();
347
348        assert_eq!(last_tick.kind, SliderEventType::LastTick);
349        assert!(
350            (last_tick.time - (SPAN_DURATION + SliderEventsIter::TAIL_LENIENCY)).abs()
351                < f64::EPSILON
352        );
353    }
354
355    #[test]
356    fn min_tick_dist() {
357        const VELOCITY: f64 = 5.0;
358        const MIN_DIST: f64 = VELOCITY * 10.0;
359
360        let mut buf = Vec::new();
361
362        let events = SliderEventsIter::new(
363            START_TIME,
364            SPAN_DURATION,
365            VELOCITY,
366            VELOCITY,
367            SPAN_DURATION,
368            2,
369            &mut buf,
370        );
371
372        for event in events {
373            if event.kind == SliderEventType::Tick {
374                assert!(
375                    event.time < SPAN_DURATION - MIN_DIST || event.time > SPAN_DURATION + MIN_DIST
376                );
377            }
378        }
379    }
380}