1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use std::cmp::Ordering;

use rosu_map::section::hit_objects::{BorrowedCurve, CurveBuffers};

pub use rosu_map::{
    section::hit_objects::{hit_samples::HitSoundType, PathControlPoint, PathType, SplineType},
    util::Pos,
};

/// All hitobject related data required for difficulty and performance
/// calculation except for the [`HitSoundType`].
#[derive(Clone, Debug, PartialEq)]
pub struct HitObject {
    pub pos: Pos,
    pub start_time: f64,
    pub kind: HitObjectKind,
}

impl HitObject {
    /// Whether the hitobject is a circle.
    pub const fn is_circle(&self) -> bool {
        matches!(&self.kind, HitObjectKind::Circle)
    }

    /// Whether the hitobject is a slider.
    pub const fn is_slider(&self) -> bool {
        matches!(&self.kind, HitObjectKind::Slider(_))
    }

    /// Whether the hitobject is a spinner.
    pub const fn is_spinner(&self) -> bool {
        matches!(&self.kind, HitObjectKind::Spinner(_))
    }

    /// The end time of the object.
    ///
    /// Note that this will not return the correct value for sliders.
    pub(crate) fn end_time(&self) -> f64 {
        match &self.kind {
            HitObjectKind::Circle | HitObjectKind::Slider { .. } => self.start_time,
            HitObjectKind::Spinner(Spinner { duration })
            | HitObjectKind::Hold(HoldNote { duration }) => self.start_time + *duration,
        }
    }
}

impl PartialOrd for HitObject {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.start_time.partial_cmp(&other.start_time)
    }
}

/// Additional data for a [`HitObject`].
///
/// Note that each mode handles hit objects differently.
#[derive(Clone, Debug, PartialEq)]
pub enum HitObjectKind {
    Circle,
    Slider(Slider),
    Spinner(Spinner),
    Hold(HoldNote),
}

/// A slider.
#[derive(Clone, Debug, PartialEq)]
pub struct Slider {
    pub expected_dist: Option<f64>,
    pub repeats: usize,
    pub control_points: Box<[PathControlPoint]>,
    pub node_sounds: Box<[HitSoundType]>,
}

impl Slider {
    /// The amount of spans of the slider.
    pub const fn span_count(&self) -> usize {
        self.repeats + 1
    }

    pub(crate) fn curve<'a>(&self, bufs: &'a mut CurveBuffers) -> BorrowedCurve<'a> {
        BorrowedCurve::new(&self.control_points, self.expected_dist, bufs)
    }
}

/// A spinner.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Spinner {
    pub duration: f64,
}

/// A hold note.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct HoldNote {
    pub duration: f64,
}