mod draw_line;
mod solver;
pub use draw_line::VrellisAlgorithm;
use serde::{Deserialize, Serialize};
use std::{collections::VecDeque, f32::consts::PI};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum VrellisShape {
Circle,
Triangle,
Square,
Parabola,
Polygon {
corners: Vec<(f32, f32)>,
},
Custom {
points: Vec<VrellisPoint>,
},
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct VrellisPoint {
pub n: u32,
pub x: u32,
pub y: u32,
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub enum VrellisColorMode {
Grayscale = 0,
Colorful = 1,
LayerMask = 2,
}
impl Default for VrellisShape {
fn default() -> Self {
Self::Circle
}
}
impl Default for VrellisPoint {
fn default() -> Self {
Self { n: 0, x: 0, y: 0 }
}
}
impl Default for VrellisColorMode {
fn default() -> Self {
Self::Grayscale
}
}
impl VrellisShape {
pub fn sample(&self, num: u32, width: u32, height: u32) -> Vec<VrellisPoint> {
assert!(num > 7, "too less samples!");
let mut out = Vec::with_capacity(num as usize);
match self {
VrellisShape::Circle => {
for n in 0..num {
let x = 1.0 + (2.0 * PI * n as f32 / num as f32).cos();
let y = 1.0 - (2.0 * PI * n as f32 / num as f32).sin();
out.push(VrellisPoint {
n,
x: (x * width as f32 / 2.0).round() as u32,
y: (y * height as f32 / 2.0).round() as u32,
})
}
}
VrellisShape::Triangle => {
let poly = Self::Polygon { corners: vec![(0.5, 1.0 - 3.0_f32.sqrt() / 2.0), (1.0, 1.0), (0.0, 1.0)] };
return poly.sample(num, width, height);
}
VrellisShape::Square => {
let poly = Self::Polygon { corners: vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)] };
return poly.sample(num, width, height);
}
VrellisShape::Polygon { corners } => {
let mut shifted = VecDeque::from(corners.clone());
let head = shifted.pop_front().unwrap();
shifted.push_back(head);
let mut circumference = 0.0;
let mut temp_line = vec![];
for (a, b) in corners.iter().zip(shifted.iter()) {
let p1 = circumference;
circumference += ((a.0 - b.0).powf(2.0) + (a.1 - b.1).powf(2.0)).sqrt();
let p2 = circumference;
temp_line.push(VrellisLine { p1, p2, x1: a.0, y1: a.1, x2: b.0, y2: b.1 });
}
temp_line.iter_mut().for_each(|e| e.resize(circumference));
let mut edges = temp_line.into_iter();
let mut this_edge = edges.next().unwrap();
for n in 0..num {
let percent = n as f32 / num as f32;
while percent > this_edge.p2 {
match edges.next() {
Some(s) => {
this_edge = s;
}
None => {
return out;
}
}
}
let (x, y) = this_edge.get_percent_position(percent);
out.push(VrellisPoint { n, x: (x * width as f32).round() as u32, y: (y * height as f32).round() as u32 })
}
}
VrellisShape::Parabola => unimplemented!(),
VrellisShape::Custom { points } => return points.clone(),
}
return out;
}
}
struct VrellisLine {
p1: f32,
p2: f32,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
}
impl VrellisLine {
fn resize(&mut self, c: f32) {
self.p1 /= c;
self.p2 /= c;
}
fn rescale_p(&self, p: f32) -> f32 {
(p - self.p1) / (self.p2 - self.p1)
}
fn percent_x(&self, p: f32) -> f32 {
self.x1 + self.rescale_p(p) * (self.x2 - self.x1)
}
fn percent_y(&self, p: f32) -> f32 {
self.y1 + self.rescale_p(p) * (self.y2 - self.y1)
}
fn get_percent_position(&self, p: f32) -> (f32, f32) {
assert!(self.p1 <= p && p <= self.p2);
(self.percent_x(p), self.percent_y(p))
}
}