use std::collections::HashSet;
use std::sync::atomic::{AtomicUsize, Ordering};
use rand::{thread_rng, Rng};
use serde::{Deserialize, Serialize};
use crate::line::Line;
use crate::utils;
static COUNTER: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub struct Peg {
pub x: u32,
pub y: u32,
pub id: usize,
}
impl Peg {
pub fn new(x: u32, y: u32) -> Self {
let id = COUNTER.fetch_add(1, Ordering::SeqCst);
Self { x, y, id }
}
pub fn line_to(&self, other: &Peg, width: u32, min_max: Option<(u32, u32, u32, u32)>) -> Line {
let mut pixels = HashSet::new();
let half_width = width as i32 / 2;
let (x_min, x_max, y_min, y_max): (i32, i32, i32, i32) = match min_max {
Some((x_min, x_max, y_min, y_max)) => {
(x_min as i32, x_max as i32, y_min as i32, y_max as i32)
}
None => (0, i32::MAX, 0, i32::MAX),
};
let dx = self.x.abs_diff(other.x) as i32;
let dy = self.y.abs_diff(other.y) as i32;
let sx = if self.x < other.x { 1 } else { -1 };
let sy = if self.y < other.y { 1 } else { -1 };
let mut err = dx - dy;
let mut x = self.x as i32;
let mut y = self.y as i32;
let steps = dx.max(dy);
for _ in 0..=steps {
for ox in -(half_width)..=(half_width) {
for oy in -(half_width)..=(half_width) {
pixels.insert(((x + ox).clamp(x_min, x_max), (y + oy).clamp(y_min, y_max)));
}
}
if x == other.x as i32 && y == other.y as i32 {
break;
}
let e2 = 2 * err;
if e2 > -dy {
err -= dy;
x += sx;
}
if e2 < dx {
err += dx;
y += sy;
}
}
let (x_vec, y_vec): (Vec<u32>, Vec<u32>) = pixels
.into_iter()
.map(|(x, y)| (x as u32, y as u32))
.unzip();
Line::new(x_vec, y_vec, self.dist_to(other))
}
pub fn around(&self, radius: u32) -> (Vec<u32>, Vec<u32>) {
utils::pixels_around((self.x, self.y), radius)
}
pub fn dist_to(&self, other: &Peg) -> u32 {
let delta_x = utils::abs_diff(self.x, other.x);
let delta_y = utils::abs_diff(self.y, other.y);
((delta_x * delta_x + delta_y * delta_y) as f64)
.sqrt()
.round() as u32
}
pub fn with_jitter(&self, jitter: i64) -> Self {
let mut rng = thread_rng();
Self {
x: (self.x as i64 + rng.gen_range(-jitter..jitter)) as u32,
y: (self.y as i64 + rng.gen_range(-jitter..jitter)) as u32,
id: self.id,
}
}
}
pub mod shape {
use super::*;
fn coords_to_pegs(coords: (Vec<u32>, Vec<u32>)) -> Vec<Peg> {
coords
.0
.into_iter()
.zip(coords.1)
.map(|(x, y)| Peg::new(x, y))
.collect()
}
pub fn square(top_left: (u32, u32), length: u32, n_pegs: usize) -> Vec<Peg> {
coords_to_pegs(utils::square_coords(top_left, length, n_pegs))
}
pub fn rectangle(top_left: (u32, u32), width: u32, height: u32, n_pegs: usize) -> Vec<Peg> {
coords_to_pegs(utils::rectangle_coords(top_left, width, height, n_pegs))
}
pub fn circle(center: (u32, u32), radius: u32, n_pegs: usize) -> Vec<Peg> {
coords_to_pegs(utils::circle_coords(center, radius, n_pegs))
}
pub fn line(start: (u32, u32), end: (u32, u32), n_pegs: usize) -> Vec<Peg> {
coords_to_pegs(utils::line_coords(start, end, n_pegs))
}
}
#[derive(Debug, Clone)]
pub struct Yarn {
pub width: f32,
pub opacity: f64,
pub color: (u8, u8, u8),
}
impl Default for Yarn {
fn default() -> Self {
Self {
width: 1.,
opacity: 0.2,
color: (0, 0, 0),
}
}
}
impl Yarn {
pub fn new(width: f32, opacity: f64, color: (u8, u8, u8)) -> Self {
Self {
width,
opacity,
color,
}
}
pub fn set_color(&mut self, color: (u8, u8, u8)) {
self.color = color
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn peg_line_to() {
let peg_a = Peg::new(0, 0);
let peg_b = Peg::new(1, 1);
let mut line = peg_a.line_to(&peg_b, 1, None);
line.x.sort();
line.y.sort();
assert_eq!(line.x, vec![0, 1]);
assert_eq!(line.y, vec![0, 1]);
assert_eq!(line.dist, f32::sqrt(2.0) as u32);
let peg_a = Peg::new(1, 1);
let peg_b = Peg::new(0, 0);
let mut line = peg_a.line_to(&peg_b, 1, None);
line.x.sort();
line.y.sort();
assert_eq!(line.x, vec![0, 1]);
assert_eq!(line.y, vec![0, 1]);
assert_eq!(line.dist, f32::sqrt(2.0) as u32);
let peg_a = Peg::new(0, 1);
let peg_b = Peg::new(3, 1);
let mut line = peg_a.line_to(&peg_b, 1, None);
line.x.sort();
line.y.sort();
assert_eq!(line.x, vec![0, 1, 2, 3]);
assert_eq!(line.y, vec![1, 1, 1, 1]);
assert_eq!(line.dist, 3);
let peg_a = Peg::new(0, 0);
let peg_b = Peg::new(0, 1);
let mut line = peg_a.line_to(&peg_b, 1, None);
line.x.sort();
line.y.sort();
assert_eq!(line.x, vec![0, 0]);
assert_eq!(line.y, vec![0, 1]);
assert_eq!(line.dist, 1);
assert_eq!(line.zip().len(), 2);
}
#[test]
fn peg_line_to_width() {
let peg_a = Peg::new(5, 5);
let peg_b = Peg::new(5, 5);
let line = peg_a.line_to(&peg_b, 0, None);
assert_eq!(line.x, vec![5]);
assert_eq!(line.y, vec![5]);
let line = peg_a.line_to(&peg_b, 1, None);
assert_eq!(line.x, vec![5]);
assert_eq!(line.y, vec![5]);
assert_eq!(line.dist, 0);
let line = peg_a.line_to(&peg_b, 2, None);
assert_eq!(*line.x.iter().max().unwrap(), 6);
assert_eq!(*line.x.iter().min().unwrap(), 4);
assert_eq!(*line.y.iter().max().unwrap(), 6);
assert_eq!(*line.y.iter().min().unwrap(), 4);
assert_eq!(line.dist, 0);
let line = peg_a.line_to(&peg_b, 3, None);
assert_eq!(*line.x.iter().max().unwrap(), 6);
assert_eq!(*line.x.iter().min().unwrap(), 4);
assert_eq!(*line.y.iter().max().unwrap(), 6);
assert_eq!(*line.y.iter().min().unwrap(), 4);
assert_eq!(line.dist, 0);
let line = peg_a.line_to(&peg_b, 4, None);
assert_eq!(*line.x.iter().max().unwrap(), 7);
assert_eq!(*line.x.iter().min().unwrap(), 3);
assert_eq!(*line.y.iter().max().unwrap(), 7);
assert_eq!(*line.y.iter().min().unwrap(), 3);
assert_eq!(line.dist, 0);
let peg_a = Peg::new(5, 5);
let peg_b = Peg::new(6, 6);
let line = peg_a.line_to(&peg_b, 2, None);
assert_eq!(*line.x.iter().min().unwrap(), 4);
assert_eq!(*line.x.iter().max().unwrap(), 7);
assert_eq!(*line.y.iter().min().unwrap(), 4);
assert_eq!(*line.y.iter().max().unwrap(), 7);
assert_eq!(line.dist, 1);
}
#[test]
fn peg_line_to_width_min_max() {
let peg_a = Peg::new(0, 5);
let peg_b = Peg::new(10, 5);
let line = peg_a.line_to(&peg_b, 2, Some((0, 10, 3, 7)));
assert_eq!(*line.x.iter().max().unwrap(), 10);
assert_eq!(*line.x.iter().min().unwrap(), 0);
assert_eq!(*line.y.iter().max().unwrap(), 6);
assert_eq!(*line.y.iter().min().unwrap(), 4);
}
#[test]
fn peg_around() {
let peg = Peg::new(10, 10);
let (x_coords, y_coords) = peg.around(1);
assert_eq!(x_coords, vec![9, 10, 10, 10, 11]);
assert_eq!(y_coords, vec![10, 9, 10, 11, 10]);
}
#[test]
fn peg_jitter() {
let peg = Peg::new(10, 10);
let jitter = 2;
let peg_jitter = peg.with_jitter(jitter);
assert!(peg_jitter.x <= (peg.x as i64 + jitter) as u32);
assert!(peg_jitter.x >= (peg.x as i64 - jitter) as u32);
assert_eq!(peg_jitter.id, peg.id);
}
}