gled_model/patching/
geometry.rs1use crate::patching::definition::FixtureDefinition;
4use crate::patching::fixture::{LayoutPosition, UNITS_PER_METRE};
5use emath::Rot2;
6use emath::{Pos2, Vec2, pos2, vec2};
7
8impl LayoutPosition {
9 pub fn scaled(&self, metres: [f32; 2]) -> Vec2 {
13 vec2(metres[0] * self.scale[0], metres[1] * self.scale[1]) * UNITS_PER_METRE
14 }
15
16 fn scene_offset(&self, offset_m: [f32; 3]) -> Vec2 {
19 Rot2::from_angle(self.angle) * self.scaled([offset_m[0], offset_m[1]])
20 }
21}
22
23impl FixtureDefinition {
24 pub fn pixel_scene_positions(&self, position: &LayoutPosition) -> Vec<Pos2> {
26 self.pixels
27 .iter()
28 .map(|pixel| position.pos + position.scene_offset(pixel.position_m))
29 .collect()
30 }
31}
32
33pub fn centroid(positions: &[Pos2]) -> Option<Pos2> {
35 if positions.is_empty() {
36 return None;
37 }
38 let sum = positions
39 .iter()
40 .fold(Vec2::ZERO, |acc, pos| acc + pos.to_vec2());
41 Some(pos2(
42 sum.x / positions.len() as f32,
43 sum.y / positions.len() as f32,
44 ))
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use crate::test_support::rgb_bar as bar;
51
52 #[test]
53 fn a_one_metre_bar_spans_one_hundred_units() {
54 let definition = bar(2, 1.0);
55 let position = LayoutPosition::at(pos2(0.0, 0.0));
56 let positions = definition.pixel_scene_positions(&position);
57 assert_eq!(positions[0], pos2(-25.0, 0.0));
58 assert_eq!(positions[1], pos2(25.0, 0.0));
59 assert_eq!(positions[1].x - positions[0].x, 50.0);
61 }
62
63 #[test]
64 fn scale_stretches_per_axis() {
65 let definition = bar(2, 1.0);
66 let position = LayoutPosition::new(pos2(0.0, 0.0), 0.0, [2.0, 1.0]);
67 let positions = definition.pixel_scene_positions(&position);
68 assert_eq!(positions[0], pos2(-50.0, 0.0));
69 assert_eq!(positions[1], pos2(50.0, 0.0));
70 }
71
72 #[test]
73 fn angle_rotates_the_pixels_about_the_origin() {
74 let definition = bar(2, 1.0);
75 let position =
76 LayoutPosition::new(pos2(10.0, 10.0), std::f32::consts::FRAC_PI_2, [1.0, 1.0]);
77 let positions = definition.pixel_scene_positions(&position);
78 assert!((positions[0].x - 10.0).abs() < 1e-3);
80 assert!((positions[0].y - -15.0).abs() < 1e-3);
81 assert!((positions[1].y - 35.0).abs() < 1e-3);
82 }
83
84 #[test]
85 fn centroid_of_a_group_is_its_midpoint() {
86 let positions = [pos2(0.0, 0.0), pos2(10.0, 20.0)];
87 assert_eq!(centroid(&positions), Some(pos2(5.0, 10.0)));
88 assert_eq!(centroid(&[]), None);
89 }
90}