pixel_sort/render/
mod.rs

1mod draw_line;
2mod solver;
3
4pub use draw_line::VrellisAlgorithm;
5use serde::{Deserialize, Serialize};
6use std::{collections::VecDeque, f32::consts::PI};
7
8#[derive(Serialize, Deserialize, Debug, Clone)]
9pub enum VrellisShape {
10    Circle,
11    Triangle,
12    Square,
13    Parabola,
14    /// Note that it must be a convex hull
15    Polygon {
16        corners: Vec<(f32, f32)>,
17    },
18    /// Note that it must be a convex curve
19    Custom {
20        points: Vec<VrellisPoint>,
21    },
22}
23
24#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
25pub struct VrellisPoint {
26    pub n: u32,
27    pub x: u32,
28    pub y: u32,
29}
30
31#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
32pub enum VrellisColorMode {
33    Grayscale = 0,
34    Colorful = 1,
35    LayerMask = 2,
36}
37
38impl Default for VrellisShape {
39    fn default() -> Self {
40        Self::Circle
41    }
42}
43
44impl Default for VrellisPoint {
45    fn default() -> Self {
46        Self { n: 0, x: 0, y: 0 }
47    }
48}
49
50impl Default for VrellisColorMode {
51    fn default() -> Self {
52        Self::Grayscale
53    }
54}
55
56impl VrellisShape {
57    pub fn sample(&self, num: u32, width: u32, height: u32) -> Vec<VrellisPoint> {
58        assert!(num > 7, "too less samples!");
59        let mut out = Vec::with_capacity(num as usize);
60        match self {
61            VrellisShape::Circle => {
62                for n in 0..num {
63                    // 奇变偶不变, 符号看象限
64                    let x = 1.0 + (2.0 * PI * n as f32 / num as f32).cos();
65                    let y = 1.0 - (2.0 * PI * n as f32 / num as f32).sin();
66                    out.push(VrellisPoint {
67                        n,
68                        x: (x * width as f32 / 2.0).round() as u32,
69                        y: (y * height as f32 / 2.0).round() as u32,
70                    })
71                }
72            }
73            VrellisShape::Triangle => {
74                let poly = Self::Polygon { corners: vec![(0.5, 1.0 - 3.0_f32.sqrt() / 2.0), (1.0, 1.0), (0.0, 1.0)] };
75                return poly.sample(num, width, height);
76            }
77            VrellisShape::Square => {
78                let poly = Self::Polygon { corners: vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)] };
79                return poly.sample(num, width, height);
80            }
81            VrellisShape::Polygon { corners } => {
82                // FIXME: better way to get shift pair
83                let mut shifted = VecDeque::from(corners.clone());
84                let head = shifted.pop_front().unwrap();
85                shifted.push_back(head);
86                // build edges
87                let mut circumference = 0.0;
88                let mut temp_line = vec![];
89                for (a, b) in corners.iter().zip(shifted.iter()) {
90                    let p1 = circumference;
91                    circumference += ((a.0 - b.0).powf(2.0) + (a.1 - b.1).powf(2.0)).sqrt();
92                    let p2 = circumference;
93                    temp_line.push(VrellisLine { p1, p2, x1: a.0, y1: a.1, x2: b.0, y2: b.1 });
94                }
95                temp_line.iter_mut().for_each(|e| e.resize(circumference));
96                // find points
97                let mut edges = temp_line.into_iter();
98                let mut this_edge = edges.next().unwrap();
99                for n in 0..num {
100                    let percent = n as f32 / num as f32;
101                    while percent > this_edge.p2 {
102                        match edges.next() {
103                            Some(s) => {
104                                this_edge = s;
105                            }
106                            None => {
107                                return out;
108                            }
109                        }
110                    }
111                    let (x, y) = this_edge.get_percent_position(percent);
112                    out.push(VrellisPoint { n, x: (x * width as f32).round() as u32, y: (y * height as f32).round() as u32 })
113                }
114            }
115            VrellisShape::Parabola => unimplemented!(),
116            VrellisShape::Custom { points } => return points.clone(),
117        }
118        return out;
119    }
120}
121
122struct VrellisLine {
123    p1: f32,
124    p2: f32,
125    x1: f32,
126    y1: f32,
127    x2: f32,
128    y2: f32,
129}
130
131impl VrellisLine {
132    fn resize(&mut self, c: f32) {
133        self.p1 /= c;
134        self.p2 /= c;
135    }
136    fn rescale_p(&self, p: f32) -> f32 {
137        (p - self.p1) / (self.p2 - self.p1)
138    }
139    fn percent_x(&self, p: f32) -> f32 {
140        self.x1 + self.rescale_p(p) * (self.x2 - self.x1)
141    }
142    fn percent_y(&self, p: f32) -> f32 {
143        self.y1 + self.rescale_p(p) * (self.y2 - self.y1)
144    }
145    fn get_percent_position(&self, p: f32) -> (f32, f32) {
146        assert!(self.p1 <= p && p <= self.p2);
147        (self.percent_x(p), self.percent_y(p))
148    }
149}