use std::{error::Error, f64::consts::PI, path::Path};
use crate::peg::Peg;
use indicatif::{ProgressBar, ProgressStyle};
use log::debug;
macro_rules! iter_or_par_iter {
($iter:expr) => {{
#[cfg(feature = "parallel")]
{
$iter.par_iter()
}
#[cfg(not(feature = "parallel"))]
{
$iter.iter()
}
}};
($iter:expr, into) => {{
#[cfg(feature = "parallel")]
{
$iter.into_par_iter()
}
#[cfg(not(feature = "parallel"))]
{
$iter.into_iter()
}
}};
}
pub(crate) use iter_or_par_iter;
pub fn circle_coords(
(center_x, center_y): (u32, u32),
radius: u32,
n_points: usize,
) -> (Vec<u32>, Vec<u32>) {
let radius = radius as f64;
let angle = 2. * PI / n_points as f64;
let mut points_x = vec![];
let mut points_y = vec![];
let mut i_angle: f64;
for i in 0..n_points {
i_angle = i as f64 * angle;
points_x.push((radius * (i_angle).cos() + center_x as f64).round() as u32);
points_y.push((radius * (i_angle).sin() + center_y as f64).round() as u32);
}
(points_x, points_y)
}
pub fn line_coords(start: (u32, u32), end: (u32, u32), n_points: usize) -> (Vec<u32>, Vec<u32>) {
let (x1, y1): (f64, f64) = (start.0 as f64, start.1 as f64);
let (x2, y2): (f64, f64) = (end.0 as f64, end.1 as f64);
let dx = (x2 - x1) / n_points as f64;
let dy = (y2 - y1) / n_points as f64;
(0..n_points)
.map(|i| {
let t = i as f64;
((x1 + t * dx).round() as u32, (y1 + t * dy).round() as u32)
})
.unzip()
}
pub fn square_coords(top_left: (u32, u32), length: u32, n_points: usize) -> (Vec<u32>, Vec<u32>) {
let (x0, y0) = top_left;
let top_right = (x0 + length, y0);
let bottom_right = (x0 + length, y0 + length);
let bottom_left = (x0, y0 + length);
let n_points_side = n_points / 4;
let top = line_coords(top_left, top_right, n_points_side);
let right = line_coords(top_right, bottom_right, n_points_side);
let bottom = line_coords(bottom_right, bottom_left, n_points_side);
let left = line_coords(bottom_left, top_left, n_points_side);
(
[top.0, right.0, bottom.0, left.0].concat(),
[top.1, right.1, bottom.1, left.1].concat(),
)
}
pub fn rectangle_coords(
top_left: (u32, u32),
width: u32,
height: u32,
n_points: usize,
) -> (Vec<u32>, Vec<u32>) {
let (x0, y0) = top_left;
let top_right = (x0 + width, y0);
let bottom_right = (x0 + width, y0 + height);
let bottom_left = (x0, y0 + height);
let perimeter = 2 * width + 2 * height;
let width_points = (n_points as u32 * width / perimeter) as usize;
let height_points = (n_points as u32 * height / perimeter) as usize;
debug!("Points along (width, height): ({width_points}, {height_points})");
let top = line_coords(top_left, top_right, width_points);
let right = line_coords(top_right, bottom_right, height_points);
let bottom = line_coords(bottom_right, bottom_left, width_points);
let left = line_coords(bottom_left, top_left, height_points);
(
[top.0, right.0, bottom.0, left.0].concat(),
[top.1, right.1, bottom.1, left.1].concat(),
)
}
pub fn pixels_around((center_x, center_y): (u32, u32), radius: u32) -> (Vec<u32>, Vec<u32>) {
let mut x_coords: Vec<u32> = vec![];
let mut y_coords: Vec<u32> = vec![];
let radius = radius as i64;
let center_x = center_x as i64;
let center_y = center_y as i64;
for x in -radius..radius + 1 {
for y in -radius..radius + 1 {
if x * x + y * y <= radius * radius {
x_coords.push((center_x + x) as u32);
y_coords.push((center_y + y) as u32);
}
}
}
(x_coords, y_coords)
}
pub(crate) fn progress_style() -> Result<ProgressStyle, Box<dyn Error>> {
Ok(ProgressStyle::with_template(
"{msg}: {wide_bar} {elapsed_precise} {pos}/{len}",
)?)
}
pub(crate) fn pbar(len: u64, hidden: bool) -> Result<ProgressBar, Box<dyn Error>> {
let style = progress_style()?;
Ok(if hidden {
ProgressBar::hidden()
} else {
ProgressBar::new(len)
}
.with_style(style))
}
pub(crate) fn spinner(hidden: bool) -> ProgressBar {
if hidden {
ProgressBar::hidden()
} else {
ProgressBar::new_spinner()
}
}
pub(crate) fn abs_diff<T>(a: T, b: T) -> T
where
T: std::cmp::PartialOrd + std::ops::Sub<Output = T>,
{
if a > b {
a - b
} else {
b - a
}
}
pub fn hash_key(peg_a: &Peg, peg_b: &Peg) -> (usize, usize) {
if peg_a.id < peg_b.id {
(peg_a.id, peg_b.id)
} else {
(peg_b.id, peg_a.id)
}
}
pub fn open_img_transparency_to_white<P: AsRef<Path>>(
image_file: P,
) -> Result<image::ImageBuffer<image::Rgb<u8>, Vec<u8>>, Box<dyn Error>> {
let mut img_rgba = image::open(image_file)?.into_rgba8();
for pixel in img_rgba.pixels_mut() {
if pixel.0[3] == 0 {
pixel.0 = [255, 255, 255, 255]
}
}
Ok(image::DynamicImage::ImageRgba8(img_rgba).to_rgb8())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_abs_diff() {
assert_eq!(abs_diff(1., 2.), 1.);
assert_eq!(abs_diff(2., 1.), 1.);
assert_eq!(abs_diff(1, 2), 1);
assert_eq!(abs_diff(2, 1), 1);
}
#[test]
fn test_line_coords() {
let n_points = 5;
let (x1, y1) = (0, 0);
let (x2, y2) = (10, 0);
let (x, y) = line_coords((x1, y1), (x2, y2), n_points);
assert_eq!(x.len(), n_points);
assert_eq!(y.len(), n_points);
assert_eq!(x.first(), Some(&x1));
assert_eq!(y.first(), Some(&y1));
let (x_end, y_end) = (8, 0);
assert_eq!(x.last(), Some(&x_end));
assert_eq!(y.last(), Some(&y_end));
}
}