···2525}
26262727impl DistributionType {
2828- #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")]
2828+ #[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
2929+ #[allow(deprecated)]
3030+ fn compute_beat_offset(
3131+ &self,
3232+ light_id: i32,
3333+ group_size: i32,
3434+ filter: &Filter,
3535+ dist_value: f32,
3636+ last_data_offset: Option<f32>,
3737+ easing: Option<Easing>,
3838+ ) -> f32 {
3939+ let filtered_id = filter.get_relative_index(light_id, group_size);
4040+4141+ let filtered_size = if let Some(limit_behaviour) = filter.limit_behaviour
4242+ && limit_behaviour.beat_enabled()
4343+ {
4444+ filter.count_filtered(group_size)
4545+ } else {
4646+ filter.count_filtered_without_limit(group_size)
4747+ };
4848+4949+ self.compute_offset(
5050+ filtered_id,
5151+ filtered_size,
5252+ dist_value,
5353+ last_data_offset,
5454+ easing,
5555+ )
5656+ }
5757+5858+ #[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
2959 #[allow(deprecated)]
3030- fn compute_offset(
6060+ fn compute_value_offset(
3161 &self,
3262 light_id: i32,
3363 group_size: i32,
···3666 last_data_offset: Option<f32>,
3767 easing: Option<Easing>,
3868 ) -> f32 {
6969+ let filtered_id = filter.get_relative_index(light_id, group_size);
7070+7171+ let filtered_size = if let Some(limit_behaviour) = filter.limit_behaviour
7272+ && limit_behaviour.value_enabled()
7373+ {
7474+ filter.count_filtered(group_size)
7575+ } else {
7676+ filter.count_filtered_without_limit(group_size)
7777+ };
7878+7979+ self.compute_offset(
8080+ filtered_id,
8181+ filtered_size,
8282+ dist_value,
8383+ last_data_offset,
8484+ easing,
8585+ )
8686+ }
8787+8888+ #[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
8989+ #[allow(deprecated)]
9090+ #[inline(always)]
9191+ fn compute_offset(
9292+ &self,
9393+ filtered_id: i32,
9494+ filtered_size: i32,
9595+ dist_value: f32,
9696+ last_data_offset: Option<f32>,
9797+ easing: Option<Easing>,
9898+ ) -> f32 {
9999+ let filtered_id = filtered_id as f32;
100100+ let filtered_size = filtered_size as f32;
101101+39102 if dist_value == 0.0 {
40103 return 0.0;
41104 }
4242-4343- let filtered_size = filter.count_filtered(group_size) as f32;
4444- let filtered_id = filter.get_relative_index(light_id, group_size) as f32;
4510546106 match self {
47107 DistributionType::Wave => {
···61121 DistributionType::Unknown(_) => 0.0,
62122 }
63123 }
124124+125125+ /// Tests that both [`self.compute_beat_offset`] and [`self.compute_value_offset`] have the same return value, returning that value.
126126+ ///
127127+ /// This only makes sense if the [`LimitBehaviour`] is either `None` or `Both`.
128128+ #[cfg(test)]
129129+ #[allow(deprecated)]
130130+ fn compute_both(
131131+ &self,
132132+ light_id: i32,
133133+ group_size: i32,
134134+ filter: &Filter,
135135+ dist_value: f32,
136136+ last_data_offset: Option<f32>,
137137+ easing: Option<Easing>,
138138+ ) -> f32 {
139139+ assert!(
140140+ matches!(
141141+ filter.limit_behaviour,
142142+ None | Some(LimitBehaviour::None) | Some(LimitBehaviour::Both)
143143+ ),
144144+ "This test method only makes sense if LimitBehaviour is `None` or `Both`"
145145+ );
146146+147147+ let beat = self.compute_beat_offset(
148148+ light_id,
149149+ group_size,
150150+ filter,
151151+ dist_value,
152152+ last_data_offset,
153153+ easing,
154154+ );
155155+ let value = self.compute_value_offset(
156156+ light_id,
157157+ group_size,
158158+ filter,
159159+ dist_value,
160160+ last_data_offset,
161161+ easing,
162162+ );
163163+ assert_eq!(beat, value);
164164+ beat
165165+ }
64166}
6516766168loose_enum! {
···98200 fn wave() {
99201 for i in 0..12 {
100202 assert_eq!(
101101- DistributionType::Wave.compute_offset(i, 12, &Filter::default(), 12.0, None, None),
203203+ DistributionType::Wave.compute_both(i, 12, &Filter::default(), 12.0, None, None),
102204 i as f32
103205 );
104206 }
···108210 fn step() {
109211 for i in 0..12 {
110212 assert_eq!(
111111- DistributionType::Step.compute_offset(i, 12, &Filter::default(), 1.0, None, None),
213213+ DistributionType::Step.compute_both(i, 12, &Filter::default(), 1.0, None, None),
112214 i as f32
113215 );
114216 }
···118220 fn wave_negative() {
119221 for i in 0..12 {
120222 assert_eq!(
121121- DistributionType::Wave.compute_offset(i, 12, &Filter::default(), -12.0, None, None),
223223+ DistributionType::Wave.compute_both(i, 12, &Filter::default(), -12.0, None, None),
122224 -i as f32
123225 );
124226 }
···128230 fn step_negative() {
129231 for i in 0..12 {
130232 assert_eq!(
131131- DistributionType::Step.compute_offset(i, 12, &Filter::default(), -1.0, None, None),
233233+ DistributionType::Step.compute_both(i, 12, &Filter::default(), -1.0, None, None),
132234 -i as f32
133235 );
134236 }
···138240 fn wave_zero() {
139241 for i in 0..12 {
140242 assert_eq!(
141141- DistributionType::Wave.compute_offset(i, 12, &Filter::default(), 0.0, None, None),
243243+ DistributionType::Wave.compute_both(i, 12, &Filter::default(), 0.0, None, None),
142244 0.0
143245 );
144246 }
···148250 fn step_zero() {
149251 for i in 0..12 {
150252 assert_eq!(
151151- DistributionType::Step.compute_offset(i, 12, &Filter::default(), 0.0, None, None),
253253+ DistributionType::Step.compute_both(i, 12, &Filter::default(), 0.0, None, None),
152254 0.0
153255 );
154256 }
···158260 fn wave_with_division_filter() {
159261 for i in 0..6 {
160262 assert_eq!(
161161- DistributionType::Wave.compute_offset(
263263+ DistributionType::Wave.compute_both(
162264 i + 6,
163265 12,
164266 &Filter {
···180282 fn step_with_division_filter() {
181283 for i in 0..6 {
182284 assert_eq!(
183183- DistributionType::Step.compute_offset(
285285+ DistributionType::Step.compute_both(
184286 i + 6,
185287 12,
186288 &Filter {
···202304 fn wave_with_step_filter() {
203305 for i in 0..6 {
204306 assert_eq!(
205205- DistributionType::Wave.compute_offset(
307307+ DistributionType::Wave.compute_both(
206308 i * 2,
207309 12,
208310 &Filter {
···224326 fn step_with_step_filter() {
225327 for i in 0..6 {
226328 assert_eq!(
227227- DistributionType::Step.compute_offset(
329329+ DistributionType::Step.compute_both(
228330 i * 2,
229331 12,
230332 &Filter {
···246348 fn wave_with_reverse_filter() {
247349 for i in 0..12 {
248350 assert_eq!(
249249- DistributionType::Wave.compute_offset(
351351+ DistributionType::Wave.compute_both(
250352 i,
251353 12,
252354 &Filter {
···266368 fn step_with_reverse_filter() {
267369 for i in 0..12 {
268370 assert_eq!(
269269- DistributionType::Step.compute_offset(
371371+ DistributionType::Step.compute_both(
270372 i,
271373 12,
272374 &Filter {
···290392 ..Default::default()
291393 };
292394 assert_eq!(
293293- DistributionType::Wave.compute_offset(i * 2, 12, &filter, 6.0, None, None),
395395+ DistributionType::Wave.compute_both(i * 2, 12, &filter, 6.0, None, None),
294396 i as f32
295397 );
296398 assert_eq!(
297297- DistributionType::Wave.compute_offset(i * 2 + 1, 12, &filter, 6.0, None, None),
399399+ DistributionType::Wave.compute_both(i * 2 + 1, 12, &filter, 6.0, None, None),
298400 i as f32
299401 );
300402 }
···308410 ..Default::default()
309411 };
310412 assert_eq!(
311311- DistributionType::Step.compute_offset(i * 2, 12, &filter, 1.0, None, None),
413413+ DistributionType::Step.compute_both(i * 2, 12, &filter, 1.0, None, None),
312414 i as f32
313415 );
314416 assert_eq!(
315315- DistributionType::Step.compute_offset(i * 2 + 1, 12, &filter, 1.0, None, None),
417417+ DistributionType::Step.compute_both(i * 2 + 1, 12, &filter, 1.0, None, None),
316418 i as f32
317419 );
318420 }
···326428 ..Default::default()
327429 };
328430 assert_eq!(
329329- DistributionType::Wave.compute_offset(i, 12, &filter, 2.0, None, None),
431431+ DistributionType::Wave.compute_both(i, 12, &filter, 2.0, None, None),
330432 0.0
331433 );
332434 assert_eq!(
333333- DistributionType::Wave.compute_offset(i + 6, 12, &filter, 2.0, None, None),
435435+ DistributionType::Wave.compute_both(i + 6, 12, &filter, 2.0, None, None),
334436 1.0
335437 );
336438 }
···344446 ..Default::default()
345447 };
346448 assert_eq!(
347347- DistributionType::Step.compute_offset(i, 12, &filter, 1.0, None, None),
449449+ DistributionType::Step.compute_both(i, 12, &filter, 1.0, None, None),
348450 0.0
349451 );
350452 assert_eq!(
351351- DistributionType::Step.compute_offset(i + 6, 12, &filter, 1.0, None, None),
453453+ DistributionType::Step.compute_both(i + 6, 12, &filter, 1.0, None, None),
352454 1.0
353455 );
354456 }
···356458357459 #[test]
358460 fn wave_with_chunks_out_of_bounds() {
461461+ let filter = Filter {
462462+ chunks: Some(24),
463463+ ..Default::default()
464464+ };
465465+359466 for i in 0..12 {
360360- let filter = Filter {
361361- chunks: Some(24),
362362- ..Default::default()
363363- };
364467 assert_eq!(
365365- DistributionType::Wave.compute_offset(i, 12, &filter, 12.0, None, None),
468468+ DistributionType::Wave.compute_both(i, 12, &filter, 12.0, None, None),
366469 i as f32
367470 );
368471 }
···370473371474 #[test]
372475 fn step_with_chunks_out_of_bounds() {
476476+ let filter = Filter {
477477+ chunks: Some(24),
478478+ ..Default::default()
479479+ };
480480+373481 for i in 0..12 {
374374- let filter = Filter {
375375- chunks: Some(24),
376376- ..Default::default()
377377- };
378482 assert_eq!(
379379- DistributionType::Step.compute_offset(i, 12, &filter, 1.0, None, None),
483483+ DistributionType::Step.compute_both(i, 12, &filter, 1.0, None, None),
484484+ i as f32
485485+ );
486486+ }
487487+ }
488488+489489+ #[test]
490490+ fn wave_with_limit() {
491491+ let filter = Filter {
492492+ limit_percent: Some(0.5),
493493+ ..Default::default()
494494+ };
495495+496496+ for i in 0..6 {
497497+ assert_eq!(
498498+ DistributionType::Wave.compute_both(i, 12, &filter, 12.0, None, None),
499499+ i as f32
500500+ );
501501+ }
502502+ }
503503+504504+ #[test]
505505+ fn step_with_limit() {
506506+ let filter = Filter {
507507+ limit_percent: Some(0.5),
508508+ ..Default::default()
509509+ };
510510+511511+ for i in 0..6 {
512512+ assert_eq!(
513513+ DistributionType::Step.compute_both(i, 12, &filter, 1.0, None, None),
514514+ i as f32
515515+ );
516516+ }
517517+ }
518518+519519+ #[test]
520520+ fn wave_with_enabled_limit() {
521521+ let filter = Filter {
522522+ limit_behaviour: Some(LimitBehaviour::Both),
523523+ limit_percent: Some(0.5),
524524+ ..Default::default()
525525+ };
526526+527527+ for i in 0..6 {
528528+ assert_eq!(
529529+ DistributionType::Wave.compute_both(i, 12, &filter, 12.0, None, None),
530530+ (i * 2) as f32
531531+ );
532532+ }
533533+ }
534534+535535+ #[test]
536536+ fn step_with_enabled_limit() {
537537+ let filter = Filter {
538538+ limit_behaviour: Some(LimitBehaviour::Both),
539539+ limit_percent: Some(0.5),
540540+ ..Default::default()
541541+ };
542542+543543+ for i in 0..6 {
544544+ assert_eq!(
545545+ DistributionType::Step.compute_both(i, 12, &filter, 1.0, None, None),
380546 i as f32
381547 );
382548 }
+92-7
src/difficulty/lightshow/filter.rs
···7878 /// Will panic if the light ID is greater than or equal to the group size.
7979 #[must_use]
8080 #[inline]
8181- #[deprecated(note = "Experimental. Does not consider random or limit in calculations.")]
8181+ #[deprecated(note = "Experimental. Does not consider random in calculations.")]
8282 pub fn is_in_filter(&self, mut light_id: i32, mut group_size: i32) -> bool {
8383 assert!(light_id < group_size);
8484+8585+ if let Some(limit) = self.limit_percent
8686+ && limit > 0.0
8787+ && light_id >= (group_size as f32 * limit) as i32
8888+ {
8989+ return false;
9090+ }
84918592 if self.reverse.is_true() {
8693 light_id = group_size - light_id - 1;
···108115 }
109116 }
110117111111- /// Returns the number of light chunks effected by the filter.
118118+ /// Returns the number of light chunks effected by the filter, but before applying the limit.
119119+ ///
120120+ /// This is required for distribution calculations.
112121 /// # Unknown
113122 /// If the [`FilterType`] is `Unknown` then the result will be the same as `group_size`.
114123 #[must_use]
115124 #[inline]
116116- #[deprecated(note = "Experimental. Does not consider random or limit in calculations.")]
117117- pub fn count_filtered(&self, mut group_size: i32) -> i32 {
125125+ #[deprecated(note = "Experimental. Does not consider random in calculations.")]
126126+ pub(crate) fn count_filtered_without_limit(&self, mut group_size: i32) -> i32 {
118127 if let Some(chunks) = self.chunks
119128 && chunks > 0
120129 && chunks < group_size
···135144 }
136145 }
137146147147+ /// Returns the number of light chunks effected by the filter.
148148+ /// # Unknown
149149+ /// If the [`FilterType`] is `Unknown` then the result will be the same as `group_size`.
150150+ #[must_use]
151151+ #[inline]
152152+ #[deprecated(note = "Experimental. Does not consider random in calculations.")]
153153+ #[allow(deprecated)]
154154+ pub fn count_filtered(&self, group_size: i32) -> i32 {
155155+ let filtered = self.count_filtered_without_limit(group_size);
156156+157157+ if let Some(limit) = self.limit_percent {
158158+ (filtered as f32 * limit) as i32
159159+ } else {
160160+ filtered
161161+ }
162162+ }
163163+138164 #[allow(deprecated)]
139165 /// Returns the light chunk ID relative to the [filtered count](Self::count_filtered).
140166 /// # Unknown
141167 /// If the [`FilterType`] is `Unknown` then the result will be the same as `light_id`.
142168 /// # Panics
143169 /// Will panic if the light ID is greater than or equal to the group size.
170170+ // Todo what is the behaviour when the light ID is not in the filter?
144171 #[must_use]
145172 #[inline]
146146- #[deprecated(note = "Experimental. Does not consider random or limit in calculations.")]
173173+ #[deprecated(note = "Experimental. Does not consider random in calculations.")]
147174 pub fn get_relative_index(&self, mut light_id: i32, mut group_size: i32) -> i32 {
148175 assert!(light_id < group_size);
149176···202229);
203230204231loose_enum!(
232232+ /// Controls whether to extend wave distributions so they match the duration before the limit was applied.
233233+ ///
234234+ /// To see this in practice, check out [this video](https://youtube.com/watch?v=NJPPBvyHJjg&t=338).
235235+ ///
236236+ /// Includes the option to only enable for beat distribution and not value distribution, and vice versa.
205237 #[derive(Default, Copy)]
206238 LimitBehaviour: i32 {
207239 #[default]
208240 None = 0,
209209- Duration = 1,
210210- Distribution = 2,
241241+ Beat = 1,
242242+ Value = 2,
243243+ Both = 3,
211244 }
212245);
246246+247247+impl LimitBehaviour {
248248+ /// Returns true if beat limiting is enabled, that is either `Beat` or `Both`.
249249+ pub fn beat_enabled(&self) -> bool {
250250+ matches!(self, LimitBehaviour::Beat | LimitBehaviour::Both)
251251+ }
252252+253253+ /// Returns true if value limiting is enabled, that is either `Value` or `Both`.
254254+ pub fn value_enabled(&self) -> bool {
255255+ matches!(self, LimitBehaviour::Value | LimitBehaviour::Both)
256256+ }
257257+}
213258214259#[allow(deprecated)]
215260#[cfg(test)]
···450495 assert!((0..3).all(|i| filter.get_relative_index(i, 8) == 0));
451496 assert!((3..6).all(|i| filter.get_relative_index(i, 8) == 1));
452497 assert!((6..8).all(|i| filter.get_relative_index(i, 8) == 2));
498498+ }
499499+500500+ #[test]
501501+ fn limit() {
502502+ let filter = Filter {
503503+ limit_percent: Some(0.5),
504504+ ..Default::default()
505505+ };
506506+507507+ assert!((0..6).all(|i| filter.is_in_filter(i, 12)));
508508+ assert!((6..12).all(|i| !filter.is_in_filter(i, 12)));
509509+ assert_eq!(filter.count_filtered(12), 6);
510510+ assert_eq!(filter.count_filtered_without_limit(12), 12);
511511+ assert!((0..6).all(|i| filter.get_relative_index(i, 12) == i));
512512+ }
513513+514514+ #[test]
515515+ fn limit_non_factor_none() {
516516+ let filter = Filter {
517517+ limit_percent: Some(0.01),
518518+ ..Default::default()
519519+ };
520520+521521+ assert!((0..8).all(|i| !filter.is_in_filter(i, 8)));
522522+ assert_eq!(filter.count_filtered(8), 0);
523523+ assert_eq!(filter.count_filtered_without_limit(8), 8);
524524+ }
525525+526526+ #[test]
527527+ fn limit_non_factor_all_but_one() {
528528+ let filter = Filter {
529529+ limit_percent: Some(0.9),
530530+ ..Default::default()
531531+ };
532532+533533+ assert!((0..7).all(|i| filter.is_in_filter(i, 8)));
534534+ assert!(!filter.is_in_filter(7, 8));
535535+ assert_eq!(filter.count_filtered(8), 7);
536536+ assert_eq!(filter.count_filtered_without_limit(8), 8);
537537+ assert!((0..7).all(|i| filter.get_relative_index(i, 8) == i));
453538 }
454539}
+3-3
src/difficulty/lightshow/group.rs
···4444 /// Returns the number of beats that the event will be offset for a given light ID.
4545 /// # Panics
4646 /// Will panic if the light ID is greater than or equal to the group size.
4747- #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")]
4747+ #[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
4848 fn get_beat_offset(&self, light_id: i32, group_size: i32) -> f32;
49495050 /// Returns the value (i.e. brightness) that the event will be offset for a given light ID.
5151 /// # Panics
5252 /// Will panic if the light ID is greater than or equal to the group size.
5353- #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")]
5353+ #[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
5454 fn get_value_offset(&self, light_id: i32, group_size: i32) -> f32;
5555}
5656···71717272 #[allow(deprecated)]
7373 fn get_beat_offset(&self, light_id: i32, group_size: i32) -> f32 {
7474- self.beat_dist_type.compute_offset(
7474+ self.beat_dist_type.compute_beat_offset(
7575 light_id,
7676 group_size,
7777 &self.filter,
+35-2
src/difficulty/lightshow/group/color.rs
···9393 /// Returns the brightness that the event will be offset for a given light ID.
9494 /// # Panics
9595 /// Will panic if the light ID is greater than or equal to the group size.
9696- #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")]
9696+ #[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
9797 #[allow(deprecated)]
9898 pub fn get_brightness_offset(&self, light_id: i32, group_size: i32) -> f32 {
9999- self.bright_dist_type.compute_offset(
9999+ self.bright_dist_type.compute_value_offset(
100100 light_id,
101101 group_size,
102102 &self.filter,
···189189#[cfg(test)]
190190mod tests {
191191 use super::*;
192192+ use crate::LimitBehaviour;
192193 use crate::difficulty::lightshow::filter::FilterType;
193194 use crate::difficulty::lightshow::group::EventGroup;
194195···408409 };
409410410411 assert!((0..12).all(|i| group.get_brightness_offset(i, 12) == 12.0 - i as f32));
412412+ }
413413+414414+ #[test]
415415+ fn beat_wave_with_limit_filter() {
416416+ let group = ColorEventGroup {
417417+ filter: Filter {
418418+ limit_behaviour: Some(LimitBehaviour::Beat),
419419+ limit_percent: Some(0.5),
420420+ ..Default::default()
421421+ },
422422+ beat_dist_type: DistributionType::Wave,
423423+ beat_dist_value: 12.0,
424424+ ..Default::default()
425425+ };
426426+427427+ assert!((0..6).all(|i| group.get_beat_offset(i, 12) == (i * 2) as f32));
428428+ }
429429+430430+ #[test]
431431+ fn brightness_wave_with_limit_filter() {
432432+ let group = ColorEventGroup {
433433+ filter: Filter {
434434+ limit_behaviour: Some(LimitBehaviour::Value),
435435+ limit_percent: Some(0.5),
436436+ ..Default::default()
437437+ },
438438+ bright_dist_type: DistributionType::Wave,
439439+ bright_dist_value: 12.0,
440440+ ..Default::default()
441441+ };
442442+443443+ assert!((0..6).all(|i| group.get_brightness_offset(i, 12) == (i * 2) as f32));
411444 }
412445}
+2-2
src/difficulty/lightshow/group/rotation.rs
···101101 /// Returns the number of degrees that the event will be offset for a given light ID.
102102 /// # Panics
103103 /// Will panic if the light ID is greater than or equal to the group size.
104104- #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")]
104104+ #[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
105105 #[allow(deprecated)]
106106 pub fn get_rotation_offset(&self, light_id: i32, group_size: i32) -> f32 {
107107- self.rotation_dist_type.compute_offset(
107107+ self.rotation_dist_type.compute_value_offset(
108108 light_id,
109109 group_size,
110110 &self.filter,
+2-2
src/difficulty/lightshow/group/translation.rs
···107107 /// Returns the number of units that the event will be offset for a given light ID.
108108 /// # Panics
109109 /// Will panic if the light ID is greater than or equal to the group size.
110110- #[deprecated(note = "Experimental. Does not consider random or limit in filter calculations.")]
110110+ #[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
111111 #[allow(deprecated)]
112112 pub fn get_translation_offset(&self, light_id: i32, group_size: i32) -> f32 {
113113- self.translation_dist_type.compute_offset(
113113+ self.translation_dist_type.compute_value_offset(
114114 light_id,
115115 group_size,
116116 &self.filter,
+6-1
test_maps/README.md
···11-Put testing maps in this directory.11+Put testing maps in this directory.
22+33+The map test is ignored by default, so to run it use:
44+```rust
55+cargo test -- --ignored
66+```
+1
tests/parse_map.rs
···33use std::fs;
4455#[test]
66+#[ignore]
67fn parse_beatmaps() {
78 let paths = fs::read_dir("test_maps").unwrap().filter_map(|result| {
89 if let Ok(dir) = result {