gled_model/patching/
color_orders.rs1use crate::collection::Collections;
5use crate::patching::Patch;
6use crate::patching::definition::ColorOrder;
7use serde::{Deserialize, Serialize};
8use std::collections::BTreeMap;
9
10#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
13pub struct ColorOrders {
14 universes: BTreeMap<u16, Vec<ColorOrder>>,
16}
17
18impl ColorOrders {
19 pub fn set(&mut self, universe: u16, lamp: usize, order: ColorOrder) {
22 if order.is_identity() && !self.universes.contains_key(&universe) {
23 return;
24 }
25 let lamps = self.universes.entry(universe).or_default();
26 if lamps.len() <= lamp {
27 lamps.resize(lamp + 1, ColorOrder::Rgb);
28 }
29 lamps[lamp] = order;
30 }
31
32 pub fn order(&self, universe: u16, lamp: usize) -> ColorOrder {
33 self.universes
34 .get(&universe)
35 .and_then(|lamps| lamps.get(lamp))
36 .copied()
37 .unwrap_or_default()
38 }
39
40 pub fn is_empty(&self) -> bool {
41 self.universes.is_empty()
42 }
43
44 pub fn correct(&self, universe: u16, data: &mut [u8]) {
47 let Some(lamps) = self.universes.get(&universe) else {
48 return;
49 };
50 for (triple, order) in data.as_chunks_mut::<3>().0.iter_mut().zip(lamps) {
51 order.correct(triple);
52 }
53 }
54}
55
56impl Patch {
57 pub fn color_orders(&self, collections: &Collections) -> ColorOrders {
61 let mut orders = ColorOrders::default();
62 for (id, address) in self.addresses(collections) {
63 let Some(definition) = self.definition(id, collections) else {
64 continue;
65 };
66 let definition = &definition.data;
67 for (index, pixel) in definition.pixels.iter().enumerate() {
68 let Some(order) = pixel.color_order() else {
69 continue;
70 };
71 let lamp = address.lamp_index(definition.channel_offset(index));
72 orders.set(address.universe(), lamp, order);
73 }
74 }
75 orders
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::asset_id::AssetId;
83 use crate::patching::definition::FixtureDefinition;
84 use crate::patching::fixture::{Fixture, FixtureId, LayoutPosition};
85 use crate::test_support::{collections_with, rgb_bar};
86
87 fn bar(pixel_count: usize) -> FixtureDefinition {
89 rgb_bar(pixel_count, pixel_count as f32 * 0.1)
90 }
91
92 fn add(patch: &mut Patch, definition: AssetId<FixtureDefinition>) -> FixtureId {
93 patch.add_fixture(
94 Fixture::with_definition("bar", definition),
95 LayoutPosition::default(),
96 )
97 }
98
99 #[test]
100 fn a_grb_pixel_puts_its_order_into_its_lamp_slot() {
101 let mut definition = bar(4);
102 definition.pixels[2].set_color_order(ColorOrder::Grb);
103 let (collections, ids) = collections_with(vec![definition]);
104 let mut patch = Patch::default();
105 add(&mut patch, ids[0]);
106
107 let orders = patch.color_orders(&collections);
108
109 assert_eq!(orders.order(0, 2), ColorOrder::Grb);
110 assert_eq!(orders.order(0, 0), ColorOrder::Rgb);
111 }
112
113 #[test]
114 fn color_orders_follow_the_chain_address() {
115 let mut grb = bar(4);
116 grb.pixels[0].set_color_order(ColorOrder::Grb);
117 let (collections, ids) = collections_with(vec![bar(4), grb]);
118 let mut patch = Patch::default();
119 let head = add(&mut patch, ids[0]);
120 let tail = add(&mut patch, ids[1]);
121 patch.topology.make_successor(head, tail);
122
123 assert_eq!(
125 patch.color_orders(&collections).order(0, 4),
126 ColorOrder::Grb
127 );
128 }
129
130 #[test]
131 fn an_rgb_patch_needs_no_reordering() {
132 let (collections, ids) = collections_with(vec![bar(4)]);
133 let mut patch = Patch::default();
134 add(&mut patch, ids[0]);
135 assert!(patch.color_orders(&collections).is_empty());
136 }
137
138 #[test]
139 fn correcting_a_universe_touches_only_its_permuted_lamps() {
140 let mut orders = ColorOrders::default();
141 orders.set(0, 2, ColorOrder::Grb);
142 let mut data = [0u8; 512];
143 data[0..3].copy_from_slice(&[1, 2, 3]);
144 data[6..9].copy_from_slice(&[1, 2, 3]);
145
146 let mut other_universe = data;
147 orders.correct(0, &mut data);
148 orders.correct(1, &mut other_universe);
149
150 assert_eq!(&data[0..3], &[1, 2, 3]);
151 assert_eq!(&data[6..9], &[2, 1, 3]);
152 assert_eq!(&other_universe[6..9], &[1, 2, 3]);
153 }
154}