use macroquad::prelude::{Color, Vec3};
pub fn map_color_decay(orig: Color, current: f32, total: f32) -> Color {
Color::new(orig.r, orig.g, orig.b, orig.a * (1.0 - (current / total)))
}
pub fn map_float_value(values: &[f32], elapsed: f32, total: f32) -> Result<f32, String> {
let ratio = elapsed / total;
let len = values.len() - 1;
let vratio = len as f32 * ratio;
let low = (vratio.floor()) as usize;
let high = (vratio.ceil()) as usize;
let low = if low > len { len } else { low };
let high = if high > len { len } else { high };
let first_value = match values.get(low) {
Some(val) => val,
None => {
return Err(format!(
"map_float_values indexing error: {} of {}",
low, len
));
}
};
if low == high {
Ok(*first_value)
} else {
match values.get(high) {
Some(val) => {
let vratio_norm = high as f32 - vratio;
Ok((first_value * vratio_norm) + (val * (1.0 - vratio_norm)))
}
None => Err(format!(
"map_float_values indexing error: {} of {}",
high, len
)),
}
}
}
#[test]
fn map_float_value_test() {
let values = vec![0.0, 1.0];
assert_eq!(map_float_value(&values, 0.0, 1.0).unwrap_or(-1.0), 0.0);
assert_eq!(
map_float_value(&values, 2.0 / 3.0, 1.0).unwrap_or(-1.0),
2.0 / 3.0
);
let values = vec![1.0, 0.0, 0.5, 0.0];
assert_eq!(map_float_value(&values, 0.5, 1.0).unwrap_or(-1.0), 0.25);
}
pub fn map_color_value(
colors: &[Color],
elapsed: f32,
total: f32,
) -> Result<(f32, f32, f32, f32), String> {
let ratio = elapsed / total;
let len = colors.len() - 1;
let vratio = len as f32 * ratio;
let low = ((len as f32 * ratio).floor()) as usize;
let high = ((len as f32 * ratio).ceil()) as usize;
let low = if low > len { len } else { low };
let high = if high > len { len } else { high };
let first_value = match colors.get(low) {
Some(val) => val,
None => {
return Err(format!(
"map_color_value indexing error: {} of {}",
low, len
));
}
};
if low == high {
Ok((first_value.r, first_value.g, first_value.b, first_value.a))
} else {
match colors.get(high) {
Some(val) => {
let vratio_norm = high as f32 - vratio;
Ok((
(first_value.r * vratio_norm) + (val.r * (1.0 - vratio_norm)),
(first_value.g * vratio_norm) + (val.g * (1.0 - vratio_norm)),
(first_value.b * vratio_norm) + (val.b * (1.0 - vratio_norm)),
(first_value.a * vratio_norm) + (val.a * (1.0 - vratio_norm)),
))
}
None => Err(format!(
"map_color_value indexing error: {} of {}",
high, len
)),
}
}
}
#[test]
fn map_color_value_test() {
let values = vec![
Color::new(1.0, 1.0, 1.0, 1.0),
Color::new(0.0, 0.0, 0.0, 1.0),
];
assert_eq!(
map_color_value(&values, 0.5, 1.0).unwrap(),
(0.5, 0.5, 0.5, 1.0)
);
assert_eq!(
map_color_value(&values, 0.2, 1.0).unwrap(),
(0.8, 0.8, 0.8, 1.0)
);
}
pub fn map_location(
locations: &[f32],
start_location: Vec3,
end_location: Vec3,
elapsed: f32,
period: f32,
) -> Result<(f32, f32, f32), String> {
let ratio = map_float_value(locations, elapsed, period)?;
let vratio = Vec3::new(ratio, ratio, ratio);
let v = (start_location * vratio) + ((Vec3::ONE - vratio) * end_location);
Ok(v.into())
}
pub fn check_period(period: f32) -> Result<(), String> {
match period {
p if p >= 0. => Ok(()),
p => Err(format!(
"value error: {} period should be positive value",
p
)),
}
}
#[test]
fn test_check_period() {
assert_eq!(
check_period(-0.5),
Err(String::from(
"value error: -0.5 period should be positive value"
))
);
}
pub fn check_decay(decay: f32) -> Result<(), String> {
match decay {
d if d >= 0. => Ok(()),
d => Err(format!("value error: {} decay should be positive value", d)),
}
}
#[test]
fn test_check_decay() {
assert_eq!(
check_decay(-0.5),
Err(String::from(
"value error: -0.5 decay should be positive value"
))
);
}
pub fn check_locations(locations: &[f32]) -> Result<(), String> {
if locations.is_empty() {
return Err(String::from("empty: argument 'locations' cannot be empty"));
}
for l in locations.iter() {
if *l > 1. || *l < 0. {
return Err(format!(
"value error: {} location interpolation should be between 0 and 1 inclusive",
*l
));
};
}
Ok(())
}
#[test]
fn test_check_locations() {
assert_eq!(
check_locations(&Vec::new()),
Err(String::from("empty: argument 'locations' cannot be empty"))
);
assert_eq!(
check_locations(&vec!(2.)),
Err(String::from(
"value error: 2 location interpolation should be between 0 and 1 inclusive"
))
);
}
pub fn check_densities(densities: &[f32]) -> Result<(), String> {
if densities.is_empty() {
return Err(String::from("empty: argument 'densities' cannot be empty"));
}
for d in densities.iter() {
if *d > 1. || *d < 0. {
return Err(format!(
"value error: {} density value should be between 0 and 1 inclusive",
*d
));
};
}
Ok(())
}
#[test]
fn test_check_densities() {
assert_eq!(
check_densities(&Vec::new()),
Err(String::from("empty: argument 'densities' cannot be empty"))
);
assert_eq!(
check_densities(&vec!(2.)),
Err(String::from(
"value error: 2 density value should be between 0 and 1 inclusive"
))
);
}
pub fn check_colors(colors: &[Color]) -> Result<(), String> {
if colors.is_empty() {
return Err(String::from("empty: argument 'colors' cannot be empty"));
}
Ok(())
}
#[test]
fn test_check_colors() {
assert_eq!(
check_colors(&Vec::new()),
Err(String::from("empty: argument 'colors' cannot be empty"))
);
}