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.

Merge pull request #13 from AlephCubed/chunk-calculations

Added support for chunks in lighting calculations.

authored by

AlephCubed and committed by
GitHub
0121a06d b0a70888

+208 -38
+1 -1
Cargo.lock
··· 156 156 157 157 [[package]] 158 158 name = "bsru" 159 - version = "0.1.0" 159 + version = "0.2.0-beta.1" 160 160 dependencies = [ 161 161 "bevy_color", 162 162 "bevy_reflect",
+1 -1
Cargo.toml
··· 1 1 [package] 2 2 name = "bsru" 3 - version = "0.1.0" 3 + version = "0.2.0-beta.1" 4 4 edition = "2024" 5 5 description = "Beatsaber Rust Utilities: A Beatsaber V3 parsing library." 6 6 categories = ["game-development", "data-structures", "parser-implementations"]
+114 -6
src/difficulty/lightshow.rs
··· 25 25 } 26 26 27 27 impl DistributionType { 28 - #[deprecated( 29 - note = "Experimental. Does not consider chunks, random, or limit in filter calculations." 30 - )] 28 + #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")] 31 29 #[allow(deprecated)] 32 30 fn compute_offset( 33 31 &self, ··· 107 105 } 108 106 109 107 #[test] 108 + fn step() { 109 + for i in 0..12 { 110 + assert_eq!( 111 + DistributionType::Step.compute_offset(i, 12, &Filter::default(), 1.0, None, None), 112 + i as f32 113 + ); 114 + } 115 + } 116 + 117 + #[test] 110 118 fn wave_negative() { 111 119 for i in 0..12 { 112 120 assert_eq!( ··· 117 125 } 118 126 119 127 #[test] 120 - fn step() { 128 + fn step_negative() { 121 129 for i in 0..12 { 122 130 assert_eq!( 123 - DistributionType::Step.compute_offset(i, 12, &Filter::default(), 1.0, None, None), 124 - i as f32 131 + DistributionType::Step.compute_offset(i, 12, &Filter::default(), -1.0, None, None), 132 + -i as f32 125 133 ); 126 134 } 127 135 } ··· 270 278 None 271 279 ), 272 280 12.0 - i as f32 281 + ); 282 + } 283 + } 284 + 285 + #[test] 286 + fn wave_with_chunks_of_size_two() { 287 + for i in 0..6 { 288 + let filter = Filter { 289 + chunks: Some(6), 290 + ..Default::default() 291 + }; 292 + assert_eq!( 293 + DistributionType::Wave.compute_offset(i * 2, 12, &filter, 6.0, None, None), 294 + i as f32 295 + ); 296 + assert_eq!( 297 + DistributionType::Wave.compute_offset(i * 2 + 1, 12, &filter, 6.0, None, None), 298 + i as f32 299 + ); 300 + } 301 + } 302 + 303 + #[test] 304 + fn step_with_chunks_of_size_two() { 305 + for i in 0..6 { 306 + let filter = Filter { 307 + chunks: Some(6), 308 + ..Default::default() 309 + }; 310 + assert_eq!( 311 + DistributionType::Step.compute_offset(i * 2, 12, &filter, 1.0, None, None), 312 + i as f32 313 + ); 314 + assert_eq!( 315 + DistributionType::Step.compute_offset(i * 2 + 1, 12, &filter, 1.0, None, None), 316 + i as f32 317 + ); 318 + } 319 + } 320 + 321 + #[test] 322 + fn wave_with_chunks_of_size_six() { 323 + for i in 0..6 { 324 + let filter = Filter { 325 + chunks: Some(2), 326 + ..Default::default() 327 + }; 328 + assert_eq!( 329 + DistributionType::Wave.compute_offset(i, 12, &filter, 2.0, None, None), 330 + 0.0 331 + ); 332 + assert_eq!( 333 + DistributionType::Wave.compute_offset(i + 6, 12, &filter, 2.0, None, None), 334 + 1.0 335 + ); 336 + } 337 + } 338 + 339 + #[test] 340 + fn step_with_chunks_of_size_six() { 341 + for i in 0..6 { 342 + let filter = Filter { 343 + chunks: Some(2), 344 + ..Default::default() 345 + }; 346 + assert_eq!( 347 + DistributionType::Step.compute_offset(i, 12, &filter, 1.0, None, None), 348 + 0.0 349 + ); 350 + assert_eq!( 351 + DistributionType::Step.compute_offset(i + 6, 12, &filter, 1.0, None, None), 352 + 1.0 353 + ); 354 + } 355 + } 356 + 357 + #[test] 358 + fn wave_with_chunks_out_of_bounds() { 359 + for i in 0..12 { 360 + let filter = Filter { 361 + chunks: Some(24), 362 + ..Default::default() 363 + }; 364 + assert_eq!( 365 + DistributionType::Wave.compute_offset(i, 12, &filter, 12.0, None, None), 366 + i as f32 367 + ); 368 + } 369 + } 370 + 371 + #[test] 372 + fn step_with_chunks_out_of_bounds() { 373 + for i in 0..12 { 374 + let filter = Filter { 375 + chunks: Some(24), 376 + ..Default::default() 377 + }; 378 + assert_eq!( 379 + DistributionType::Step.compute_offset(i, 12, &filter, 1.0, None, None), 380 + i as f32 273 381 ); 274 382 } 275 383 }
+87 -15
src/difficulty/lightshow/filter.rs
··· 61 61 parameter1: 1, 62 62 parameter2: 0, 63 63 reverse: LooseBool::False, 64 - chunks: Some(1), 64 + chunks: Some(0), 65 65 random_behaviour: Some(RandomBehaviour::None), 66 66 random_seed: Some(0), 67 67 limit_behaviour: Some(LimitBehaviour::None), ··· 78 78 /// Will panic if the light ID is greater than or equal to the group size. 79 79 #[must_use] 80 80 #[inline] 81 - #[deprecated( 82 - note = "Experimental. Does not consider chunks, random, or limit in calculations." 83 - )] 84 - pub fn is_in_filter(&self, mut light_id: i32, group_size: i32) -> bool { 81 + #[deprecated(note = "Experimental. Does not consider random or limit in calculations.")] 82 + pub fn is_in_filter(&self, mut light_id: i32, mut group_size: i32) -> bool { 85 83 assert!(light_id < group_size); 86 84 87 85 if self.reverse.is_true() { 88 86 light_id = group_size - light_id - 1; 89 87 } 90 88 89 + if let Some(chunks) = self.chunks 90 + && chunks > 0 91 + && chunks < group_size 92 + { 93 + light_id = (light_id as f32 / (group_size as f32 / chunks as f32)) as i32; 94 + group_size = chunks; 95 + } 96 + 91 97 match self.filter_type { 92 98 FilterType::Division => { 93 99 let start = self.parameter2 * group_size / self.parameter1.max(1); ··· 102 108 } 103 109 } 104 110 105 - /// Returns the number of lights effected by the filter. 111 + /// Returns the number of light chunks effected by the filter. 106 112 /// # Unknown 107 113 /// If the [`FilterType`] is `Unknown` then the result will be the same as `group_size`. 108 114 #[must_use] 109 115 #[inline] 110 - #[deprecated( 111 - note = "Experimental. Does not consider chunks, random, or limit in calculations." 112 - )] 113 - pub fn count_filtered(&self, group_size: i32) -> i32 { 116 + #[deprecated(note = "Experimental. Does not consider random or limit in calculations.")] 117 + pub fn count_filtered(&self, mut group_size: i32) -> i32 { 118 + if let Some(chunks) = self.chunks 119 + && chunks > 0 120 + && chunks < group_size 121 + { 122 + group_size = chunks; 123 + } 124 + 114 125 match self.filter_type { 115 126 FilterType::Division => { 116 127 let start = self.parameter2 * group_size / self.parameter1.max(1); ··· 124 135 } 125 136 } 126 137 127 - /// Returns the light ID relative to the filtered count. 138 + #[allow(deprecated)] 139 + /// Returns the light chunk ID relative to the [filtered count](Self::count_filtered). 128 140 /// # Unknown 129 141 /// If the [`FilterType`] is `Unknown` then the result will be the same as `light_id`. 130 142 /// # Panics 131 143 /// Will panic if the light ID is greater than or equal to the group size. 132 144 #[must_use] 133 145 #[inline] 134 - #[deprecated( 135 - note = "Experimental. Does not consider chunks, random, or limit in calculations." 136 - )] 137 - pub fn get_relative_index(&self, mut light_id: i32, group_size: i32) -> i32 { 146 + #[deprecated(note = "Experimental. Does not consider random or limit in calculations.")] 147 + pub fn get_relative_index(&self, mut light_id: i32, mut group_size: i32) -> i32 { 138 148 assert!(light_id < group_size); 139 149 140 150 if self.reverse.is_true() { 141 151 light_id = group_size - light_id; 152 + } 153 + 154 + if let Some(chunks) = self.chunks 155 + && chunks > 0 156 + && chunks < group_size 157 + { 158 + light_id = (light_id as f32 / (group_size as f32 / chunks as f32)) as i32; 159 + group_size = chunks; 142 160 } 143 161 144 162 match self.filter_type { ··· 378 396 } 379 397 } 380 398 assert_eq!(filter.count_filtered(12), 6); 399 + } 400 + 401 + #[test] 402 + fn chunks_of_two() { 403 + let filter = Filter { 404 + chunks: Some(6), 405 + ..Default::default() 406 + }; 407 + 408 + assert!((0..12).all(|i| filter.is_in_filter(i, 12))); 409 + assert_eq!(filter.count_filtered(12), 6); 410 + assert!((0..6).all(|i| { 411 + filter.get_relative_index(i * 2, 12) == i 412 + && filter.get_relative_index(i * 2 + 1, 12) == i 413 + })); 414 + } 415 + 416 + #[test] 417 + fn chunks_of_six() { 418 + let filter = Filter { 419 + chunks: Some(2), 420 + ..Default::default() 421 + }; 422 + 423 + assert!((0..12).all(|i| filter.is_in_filter(i, 12))); 424 + assert_eq!(filter.count_filtered(12), 2); 425 + assert!((0..6).all(|i| filter.get_relative_index(i, 12) == 0)); 426 + assert!((0..6).all(|i| filter.get_relative_index(i + 6, 12) == 1)); 427 + } 428 + 429 + #[test] 430 + fn chunks_out_of_bounds() { 431 + let filter = Filter { 432 + chunks: Some(24), 433 + ..Default::default() 434 + }; 435 + 436 + assert!((0..12).all(|i| filter.is_in_filter(i, 12))); 437 + assert_eq!(filter.count_filtered(12), 12); 438 + assert!((0..12).all(|i| filter.get_relative_index(i, 12) == i)); 439 + } 440 + 441 + #[test] 442 + fn chunks_non_factor() { 443 + let filter = Filter { 444 + chunks: Some(3), 445 + ..Default::default() 446 + }; 447 + 448 + assert!((0..8).all(|i| filter.is_in_filter(i, 8))); 449 + assert_eq!(filter.count_filtered(8), 3); 450 + assert!((0..3).all(|i| filter.get_relative_index(i, 8) == 0)); 451 + assert!((3..6).all(|i| filter.get_relative_index(i, 8) == 1)); 452 + assert!((6..8).all(|i| filter.get_relative_index(i, 8) == 2)); 381 453 } 382 454 }
+2 -6
src/difficulty/lightshow/group.rs
··· 44 44 /// Returns the number of beats that the event will be offset for a given light ID. 45 45 /// # Panics 46 46 /// Will panic if the light ID is greater than or equal to the group size. 47 - #[deprecated( 48 - note = "Experimental. Does not consider chunks, random, or limit in filter calculations." 49 - )] 47 + #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")] 50 48 fn get_beat_offset(&self, light_id: i32, group_size: i32) -> f32; 51 49 52 50 /// Returns the value (i.e. brightness) that the event will be offset for a given light ID. 53 51 /// # Panics 54 52 /// Will panic if the light ID is greater than or equal to the group size. 55 - #[deprecated( 56 - note = "Experimental. Does not consider chunks, random, or limit in filter calculations." 57 - )] 53 + #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")] 58 54 fn get_value_offset(&self, light_id: i32, group_size: i32) -> f32; 59 55 } 60 56
+1 -3
src/difficulty/lightshow/group/color.rs
··· 93 93 /// Returns the brightness that the event will be offset for a given light ID. 94 94 /// # Panics 95 95 /// Will panic if the light ID is greater than or equal to the group size. 96 - #[deprecated( 97 - note = "Experimental. Does not consider chunks, random, or limit in filter calculations." 98 - )] 96 + #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")] 99 97 #[allow(deprecated)] 100 98 pub fn get_brightness_offset(&self, light_id: i32, group_size: i32) -> f32 { 101 99 self.bright_dist_type.compute_offset(
+1 -3
src/difficulty/lightshow/group/rotation.rs
··· 101 101 /// Returns the number of degrees that the event will be offset for a given light ID. 102 102 /// # Panics 103 103 /// Will panic if the light ID is greater than or equal to the group size. 104 - #[deprecated( 105 - note = "Experimental. Does not consider chunks, random, or limit in filter calculations." 106 - )] 104 + #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")] 107 105 #[allow(deprecated)] 108 106 pub fn get_rotation_offset(&self, light_id: i32, group_size: i32) -> f32 { 109 107 self.rotation_dist_type.compute_offset(
+1 -3
src/difficulty/lightshow/group/translation.rs
··· 107 107 /// Returns the number of units that the event will be offset for a given light ID. 108 108 /// # Panics 109 109 /// Will panic if the light ID is greater than or equal to the group size. 110 - #[deprecated( 111 - note = "Experimental. Does not consider chunks, random, or limit in filter calculations." 112 - )] 110 + #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")] 113 111 #[allow(deprecated)] 114 112 pub fn get_translation_offset(&self, light_id: i32, group_size: i32) -> f32 { 115 113 self.translation_dist_type.compute_offset(