doryen_extra/noise/algorithms/perlin.rs
1/* BSD 3-Clause License
2 *
3 * Copyright © 2019, Alexander Krivács Schrøder <alexschrod@gmail.com>.
4 * Copyright © 2008-2019, Jice and the libtcod contributors.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34use crate::noise::algorithms::AlgorithmInitializer;
35use crate::noise::{Algorithm, MAX_DIMENSIONS};
36use crate::random::algorithms::Algorithm as RandomAlgorithm;
37use derivative::Derivative;
38use ilyvion_util::multi_dimensional::Window2D;
39
40/// Perlin noise algorithm.
41#[derive(Clone, Copy, Derivative)]
42#[derivative(Debug)]
43pub struct Perlin {
44 dimensions: usize,
45 /** Randomized map of indexes into buffer */
46 #[derivative(Debug = "ignore")]
47 pub map: [u8; 256],
48 /** Random 256 x ndim buffer */
49 #[derivative(Debug = "ignore")]
50 pub buffer: [f32; MAX_DIMENSIONS * 256],
51}
52
53impl Perlin {
54 #[allow(clippy::too_many_arguments)]
55 fn lattice(
56 &self,
57 ix: i32,
58 fx: f32,
59 iy: i32,
60 fy: f32,
61 iz: i32,
62 fz: f32,
63 iw: i32,
64 fw: f32,
65 ) -> f32 {
66 let n: [i32; 4] = [ix, iy, iz, iw];
67 let f: [f32; 4] = [fx, fy, fz, fw];
68 let mut n_index = 0;
69 for &ni in n.iter().take(self.dimensions) {
70 n_index = i32::from(self.map[((n_index + ni) & 0xFF) as usize]);
71 }
72 let buffer_window = Window2D::new_ref_unchecked(&self.buffer, 256, MAX_DIMENSIONS);
73
74 Iterator::zip(buffer_window[n_index as usize].iter(), f.iter())
75 .take(self.dimensions)
76 .map(|(b, f)| b * f)
77 .sum()
78 }
79
80 fn perlin_1d(
81 &self,
82 n: [i32; MAX_DIMENSIONS],
83 r: [f32; MAX_DIMENSIONS],
84 w: [f32; MAX_DIMENSIONS],
85 ) -> f32 {
86 lerp!(
87 self.lattice(n[0], r[0], 0, 0.0, 0, 0.0, 0, 0.0),
88 self.lattice(n[0] + 1, r[0] - 1.0, 0, 0.0, 0, 0.0, 0, 0.0),
89 w[0]
90 )
91 }
92
93 fn perlin_2d(
94 &self,
95 n: [i32; MAX_DIMENSIONS],
96 r: [f32; MAX_DIMENSIONS],
97 w: [f32; MAX_DIMENSIONS],
98 ) -> f32 {
99 lerp!(
100 lerp!(
101 self.lattice(n[0], r[0], n[1], r[1], 0, 0.0, 0, 0.0),
102 self.lattice(n[0] + 1, r[0] - 1.0, n[1], r[1], 0, 0.0, 0, 0.0),
103 w[0]
104 ),
105 lerp!(
106 self.lattice(n[0], r[0], n[1] + 1, r[1] - 1.0, 0, 0.0, 0, 0.0),
107 self.lattice(n[0] + 1, r[0] - 1.0, n[1] + 1, r[1] - 1.0, 0, 0.0, 0, 0.0),
108 w[0]
109 ),
110 w[1]
111 )
112 }
113
114 fn perlin_3d(
115 &self,
116 n: [i32; MAX_DIMENSIONS],
117 r: [f32; MAX_DIMENSIONS],
118 w: [f32; MAX_DIMENSIONS],
119 ) -> f32 {
120 lerp!(
121 lerp!(
122 lerp!(
123 self.lattice(n[0], r[0], n[1], r[1], n[2], r[2], 0, 0.0),
124 self.lattice(n[0] + 1, r[0] - 1.0, n[1], r[1], n[2], r[2], 0, 0.0),
125 w[0]
126 ),
127 lerp!(
128 self.lattice(n[0], r[0], n[1] + 1, r[1] - 1.0, n[2], r[2], 0, 0.0),
129 self.lattice(
130 n[0] + 1,
131 r[0] - 1.0,
132 n[1] + 1,
133 r[1] - 1.0,
134 n[2],
135 r[2],
136 0,
137 0.0
138 ),
139 w[0]
140 ),
141 w[1]
142 ),
143 lerp!(
144 lerp!(
145 self.lattice(n[0], r[0], n[1], r[1], n[2] + 1, r[2] - 1.0, 0, 0.0),
146 self.lattice(
147 n[0] + 1,
148 r[0] - 1.0,
149 n[1],
150 r[1],
151 n[2] + 1,
152 r[2] - 1.0,
153 0,
154 0.0
155 ),
156 w[0]
157 ),
158 lerp!(
159 self.lattice(
160 n[0],
161 r[0],
162 n[1] + 1,
163 r[1] - 1.0,
164 n[2] + 1,
165 r[2] - 1.0,
166 0,
167 0.0
168 ),
169 self.lattice(
170 n[0] + 1,
171 r[0] - 1.0,
172 n[1] + 1,
173 r[1] - 1.0,
174 n[2] + 1,
175 r[2] - 1.0,
176 0,
177 0.0
178 ),
179 w[0]
180 ),
181 w[1]
182 ),
183 w[2]
184 )
185 }
186
187 #[allow(clippy::too_many_lines)]
188 fn perlin_4d(
189 &self,
190 n: [i32; MAX_DIMENSIONS],
191 r: [f32; MAX_DIMENSIONS],
192 w: [f32; MAX_DIMENSIONS],
193 ) -> f32 {
194 lerp!(
195 lerp!(
196 lerp!(
197 lerp!(
198 self.lattice(n[0], r[0], n[1], r[1], n[2], r[2], n[3], r[3]),
199 self.lattice(n[0] + 1, r[0] - 1.0, n[1], r[1], n[2], r[2], n[3], r[3]),
200 w[0]
201 ),
202 lerp!(
203 self.lattice(n[0], r[0], n[1] + 1, r[1] - 1.0, n[2], r[2], n[3], r[3]),
204 self.lattice(
205 n[0] + 1,
206 r[0] - 1.0,
207 n[1] + 1,
208 r[1] - 1.0,
209 n[2],
210 r[2],
211 n[3],
212 r[3]
213 ),
214 w[0]
215 ),
216 w[1]
217 ),
218 lerp!(
219 lerp!(
220 self.lattice(n[0], r[0], n[1], r[1], n[2] + 1, r[2] - 1.0, n[3], r[3]),
221 self.lattice(
222 n[0] + 1,
223 r[0] - 1.0,
224 n[1],
225 r[1],
226 n[2] + 1,
227 r[2] - 1.0,
228 n[3],
229 r[3]
230 ),
231 w[0]
232 ),
233 lerp!(
234 self.lattice(
235 n[0],
236 r[0],
237 n[1] + 1,
238 r[1] - 1.0,
239 n[2] + 1,
240 r[2] - 1.0,
241 0,
242 0.0
243 ),
244 self.lattice(
245 n[0] + 1,
246 r[0] - 1.0,
247 n[1] + 1,
248 r[1] - 1.0,
249 n[2] + 1,
250 r[2] - 1.0,
251 n[3],
252 r[3]
253 ),
254 w[0]
255 ),
256 w[1]
257 ),
258 w[2]
259 ),
260 lerp!(
261 lerp!(
262 lerp!(
263 self.lattice(n[0], r[0], n[1], r[1], n[2], r[2], n[3] + 1, r[3] - 1.0),
264 self.lattice(
265 n[0] + 1,
266 r[0] - 1.0,
267 n[1],
268 r[1],
269 n[2],
270 r[2],
271 n[3] + 1,
272 r[3] - 1.0
273 ),
274 w[0]
275 ),
276 lerp!(
277 self.lattice(
278 n[0],
279 r[0],
280 n[1] + 1,
281 r[1] - 1.0,
282 n[2],
283 r[2],
284 n[3] + 1,
285 r[3] - 1.0
286 ),
287 self.lattice(
288 n[0] + 1,
289 r[0] - 1.0,
290 n[1] + 1,
291 r[1] - 1.0,
292 n[2],
293 r[2],
294 n[3] + 1,
295 r[3] - 1.0
296 ),
297 w[0]
298 ),
299 w[1]
300 ),
301 lerp!(
302 lerp!(
303 self.lattice(
304 n[0],
305 r[0],
306 n[1],
307 r[1],
308 n[2] + 1,
309 r[2] - 1.0,
310 n[3] + 1,
311 r[3] - 1.0
312 ),
313 self.lattice(
314 n[0] + 1,
315 r[0] - 1.0,
316 n[1],
317 r[1],
318 n[2] + 1,
319 r[2] - 1.0,
320 n[3] + 1,
321 r[3] - 1.0
322 ),
323 w[0]
324 ),
325 lerp!(
326 self.lattice(
327 n[0],
328 r[0],
329 n[1] + 1,
330 r[1] - 1.0,
331 n[2] + 1,
332 r[2] - 1.0,
333 0,
334 0.0
335 ),
336 self.lattice(
337 n[0] + 1,
338 r[0] - 1.0,
339 n[1] + 1,
340 r[1] - 1.0,
341 n[2] + 1,
342 r[2] - 1.0,
343 n[3] + 1,
344 r[3] - 1.0
345 ),
346 w[0]
347 ),
348 w[1]
349 ),
350 w[2]
351 ),
352 w[3]
353 )
354 }
355
356 fn cubic_f32(a: f32) -> f32 {
357 a * a * (3.0 - 2.0 * a)
358 }
359}
360
361impl Algorithm for Perlin {
362 fn new<R: RandomAlgorithm>(
363 dimensions: usize,
364 mut initializer: AlgorithmInitializer<R>,
365 ) -> Self {
366 Self {
367 dimensions,
368 map: initializer.map(),
369 buffer: initializer.buffer(dimensions),
370 }
371 }
372
373 fn generate(&self, f: &[f32]) -> f32 {
374 let mut n: [i32; MAX_DIMENSIONS] = [0; MAX_DIMENSIONS]; /* Indexes to pass to lattice function */
375 let mut r: [f32; MAX_DIMENSIONS] = [0.0; MAX_DIMENSIONS]; /* Remainders to pass to lattice function */
376 let mut w: [f32; MAX_DIMENSIONS] = [0.0; MAX_DIMENSIONS]; /* Cubic values to pass to interpolation function */
377 for i in 0..self.dimensions {
378 n[i] = f[i].floor() as i32;
379 r[i] = f[i] - n[i] as f32;
380 w[i] = Self::cubic_f32(r[i]);
381 }
382
383 let value = match self.dimensions {
384 1 => self.perlin_1d(n, r, w),
385 2 => self.perlin_2d(n, r, w),
386 3 => self.perlin_3d(n, r, w),
387 4 => self.perlin_4d(n, r, w),
388 _ => unreachable!(),
389 };
390
391 value.max(-0.99999).min(0.99999)
392 }
393}