pub mod color;
pub mod fx;
pub mod rotation;
pub mod translation;
#[doc(hidden)]
pub use color::*;
#[doc(hidden)]
pub use fx::*;
#[doc(hidden)]
pub use rotation::*;
#[doc(hidden)]
pub use translation::*;
use crate::difficulty::lightshow::filter::Filter;
use crate::timing_traits::Timed;
pub trait EventBox: Timed {
type Group: EventGroup<Data = Self::Data>;
type Data: EventData;
fn get_groups(&self) -> &Vec<Self::Group>;
}
#[macro_export]
#[doc(hidden)]
macro_rules! impl_event_box {
($ident:ident, $group:ident, $data:ident) => {
impl crate::difficulty::lightshow::group::EventBox for $ident {
type Group = $group;
type Data = $data;
fn get_groups(&self) -> &Vec<Self::Group> {
&self.groups
}
}
};
}
pub trait EventGroup {
type Data: EventData;
fn get_filter(&self) -> &Filter;
fn get_data(&self) -> &Vec<Self::Data>;
#[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
fn get_beat_offset(&self, light_id: i32, group_size: i32) -> f32;
#[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
fn get_value_offset(&self, light_id: i32, group_size: i32) -> f32;
#[deprecated(note = "Experimental. Does not consider random in filter calculations.")]
fn get_duration(&self, group_size: i32) -> f32;
}
#[macro_export]
#[doc(hidden)]
macro_rules! impl_event_group {
($ident:ident::$value_offset:ident, $data:ident) => {
impl crate::difficulty::lightshow::group::EventGroup for $ident {
type Data = $data;
fn get_filter(&self) -> &Filter {
&self.filter
}
fn get_data(&self) -> &Vec<Self::Data> {
&self.data
}
#[allow(deprecated)]
fn get_beat_offset(&self, light_id: i32, group_size: i32) -> f32 {
self.beat_dist_type.compute_beat_offset(
light_id,
group_size,
&self.filter,
self.beat_dist_value,
self.data.last().map(|data| data.beat_offset),
None,
)
}
#[allow(deprecated)]
fn get_value_offset(&self, light_id: i32, group_size: i32) -> f32 {
self.$value_offset(light_id, group_size)
}
#[allow(deprecated)]
fn get_duration(&self, group_size: i32) -> f32 {
let filtered_size = self.filter.count_filtered(group_size);
if filtered_size == 0 {
return 0.0;
}
let Some(data) = self.get_data().last() else {
return 0.0;
};
match self.beat_dist_type {
DistributionType::Wave => {
if let Some(limit_behaviour) = self.filter.limit_behaviour
&& !limit_behaviour.beat_enabled()
&& let Some(limit_percent) = self.filter.limit_percent
&& limit_percent > 0.0
{
(self.beat_dist_value * limit_percent).max(data.beat_offset)
} else {
self.beat_dist_value.max(data.beat_offset)
}
}
DistributionType::Step => {
data.beat_offset + self.beat_dist_value * filtered_size as f32
}
DistributionType::Undefined(_) => data.beat_offset,
}
}
}
};
}
pub trait EventData {
fn get_beat_offset(&self) -> f32;
}
#[macro_export]
#[doc(hidden)]
macro_rules! impl_event_data {
($ident:ident) => {
impl crate::difficulty::lightshow::group::EventData for $ident {
fn get_beat_offset(&self) -> f32 {
self.beat_offset
}
}
};
}
#[allow(deprecated)]
#[cfg(test)]
mod tests {
use super::*;
use crate::{DistributionType, LimitBehaviour};
#[test]
fn get_duration_no_distribution() {
assert_eq!(ColorEventGroup::default().get_duration(12), 0.0);
}
#[test]
fn get_duration_wave() {
let group = ColorEventGroup {
beat_dist_type: DistributionType::Wave,
beat_dist_value: 12.0,
..Default::default()
};
assert_eq!(group.get_duration(12), 12.0);
}
#[test]
fn get_duration_step() {
let group = ColorEventGroup {
beat_dist_type: DistributionType::Step,
beat_dist_value: 1.0,
..Default::default()
};
assert_eq!(group.get_duration(12), 12.0);
}
#[test]
fn get_duration_wave_with_limit() {
let group = ColorEventGroup {
filter: Filter {
limit_behaviour: Some(LimitBehaviour::None),
limit_percent: Some(0.5),
..Default::default()
},
beat_dist_type: DistributionType::Wave,
beat_dist_value: 12.0,
..Default::default()
};
assert_eq!(group.get_duration(12), 6.0);
}
#[test]
fn get_duration_step_with_limit() {
let group = ColorEventGroup {
filter: Filter {
limit_behaviour: Some(LimitBehaviour::None),
limit_percent: Some(0.5),
..Default::default()
},
beat_dist_type: DistributionType::Step,
beat_dist_value: 1.0,
..Default::default()
};
assert_eq!(group.get_duration(12), 6.0);
}
#[test]
fn get_duration_wave_with_limit_adjusted() {
let group = ColorEventGroup {
filter: Filter {
limit_behaviour: Some(LimitBehaviour::Beat),
limit_percent: Some(0.5),
..Default::default()
},
beat_dist_type: DistributionType::Wave,
beat_dist_value: 12.0,
..Default::default()
};
assert_eq!(group.get_duration(12), 12.0);
}
#[test]
fn get_duration_step_with_limit_adjusted() {
let group = ColorEventGroup {
filter: Filter {
limit_behaviour: Some(LimitBehaviour::Beat),
limit_percent: Some(0.5),
..Default::default()
},
beat_dist_type: DistributionType::Step,
beat_dist_value: 1.0,
..Default::default()
};
assert_eq!(group.get_duration(12), 6.0);
}
#[test]
fn get_duration_step_with_limit_zero() {
let group = ColorEventGroup {
filter: Filter {
limit_percent: Some(0.0),
..Default::default()
},
beat_dist_type: DistributionType::Step,
beat_dist_value: 1.0,
..Default::default()
};
assert_eq!(group.get_duration(12), 12.0);
}
}