Beatsaber Rust Utilities: A Beatsaber V3 parsing library.
beatsaber beatmap
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at dev 263 lines 8.0 kB view raw
1//! The advanced group lighting system events. 2 3pub mod color; 4pub mod fx; 5pub mod rotation; 6pub mod translation; 7 8#[doc(hidden)] 9pub use color::*; 10#[doc(hidden)] 11pub use fx::*; 12#[doc(hidden)] 13pub use rotation::*; 14#[doc(hidden)] 15pub use translation::*; 16 17use crate::difficulty::lightshow::filter::Filter; 18use crate::timing_traits::Timed; 19 20/// A collection of [`EventGroup`]s that share the same group ID and beat. 21pub trait EventBox: Timed { 22 type Group: EventGroup<Data = Self::Data>; 23 type Data: EventData; 24 25 fn get_groups(&self) -> &Vec<Self::Group>; 26} 27 28#[macro_export] 29#[doc(hidden)] 30macro_rules! impl_event_box { 31 ($ident:ident, $group:ident, $data:ident) => { 32 impl crate::difficulty::lightshow::group::EventBox for $ident { 33 type Group = $group; 34 type Data = $data; 35 36 fn get_groups(&self) -> &Vec<Self::Group> { 37 &self.groups 38 } 39 } 40 }; 41} 42 43/// A collection of [`EventData`] that share the same [`Filter`] and distribution. 44pub trait EventGroup { 45 type Data: EventData; 46 47 fn get_filter(&self) -> &Filter; 48 fn get_data(&self) -> &Vec<Self::Data>; 49 50 /// Returns the number of beats that the event will be offset for a given light ID. 51 /// # Panics 52 /// Will panic if the light ID is greater than or equal to the group size. 53 #[deprecated(note = "Experimental. Does not consider random in filter calculations.")] 54 fn get_beat_offset(&self, light_id: i32, group_size: i32) -> f32; 55 56 /// Returns the value (i.e. brightness) that the event will be offset for a given light ID. 57 /// # Panics 58 /// Will panic if the light ID is greater than or equal to the group size. 59 #[deprecated(note = "Experimental. Does not consider random in filter calculations.")] 60 fn get_value_offset(&self, light_id: i32, group_size: i32) -> f32; 61 62 /// Returns the duration of the group in beats. 63 #[deprecated(note = "Experimental. Does not consider random in filter calculations.")] 64 fn get_duration(&self, group_size: i32) -> f32; 65} 66 67// Todo This macro could be a default trait implementation if other getters are added. 68#[macro_export] 69#[doc(hidden)] 70macro_rules! impl_event_group { 71 ($ident:ident::$value_offset:ident, $data:ident) => { 72 impl crate::difficulty::lightshow::group::EventGroup for $ident { 73 type Data = $data; 74 75 fn get_filter(&self) -> &Filter { 76 &self.filter 77 } 78 79 fn get_data(&self) -> &Vec<Self::Data> { 80 &self.data 81 } 82 83 #[allow(deprecated)] 84 fn get_beat_offset(&self, light_id: i32, group_size: i32) -> f32 { 85 self.beat_dist_type.compute_beat_offset( 86 light_id, 87 group_size, 88 &self.filter, 89 self.beat_dist_value, 90 self.data.last().map(|data| data.beat_offset), 91 None, 92 ) 93 } 94 95 #[allow(deprecated)] 96 fn get_value_offset(&self, light_id: i32, group_size: i32) -> f32 { 97 self.$value_offset(light_id, group_size) 98 } 99 100 #[allow(deprecated)] 101 fn get_duration(&self, group_size: i32) -> f32 { 102 let filtered_size = self.filter.count_filtered(group_size); 103 104 if filtered_size == 0 { 105 return 0.0; 106 } 107 108 let Some(data) = self.get_data().last() else { 109 return 0.0; 110 }; 111 112 match self.beat_dist_type { 113 DistributionType::Wave => { 114 if let Some(limit_behaviour) = self.filter.limit_behaviour 115 && !limit_behaviour.beat_enabled() 116 && let Some(limit_percent) = self.filter.limit_percent 117 && limit_percent > 0.0 118 { 119 (self.beat_dist_value * limit_percent).max(data.beat_offset) 120 } else { 121 self.beat_dist_value.max(data.beat_offset) 122 } 123 } 124 DistributionType::Step => { 125 data.beat_offset + self.beat_dist_value * filtered_size as f32 126 } 127 DistributionType::Undefined(_) => data.beat_offset, 128 } 129 } 130 } 131 }; 132} 133 134/// The lowest-level group event type, which determines the base value of the event. 135pub trait EventData { 136 /// Returns the number of beats the event will be offset from the [`EventBox`]'s beat. 137 fn get_beat_offset(&self) -> f32; 138} 139 140#[macro_export] 141#[doc(hidden)] 142macro_rules! impl_event_data { 143 ($ident:ident) => { 144 impl crate::difficulty::lightshow::group::EventData for $ident { 145 fn get_beat_offset(&self) -> f32 { 146 self.beat_offset 147 } 148 } 149 }; 150} 151 152#[allow(deprecated)] 153#[cfg(test)] 154mod tests { 155 use super::*; 156 use crate::{DistributionType, LimitBehaviour}; 157 158 #[test] 159 fn get_duration_no_distribution() { 160 assert_eq!(ColorEventGroup::default().get_duration(12), 0.0); 161 } 162 163 #[test] 164 fn get_duration_wave() { 165 let group = ColorEventGroup { 166 beat_dist_type: DistributionType::Wave, 167 beat_dist_value: 12.0, 168 ..Default::default() 169 }; 170 171 assert_eq!(group.get_duration(12), 12.0); 172 } 173 174 #[test] 175 fn get_duration_step() { 176 let group = ColorEventGroup { 177 beat_dist_type: DistributionType::Step, 178 beat_dist_value: 1.0, 179 ..Default::default() 180 }; 181 182 assert_eq!(group.get_duration(12), 12.0); 183 } 184 185 #[test] 186 fn get_duration_wave_with_limit() { 187 let group = ColorEventGroup { 188 filter: Filter { 189 limit_behaviour: Some(LimitBehaviour::None), 190 limit_percent: Some(0.5), 191 ..Default::default() 192 }, 193 beat_dist_type: DistributionType::Wave, 194 beat_dist_value: 12.0, 195 ..Default::default() 196 }; 197 198 assert_eq!(group.get_duration(12), 6.0); 199 } 200 201 #[test] 202 fn get_duration_step_with_limit() { 203 let group = ColorEventGroup { 204 filter: Filter { 205 limit_behaviour: Some(LimitBehaviour::None), 206 limit_percent: Some(0.5), 207 ..Default::default() 208 }, 209 beat_dist_type: DistributionType::Step, 210 beat_dist_value: 1.0, 211 ..Default::default() 212 }; 213 214 assert_eq!(group.get_duration(12), 6.0); 215 } 216 217 #[test] 218 fn get_duration_wave_with_limit_adjusted() { 219 let group = ColorEventGroup { 220 filter: Filter { 221 limit_behaviour: Some(LimitBehaviour::Beat), 222 limit_percent: Some(0.5), 223 ..Default::default() 224 }, 225 beat_dist_type: DistributionType::Wave, 226 beat_dist_value: 12.0, 227 ..Default::default() 228 }; 229 230 assert_eq!(group.get_duration(12), 12.0); 231 } 232 233 #[test] 234 fn get_duration_step_with_limit_adjusted() { 235 let group = ColorEventGroup { 236 filter: Filter { 237 limit_behaviour: Some(LimitBehaviour::Beat), 238 limit_percent: Some(0.5), 239 ..Default::default() 240 }, 241 beat_dist_type: DistributionType::Step, 242 beat_dist_value: 1.0, 243 ..Default::default() 244 }; 245 246 assert_eq!(group.get_duration(12), 6.0); 247 } 248 249 #[test] 250 fn get_duration_step_with_limit_zero() { 251 let group = ColorEventGroup { 252 filter: Filter { 253 limit_percent: Some(0.0), 254 ..Default::default() 255 }, 256 beat_dist_type: DistributionType::Step, 257 beat_dist_value: 1.0, 258 ..Default::default() 259 }; 260 261 assert_eq!(group.get_duration(12), 12.0); 262 } 263}