use super::*;
use crate::patching::definition::{ColorOrder, Model, Photo};
fn pixel(name: &str, x_m: f32) -> Pixel {
Pixel::new(name, [x_m, 0.0, 0.0], ColorOrder::Rgb)
}
fn six() -> FixtureDefinition {
FixtureDefinition {
pixels: ["a", "b", "c", "d", "e", "f"]
.iter()
.enumerate()
.map(|(i, name)| pixel(name, i as f32 * 0.1))
.collect(),
modes: vec![
Mode::individual(6),
Mode {
name: "pairs".to_string(),
group_of: vec![0, 0, 1, 1, 2, 2],
},
],
..Default::default()
}
}
fn names(definition: &FixtureDefinition) -> Vec<&str> {
definition.pixels.iter().map(|p| p.name.as_str()).collect()
}
fn set(indices: &[usize]) -> BTreeSet<usize> {
indices.iter().copied().collect()
}
#[test]
fn normalize_relabels_groups_by_first_appearance() {
let mut mode = Mode {
name: "m".to_string(),
group_of: vec![5, 5, 2, 7],
};
mode.normalize();
assert_eq!(mode.group_of, vec![0, 0, 1, 2]);
}
#[test]
fn a_mode_with_a_gap_in_its_ids_is_no_partition() {
let mode = Mode {
name: "m".to_string(),
group_of: vec![0, 2],
};
assert!(!mode.is_partition_of(2));
}
#[test]
fn inserted_pixels_get_their_own_groups() {
let mut definition = six();
let range = definition.insert_pixels(vec![pixel("g", 0.6), pixel("h", 0.7)]);
assert_eq!(range, 6..8);
assert_eq!(definition.modes[1].group_of, vec![0, 0, 1, 1, 2, 2, 3, 4]);
}
#[test]
fn removing_pixels_keeps_every_mode_a_partition() {
let mut definition = six();
definition.remove_pixels(&set(&[2, 3]));
assert_eq!(names(&definition), ["a", "b", "e", "f"]);
assert_eq!(definition.modes[0].group_of, vec![0, 1, 2, 3]);
assert_eq!(definition.modes[1].group_of, vec![0, 0, 1, 1]);
}
#[test]
fn reordering_moves_the_dmx_offsets_with_the_pixels() {
let mut definition = six();
definition.pixels[5].set_color_order(ColorOrder::Grb);
definition
.reorder(&[5, 0, 1, 2, 3, 4])
.expect("a permutation of six");
assert_eq!(definition.pixels[0].color_order(), Some(ColorOrder::Grb));
assert_eq!(definition.modes[1].group_of, vec![0, 1, 1, 2, 2, 0]);
}
#[test]
fn an_order_that_is_no_permutation_is_refused() {
let mut definition = six();
assert_eq!(
definition.reorder(&[0, 0, 1, 2, 3, 4]),
Err(DefinitionError::NotAPermutation { expected: 6 })
);
}
#[test]
fn reversing_a_subset_leaves_the_others_in_place() {
let mut definition = six();
definition.reverse_pixels(&set(&[1, 2, 4]));
assert_eq!(names(&definition), ["a", "e", "c", "d", "b", "f"]);
}
#[test]
fn moving_to_the_front_keeps_the_relative_order() {
let mut definition = six();
definition.move_to_front(&set(&[4, 1]));
assert_eq!(names(&definition), ["b", "e", "a", "c", "d", "f"]);
}
#[test]
fn duplicates_are_appended_and_grouped_alone() {
let mut definition = six();
let copies = definition.duplicate_pixels(&set(&[0, 1]), [0.0, 0.5, 0.0]);
assert_eq!(copies, 6..8);
assert_eq!(definition.pixels[6].position_m, [0.0, 0.5, 0.0]);
assert_eq!(definition.modes[1].group_of[6..], [3, 4]);
}
#[test]
fn grouping_then_ungrouping_leaves_a_partition() {
let mut definition = six();
let selection = set(&[1, 2]);
definition
.group_selection(1, &selection)
.expect("pairs is editable");
assert_eq!(definition.modes[1].group_of, vec![0, 1, 1, 2, 3, 3]);
definition
.ungroup_selection(1, &selection)
.expect("pairs is editable");
assert!(definition.modes[1].is_partition_of(6));
assert_eq!(definition.modes[1].group_count(), 5);
}
#[test]
fn the_individual_mode_cannot_be_grouped() {
let mut definition = six();
assert_eq!(
definition.group_selection(0, &set(&[0, 1])),
Err(DefinitionError::IndividualMode)
);
}
#[test]
fn the_individual_mode_cannot_be_removed() {
let mut definition = six();
assert_eq!(
definition.remove_mode(0),
Err(DefinitionError::IndividualMode)
);
assert_eq!(definition.remove_mode(1), Ok(()));
assert_eq!(definition.modes.len(), 1);
}
#[test]
fn a_used_model_cannot_be_removed() {
let mut definition = six();
definition.models = vec![Model::circle("led", 0.01), Model::frame("body", [0.6, 0.1])];
definition.outline = Some(1);
assert_eq!(
definition.remove_model(0),
Err(DefinitionError::ModelInUse {
name: "led".to_string(),
pixels: 6
})
);
assert_eq!(definition.remove_model(1), Ok(()));
assert_eq!(definition.outline, None);
}
#[test]
fn fitting_the_size_covers_pixels_and_outline() {
let mut definition = six();
definition.models = vec![Model::circle("led", 0.02), Model::frame("body", [0.2, 0.3])];
definition.outline = Some(1);
definition.fit_size();
assert!((definition.size_m[0] - 1.02).abs() < 1e-5);
assert!((definition.size_m[1] - 0.3).abs() < 1e-5);
}
#[test]
fn centring_puts_the_pixels_around_the_origin() {
let mut definition = six();
definition.centre_pixels();
assert!((definition.pixels[0].position_m[0] + 0.25).abs() < 1e-5);
assert!((definition.pixels[5].position_m[0] - 0.25).abs() < 1e-5);
}
fn photo(offset_m: [f32; 2], size_m: [f32; 2]) -> Photo {
Photo {
file: "lamp.png".into(),
size_m,
offset_m,
opacity: 0.1,
}
}
#[test]
fn centring_the_pixels_keeps_the_photo_under_them() {
let mut definition = six();
definition.photo = Some(photo([0.3, 0.1], [0.8, 0.2]));
definition.centre_pixels();
let photo = definition.photo.expect("a photo");
assert!((photo.offset_m[0] - 0.05).abs() < 1e-5, "{photo:?}");
assert!((photo.offset_m[1] - 0.1).abs() < 1e-5, "{photo:?}");
}
#[test]
fn fitting_the_size_to_the_photo_covers_it_on_both_sides() {
let mut definition = six();
definition.models = vec![Model::circle("led", 0.02)];
definition.photo = Some(photo([0.0, 0.05], [0.4, 0.3]));
definition.fit_size_to_photo();
assert!((definition.size_m[0] - 1.02).abs() < 1e-5);
assert!((definition.size_m[1] - 0.4).abs() < 1e-5);
}
#[test]
fn without_a_photo_fitting_to_it_fits_the_pixels() {
let mut fitted = six();
fitted.fit_size();
let mut to_photo = six();
to_photo.fit_size_to_photo();
assert_eq!(fitted.size_m, to_photo.size_m);
}
fn six_with_modes_that_do_not_fit() -> FixtureDefinition {
let mut definition = six();
definition.modes[1].group_of.truncate(3);
definition.modes.push(Mode {
name: "long".to_string(),
group_of: vec![4, 4, 4, 9, 9, 9, 9, 9],
});
definition
}
#[test]
fn removing_pixels_fits_a_mode_read_too_short() {
let mut definition = six_with_modes_that_do_not_fit();
definition.remove_pixels(&set(&[0]));
assert_eq!(definition.modes[1].group_of, [0, 1, 2, 3, 4]);
assert_eq!(definition.modes[2].group_of, [0, 0, 1, 1, 1]);
}
#[test]
fn grouping_fits_a_mode_read_too_short() {
let mut definition = six_with_modes_that_do_not_fit();
definition
.group_selection(1, &set(&[4, 5]))
.expect("the mode exists");
assert_eq!(definition.modes[1].group_of, [0, 0, 1, 2, 3, 3]);
}
#[test]
fn adding_pixels_fits_every_mode_first() {
let mut definition = six_with_modes_that_do_not_fit();
definition.insert_pixels(vec![pixel("g", 0.6)]);
assert!(
definition
.modes
.iter()
.all(|mode| mode.is_partition_of(definition.pixels.len()))
);
}