rosu-map 0.2.1

Library to de- and encode .osu files
Documentation
/// The type of a [`SliderEvent`].
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SliderEventType {
    Head,
    Tick,
    Repeat,
    LastTick,
    Tail,
}

/// A [`HitObjectSlider`] event as generated by [`SliderEventsIter`].
///
/// [`HitObjectSlider`]: crate::section::hit_objects::HitObjectSlider
#[derive(Clone, Debug, PartialEq)]
pub struct SliderEvent {
    pub kind: SliderEventType,
    pub span_idx: i32,
    pub span_start_time: f64,
    pub time: f64,
    pub path_progress: f64,
}

/// Iterator over [`SliderEvent`]s for a [`HitObjectSlider`].
///
/// [`HitObjectSlider`]: crate::section::hit_objects::HitObjectSlider
#[derive(Debug, PartialEq)]
pub struct SliderEventsIter<'ticks_buf> {
    start_time: f64,
    span_duration: f64,
    min_dist_from_end: f64,
    tick_dist: f64,
    len: f64,
    span_count: i32,
    ticks: &'ticks_buf mut Vec<SliderEvent>,
    state: SliderEventsIterState,
}

impl<'ticks_buf> SliderEventsIter<'ticks_buf> {
    const MAX_LEN: f64 = 100_000.0;
    const TAIL_LENIENCY: f64 = -36.0;

    /// Create a new [`SliderEventsIter`].
    ///
    /// The `ticks` buffer is used to store pending ticks internally and is
    /// passed by reference so that it can be re-used for multiple sliders.
    pub fn new(
        start_time: f64,
        span_duration: f64,
        velocity: f64,
        mut tick_dist: f64,
        total_dist: f64,
        span_count: i32,
        ticks: &'ticks_buf mut Vec<SliderEvent>,
    ) -> Self {
        let len = Self::MAX_LEN.min(total_dist);
        tick_dist = tick_dist.clamp(0.0, len);
        ticks.clear();

        Self {
            start_time,
            span_duration,
            min_dist_from_end: velocity * 10.0,
            tick_dist,
            len,
            span_count,
            ticks,
            state: SliderEventsIterState::Head,
        }
    }
}

