1use super::ease::*;
4use super::hash::*;
5use super::map3base::*;
6use super::math::*;
7use super::*;
8extern crate alloc;
9use alloc::{boxed::Box, string::String};
10
11#[derive(Clone)]
13pub struct VNoise<H: Hasher> {
14 seed: u64,
15 frequency: f32,
16 hasher: H,
17 ease: Ease,
18}
19
20pub fn vnoise<H: 'static + Hasher>(
21 seed: u64,
22 frequency: f32,
23 ease: Ease,
24 hasher: H,
25) -> Box<dyn Texture> {
26 Box::new(VNoise {
27 seed,
28 frequency,
29 ease,
30 hasher,
31 })
32}
33
34pub fn vnoise_basis<H: 'static + Hasher>(seed: u64, ease: Ease, hasher: H) -> Box<dyn Texture> {
35 Box::new(VNoise {
36 seed,
37 frequency: 1.0,
38 ease,
39 hasher,
40 })
41}
42
43impl<H: Hasher> Texture for VNoise<H> {
44 fn at_frequency(&self, point: Vec3a, frequency: Option<f32>) -> Vec3a {
45 let frequency = frequency.unwrap_or(self.frequency);
46 let basis = self.hasher.query(self.seed, frequency, point);
47 let mut result = Vec3a::zero();
48
49 for dx in -1..=1 {
50 let hx = self.hasher.hash_x(&basis, 0, dx);
51 for dy in -1..=1 {
52 let hxy = self.hasher.hash_y(&basis, hx, dy);
53 let mut offset = Vec3a::new(dx as f32, dy as f32, 0.0) - basis.d;
54 for dz in -1..=1 {
55 let mut hash = self.hasher.hash_z(&basis, hxy, dz);
56 let n = match hash & 7 {
58 0 => 0,
59 1 | 2 | 3 => 1,
60 4 | 5 | 6 => 2,
61 _ => 3,
62 };
63 offset = vec3a(offset.x, offset.y, dz as f32 - basis.d.z);
65 for i in 0..n {
66 let p = hash_01(hash);
68 let distance2: f32 = (p + offset).length_squared();
69 let radius: f32 = 1.0;
71 if distance2 < radius * radius {
72 let distance = sqrt(distance2) / radius;
73 let color = hash_11(hash);
74 let blend = self.ease.at(1.0 - distance);
75 result += color * blend;
76 }
77 if i + 1 < n {
78 hash = hash64c(hash);
79 }
80 }
81 }
82 }
83 }
84 result
85 }
86
87 fn get_code(&self) -> String {
88 format!(
89 "vnoise({}, {}, {}, {})",
90 self.seed,
91 self.frequency,
92 self.ease.get_code(),
93 self.hasher.get_code()
94 )
95 }
96
97 fn get_basis_code(&self) -> String {
98 format!(
99 "vnoise_basis({}, {}, {})",
100 self.seed,
101 self.ease.get_code(),
102 self.hasher.get_code()
103 )
104 }
105}
106
107#[derive(Clone)]
109pub struct Noise<H: Hasher> {
110 seed: u64,
111 frequency: f32,
112 hasher: H,
113}
114
115pub fn noise<H: 'static + Hasher>(seed: u64, frequency: f32, hasher: H) -> Box<dyn Texture> {
116 Box::new(Noise {
117 seed,
118 frequency,
119 hasher,
120 })
121}
122
123pub fn noise_basis<H: 'static + Hasher>(seed: u64, hasher: H) -> Box<dyn Texture> {
124 Box::new(Noise {
125 seed,
126 frequency: 1.0,
127 hasher,
128 })
129}
130
131impl<H: Hasher> Texture for Noise<H> {
132 fn at_frequency(&self, point: Vec3a, frequency: Option<f32>) -> Vec3a {
133 let frequency = frequency.unwrap_or(self.frequency);
134 let basis = self.hasher.query(self.seed, frequency, point);
135 let mut result = Vec3a::zero();
136
137 for dx in -1..=1 {
138 let hx = self.hasher.hash_x(&basis, 0, dx);
139 for dy in -1..=1 {
140 let hxy = self.hasher.hash_y(&basis, hx, dy);
141 let mut offset = Vec3a::new(dx as f32, dy as f32, 0.0) - basis.d;
142 for dz in -1..=1 {
143 let mut hash = self.hasher.hash_z(&basis, hxy, dz);
144 let n = match hash & 7 {
146 0 | 1 | 2 => 1,
147 3 | 4 | 5 => 2,
148 _ => 3,
149 };
150 offset = vec3a(offset.x, offset.y, dz as f32 - basis.d.z);
152 for i in 0..n {
153 let p = hash_01(hash);
155 let distance2: f32 = (p + offset).length_squared();
156 let radius: f32 = 1.0;
158 if distance2 < radius * radius {
159 let distance = sqrt(distance2) / radius;
160 let color = hash_11(hash);
161 let gradient = hash_unit(hash64d(hash));
162 let blend = 1.0 - smooth5(distance);
163 result += color * blend * gradient.dot(p + offset);
164 }
165 if i + 1 < n {
166 hash = hash64c(hash);
167 }
168 }
169 }
170 }
171 }
172 result * 3.0
173 }
174
175 fn get_code(&self) -> String {
176 format!(
177 "noise({}, {}, {})",
178 self.seed,
179 self.frequency,
180 self.hasher.get_code()
181 )
182 }
183
184 fn get_basis_code(&self) -> String {
185 format!("noise_basis({}, {})", self.seed, self.hasher.get_code())
186 }
187}