1use std::sync::Arc;
2use wassily::prelude::*;
3const WIDTH: u32 = 1080;
4const HEIGHT: u32 = 1080;
5
6fn main() {
7 let mut canvas = Canvas::new(WIDTH, HEIGHT);
8 let mut rng = SmallRng::seed_from_u64(23);
9 let mut color_wheel = ColorWheel::new(&mut rng, 7, 3);
10 for i in 0..WIDTH {
11 for j in 0..HEIGHT {
12 let x = i as f32 / WIDTH as f32;
13 let y = j as f32 / HEIGHT as f32;
14 let c = color_wheel.get_color(&mut rng, x, y);
15 canvas.dot(i as f32, j as f32, c);
16 }
17 }
18 canvas.save_png("./wheel4.png");
19}
20
21#[derive(Clone)]
22pub struct ColorWheel {
23 pub octaves: usize,
24 pub base_color: [f32; 3],
25 pub phases: Vec<[f32; 3]>,
26 pub frequencies: [f32; 7],
27 pub noise: Arc<Fbm<Perlin>>,
28 scale: f32,
29}
30
31impl ColorWheel {
32 pub fn new<R: Rng>(rng: &mut R, octaves: usize, noise_octaves: usize) -> Self {
33 assert!(octaves <= 7, "Maximum ocatves is 7");
34 let mut phases = ColorWheel::PHASES;
35 phases.shuffle(rng);
36 let phases: Vec<[f32; 3]> = phases.into_iter().map(|v| Self::shuffle3(rng, v)).collect();
37 let noise = Fbm::<Perlin>::default();
38 let seed: u32 = rng.gen();
39 let noise = noise.set_seed(seed).set_octaves(noise_octaves);
40 let x: f32 = 0.5 + rng.gen_range(-0.1..0.1);
41 let y: f32 = 0.3 + rng.gen_range(-0.1..0.1);
42 let z: f32 = 0.4 + rng.gen_range(-0.1..0.1);
43 let base_color = [x, y, z];
44 let mut scale: f32 = Self::AMPLITUDES[0..octaves].iter().sum();
45 scale += 0.4;
46 Self {
47 octaves,
48 phases,
49 frequencies: Self::FREQUENCIES,
50 noise: Arc::new(noise),
51 base_color,
52 scale,
53 }
54 }
55
56 const AMPLITUDES: [f32; 7] = [0.12, 0.11, 0.1, 0.09, 0.08, 0.07, 0.06];
57
58 const PHASES: [[f32; 3]; 9] = [
59 [0.0, 0.8, 1.0],
60 [0.3, 0.4, 0.1],
61 [0.1, 0.7, 1.1],
62 [0.2, 0.8, 1.4],
63 [0.2, 0.6, 0.7],
64 [0.1, 0.6, 0.7],
65 [0.0, 0.5, 0.8],
66 [0.1, 0.4, 0.7],
67 [1.1, 1.4, 2.7],
68 ];
69
70 const FREQUENCIES: [f32; 7] = [1.0, 3.1, 5.1, 9.1, 17.1, 31.1, 65.1];
71
72 fn shuffle3<R: Rng>(rng: &mut R, p: [f32; 3]) -> [f32; 3] {
73 let i: usize = rng.gen_range(0..=2);
74 let j = (i + rng.gen_range(1..=2)) % 3;
75 let x = p[i];
76 let y = p[j];
77 let z = p[3 - i - j];
78 [x, y, z]
79 }
80
81 fn term(&self, t: f32, amplitude: f32, freq: f32, phases: [f32; 3]) -> [f32; 3] {
82 phases.map(|p| amplitude * (TAU * t * freq + p).cos())
83 }
84
85 pub fn get_color<R: Rng>(&mut self, rng: &mut R, x: f32, y: f32) -> Color {
86 let mut rgb = self.base_color;
87 for i in 0..self.octaves {
88 let t = 0.5
89 + 0.5 * self.noise.get([3.0 * x as f64, 3.0 * y as f64]) as f32
90 + 0.02 * (rng.gen_range(0.0..1.0) + rng.gen_range(0.0..1.0));
91 let a = self.term(t, Self::AMPLITUDES[i], Self::FREQUENCIES[i], self.phases[i]);
92 for j in 0..3 {
93 rgb[j] += a[j];
94 }
95 }
96 Color::from_rgba(
97 (rgb[0] / self.scale).clamp(0.0, 1.0),
98 (rgb[1] / self.scale).clamp(0.2, 0.8),
99 (rgb[2] / self.scale).clamp(0.0, 1.0),
100 1.0,
101 )
102 .unwrap()
103 }
104}