1use std::collections::HashSet;
2use std::sync::atomic::{AtomicUsize, Ordering};
3
4use rand::{thread_rng, Rng};
5use serde::{Deserialize, Serialize};
6
7use crate::line::Line;
8use crate::utils;
9
10static COUNTER: AtomicUsize = AtomicUsize::new(0);
11
12#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
13pub struct Peg {
15 pub x: u32,
17 pub y: u32,
19 pub id: usize,
21}
22
23impl Peg {
24 pub fn new(x: u32, y: u32) -> Self {
26 let id = COUNTER.fetch_add(1, Ordering::SeqCst);
27 Self { x, y, id }
28 }
29
30 pub fn line_to(&self, other: &Peg, width: u32, min_max: Option<(u32, u32, u32, u32)>) -> Line {
45 let mut pixels = HashSet::new();
46 let half_width = width as i32 / 2;
47 let (x_min, x_max, y_min, y_max): (i32, i32, i32, i32) = match min_max {
48 Some((x_min, x_max, y_min, y_max)) => {
49 (x_min as i32, x_max as i32, y_min as i32, y_max as i32)
50 }
51 None => (0, i32::MAX, 0, i32::MAX),
52 };
53
54 let dx = self.x.abs_diff(other.x) as i32;
56 let dy = self.y.abs_diff(other.y) as i32;
57 let sx = if self.x < other.x { 1 } else { -1 };
58 let sy = if self.y < other.y { 1 } else { -1 };
59 let mut err = dx - dy;
60
61 let mut x = self.x as i32;
62 let mut y = self.y as i32;
63
64 let steps = dx.max(dy);
66
67 for _ in 0..=steps {
69 for ox in -(half_width)..=(half_width) {
71 for oy in -(half_width)..=(half_width) {
72 pixels.insert(((x + ox).clamp(x_min, x_max), (y + oy).clamp(y_min, y_max)));
73 }
74 }
75 if x == other.x as i32 && y == other.y as i32 {
76 break;
77 }
78 let e2 = 2 * err;
79 if e2 > -dy {
81 err -= dy;
82 x += sx;
83 }
84 if e2 < dx {
86 err += dx;
87 y += sy;
88 }
89 }
90
91 let (x_vec, y_vec): (Vec<u32>, Vec<u32>) = pixels
93 .into_iter()
94 .map(|(x, y)| (x as u32, y as u32))
95 .unzip();
96 Line::new(x_vec, y_vec, self.dist_to(other))
97 }
98
99 pub fn around(&self, radius: u32) -> (Vec<u32>, Vec<u32>) {
105 utils::pixels_around((self.x, self.y), radius)
106 }
107
108 pub fn dist_to(&self, other: &Peg) -> u32 {
110 let delta_x = utils::abs_diff(self.x, other.x);
111 let delta_y = utils::abs_diff(self.y, other.y);
112 ((delta_x * delta_x + delta_y * delta_y) as f64)
113 .sqrt()
114 .round() as u32
115 }
116
117 pub fn with_jitter(&self, jitter: i64) -> Self {
132 let mut rng = thread_rng();
133 Self {
134 x: (self.x as i64 + rng.gen_range(-jitter..jitter)) as u32,
135 y: (self.y as i64 + rng.gen_range(-jitter..jitter)) as u32,
136 id: self.id,
137 }
138 }
139}
140
141pub mod shape {
143 use super::*;
144
145 fn coords_to_pegs(coords: (Vec<u32>, Vec<u32>)) -> Vec<Peg> {
146 coords
147 .0
148 .into_iter()
149 .zip(coords.1)
150 .map(|(x, y)| Peg::new(x, y))
151 .collect()
152 }
153
154 pub fn square(top_left: (u32, u32), length: u32, n_pegs: usize) -> Vec<Peg> {
162 coords_to_pegs(utils::square_coords(top_left, length, n_pegs))
163 }
164
165 pub fn rectangle(top_left: (u32, u32), width: u32, height: u32, n_pegs: usize) -> Vec<Peg> {
174 coords_to_pegs(utils::rectangle_coords(top_left, width, height, n_pegs))
175 }
176
177 pub fn circle(center: (u32, u32), radius: u32, n_pegs: usize) -> Vec<Peg> {
185 coords_to_pegs(utils::circle_coords(center, radius, n_pegs))
186 }
187
188 pub fn line(start: (u32, u32), end: (u32, u32), n_pegs: usize) -> Vec<Peg> {
196 coords_to_pegs(utils::line_coords(start, end, n_pegs))
197 }
198}
199
200#[derive(Debug, Clone)]
201pub struct Yarn {
204 pub width: f32,
206 pub opacity: f64,
208 pub color: (u8, u8, u8),
210}
211
212impl Default for Yarn {
213 fn default() -> Self {
214 Self {
215 width: 1.,
216 opacity: 0.2,
217 color: (0, 0, 0),
218 }
219 }
220}
221
222impl Yarn {
223 pub fn new(width: f32, opacity: f64, color: (u8, u8, u8)) -> Self {
225 Self {
226 width,
227 opacity,
228 color,
229 }
230 }
231
232 pub fn set_color(&mut self, color: (u8, u8, u8)) {
233 self.color = color
234 }
235}
236
237#[cfg(test)]
238mod test {
239 use super::*;
240
241 #[test]
242 fn peg_line_to() {
243 let peg_a = Peg::new(0, 0);
244 let peg_b = Peg::new(1, 1);
245 let mut line = peg_a.line_to(&peg_b, 1, None);
246 line.x.sort();
247 line.y.sort();
248
249 assert_eq!(line.x, vec![0, 1]);
250 assert_eq!(line.y, vec![0, 1]);
251 assert_eq!(line.dist, f32::sqrt(2.0) as u32);
252
253 let peg_a = Peg::new(1, 1);
254 let peg_b = Peg::new(0, 0);
255 let mut line = peg_a.line_to(&peg_b, 1, None);
256 line.x.sort();
257 line.y.sort();
258 assert_eq!(line.x, vec![0, 1]);
259 assert_eq!(line.y, vec![0, 1]);
260 assert_eq!(line.dist, f32::sqrt(2.0) as u32);
261
262 let peg_a = Peg::new(0, 1);
264 let peg_b = Peg::new(3, 1);
265 let mut line = peg_a.line_to(&peg_b, 1, None);
266 line.x.sort();
267 line.y.sort();
268 assert_eq!(line.x, vec![0, 1, 2, 3]);
269 assert_eq!(line.y, vec![1, 1, 1, 1]);
270 assert_eq!(line.dist, 3);
271
272 let peg_a = Peg::new(0, 0);
274 let peg_b = Peg::new(0, 1);
275 let mut line = peg_a.line_to(&peg_b, 1, None);
276 line.x.sort();
277 line.y.sort();
278 assert_eq!(line.x, vec![0, 0]);
279 assert_eq!(line.y, vec![0, 1]);
280 assert_eq!(line.dist, 1);
281 assert_eq!(line.zip().len(), 2);
282 }
283
284 #[test]
285 fn peg_line_to_width() {
286 let peg_a = Peg::new(5, 5);
287 let peg_b = Peg::new(5, 5);
288 let line = peg_a.line_to(&peg_b, 0, None);
289 assert_eq!(line.x, vec![5]);
290 assert_eq!(line.y, vec![5]);
291 let line = peg_a.line_to(&peg_b, 1, None);
292 assert_eq!(line.x, vec![5]);
293 assert_eq!(line.y, vec![5]);
294 assert_eq!(line.dist, 0);
295 let line = peg_a.line_to(&peg_b, 2, None);
296 assert_eq!(*line.x.iter().max().unwrap(), 6);
298 assert_eq!(*line.x.iter().min().unwrap(), 4);
299 assert_eq!(*line.y.iter().max().unwrap(), 6);
300 assert_eq!(*line.y.iter().min().unwrap(), 4);
301 assert_eq!(line.dist, 0);
302 let line = peg_a.line_to(&peg_b, 3, None);
303 assert_eq!(*line.x.iter().max().unwrap(), 6);
305 assert_eq!(*line.x.iter().min().unwrap(), 4);
306 assert_eq!(*line.y.iter().max().unwrap(), 6);
307 assert_eq!(*line.y.iter().min().unwrap(), 4);
308 assert_eq!(line.dist, 0);
309 let line = peg_a.line_to(&peg_b, 4, None);
310 assert_eq!(*line.x.iter().max().unwrap(), 7);
312 assert_eq!(*line.x.iter().min().unwrap(), 3);
313 assert_eq!(*line.y.iter().max().unwrap(), 7);
314 assert_eq!(*line.y.iter().min().unwrap(), 3);
315 assert_eq!(line.dist, 0);
316
317 let peg_a = Peg::new(5, 5);
318 let peg_b = Peg::new(6, 6);
319 let line = peg_a.line_to(&peg_b, 2, None);
320 assert_eq!(*line.x.iter().min().unwrap(), 4);
321 assert_eq!(*line.x.iter().max().unwrap(), 7);
322 assert_eq!(*line.y.iter().min().unwrap(), 4);
323 assert_eq!(*line.y.iter().max().unwrap(), 7);
324 assert_eq!(line.dist, 1);
325 }
326
327 #[test]
328 fn peg_line_to_width_min_max() {
329 let peg_a = Peg::new(0, 5);
330 let peg_b = Peg::new(10, 5);
331 let line = peg_a.line_to(&peg_b, 2, Some((0, 10, 3, 7)));
332 assert_eq!(*line.x.iter().max().unwrap(), 10);
333 assert_eq!(*line.x.iter().min().unwrap(), 0);
334
335 assert_eq!(*line.y.iter().max().unwrap(), 6);
336 assert_eq!(*line.y.iter().min().unwrap(), 4);
337 }
338
339 #[test]
340 fn peg_around() {
341 let peg = Peg::new(10, 10);
342 let (x_coords, y_coords) = peg.around(1);
343 assert_eq!(x_coords, vec![9, 10, 10, 10, 11]);
344 assert_eq!(y_coords, vec![10, 9, 10, 11, 10]);
345 }
346
347 #[test]
348 fn peg_jitter() {
349 let peg = Peg::new(10, 10);
350 let jitter = 2;
351 let peg_jitter = peg.with_jitter(jitter);
352 assert!(peg_jitter.x <= (peg.x as i64 + jitter) as u32);
353 assert!(peg_jitter.x >= (peg.x as i64 - jitter) as u32);
354 assert_eq!(peg_jitter.id, peg.id);
355 }
356}