gled_model/patching/definition/
edit.rs1use crate::patching::definition::{FixtureDefinition, Mode, Pixel, group_ids};
5use std::collections::BTreeSet;
6use std::ops::Range;
7
8#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
10pub enum DefinitionError {
11 #[error("the new order is not a permutation of the {expected} pixels")]
12 NotAPermutation { expected: usize },
13 #[error("there is no mode {0}")]
14 NoSuchMode(usize),
15 #[error("the first mode, \"individual\", gives every pixel its own group and cannot change")]
16 IndividualMode,
17 #[error("there is no model {0}")]
18 NoSuchModel(usize),
19 #[error("\"{name}\" still draws {pixels} pixels; give them another model first")]
20 ModelInUse { name: String, pixels: usize },
21}
22
23impl Mode {
24 pub(crate) fn normalize(&mut self) {
28 self.group_of = group_ids(self.group_of.iter().copied());
29 }
30
31 pub fn is_partition_of(&self, pixel_count: usize) -> bool {
34 let ids: BTreeSet<usize> = self.group_of.iter().copied().collect();
35 self.group_of.len() == pixel_count && ids.iter().copied().eq(0..ids.len())
36 }
37
38 fn fit(&mut self, pixel_count: usize) {
42 self.group_of.truncate(pixel_count);
43 self.extend_individual(pixel_count - self.group_of.len());
44 self.normalize();
45 }
46
47 fn extend_individual(&mut self, added: usize) {
49 let next = self.group_of.iter().max().map_or(0, |max| max + 1);
50 self.group_of.extend(next..next + added);
51 }
52
53 fn remap(&mut self, order: &[usize]) {
56 self.group_of = order.iter().map(|old| self.group_of[*old]).collect();
57 self.normalize();
58 }
59}
60
61impl FixtureDefinition {
62 pub fn insert_pixels(&mut self, pixels: Vec<Pixel>) -> Range<usize> {
65 self.fit_modes();
66 let start = self.pixels.len();
67 let added = pixels.len();
68 self.pixels.extend(pixels);
69 for mode in &mut self.modes {
70 mode.extend_individual(added);
71 }
72 self.debug_check();
73 start..start + added
74 }
75
76 pub fn remove_pixels(&mut self, indices: &BTreeSet<usize>) {
77 let keep: Vec<usize> = (0..self.pixels.len())
78 .filter(|index| !indices.contains(index))
79 .collect();
80 self.apply_order(&keep);
81 }
82
83 pub fn reorder(&mut self, order: &[usize]) -> Result<(), DefinitionError> {
86 let unique: BTreeSet<usize> = order.iter().copied().collect();
87 if order.len() != self.pixels.len() || !unique.iter().copied().eq(0..self.pixels.len()) {
88 return Err(DefinitionError::NotAPermutation {
89 expected: self.pixels.len(),
90 });
91 }
92 self.apply_order(order);
93 Ok(())
94 }
95
96 pub fn reverse_pixels(&mut self, indices: &BTreeSet<usize>) {
99 let slots: Vec<usize> = self.existing(indices).collect();
100 let mut order: Vec<usize> = (0..self.pixels.len()).collect();
101 for (slot, source) in slots.iter().zip(slots.iter().rev()) {
102 order[*slot] = *source;
103 }
104 self.apply_order(&order);
105 }
106
107 pub fn move_to_front(&mut self, indices: &BTreeSet<usize>) {
110 let selected: Vec<usize> = self.existing(indices).collect();
111 let order: Vec<usize> = selected
112 .iter()
113 .copied()
114 .chain((0..self.pixels.len()).filter(|index| !indices.contains(index)))
115 .collect();
116 self.apply_order(&order);
117 }
118
119 pub fn duplicate_pixels(
122 &mut self,
123 indices: &BTreeSet<usize>,
124 offset_m: [f32; 3],
125 ) -> Range<usize> {
126 let copies = self
127 .existing(indices)
128 .map(|index| {
129 let mut copy = self.pixels[index].clone();
130 copy.name = format!("{} copy", copy.name);
131 for (axis, offset) in copy.position_m.iter_mut().zip(offset_m) {
132 *axis += offset;
133 }
134 copy
135 })
136 .collect();
137 self.insert_pixels(copies)
138 }
139
140 pub fn set_model_of(
141 &mut self,
142 indices: &BTreeSet<usize>,
143 model: usize,
144 ) -> Result<(), DefinitionError> {
145 if model >= self.models.len() {
146 return Err(DefinitionError::NoSuchModel(model));
147 }
148 for index in self.existing(indices).collect::<Vec<_>>() {
149 self.pixels[index].model = model;
150 }
151 Ok(())
152 }
153
154 pub fn remove_model(&mut self, model: usize) -> Result<(), DefinitionError> {
156 if model >= self.models.len() {
157 return Err(DefinitionError::NoSuchModel(model));
158 }
159 let users = self
160 .pixels
161 .iter()
162 .filter(|pixel| pixel.model == model)
163 .count();
164 if users > 0 {
165 return Err(DefinitionError::ModelInUse {
166 name: self.models[model].name.clone(),
167 pixels: users,
168 });
169 }
170 self.models.remove(model);
171 for pixel in &mut self.pixels {
172 if pixel.model > model {
173 pixel.model -= 1;
174 }
175 }
176 self.outline = match self.outline {
177 Some(outline) if outline == model => None,
178 Some(outline) if outline > model => Some(outline - 1),
179 outline => outline,
180 };
181 Ok(())
182 }
183
184 pub fn pixel_bounds_m(&self) -> Option<([f32; 2], [f32; 2])> {
187 let mut bounds: Option<([f32; 2], [f32; 2])> = None;
188 for pixel in &self.pixels {
189 let half = self.model(pixel.model).map_or([0.0; 2], |model| {
190 [model.size_m[0] / 2.0, model.size_m[1] / 2.0]
191 });
192 let (sin, cos) = pixel.rotation.sin_cos();
193 let extent = [
195 (half[0] * cos).abs() + (half[1] * sin).abs(),
196 (half[0] * sin).abs() + (half[1] * cos).abs(),
197 ];
198 let [x, y, _] = pixel.position_m;
199 let low = [x - extent[0], y - extent[1]];
200 let high = [x + extent[0], y + extent[1]];
201 bounds = Some(match bounds {
202 None => (low, high),
203 Some((min, max)) => (
204 [min[0].min(low[0]), min[1].min(low[1])],
205 [max[0].max(high[0]), max[1].max(high[1])],
206 ),
207 });
208 }
209 bounds
210 }
211
212 pub fn fit_size(&mut self) {
216 let pixels = self.pixel_bounds_m().map_or([0.0; 2], |(min, max)| {
217 [
218 min[0].abs().max(max[0].abs()),
219 min[1].abs().max(max[1].abs()),
220 ]
221 });
222 let outline = self
223 .outline
224 .and_then(|outline| self.model(outline))
225 .map_or([0.0; 2], |outline| outline.size_m.map(|size| size / 2.0));
226 self.size_m[0] = 2.0 * pixels[0].max(outline[0]);
227 self.size_m[1] = 2.0 * pixels[1].max(outline[1]);
228 }
229
230 pub fn fit_size_to_photo(&mut self) {
234 self.fit_size();
235 if let Some(photo) = &self.photo {
236 for (size, reach) in self.size_m.iter_mut().zip(photo.reach_m()) {
237 *size = size.max(2.0 * reach);
238 }
239 }
240 }
241
242 pub fn centre_pixels(&mut self) {
246 let Some((min, max)) = self.pixel_bounds_m() else {
247 return;
248 };
249 let centre = [(min[0] + max[0]) / 2.0, (min[1] + max[1]) / 2.0];
250 for pixel in &mut self.pixels {
251 pixel.position_m[0] -= centre[0];
252 pixel.position_m[1] -= centre[1];
253 }
254 if let Some(photo) = &mut self.photo {
255 photo.offset_m[0] -= centre[0];
256 photo.offset_m[1] -= centre[1];
257 }
258 }
259
260 pub fn add_mode(&mut self, name: impl Into<String>) -> usize {
263 let mut mode = Mode::individual(self.pixels.len());
264 mode.name = name.into();
265 self.modes.push(mode);
266 self.modes.len() - 1
267 }
268
269 pub fn remove_mode(&mut self, index: usize) -> Result<(), DefinitionError> {
273 self.editable_mode(index)?;
274 self.modes.remove(index);
275 Ok(())
276 }
277
278 pub fn group_selection(
280 &mut self,
281 mode: usize,
282 pixels: &BTreeSet<usize>,
283 ) -> Result<(), DefinitionError> {
284 let selected: Vec<usize> = self.existing(pixels).collect();
285 let mode = self.editable_mode(mode)?;
286 let joined = mode.group_of.iter().max().map_or(0, |max| max + 1);
288 for pixel in selected {
289 mode.group_of[pixel] = joined;
290 }
291 mode.normalize();
292 Ok(())
293 }
294
295 pub fn ungroup_selection(
297 &mut self,
298 mode: usize,
299 pixels: &BTreeSet<usize>,
300 ) -> Result<(), DefinitionError> {
301 let selected: Vec<usize> = self.existing(pixels).collect();
302 let mode = self.editable_mode(mode)?;
303 let next = mode.group_of.iter().max().map_or(0, |max| max + 1);
304 for (offset, pixel) in selected.into_iter().enumerate() {
305 mode.group_of[pixel] = next + offset;
306 }
307 mode.normalize();
308 Ok(())
309 }
310
311 pub fn each_own_group(&mut self, mode: usize) -> Result<(), DefinitionError> {
313 let pixel_count = self.pixels.len();
314 let mode = self.editable_mode(mode)?;
315 mode.group_of = (0..pixel_count).collect();
316 Ok(())
317 }
318
319 pub fn next_pixel_number(&self) -> usize {
321 self.pixels.len()
322 }
323
324 fn editable_mode(&mut self, index: usize) -> Result<&mut Mode, DefinitionError> {
326 if index == self.default_mode() {
327 return Err(DefinitionError::IndividualMode);
328 }
329 self.fit_modes();
330 self.modes
331 .get_mut(index)
332 .ok_or(DefinitionError::NoSuchMode(index))
333 }
334
335 fn existing<'a>(&self, indices: &'a BTreeSet<usize>) -> impl Iterator<Item = usize> + 'a {
337 let pixel_count = self.pixels.len();
338 indices
339 .iter()
340 .copied()
341 .filter(move |index| *index < pixel_count)
342 }
343
344 fn apply_order(&mut self, order: &[usize]) {
347 self.fit_modes();
348 self.pixels = order.iter().map(|old| self.pixels[*old].clone()).collect();
349 for mode in &mut self.modes {
350 mode.remap(order);
351 }
352 self.debug_check();
353 }
354
355 fn fit_modes(&mut self) {
358 let pixel_count = self.pixels.len();
359 for mode in &mut self.modes {
360 mode.fit(pixel_count);
361 }
362 }
363
364 fn debug_check(&self) {
365 debug_assert!(
366 self.modes
367 .iter()
368 .all(|mode| mode.is_partition_of(self.pixels.len())),
369 "every mode must stay a partition of the pixels"
370 );
371 }
372}
373
374#[cfg(test)]
375mod tests;