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.

Limit calculations in distribution methods.

+260 -45
+200 -34
src/difficulty/lightshow.rs
··· 27 27 impl DistributionType { 28 28 #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")] 29 29 #[allow(deprecated)] 30 - fn compute_offset( 30 + fn compute_beat_offset( 31 + &self, 32 + light_id: i32, 33 + group_size: i32, 34 + filter: &Filter, 35 + dist_value: f32, 36 + last_data_offset: Option<f32>, 37 + easing: Option<Easing>, 38 + ) -> f32 { 39 + let filtered_id = filter.get_relative_index(light_id, group_size); 40 + 41 + let filtered_size = if let Some(limit_behaviour) = filter.limit_behaviour 42 + && limit_behaviour.duration() 43 + { 44 + filter.count_filtered(group_size) 45 + } else { 46 + filter.count_filtered_without_limit(group_size) 47 + }; 48 + 49 + self.compute_offset( 50 + filtered_id, 51 + filtered_size, 52 + dist_value, 53 + last_data_offset, 54 + easing, 55 + ) 56 + } 57 + 58 + #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")] 59 + #[allow(deprecated)] 60 + fn compute_value_offset( 31 61 &self, 32 62 light_id: i32, 33 63 group_size: i32, ··· 36 66 last_data_offset: Option<f32>, 37 67 easing: Option<Easing>, 38 68 ) -> f32 { 69 + let filtered_id = filter.get_relative_index(light_id, group_size); 70 + 71 + let filtered_size = if let Some(limit_behaviour) = filter.limit_behaviour 72 + && limit_behaviour.distribution() 73 + { 74 + filter.count_filtered(group_size) 75 + } else { 76 + filter.count_filtered_without_limit(group_size) 77 + }; 78 + 79 + self.compute_offset( 80 + filtered_id, 81 + filtered_size, 82 + dist_value, 83 + last_data_offset, 84 + easing, 85 + ) 86 + } 87 + 88 + #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")] 89 + #[allow(deprecated)] 90 + #[inline(always)] 91 + fn compute_offset( 92 + &self, 93 + filtered_id: i32, 94 + filtered_size: i32, 95 + dist_value: f32, 96 + last_data_offset: Option<f32>, 97 + easing: Option<Easing>, 98 + ) -> f32 { 99 + let filtered_id = filtered_id as f32; 100 + let filtered_size = filtered_size as f32; 101 + 39 102 if dist_value == 0.0 { 40 103 return 0.0; 41 104 } 42 - 43 - let filtered_size = filter.count_filtered(group_size) as f32; 44 - let filtered_id = filter.get_relative_index(light_id, group_size) as f32; 45 105 46 106 match self { 47 107 DistributionType::Wave => { ··· 61 121 DistributionType::Unknown(_) => 0.0, 62 122 } 63 123 } 124 + 125 + /// Tests that both [`self.compute_beat_offset`] and [`self.compute_value_offset`] have the same return value, returning that value. 126 + /// 127 + /// This only makes sense if the [`LimitBehaviour`] is either `None` or `Both`. 128 + #[cfg(test)] 129 + #[allow(deprecated)] 130 + fn compute_both( 131 + &self, 132 + light_id: i32, 133 + group_size: i32, 134 + filter: &Filter, 135 + dist_value: f32, 136 + last_data_offset: Option<f32>, 137 + easing: Option<Easing>, 138 + ) -> f32 { 139 + assert!( 140 + matches!( 141 + filter.limit_behaviour, 142 + None | Some(LimitBehaviour::None) | Some(LimitBehaviour::Both) 143 + ), 144 + "This test method only makes sense if LimitBehaviour is `None` or `Both`" 145 + ); 146 + 147 + let beat = self.compute_beat_offset( 148 + light_id, 149 + group_size, 150 + filter, 151 + dist_value, 152 + last_data_offset, 153 + easing, 154 + ); 155 + let value = self.compute_value_offset( 156 + light_id, 157 + group_size, 158 + filter, 159 + dist_value, 160 + last_data_offset, 161 + easing, 162 + ); 163 + assert_eq!(beat, value); 164 + beat 165 + } 64 166 } 65 167 66 168 loose_enum! { ··· 98 200 fn wave() { 99 201 for i in 0..12 { 100 202 assert_eq!( 101 - DistributionType::Wave.compute_offset(i, 12, &Filter::default(), 12.0, None, None), 203 + DistributionType::Wave.compute_both(i, 12, &Filter::default(), 12.0, None, None), 102 204 i as f32 103 205 ); 104 206 } ··· 108 210 fn step() { 109 211 for i in 0..12 { 110 212 assert_eq!( 111 - DistributionType::Step.compute_offset(i, 12, &Filter::default(), 1.0, None, None), 213 + DistributionType::Step.compute_both(i, 12, &Filter::default(), 1.0, None, None), 112 214 i as f32 113 215 ); 114 216 } ··· 118 220 fn wave_negative() { 119 221 for i in 0..12 { 120 222 assert_eq!( 121 - DistributionType::Wave.compute_offset(i, 12, &Filter::default(), -12.0, None, None), 223 + DistributionType::Wave.compute_both(i, 12, &Filter::default(), -12.0, None, None), 122 224 -i as f32 123 225 ); 124 226 } ··· 128 230 fn step_negative() { 129 231 for i in 0..12 { 130 232 assert_eq!( 131 - DistributionType::Step.compute_offset(i, 12, &Filter::default(), -1.0, None, None), 233 + DistributionType::Step.compute_both(i, 12, &Filter::default(), -1.0, None, None), 132 234 -i as f32 133 235 ); 134 236 } ··· 138 240 fn wave_zero() { 139 241 for i in 0..12 { 140 242 assert_eq!( 141 - DistributionType::Wave.compute_offset(i, 12, &Filter::default(), 0.0, None, None), 243 + DistributionType::Wave.compute_both(i, 12, &Filter::default(), 0.0, None, None), 142 244 0.0 143 245 ); 144 246 } ··· 148 250 fn step_zero() { 149 251 for i in 0..12 { 150 252 assert_eq!( 151 - DistributionType::Step.compute_offset(i, 12, &Filter::default(), 0.0, None, None), 253 + DistributionType::Step.compute_both(i, 12, &Filter::default(), 0.0, None, None), 152 254 0.0 153 255 ); 154 256 } ··· 158 260 fn wave_with_division_filter() { 159 261 for i in 0..6 { 160 262 assert_eq!( 161 - DistributionType::Wave.compute_offset( 263 + DistributionType::Wave.compute_both( 162 264 i + 6, 163 265 12, 164 266 &Filter { ··· 180 282 fn step_with_division_filter() { 181 283 for i in 0..6 { 182 284 assert_eq!( 183 - DistributionType::Step.compute_offset( 285 + DistributionType::Step.compute_both( 184 286 i + 6, 185 287 12, 186 288 &Filter { ··· 202 304 fn wave_with_step_filter() { 203 305 for i in 0..6 { 204 306 assert_eq!( 205 - DistributionType::Wave.compute_offset( 307 + DistributionType::Wave.compute_both( 206 308 i * 2, 207 309 12, 208 310 &Filter { ··· 224 326 fn step_with_step_filter() { 225 327 for i in 0..6 { 226 328 assert_eq!( 227 - DistributionType::Step.compute_offset( 329 + DistributionType::Step.compute_both( 228 330 i * 2, 229 331 12, 230 332 &Filter { ··· 246 348 fn wave_with_reverse_filter() { 247 349 for i in 0..12 { 248 350 assert_eq!( 249 - DistributionType::Wave.compute_offset( 351 + DistributionType::Wave.compute_both( 250 352 i, 251 353 12, 252 354 &Filter { ··· 266 368 fn step_with_reverse_filter() { 267 369 for i in 0..12 { 268 370 assert_eq!( 269 - DistributionType::Step.compute_offset( 371 + DistributionType::Step.compute_both( 270 372 i, 271 373 12, 272 374 &Filter { ··· 290 392 ..Default::default() 291 393 }; 292 394 assert_eq!( 293 - DistributionType::Wave.compute_offset(i * 2, 12, &filter, 6.0, None, None), 395 + DistributionType::Wave.compute_both(i * 2, 12, &filter, 6.0, None, None), 294 396 i as f32 295 397 ); 296 398 assert_eq!( 297 - DistributionType::Wave.compute_offset(i * 2 + 1, 12, &filter, 6.0, None, None), 399 + DistributionType::Wave.compute_both(i * 2 + 1, 12, &filter, 6.0, None, None), 298 400 i as f32 299 401 ); 300 402 } ··· 308 410 ..Default::default() 309 411 }; 310 412 assert_eq!( 311 - DistributionType::Step.compute_offset(i * 2, 12, &filter, 1.0, None, None), 413 + DistributionType::Step.compute_both(i * 2, 12, &filter, 1.0, None, None), 312 414 i as f32 313 415 ); 314 416 assert_eq!( 315 - DistributionType::Step.compute_offset(i * 2 + 1, 12, &filter, 1.0, None, None), 417 + DistributionType::Step.compute_both(i * 2 + 1, 12, &filter, 1.0, None, None), 316 418 i as f32 317 419 ); 318 420 } ··· 326 428 ..Default::default() 327 429 }; 328 430 assert_eq!( 329 - DistributionType::Wave.compute_offset(i, 12, &filter, 2.0, None, None), 431 + DistributionType::Wave.compute_both(i, 12, &filter, 2.0, None, None), 330 432 0.0 331 433 ); 332 434 assert_eq!( 333 - DistributionType::Wave.compute_offset(i + 6, 12, &filter, 2.0, None, None), 435 + DistributionType::Wave.compute_both(i + 6, 12, &filter, 2.0, None, None), 334 436 1.0 335 437 ); 336 438 } ··· 344 446 ..Default::default() 345 447 }; 346 448 assert_eq!( 347 - DistributionType::Step.compute_offset(i, 12, &filter, 1.0, None, None), 449 + DistributionType::Step.compute_both(i, 12, &filter, 1.0, None, None), 348 450 0.0 349 451 ); 350 452 assert_eq!( 351 - DistributionType::Step.compute_offset(i + 6, 12, &filter, 1.0, None, None), 453 + DistributionType::Step.compute_both(i + 6, 12, &filter, 1.0, None, None), 352 454 1.0 353 455 ); 354 456 } ··· 356 458 357 459 #[test] 358 460 fn wave_with_chunks_out_of_bounds() { 461 + let filter = Filter { 462 + chunks: Some(24), 463 + ..Default::default() 464 + }; 465 + 359 466 for i in 0..12 { 360 - let filter = Filter { 361 - chunks: Some(24), 362 - ..Default::default() 363 - }; 364 467 assert_eq!( 365 - DistributionType::Wave.compute_offset(i, 12, &filter, 12.0, None, None), 468 + DistributionType::Wave.compute_both(i, 12, &filter, 12.0, None, None), 366 469 i as f32 367 470 ); 368 471 } ··· 370 473 371 474 #[test] 372 475 fn step_with_chunks_out_of_bounds() { 476 + let filter = Filter { 477 + chunks: Some(24), 478 + ..Default::default() 479 + }; 480 + 373 481 for i in 0..12 { 374 - let filter = Filter { 375 - chunks: Some(24), 376 - ..Default::default() 377 - }; 482 + assert_eq!( 483 + DistributionType::Step.compute_both(i, 12, &filter, 1.0, None, None), 484 + i as f32 485 + ); 486 + } 487 + } 488 + 489 + #[test] 490 + fn wave_with_limit() { 491 + let filter = Filter { 492 + limit_percent: Some(0.5), 493 + ..Default::default() 494 + }; 495 + 496 + for i in 0..6 { 497 + assert_eq!( 498 + DistributionType::Wave.compute_both(i, 12, &filter, 12.0, None, None), 499 + i as f32 500 + ); 501 + } 502 + } 503 + 504 + #[test] 505 + fn step_with_limit() { 506 + let filter = Filter { 507 + limit_percent: Some(0.5), 508 + ..Default::default() 509 + }; 510 + 511 + for i in 0..6 { 378 512 assert_eq!( 379 - DistributionType::Step.compute_offset(i, 12, &filter, 1.0, None, None), 513 + DistributionType::Step.compute_both(i, 12, &filter, 1.0, None, None), 514 + i as f32 515 + ); 516 + } 517 + } 518 + 519 + #[test] 520 + fn wave_with_enabled_limit() { 521 + let filter = Filter { 522 + limit_behaviour: Some(LimitBehaviour::Both), 523 + limit_percent: Some(0.5), 524 + ..Default::default() 525 + }; 526 + 527 + for i in 0..6 { 528 + assert_eq!( 529 + DistributionType::Wave.compute_both(i, 12, &filter, 12.0, None, None), 530 + (i * 2) as f32 531 + ); 532 + } 533 + } 534 + 535 + #[test] 536 + fn step_with_enabled_limit() { 537 + let filter = Filter { 538 + limit_behaviour: Some(LimitBehaviour::Both), 539 + limit_percent: Some(0.5), 540 + ..Default::default() 541 + }; 542 + 543 + for i in 0..6 { 544 + assert_eq!( 545 + DistributionType::Step.compute_both(i, 12, &filter, 1.0, None, None), 380 546 i as f32 381 547 ); 382 548 }
+23 -7
src/difficulty/lightshow/filter.rs
··· 114 114 } 115 115 } 116 116 117 - /// Returns the number of light chunks effected by the filter. 117 + /// Returns the number of light chunks effected by the filter, but before applying the limit. 118 + /// 119 + /// This is required for distribution calculations. 118 120 /// # Unknown 119 121 /// If the [`FilterType`] is `Unknown` then the result will be the same as `group_size`. 120 122 #[must_use] 121 123 #[inline] 122 124 #[deprecated(note = "Experimental. Does not consider random or limit in calculations.")] 123 - pub fn count_filtered(&self, mut group_size: i32) -> i32 { 125 + pub(crate) fn count_filtered_without_limit(&self, mut group_size: i32) -> i32 { 124 126 if let Some(chunks) = self.chunks 125 127 && chunks > 0 126 128 && chunks < group_size ··· 128 130 group_size = chunks; 129 131 } 130 132 131 - let mut filtered = match self.filter_type { 133 + match self.filter_type { 132 134 FilterType::Division => { 133 135 let start = self.parameter2 * group_size / self.parameter1.max(1); 134 136 let end = (self.parameter2 + 1) * group_size / self.parameter1.max(1); ··· 138 140 group_size / self.parameter2.max(1) - self.parameter1 / self.parameter2.max(1) 139 141 } 140 142 FilterType::Unknown(_) => group_size, 141 - }; 143 + } 144 + } 145 + 146 + /// Returns the number of light chunks effected by the filter. 147 + /// # Unknown 148 + /// If the [`FilterType`] is `Unknown` then the result will be the same as `group_size`. 149 + #[must_use] 150 + #[inline] 151 + #[deprecated(note = "Experimental. Does not consider random or limit in calculations.")] 152 + #[allow(deprecated)] 153 + pub fn count_filtered(&self, group_size: i32) -> i32 { 154 + let filtered = self.count_filtered_without_limit(group_size); 142 155 143 156 if let Some(limit) = self.limit_percent { 144 - filtered = (filtered as f32 * limit) as i32; 157 + (filtered as f32 * limit) as i32 158 + } else { 159 + filtered 145 160 } 146 - 147 - filtered 148 161 } 149 162 150 163 #[allow(deprecated)] ··· 488 501 assert!((0..6).all(|i| filter.is_in_filter(i, 12))); 489 502 assert!((6..12).all(|i| !filter.is_in_filter(i, 12))); 490 503 assert_eq!(filter.count_filtered(12), 6); 504 + assert_eq!(filter.count_filtered_without_limit(12), 12); 491 505 assert!((0..6).all(|i| filter.get_relative_index(i, 12) == i)); 492 506 } 493 507 ··· 500 514 501 515 assert!((0..8).all(|i| !filter.is_in_filter(i, 8))); 502 516 assert_eq!(filter.count_filtered(8), 0); 517 + assert_eq!(filter.count_filtered_without_limit(8), 8); 503 518 } 504 519 505 520 #[test] ··· 512 527 assert!((0..7).all(|i| filter.is_in_filter(i, 8))); 513 528 assert!(!filter.is_in_filter(7, 8)); 514 529 assert_eq!(filter.count_filtered(8), 7); 530 + assert_eq!(filter.count_filtered_without_limit(8), 8); 515 531 assert!((0..7).all(|i| filter.get_relative_index(i, 8) == i)); 516 532 } 517 533 }
+1 -1
src/difficulty/lightshow/group.rs
··· 71 71 72 72 #[allow(deprecated)] 73 73 fn get_beat_offset(&self, light_id: i32, group_size: i32) -> f32 { 74 - self.beat_dist_type.compute_offset( 74 + self.beat_dist_type.compute_beat_offset( 75 75 light_id, 76 76 group_size, 77 77 &self.filter,
+34 -1
src/difficulty/lightshow/group/color.rs
··· 96 96 #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")] 97 97 #[allow(deprecated)] 98 98 pub fn get_brightness_offset(&self, light_id: i32, group_size: i32) -> f32 { 99 - self.bright_dist_type.compute_offset( 99 + self.bright_dist_type.compute_value_offset( 100 100 light_id, 101 101 group_size, 102 102 &self.filter, ··· 189 189 #[cfg(test)] 190 190 mod tests { 191 191 use super::*; 192 + use crate::LimitBehaviour; 192 193 use crate::difficulty::lightshow::filter::FilterType; 193 194 use crate::difficulty::lightshow::group::EventGroup; 194 195 ··· 408 409 }; 409 410 410 411 assert!((0..12).all(|i| group.get_brightness_offset(i, 12) == 12.0 - i as f32)); 412 + } 413 + 414 + #[test] 415 + fn beat_wave_with_limit_filter() { 416 + let group = ColorEventGroup { 417 + filter: Filter { 418 + limit_behaviour: Some(LimitBehaviour::Duration), 419 + limit_percent: Some(0.5), 420 + ..Default::default() 421 + }, 422 + beat_dist_type: DistributionType::Wave, 423 + beat_dist_value: 12.0, 424 + ..Default::default() 425 + }; 426 + 427 + assert!((0..6).all(|i| group.get_beat_offset(i, 12) == (i * 2) as f32)); 428 + } 429 + 430 + #[test] 431 + fn brightness_wave_with_limit_filter() { 432 + let group = ColorEventGroup { 433 + filter: Filter { 434 + limit_behaviour: Some(LimitBehaviour::Distribution), 435 + limit_percent: Some(0.5), 436 + ..Default::default() 437 + }, 438 + bright_dist_type: DistributionType::Wave, 439 + bright_dist_value: 12.0, 440 + ..Default::default() 441 + }; 442 + 443 + assert!((0..6).all(|i| group.get_brightness_offset(i, 12) == (i * 2) as f32)); 411 444 } 412 445 }
+1 -1
src/difficulty/lightshow/group/rotation.rs
··· 104 104 #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")] 105 105 #[allow(deprecated)] 106 106 pub fn get_rotation_offset(&self, light_id: i32, group_size: i32) -> f32 { 107 - self.rotation_dist_type.compute_offset( 107 + self.rotation_dist_type.compute_value_offset( 108 108 light_id, 109 109 group_size, 110 110 &self.filter,
+1 -1
src/difficulty/lightshow/group/translation.rs
··· 110 110 #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")] 111 111 #[allow(deprecated)] 112 112 pub fn get_translation_offset(&self, light_id: i32, group_size: i32) -> f32 { 113 - self.translation_dist_type.compute_offset( 113 + self.translation_dist_type.compute_value_offset( 114 114 light_id, 115 115 group_size, 116 116 &self.filter,