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 = if self.tick_dist.abs() >= f64::EPSILON {
80                        SliderEventsIterState::Ticks { span: 0 }
81                    } else {
82                        SliderEventsIterState::LastTick
83                    };
84
85                    return Some(SliderEvent {
86                        kind: SliderEventType::Head,
87                        span_idx: 0,
88                        span_start_time: self.start_time,
89                        time: self.start_time,
90                        path_progress: 0.0,
91                    });
92                }
93                SliderEventsIterState::Ticks { ref mut span } => {
94                    if let Some(event) = self.ticks.pop() {
95                        return Some(event);
96                    }
97
98                    if *span == self.span_count {
99                        self.state = SliderEventsIterState::LastTick;
100                    } else {
101                        let curr_span = *span;
102                        *span += 1;
103                        generate_ticks(self, curr_span);
104                    }
105                }
106                SliderEventsIterState::LastTick => {
107                    let total_duration = f64::from(self.span_count) * self.span_duration;
108                    let final_span_idx = self.span_count - 1;
109                    let final_span_start_time =
110                        self.start_time + f64::from(final_span_idx) * self.span_duration;
111                    let last_tick_time = (self.start_time + total_duration / 2.0)
112                        .max((final_span_start_time + self.span_duration) + Self::TAIL_LENIENCY);
113                    let mut last_tick_progress =
114                        (last_tick_time - final_span_start_time) / self.span_duration;
115
116                    if self.span_count % 2 == 0 {
117                        last_tick_progress = 1.0 - last_tick_progress;
118                    }
119
120                    self.state = SliderEventsIterState::Tail;
121
122                    return Some(SliderEvent {
123                        kind: SliderEventType::LastTick,
124                        span_idx: final_span_idx,
125                        span_start_time: final_span_start_time,
126                        time: last_tick_time,
127                        path_progress: last_tick_progress,
128                    });
129                }
130                SliderEventsIterState::Tail => {
131                    let total_duration = f64::from(self.span_count) * self.span_duration;
132                    let final_span_idx = self.span_count - 1;
133
134                    self.state = SliderEventsIterState::Done;
135
136                    return Some(SliderEvent {
137                        kind: SliderEventType::Tail,
138                        span_idx: final_span_idx,
139                        span_start_time: self.start_time
140                            + f64::from(self.span_count - 1) * self.span_duration,
141                        time: self.start_time + total_duration,
142                        path_progress: f64::from(self.span_count % 2),
143                    });
144                }
145                SliderEventsIterState::Done => return None,
146            }
147        }
148    }
149
150    fn size_hint(&self) -> (usize, Option<usize>) {
151        match self.state {
152            SliderEventsIterState::Head => (3, None),
153            SliderEventsIterState::Ticks { .. } => (2 + self.ticks.len(), None),
154            SliderEventsIterState::LastTick => (2, Some(2)),
155            SliderEventsIterState::Tail => (1, Some(1)),
156            SliderEventsIterState::Done => (0, Some(0)),
157        }
158    }
159}
160
161#[derive(Copy, Clone, Debug, PartialEq, Eq)]
162enum SliderEventsIterState {
163    Head,
164    Ticks { span: i32 },
165    LastTick,
166    Tail,
167    Done,
168}
169
170fn generate_ticks(iter: &mut SliderEventsIter<'_>, span: i32) {
171    let reversed = span % 2 == 1;
172    let span_start_time = iter.start_time + f64::from(span) * iter.span_duration;
173    let with_repeat = span < iter.span_count - 1;
174
175    if reversed && with_repeat {
176        let repeat = new_repeat_point(span, span_start_time, iter.span_duration);
177        iter.ticks.push(repeat);
178    }
179
180    let mut d = iter.tick_dist;
181
182    while d <= iter.len {
183        if d >= iter.len - iter.min_dist_from_end {
184            break;
185        }
186
187        let path_progress = d / iter.len;
188
189        let time_progres = if reversed {
190            1.0 - path_progress
191        } else {
192            path_progress
193        };
194
195        let tick = SliderEvent {
196            kind: SliderEventType::Tick,
197            span_idx: span,
198            span_start_time,
199            time: span_start_time + time_progres * iter.span_duration,
200            path_progress,
201        };
202
203        iter.ticks.push(tick);
204        d += iter.tick_dist;
205    }
206
207    // We pop from the back so we want to double-reverse
208    if !reversed {
209        if with_repeat {
210            let repeat = new_repeat_point(span, span_start_time, iter.span_duration);
211            iter.ticks.push(repeat);
212        }
213
214        iter.ticks.reverse();
215    }
216}
217
218fn new_repeat_point(span: i32, span_start_time: f64, span_duration: f64) -> SliderEvent {
219    SliderEvent {
220        kind: SliderEventType::Repeat,
221        span_idx: span,
222        span_start_time,
223        time: span_start_time + span_duration,
224        path_progress: f64::from((span + 1) % 2),
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231
232    const START_TIME: f64 = 0.0;
233    const SPAN_DURATION: f64 = 1000.0;
234
235    #[test]
236    fn single_span() {
237        let mut buf = Vec::new();
238
239        let events: Vec<_> = SliderEventsIter::new(
240            START_TIME,
241            SPAN_DURATION,
242            1.0,
243            SPAN_DURATION / 2.0,
244            SPAN_DURATION,
245            1,
246            &mut buf,
247        )
248        .collect();
249
250        assert_eq!(events[0].kind, SliderEventType::Head);
251        assert!((events[0].time - START_TIME).abs() < f64::EPSILON);
252
253        assert_eq!(events[1].kind, SliderEventType::Tick);
254        assert!((events[1].time - (SPAN_DURATION / 2.0)).abs() < f64::EPSILON);
255
256        assert_eq!(events[3].kind, SliderEventType::Tail);
257        assert!((events[3].time - SPAN_DURATION).abs() < f64::EPSILON);
258    }
259
260    #[test]
261    fn repeat() {
262        let mut buf = Vec::new();
263
264        let events: Vec<_> = SliderEventsIter::new(
265            START_TIME,
266            SPAN_DURATION,
267            1.0,
268            SPAN_DURATION / 2.0,
269            SPAN_DURATION,
270            2,
271            &mut buf,
272        )
273        .collect();
274
275        assert_eq!(events[0].kind, SliderEventType::Head);
276        assert!((events[0].time - START_TIME).abs() < f64::EPSILON);
277
278        assert_eq!(events[1].kind, SliderEventType::Tick);
279        assert!((events[1].time - (SPAN_DURATION / 2.0)).abs() < f64::EPSILON);
280
281        assert_eq!(events[2].kind, SliderEventType::Repeat);
282        assert!((events[2].time - SPAN_DURATION).abs() < f64::EPSILON);
283
284        assert_eq!(events[3].kind, SliderEventType::Tick);
285        assert!((events[3].time - (SPAN_DURATION + SPAN_DURATION / 2.0)).abs() < f64::EPSILON);
286
287        assert_eq!(events[5].kind, SliderEventType::Tail);
288        assert!((events[5].time - (2.0 * SPAN_DURATION)).abs() < f64::EPSILON);
289    }
290
291    #[test]
292    fn non_even_ticks() {
293        let mut buf = Vec::new();
294
295        let events: Vec<_> = SliderEventsIter::new(
296            START_TIME,
297            SPAN_DURATION,
298            1.0,
299            300.0,
300            SPAN_DURATION,
301            2,
302            &mut buf,
303        )
304        .collect();
305
306        assert_eq!(events[0].kind, SliderEventType::Head);
307        assert!((events[0].time - START_TIME).abs() < f64::EPSILON);
308
309        assert_eq!(events[1].kind, SliderEventType::Tick);
310        assert!((events[1].time - 300.0).abs() < f64::EPSILON);
311
312        assert_eq!(events[2].kind, SliderEventType::Tick);
313        assert!((events[2].time - 600.0).abs() < f64::EPSILON);
314
315        assert_eq!(events[3].kind, SliderEventType::Tick);
316        assert!((events[3].time - 900.0).abs() < f64::EPSILON);
317
318        assert_eq!(events[4].kind, SliderEventType::Repeat);
319        assert!((events[4].time - SPAN_DURATION).abs() < f64::EPSILON);
320
321        assert_eq!(events[5].kind, SliderEventType::Tick);
322        assert!((events[5].time - 1100.0).abs() < f64::EPSILON);
323
324        assert_eq!(events[6].kind, SliderEventType::Tick);
325        assert!((events[6].time - 1400.0).abs() < f64::EPSILON);
326
327        assert_eq!(events[7].kind, SliderEventType::Tick);
328        assert!((events[7].time - 1700.0).abs() < f64::EPSILON);
329
330        assert_eq!(events[9].kind, SliderEventType::Tail);
331        assert!((events[9].time - (2.0 * SPAN_DURATION)).abs() < f64::EPSILON);
332    }
333
334    #[test]
335    fn last_tick_offset() {
336        let mut buf = Vec::new();
337
338        let last_tick = SliderEventsIter::new(
339            START_TIME,
340            SPAN_DURATION,
341            1.0,
342            SPAN_DURATION / 2.0,
343            SPAN_DURATION,
344            1,
345            &mut buf,
346        )
347        .nth(2)
348        .unwrap();
349
350        assert_eq!(last_tick.kind, SliderEventType::LastTick);
351        assert!(
352            (last_tick.time - (SPAN_DURATION + SliderEventsIter::TAIL_LENIENCY)).abs()
353                < f64::EPSILON
354        );
355    }
356
357    #[test]
358    fn min_tick_dist() {
359        const VELOCITY: f64 = 5.0;
360        const MIN_DIST: f64 = VELOCITY * 10.0;
361
362        let mut buf = Vec::new();
363
364        let events = SliderEventsIter::new(
365            START_TIME,
366            SPAN_DURATION,
367            VELOCITY,
368            VELOCITY,
369            SPAN_DURATION,
370            2,
371            &mut buf,
372        );
373
374        for event in events {
375            if event.kind == SliderEventType::Tick {
376                assert!(
377                    event.time < SPAN_DURATION - MIN_DIST || event.time > SPAN_DURATION + MIN_DIST
378                );
379            }
380        }
381    }
382}