impl Iterator for SliderEventsIter<'_> {
    type Item = SliderEvent;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.state {
                SliderEventsIterState::Head => {
                    self.state = SliderEventsIterState::Ticks { span: 0 };

                    return Some(SliderEvent {
                        kind: SliderEventType::Head,
                        span_idx: 0,
                        span_start_time: self.start_time,
                        time: self.start_time,
                        path_progress: 0.0,
                    });
                }
                SliderEventsIterState::Ticks { ref mut span } => {
                    if let Some(event) = self.ticks.pop() {
                        return Some(event);
                    }

                    if *span == self.span_count {
                        self.state = SliderEventsIterState::LastTick;
                    } else {
                        let curr_span = *span;
                        *span += 1;
                        generate_ticks(self, curr_span);
                    }
                }
                SliderEventsIterState::LastTick => {
                    let total_duration = f64::from(self.span_count) * self.span_duration;
                    let final_span_idx = self.span_count - 1;
                    let final_span_start_time =
                        self.start_time + f64::from(final_span_idx) * self.span_duration;
                    let last_tick_time = (self.start_time + total_duration / 2.0)
                        .max((final_span_start_time + self.span_duration) + Self::TAIL_LENIENCY);
                    let mut last_tick_progress =
                        (last_tick_time - final_span_start_time) / self.span_duration;

                    if self.span_count % 2 == 0 {
                        last_tick_progress = 1.0 - last_tick_progress;
                    }

                    self.state = SliderEventsIterState::Tail;

                    return Some(SliderEvent {
                        kind: SliderEventType::LastTick,
                        span_idx: final_span_idx,
                        span_start_time: final_span_start_time,
                        time: last_tick_time,
                        path_progress: last_tick_progress,
                    });
                }
                SliderEventsIterState::Tail => {
                    let total_duration = f64::from(self.span_count) * self.span_duration;
                    let final_span_idx = self.span_count - 1;

                    self.state = SliderEventsIterState::Done;

                    return Some(SliderEvent {
                        kind: SliderEventType::Tail,
                        span_idx: final_span_idx,
                        span_start_time: self.start_time
                            + f64::from(self.span_count - 1) * self.span_duration,
                        time: self.start_time + total_duration,
                        path_progress: f64::from(self.span_count % 2),
                    });
                }
                SliderEventsIterState::Done => return None,
            }
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        match self.state {
            SliderEventsIterState::Head => (3, None),
            SliderEventsIterState::Ticks { .. } => (2 + self.ticks.len(), None),
            SliderEventsIterState::LastTick => (2, Some(2)),
            SliderEventsIterState::Tail => (1, Some(1)),
            SliderEventsIterState::Done => (0, Some(0)),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum SliderEventsIterState {
    Head,
    Ticks { span: i32 },
    LastTick,
    Tail,
    Done,
}

fn generate_ticks(iter: &mut SliderEventsIter<'_>, span: i32) {
    let reversed = span % 2 == 1;
    let span_start_time = iter.start_time + f64::from(span) * iter.span_duration;
    let with_repeat = span < iter.span_count - 1;

    if reversed && with_repeat {
        let repeat = new_repeat_point(span, span_start_time, iter.span_duration);
        iter.ticks.push(repeat);
    }

    let mut d = iter.tick_dist;

    if d > 0.0 {
        while d <= iter.len {
            if d >= iter.len - iter.min_dist_from_end {
                break;
            }

            let path_progress = d / iter.len;

            let time_progres = if reversed {
                1.0 - path_progress
            } else {
                path_progress
            };

            let tick = SliderEvent {
                kind: SliderEventType::Tick,
                span_idx: span,
                span_start_time,
                time: span_start_time + time_progres * iter.span_duration,
                path_progress,
            };

            iter.ticks.push(tick);
            d += iter.tick_dist;
        }
    }

    // We pop from the back so we want to double-reverse
    if !reversed {
        if with_repeat {
            let repeat = new_repeat_point(span, span_start_time, iter.span_duration);
            iter.ticks.push(repeat);
        }

        iter.ticks.reverse();
    }
}

fn new_repeat_point(span: i32, span_start_time: f64, span_duration: f64) -> SliderEvent {
    SliderEvent {
        kind: SliderEventType::Repeat,
        span_idx: span,
        span_start_time,
        time: span_start_time + span_duration,
        path_progress: f64::from((span + 1) % 2),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    const START_TIME: f64 = 0.0;
    const SPAN_DURATION: f64 = 1000.0;

    #[test]
    fn single_span() {
        let mut buf = Vec::new();

        let events: Vec<_> = SliderEventsIter::new(
            START_TIME,
            SPAN_DURATION,
            1.0,
            SPAN_DURATION / 2.0,
            SPAN_DURATION,
            1,
            &mut buf,
        )
        .collect();

        assert_eq!(events[0].kind, SliderEventType::Head);
        assert!((events[0].time - START_TIME).abs() < f64::EPSILON);

        assert_eq!(events[1].kind, SliderEventType::Tick);
        assert!((events[1].time - (SPAN_DURATION / 2.0)).abs() < f64::EPSILON);

        assert_eq!(events[3].kind, SliderEventType::Tail);
        assert!((events[3].time - SPAN_DURATION).abs() < f64::EPSILON);
    }

    #[test]
    fn repeat() {
        let mut buf = Vec::new();

        let events: Vec<_> = SliderEventsIter::new(
            START_TIME,
            SPAN_DURATION,
            1.0,
            SPAN_DURATION / 2.0,
            SPAN_DURATION,
            2,
            &mut buf,
        )
        .collect();

        assert_eq!(events[0].kind, SliderEventType::Head);
        assert!((events[0].time - START_TIME).abs() < f64::EPSILON);

        assert_eq!(events[1].kind, SliderEventType::Tick);
        assert!((events[1].time - (SPAN_DURATION / 2.0)).abs() < f64::EPSILON);

        assert_eq!(events[2].kind, SliderEventType::Repeat);
        assert!((events[2].time - SPAN_DURATION).abs() < f64::EPSILON);

        assert_eq!(events[3].kind, SliderEventType::Tick);
        assert!((events[3].time - (SPAN_DURATION + SPAN_DURATION / 2.0)).abs() < f64::EPSILON);

        assert_eq!(events[5].kind, SliderEventType::Tail);
        assert!((events[5].time - (2.0 * SPAN_DURATION)).abs() < f64::EPSILON);
    }

    #[test]
    fn non_even_ticks() {
        let mut buf = Vec::new();

        let events: Vec<_> = SliderEventsIter::new(
            START_TIME,
            SPAN_DURATION,
            1.0,
            300.0,
            SPAN_DURATION,
            2,
            &mut buf,
        )
        .collect();

        assert_eq!(events[0].kind, SliderEventType::Head);
        assert!((events[0].time - START_TIME).abs() < f64::EPSILON);

        assert_eq!(events[1].kind, SliderEventType::Tick);
        assert!((events[1].time - 300.0).abs() < f64::EPSILON);

        assert_eq!(events[2].kind, SliderEventType::Tick);
        assert!((events[2].time - 600.0).abs() < f64::EPSILON);

        assert_eq!(events[3].kind, SliderEventType::Tick);
        assert!((events[3].time - 900.0).abs() < f64::EPSILON);

        assert_eq!(events[4].kind, SliderEventType::Repeat);
        assert!((events[4].time - SPAN_DURATION).abs() < f64::EPSILON);

        assert_eq!(events[5].kind, SliderEventType::Tick);
        assert!((events[5].time - 1100.0).abs() < f64::EPSILON);

        assert_eq!(events[6].kind, SliderEventType::Tick);
        assert!((events[6].time - 1400.0).abs() < f64::EPSILON);

        assert_eq!(events[7].kind, SliderEventType::Tick);
        assert!((events[7].time - 1700.0).abs() < f64::EPSILON);

        assert_eq!(events[9].kind, SliderEventType::Tail);
        assert!((events[9].time - (2.0 * SPAN_DURATION)).abs() < f64::EPSILON);
    }

    #[test]
    fn last_tick_offset() {
        let mut buf = Vec::new();

        let last_tick = SliderEventsIter::new(
            START_TIME,
            SPAN_DURATION,
            1.0,
            SPAN_DURATION / 2.0,
            SPAN_DURATION,
            1,
            &mut buf,
        )
        .nth(2)
        .unwrap();

        assert_eq!(last_tick.kind, SliderEventType::LastTick);
        assert!(
            (last_tick.time - (SPAN_DURATION + SliderEventsIter::TAIL_LENIENCY)).abs()
                < f64::EPSILON
        );
    }

    #[test]
    fn min_tick_dist() {
        const VELOCITY: f64 = 5.0;
        const MIN_DIST: f64 = VELOCITY * 10.0;

        let mut buf = Vec::new();

        let events = SliderEventsIter::new(
            START_TIME,
            SPAN_DURATION,
            VELOCITY,
            VELOCITY,
            SPAN_DURATION,
            2,
            &mut buf,
        );

        for event in events {
            if event.kind == SliderEventType::Tick {
                assert!(
                    event.time < SPAN_DURATION - MIN_DIST || event.time > SPAN_DURATION + MIN_DIST
                );
            }
        }
    }
}