rosu_pp/model/control_point/
difficulty.rs1use crate::util::float_ext::FloatExt;
2
3#[derive(Clone, Debug, PartialEq)]
5pub struct DifficultyPoint {
6 pub time: f64,
7 pub slider_velocity: f64,
8 pub bpm_multiplier: f64,
9 pub generate_ticks: bool,
10}
11
12impl DifficultyPoint {
13 pub const DEFAULT_SLIDER_VELOCITY: f64 = 1.0;
14 pub const DEFAULT_BPM_MULTIPLIER: f64 = 1.0;
15 pub const DEFAULT_GENERATE_TICKS: bool = true;
16
17 pub fn new(time: f64, beat_len: f64, speed_multiplier: f64) -> Self {
18 Self {
19 time,
20 slider_velocity: speed_multiplier.clamp(0.1, 10.0),
21 bpm_multiplier: if beat_len < 0.0 {
22 f64::from((-beat_len) as f32).clamp(10.0, 10_000.0) / 100.0
23 } else {
24 1.0
25 },
26 generate_ticks: !beat_len.is_nan(),
27 }
28 }
29
30 pub fn is_redundant(&self, existing: &Self) -> bool {
31 self.generate_ticks == existing.generate_ticks
32 && self.slider_velocity.eq(existing.slider_velocity)
33 }
34}
35
36impl Default for DifficultyPoint {
37 fn default() -> Self {
38 Self {
39 time: 0.0,
40 slider_velocity: Self::DEFAULT_SLIDER_VELOCITY,
41 bpm_multiplier: Self::DEFAULT_BPM_MULTIPLIER,
42 generate_ticks: Self::DEFAULT_GENERATE_TICKS,
43 }
44 }
45}
46
47pub fn difficulty_point_at(points: &[DifficultyPoint], time: f64) -> Option<&DifficultyPoint> {
48 points
49 .binary_search_by(|probe| probe.time.total_cmp(&time))
50 .map_or_else(|i| i.checked_sub(1), Some)
51 .map(|i| &points[i])
52}