1use std::{cell::RefCell, f32::consts::PI};
5
6type Vec3 = [f32; 3];
7type Vec2 = [f32; 2];
8
9thread_local! {
10 static FIBONACCI_DIRECTIONS: RefCell<Vec<Option<Box<[Vec3]>>>> = const { RefCell::new(Vec::new()) };
14 static UNIT_CIRCLES: RefCell<Vec<Option<Box<[Vec2]>>>> = const { RefCell::new(Vec::new()) };
15}
16
17#[derive(Clone, Copy, Debug)]
19pub struct Dot {
20 pub x: f32,
21 pub y: f32,
22 pub z: f32,
23 pub r: f32,
24 pub white: f32,
26 pub a: f32,
27}
28
29impl Dot {
30 pub fn new(x: f32, y: f32, z: f32, r: f32, white: f32) -> Self {
31 Self {
32 x,
33 y,
34 z,
35 r,
36 white,
37 a: 1.0,
38 }
39 }
40
41 pub fn with_a(mut self, a: f32) -> Self {
42 self.a = a;
43 self
44 }
45}
46
47#[derive(Clone, Copy, Debug)]
49pub struct Line {
50 pub x1: f32,
51 pub y1: f32,
52 pub x2: f32,
53 pub y2: f32,
54 pub white: f32,
55 pub a: f32,
56 pub w: f32,
57}
58
59#[derive(Clone, Debug, Default)]
61pub struct Frame {
62 pub dots: Vec<Dot>,
63 pub lines: Vec<Line>,
64}
65
66impl Frame {
67 pub fn new() -> Self {
68 Self::default()
69 }
70
71 #[inline]
74 pub fn clear(&mut self) {
75 self.dots.clear();
76 self.lines.clear();
77 }
78}
79
80#[derive(Clone, Copy, Debug)]
86pub struct Proj {
87 st: f32,
88 ct: f32,
89 sy: f32,
90 cyw: f32,
91 cx: f32,
92 cy: f32,
93 scale: f32,
94}
95
96impl Proj {
97 #[inline]
98 pub fn project(&self, x: f32, y: f32, z: f32) -> (f32, f32, f32) {
99 let x1 = x * self.cyw + z * self.sy;
100 let z1 = -x * self.sy + z * self.cyw;
101 let y1 = y * self.ct - z1 * self.st;
102 let z2 = y * self.st + z1 * self.ct;
103 (self.cx + x1 * self.scale, self.cy - y1 * self.scale, z2)
104 }
105}
106
107#[inline]
108pub fn lerp(a: f32, b: f32, f: f32) -> f32 {
109 a + (b - a) * f
110}
111
112#[inline]
113pub fn frac(x: f32) -> f32 {
114 x - x.floor()
115}
116
117#[inline]
124#[allow(clippy::excessive_precision)]
125pub fn hash_d(a: f32, b: f32) -> f32 {
126 let h = (a * 12.9898 + b * 78.233).sin() * 43758.5453;
127 h - h.floor()
128}
129
130pub fn vnoise(x: f32, y: f32) -> f32 {
132 let xi = x.floor();
133 let yi = y.floor();
134 let mut fx = x - xi;
135 let mut fy = y - yi;
136 fx = fx * fx * (3.0 - 2.0 * fx);
137 fy = fy * fy * (3.0 - 2.0 * fy);
138 let a = hash_d(xi, yi);
139 let b = hash_d(xi + 1.0, yi);
140 let c = hash_d(xi, yi + 1.0);
141 let d = hash_d(xi + 1.0, yi + 1.0);
142 a + (b - a) * fx + (c - a) * fy + (a - b - c + d) * fx * fy
143}
144
145pub fn fib_dir(i: usize, n: usize) -> (f32, f32, f32) {
147 let golden = PI * (3.0 - 5.0_f32.sqrt());
148 let y = 1.0 - (2.0 * (i as f32 + 0.5)) / n as f32;
149 let rad = (1.0 - y * y).sqrt();
150 let a = i as f32 * golden;
151 (rad * a.cos(), y, rad * a.sin())
152}
153
154pub fn with_fib_dirs<R>(n: usize, f: impl FnOnce(&[Vec3]) -> R) -> R {
158 FIBONACCI_DIRECTIONS.with(|cell| {
159 let mut cache = cell.borrow_mut();
160 if cache.len() <= n {
161 cache.resize_with(n + 1, || None);
162 }
163 let directions = cache[n].get_or_insert_with(|| {
164 (0..n)
165 .map(|i| {
166 let (x, y, z) = fib_dir(i, n);
167 [x, y, z]
168 })
169 .collect::<Vec<_>>()
170 .into_boxed_slice()
171 });
172 f(directions)
173 })
174}
175
176pub fn with_unit_circle<R>(n: usize, f: impl FnOnce(&[Vec2]) -> R) -> R {
179 UNIT_CIRCLES.with(|cell| {
180 let mut cache = cell.borrow_mut();
181 if cache.len() <= n {
182 cache.resize_with(n + 1, || None);
183 }
184 let circle = cache[n].get_or_insert_with(|| {
185 let step = 2.0 * PI / n.max(1) as f32;
186 (0..n)
187 .map(|i| {
188 let a = i as f32 * step;
189 [a.cos(), a.sin()]
190 })
191 .collect::<Vec<_>>()
192 .into_boxed_slice()
193 });
194 f(circle)
195 })
196}
197
198#[inline]
200pub fn angle_delta(a: f32, b: f32) -> f32 {
201 (a - b).sin().atan2((a - b).cos())
202}
203
204pub fn make_proj(yaw: f32, tilt: f32, cx: f32, cy: f32, scale: f32) -> Proj {
206 Proj {
207 st: tilt.sin(),
208 ct: tilt.cos(),
209 sy: yaw.sin(),
210 cyw: yaw.cos(),
211 cx,
212 cy,
213 scale,
214 }
215}
216
217#[inline]
220pub fn radius_scale(size: f32, pow: f32) -> f32 {
221 (size / 300.0).powf(pow)
222}
223
224pub fn sort_dots(dots: &mut [Dot]) {
230 dots.sort_unstable_by(|a, b| a.z.partial_cmp(&b.z).unwrap_or(std::cmp::Ordering::Equal));
231}