fast_noise_lite_rs/
lib.rs

1#![allow(arithmetic_overflow)]
2
3mod lookup;
4
5fn fast_min(a: f32, b: f32) -> f32 {
6    return if a < b { a } else { b };
7}
8
9fn fast_max(a: f32, b: f32) -> f32 {
10    return if a > b { a } else { b };
11}
12
13fn fast_abs(f: f32) -> f32 {
14    return if f < 0.0 { -f } else { f };
15}
16
17fn fast_sqrt(f: f32) -> f32 {
18    return f.sqrt();
19}
20
21fn fast_floor(f: f32) -> i32 {
22    return if f >= 0.0 { f as i32 } else { f as i32 - 1 };
23}
24
25fn fast_round(f: f32) -> i32 {
26    return if f >= 0.0 {
27        (f + 0.5) as i32
28    } else {
29        (f - 0.5) as i32
30    };
31}
32
33fn lerp(a: f32, b: f32, t: f32) -> f32 {
34    return a + t * (b - a);
35}
36
37fn interp_hermite(t: f32) -> f32 {
38    return t * t * (3.0 - 2.0 * t);
39}
40
41fn interp_quintic(t: f32) -> f32 {
42    return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
43}
44
45fn cubic_lerp(a: f32, b: f32, c: f32, d: f32, t: f32) -> f32 {
46    let p = (d - c) - (a - b);
47    return t * t * t * p + t * t * ((a - b) - p) + t * (c - a) + b;
48}
49
50fn ping_pong(mut t: f32) -> f32 {
51    t -= ((t * 0.5) as i32 * 2) as f32;
52    return if t < 1.0 { t } else { 2.0 - t };
53}
54
55pub enum NoiseType {
56    OpenSimplex2,
57    OpenSimplex2S,
58    Cellular,
59    Perlin,
60    ValueCubic,
61    Value,
62}
63
64pub enum RotationType3D {
65    None,
66    ImproveXYPlanes,
67    ImproveXZPlanes,
68}
69
70pub enum FractalType {
71    None,
72    FBm,
73    Ridged,
74    PingPong,
75    DomainWarpProgressive,
76    DomainWarpIndependent,
77}
78
79#[derive(PartialEq)]
80pub enum CellularDistanceFunction {
81    Euclidean,
82    EuclideanSq,
83    Manhattan,
84    Hybrid,
85}
86
87#[derive(PartialEq, PartialOrd)]
88pub enum CellularReturnType {
89    CellValue,
90    Distance,
91    Distance2,
92    Distance2Add,
93    Distance2Sub,
94    Distance2Mul,
95    Distance2Div,
96}
97
98pub enum DomainWarpType {
99    OpenSimplex2,
100    OpenSimplex2Reduced,
101    BasicGrid,
102}
103
104pub enum TransformType3D {
105    None,
106    ImproveXYPlanes,
107    ImproveXZPlanes,
108    DefaultOpenSimplex2,
109}
110
111pub struct FastNoiseLite {
112    seed: i32,
113    frequency: f32,
114    noise_type: NoiseType,
115    rotation_type3d: RotationType3D,
116    transform_type3d: TransformType3D,
117
118    fractal_type: FractalType,
119    octaves: usize,
120    lacunarity: f32,
121    gain: f32,
122    weighted_strength: f32,
123    ping_pong_strength: f32,
124
125    fractal_bounding: f32,
126
127    cellular_distance_function: CellularDistanceFunction,
128    cellular_return_type: CellularReturnType,
129    cellular_jitter_modifier: f32,
130
131    domain_warp_type: DomainWarpType,
132    warp_transform_type3d: TransformType3D,
133    domain_warp_amp: f32,
134}
135
136impl FastNoiseLite {
137    pub fn new(seed: i32) -> Self {
138        Self {
139            seed,
140            frequency: 0.01,
141            noise_type: NoiseType::OpenSimplex2,
142            rotation_type3d: RotationType3D::None,
143            transform_type3d: TransformType3D::DefaultOpenSimplex2,
144            fractal_type: FractalType::None,
145            octaves: 3,
146            lacunarity: 2.0,
147            gain: 0.5,
148            weighted_strength: 0.0,
149            ping_pong_strength: 2.0,
150            fractal_bounding: 1.0 / 1.75,
151            cellular_distance_function: CellularDistanceFunction::EuclideanSq,
152            cellular_return_type: CellularReturnType::Distance,
153            cellular_jitter_modifier: 1.0,
154            domain_warp_type: DomainWarpType::OpenSimplex2,
155            warp_transform_type3d: TransformType3D::DefaultOpenSimplex2,
156            domain_warp_amp: 1.0,
157        }
158    }
159    pub fn set_seed(&mut self, seed: i32) {
160        self.seed = seed;
161    }
162    pub fn set_frequency(&mut self, frequency: f32) {
163        self.frequency = frequency;
164    }
165    pub fn set_noise_type(&mut self, noise_type: NoiseType) {
166        self.noise_type = noise_type;
167        self.update_transform_type_3d();
168    }
169    pub fn set_rotation_type_3d(&mut self, rotation_type3d: RotationType3D) {
170        self.rotation_type3d = rotation_type3d;
171        self.update_transform_type_3d();
172        self.update_warp_transform_type_3d();
173    }
174    pub fn set_fractal_type(&mut self, fractal_type: FractalType) {
175        self.fractal_type = fractal_type;
176    }
177    pub fn set_fractal_octaves(&mut self, octaves: usize) {
178        self.octaves = octaves;
179        self.calculate_fractal_bounding();
180    }
181    pub fn set_fractal_lacunarity(&mut self, lacunarity: f32) {
182        self.lacunarity = lacunarity;
183    }
184    pub fn set_fractal_gain(&mut self, gain: f32) {
185        self.gain = gain;
186        self.calculate_fractal_bounding();
187    }
188
189    pub fn set_fractal_weighted_strength(&mut self, weighted_strength: f32) {
190        self.weighted_strength = weighted_strength;
191    }
192
193    pub fn set_fractal_ping_pong_strength(&mut self, ping_pong_strength: f32) {
194        self.ping_pong_strength = ping_pong_strength;
195    }
196
197    pub fn set_cellular_distance_function(
198        &mut self,
199        cellular_distance_function: CellularDistanceFunction,
200    ) {
201        self.cellular_distance_function = cellular_distance_function;
202    }
203
204    pub fn set_cellular_return_type(&mut self, cellular_return_type: CellularReturnType) {
205        self.cellular_return_type = cellular_return_type;
206    }
207
208    pub fn set_cellular_jitter(&mut self, cellular_jitter: f32) {
209        self.cellular_jitter_modifier = cellular_jitter;
210    }
211
212    pub fn set_domain_warp_type(&mut self, domain_warp_type: DomainWarpType) {
213        self.domain_warp_type = domain_warp_type;
214        self.update_warp_transform_type_3d();
215    }
216
217    pub fn set_domain_warp_amp(&mut self, domain_warp_amp: f32) {
218        self.domain_warp_amp = domain_warp_amp;
219    }
220
221    pub fn get_noise_2d(&self, mut x: f32, mut y: f32) -> f32 {
222        self.transform_noise_coordinate_2d(&mut x, &mut y);
223
224        match self.fractal_type {
225            FractalType::FBm => self.gen_fractal_fbm_2d(x, y),
226            FractalType::Ridged => self.gen_fractal_ridged_2d(x, y),
227            FractalType::PingPong => self.gen_fractal_ping_pong_2d(x, y),
228            _ => self.gen_noise_single_2d(self.seed, x, y),
229        }
230    }
231
232    pub fn get_noise_3d(&self, mut x: f32, mut y: f32, mut z: f32) -> f32 {
233        self.transform_noise_coordinate_3d(&mut x, &mut y, &mut z);
234        match self.fractal_type {
235            FractalType::FBm => self.gen_fractal_fbm_3d(x, y, z),
236            FractalType::Ridged => self.gen_fractal_ridged_3d(x, y, z),
237            FractalType::PingPong => self.gen_fractal_ping_pong_3d(x, y, z),
238            _ => self.gen_noise_single_3d(self.seed, x, y, z),
239        }
240    }
241
242    pub fn domain_warp_2d(&self, x: &mut f32, y: &mut f32) {
243        match self.fractal_type {
244            FractalType::DomainWarpProgressive => self.domain_warp_fractal_progressive_2d(x, y),
245            FractalType::DomainWarpIndependent => self.domain_warp_fractal_independent_2d(x, y),
246            _ => self.domain_warp_single_2d(x, y),
247        }
248    }
249    pub fn domain_warp_3d(&self, x: &mut f32, y: &mut f32, z: &mut f32) {
250        match self.fractal_type {
251            FractalType::DomainWarpIndependent => self.domain_warp_fractal_independent_3d(x, y, z),
252            FractalType::DomainWarpProgressive => self.domain_warp_fractal_progressive_3d(x, y, z),
253            _ => self.domain_warp_single_3d(x, y, z),
254        }
255    }
256    fn calculate_fractal_bounding(&mut self) {
257        let gain = fast_abs(self.gain);
258        let mut amp = gain;
259        let mut amp_fractal = 1.0;
260        for _ in 0..self.octaves {
261            amp_fractal += amp;
262            amp *= gain;
263        }
264        self.fractal_bounding = 1.0 / amp_fractal;
265    }
266    const PRIME_X: i32 = 501125321;
267    const PRIME_Y: i32 = 1136930381;
268    const PRIME_Z: i32 = 1720413743;
269
270    fn hash_2d(seed: i32, x_primed: i32, y_primed: i32) -> i32 {
271        let mut hash = seed ^ x_primed ^ y_primed;
272        hash = hash.wrapping_mul(0x27d4eb2d);
273        return hash;
274    }
275
276    fn hash_3d(seed: i32, x_primed: i32, y_primed: i32, z_primed: i32) -> i32 {
277        let mut hash = seed ^ x_primed ^ y_primed ^ z_primed;
278        hash = hash.wrapping_mul(0x27d4eb2d);
279        return hash;
280    }
281
282    fn val_coord_2d(seed: i32, x_primed: i32, y_primed: i32) -> f32 {
283        let mut hash: i32 = Self::hash_2d(seed, x_primed, y_primed);
284
285        hash *= hash;
286        hash ^= hash << 19;
287        return hash as f32 * (1.0 / 2147483648.0);
288    }
289
290    fn val_coord_3d(seed: i32, x_primed: i32, y_primed: i32, z_primed: i32) -> f32 {
291        let mut hash: i32 = Self::hash_3d(seed, x_primed, y_primed, z_primed);
292
293        hash *= hash;
294        hash ^= hash << 19;
295        return hash as f32 * (1.0 / 2147483648.0);
296    }
297
298    fn grad_coord_2d(seed: i32, x_primed: i32, y_primed: i32, xd: f32, yd: f32) -> f32 {
299        let mut hash = Self::hash_2d(seed, x_primed, y_primed);
300        hash ^= hash >> 15;
301        hash &= 127 << 1;
302
303        let xg = lookup::GRADIENTS_2D[hash as usize];
304        let yg = lookup::GRADIENTS_2D[(hash | 1) as usize];
305
306        return xd * xg + yd * yg;
307    }
308
309    fn grad_coord_3d(
310        seed: i32,
311        x_primed: i32,
312        y_primed: i32,
313        z_primed: i32,
314        xd: f32,
315        yd: f32,
316        zd: f32,
317    ) -> f32 {
318        let mut hash: i32 = Self::hash_3d(seed, x_primed, y_primed, z_primed);
319        hash ^= hash >> 15;
320        hash &= 63 << 2;
321
322        let xg: f32 = lookup::GRADIENTS_3D[hash as usize];
323        let yg: f32 = lookup::GRADIENTS_3D[(hash | 1) as usize];
324        let zg: f32 = lookup::GRADIENTS_3D[(hash | 2) as usize];
325
326        return xd * xg + yd * yg + zd * zg;
327    }
328
329    fn grad_coord_out_2d(seed: i32, x_primed: i32, y_primed: i32, xo: &mut f32, yo: &mut f32) {
330        let hash: i32 = Self::hash_2d(seed, x_primed, y_primed) & (255 << 1);
331
332        *xo = lookup::RAND_VECS_2D[hash as usize];
333        *yo = lookup::RAND_VECS_2D[(hash | 1) as usize];
334    }
335
336    fn grad_coord_out_3d(
337        seed: i32,
338        x_primed: i32,
339        y_primed: i32,
340        z_primed: i32,
341        xo: &mut f32,
342        yo: &mut f32,
343        zo: &mut f32,
344    ) {
345        let hash: i32 = Self::hash_3d(seed, x_primed, y_primed, z_primed) & (255 << 2);
346
347        *xo = lookup::RAND_VECS_3D[hash as usize];
348        *yo = lookup::RAND_VECS_3D[(hash | 1) as usize];
349        *zo = lookup::RAND_VECS_3D[(hash | 2) as usize];
350    }
351
352    fn grad_coord_dual_2d(
353        seed: i32,
354        x_primed: i32,
355        y_primed: i32,
356        xd: f32,
357        yd: f32,
358        xo: &mut f32,
359        yo: &mut f32,
360    ) {
361        let hash: i32 = Self::hash_2d(seed, x_primed, y_primed);
362        let index1: i32 = hash & (127 << 1);
363        let index2: i32 = (hash >> 7) & (255 << 1);
364
365        let xg: f32 = lookup::GRADIENTS_2D[index1 as usize];
366        let yg: f32 = lookup::GRADIENTS_2D[(index1 | 1) as usize];
367        let value: f32 = xd * xg + yd * yg;
368
369        let xgo: f32 = lookup::RAND_VECS_2D[index2 as usize];
370        let ygo: f32 = lookup::RAND_VECS_2D[(index2 | 1) as usize];
371
372        *xo = value * xgo;
373        *yo = value * ygo;
374    }
375
376    fn grad_coord_dual_3d(
377        seed: i32,
378        x_primed: i32,
379        y_primed: i32,
380        z_primed: i32,
381        xd: f32,
382        yd: f32,
383        zd: f32,
384        xo: &mut f32,
385        yo: &mut f32,
386        zo: &mut f32,
387    ) {
388        let hash: i32 = Self::hash_3d(seed, x_primed, y_primed, z_primed);
389        let index1: i32 = hash & (63 << 2);
390        let index2: i32 = (hash >> 6) & (255 << 2);
391
392        let xg: f32 = lookup::GRADIENTS_3D[index1 as usize];
393        let yg: f32 = lookup::GRADIENTS_3D[(index1 | 1) as usize];
394        let zg: f32 = lookup::GRADIENTS_3D[(index1 | 2) as usize];
395        let value: f32 = xd * xg + yd * yg + zd * zg;
396
397        let xgo: f32 = lookup::RAND_VECS_3D[index2 as usize];
398        let ygo: f32 = lookup::RAND_VECS_3D[(index2 | 1) as usize];
399        let zgo: f32 = lookup::RAND_VECS_3D[(index2 | 2) as usize];
400
401        *xo = value * xgo;
402        *yo = value * ygo;
403        *zo = value * zgo;
404    }
405    fn gen_noise_single_2d(&self, seed: i32, x: f32, y: f32) -> f32 {
406        match self.noise_type {
407            NoiseType::OpenSimplex2 => self.single_open_simplex_2_2d(seed, x, y),
408            NoiseType::OpenSimplex2S => self.single_open_simplex2s_2d(seed, x, y),
409            NoiseType::Cellular => self.single_cellular_2d(seed, x, y),
410            NoiseType::Perlin => self.single_perlin_2d(seed, x, y),
411            NoiseType::ValueCubic => self.single_value_cubic_2d(seed, x, y),
412            NoiseType::Value => self.single_value_2d(seed, x, y),
413        }
414    }
415
416    fn gen_noise_single_3d(&self, seed: i32, x: f32, y: f32, z: f32) -> f32 {
417        match self.noise_type {
418            NoiseType::OpenSimplex2 => self.single_open_simplex_2_3d(seed, x, y, z),
419            NoiseType::OpenSimplex2S => self.single_open_simplex_2s_3d(seed, x, y, z),
420            NoiseType::Cellular => self.single_cellular_3d(seed, x, y, z),
421            NoiseType::Perlin => self.single_perlin_3d(seed, x, y, z),
422            NoiseType::ValueCubic => self.single_value_cubic_3d(seed, x, y, z),
423            NoiseType::Value => self.single_value_3d(seed, x, y, z),
424        }
425    }
426
427    fn transform_noise_coordinate_2d(&self, x: &mut f32, y: &mut f32) {
428        *x *= self.frequency;
429        *y *= self.frequency;
430
431        match self.noise_type {
432            NoiseType::OpenSimplex2 | NoiseType::OpenSimplex2S => {
433                const SQRT3: f32 = 1.7320508075688772935274463415059;
434                const F2: f32 = 0.5 * (SQRT3 - 1.0);
435                let t: f32 = (*x + *y) * F2;
436                *x += t;
437                *y += t;
438            }
439            _ => {}
440        }
441    }
442
443    fn transform_noise_coordinate_3d(&self, x: &mut f32, y: &mut f32, z: &mut f32) {
444        *x *= self.frequency;
445        *y *= self.frequency;
446        *z *= self.frequency;
447
448        match self.transform_type3d {
449            TransformType3D::ImproveXYPlanes => {
450                let xy: f32 = *x + *y;
451                let s2: f32 = xy * -0.211324865405187;
452                *z *= 0.577350269189626;
453                *x += s2 - *z;
454                *y = *y + s2 - *z;
455                *z += xy * 0.577350269189626;
456            }
457            TransformType3D::ImproveXZPlanes => {
458                let xz: f32 = *x + *z;
459                let s2: f32 = xz * -0.211324865405187;
460                *y *= 0.577350269189626;
461                *x += s2 - *y;
462                *z += s2 - *y;
463                *y += xz * 0.577350269189626;
464            }
465            TransformType3D::DefaultOpenSimplex2 => {
466                const R3: f32 = 2.0 / 3.0;
467                let r: f32 = (*x + *y + *z) * R3; // Rotation, not skew
468                *x = r - *x;
469                *y = r - *y;
470                *z = r - *z;
471            }
472            TransformType3D::None => {}
473        }
474    }
475
476    fn update_transform_type_3d(&mut self) {
477        match self.rotation_type3d {
478            RotationType3D::ImproveXYPlanes => {
479                self.transform_type3d = TransformType3D::ImproveXYPlanes;
480            }
481            RotationType3D::ImproveXZPlanes => {
482                self.transform_type3d = TransformType3D::ImproveXZPlanes;
483            }
484            RotationType3D::None => match self.noise_type {
485                NoiseType::OpenSimplex2 | NoiseType::OpenSimplex2S => {
486                    self.transform_type3d = TransformType3D::DefaultOpenSimplex2;
487                }
488                _ => {
489                    self.transform_type3d = TransformType3D::None;
490                }
491            },
492        };
493    }
494
495    // Domain Warp Coordinate Transforms
496
497    fn transform_domain_warp_coordinate_2d(&self, x: &mut f32, y: &mut f32) {
498        match self.domain_warp_type {
499            DomainWarpType::OpenSimplex2 | DomainWarpType::OpenSimplex2Reduced => {
500                const SQRT3: f32 = 1.7320508075688772935274463415059;
501                const F2: f32 = 0.5 * (SQRT3 - 1.0);
502                let t: f32 = (*x + *y) * F2;
503                *x += t;
504                *y += t;
505            }
506            _ => {}
507        }
508    }
509
510    fn transform_domain_warp_coordinate_3d(&self, x: &mut f32, y: &mut f32, z: &mut f32) {
511        match self.warp_transform_type3d {
512            TransformType3D::ImproveXYPlanes => {
513                let xy: f32 = *x + *y;
514                let s2: f32 = xy * -0.211324865405187;
515                *z *= 0.577350269189626;
516                *x += s2 - *z;
517                *y = *y + s2 - *z;
518                *z += xy * 0.577350269189626;
519            }
520            TransformType3D::ImproveXZPlanes => {
521                let xz: f32 = *x + *z;
522                let s2: f32 = xz * -0.211324865405187;
523                *y *= 0.577350269189626;
524                *x += s2 - *y;
525                *z += s2 - *y;
526                *y += xz * 0.577350269189626;
527            }
528            TransformType3D::DefaultOpenSimplex2 => {
529                const R3: f32 = 2.0 / 3.0;
530                let r: f32 = (*x + *y + *z) * R3; // Rotation, not skew
531                *x = r - *x;
532                *y = r - *y;
533                *z = r - *z;
534            }
535            _ => {}
536        }
537    }
538
539    fn update_warp_transform_type_3d(&mut self) {
540        match self.rotation_type3d {
541            RotationType3D::ImproveXYPlanes => {
542                self.warp_transform_type3d = TransformType3D::ImproveXYPlanes
543            }
544            RotationType3D::ImproveXZPlanes => {
545                self.warp_transform_type3d = TransformType3D::ImproveXZPlanes
546            }
547            RotationType3D::None => match self.domain_warp_type {
548                DomainWarpType::OpenSimplex2 | DomainWarpType::OpenSimplex2Reduced => {
549                    self.warp_transform_type3d = TransformType3D::DefaultOpenSimplex2
550                }
551                _ => self.warp_transform_type3d = TransformType3D::None,
552            },
553        }
554    }
555
556    fn gen_fractal_fbm_2d(&self, mut x: f32, mut y: f32) -> f32 {
557        let mut seed = self.seed;
558        let mut sum = 0.0;
559        let mut amp = self.fractal_bounding;
560
561        for _ in 0..self.octaves {
562            let noise = self.gen_noise_single_2d(seed, x, y);
563            seed += 1;
564            sum += noise * amp;
565            amp *= lerp(
566                1.0,
567                fast_min(noise + 1.0, 2.0) * 0.5,
568                self.weighted_strength,
569            );
570
571            x *= self.lacunarity;
572            y *= self.lacunarity;
573            amp *= self.gain;
574        }
575
576        return sum;
577    }
578
579    fn gen_fractal_fbm_3d(&self, mut x: f32, mut y: f32, mut z: f32) -> f32 {
580        let mut seed = self.seed;
581        let mut sum = 0.0;
582        let mut amp = self.fractal_bounding;
583
584        for _ in 0..self.octaves {
585            let noise = self.gen_noise_single_3d(seed, x, y, z);
586            seed += 1;
587            sum += noise * amp;
588            amp *= lerp(1.0, (noise + 1.0) * 0.5, self.weighted_strength);
589
590            x *= self.lacunarity;
591            y *= self.lacunarity;
592            z *= self.lacunarity;
593            amp *= self.gain;
594        }
595
596        return sum;
597    }
598
599    fn gen_fractal_ridged_2d(&self, mut x: f32, mut y: f32) -> f32 {
600        let mut seed = self.seed;
601        let mut sum = 0.0;
602        let mut amp = self.fractal_bounding;
603
604        for _ in 0..self.octaves {
605            let noise = fast_abs(self.gen_noise_single_2d(seed, x, y));
606            seed += 1;
607            sum += (noise * -2.0 + 1.0) * amp;
608            amp *= lerp(1.0, 1.0 - noise, self.weighted_strength);
609
610            x *= self.lacunarity;
611            y *= self.lacunarity;
612            amp *= self.gain;
613        }
614
615        return sum;
616    }
617
618    fn gen_fractal_ridged_3d(&self, mut x: f32, mut y: f32, mut z: f32) -> f32 {
619        let mut seed = self.seed;
620        let mut sum = 0.0;
621        let mut amp = self.fractal_bounding;
622
623        for _ in 0..self.octaves {
624            let noise = fast_abs(self.gen_noise_single_3d(seed, x, y, z));
625            seed += 1;
626            sum += (noise * -2.0 + 1.0) * amp;
627            amp *= lerp(1.0, 1.0 - noise, self.weighted_strength);
628
629            x *= self.lacunarity;
630            y *= self.lacunarity;
631            z *= self.lacunarity;
632            amp *= self.gain;
633        }
634
635        return sum;
636    }
637
638    fn gen_fractal_ping_pong_2d(&self, mut x: f32, mut y: f32) -> f32 {
639        let mut seed = self.seed;
640        let mut sum = 0.0;
641        let mut amp = self.fractal_bounding;
642
643        for _ in 0..self.octaves {
644            let noise =
645                ping_pong((self.gen_noise_single_2d(seed, x, y) + 1.0) * self.ping_pong_strength);
646            seed += 1;
647            sum += (noise - 0.5) * 2.0 * amp;
648            amp *= lerp(1.0, noise, self.weighted_strength);
649
650            x *= self.lacunarity;
651            y *= self.lacunarity;
652            amp *= self.gain;
653        }
654
655        return sum;
656    }
657
658    fn gen_fractal_ping_pong_3d(&self, mut x: f32, mut y: f32, mut z: f32) -> f32 {
659        let mut seed = self.seed;
660        let mut sum = 0.0;
661        let mut amp = self.fractal_bounding;
662
663        for _ in 0..self.octaves {
664            let noise = ping_pong(
665                (self.gen_noise_single_3d(seed, x, y, z) + 1.0) * self.ping_pong_strength,
666            );
667            seed += 1;
668            sum += (noise - 0.5) * 2.0 * amp;
669            amp *= lerp(1.0, noise, self.weighted_strength);
670
671            x *= self.lacunarity;
672            y *= self.lacunarity;
673            z *= self.lacunarity;
674            amp *= self.gain;
675        }
676
677        return sum;
678    }
679    fn single_open_simplex_2_2d(&self, seed: i32, x: f32, y: f32) -> f32 {
680        // 2D OpenSimplex2 case uses the same algorithm as ordinary Simplex.
681
682        const SQRT3: f32 = 1.7320508075688772935274463415059;
683        const G2: f32 = (3.0 - SQRT3) / 6.0;
684
685        /*
686         * --- Skew moved to TransformNoiseCoordinate method ---
687         * const F2: f32 = 0.5f * (SQRT3 - 1);
688         * s: f32 = (x + y) * F2;
689         * x += s; y += s;
690         */
691
692        let mut i: i32 = fast_floor(x);
693        let mut j: i32 = fast_floor(y);
694        let xi: f32 = x - i as f32;
695        let yi: f32 = y - j as f32;
696
697        let t: f32 = (xi + yi) * G2;
698        let x0: f32 = xi - t;
699        let y0: f32 = yi - t;
700
701        i = i.wrapping_mul(Self::PRIME_X);
702        j = j.wrapping_mul(Self::PRIME_Y);
703
704        let n0: f32;
705        let n1: f32;
706        let n2: f32;
707
708        let a: f32 = 0.5 - x0 * x0 - y0 * y0;
709        if a <= 0.0 {
710            n0 = 0.0;
711        } else {
712            n0 = (a * a) * (a * a) * Self::grad_coord_2d(seed, i, j, x0, y0);
713        }
714
715        let c: f32 = (2.0 * (1.0 - 2.0 * G2) * (1.0 / G2 - 2.0)) * t
716            + ((-2.0 * (1.0 - 2.0 * G2) * (1.0 - 2.0 * G2)) + a);
717        if c <= 0.0 {
718            n2 = 0.0;
719        } else {
720            let x2: f32 = x0 + (2.0 * G2 - 1.0);
721            let y2: f32 = y0 + (2.0 * G2 - 1.0);
722            n2 = (c * c)
723                * (c * c)
724                * Self::grad_coord_2d(
725                    seed,
726                    i.wrapping_add(Self::PRIME_X),
727                    j.wrapping_add(Self::PRIME_Y),
728                    x2,
729                    y2,
730                );
731        }
732
733        if y0 > x0 {
734            let x1: f32 = x0 + G2;
735            let y1: f32 = y0 + (G2 - 1.0);
736            let b: f32 = 0.5 - x1 * x1 - y1 * y1;
737            if b <= 0.0 {
738                n1 = 0.0;
739            } else {
740                n1 = (b * b)
741                    * (b * b)
742                    * Self::grad_coord_2d(seed, i, j.wrapping_add(Self::PRIME_Y), x1, y1);
743            }
744        } else {
745            let x1: f32 = x0 + (G2 - 1.0);
746            let y1: f32 = y0 + G2;
747            let b: f32 = 0.5 - x1 * x1 - y1 * y1;
748            if b <= 0.0 {
749                n1 = 0.0;
750            } else {
751                n1 = (b * b)
752                    * (b * b)
753                    * Self::grad_coord_2d(seed, i.wrapping_add(Self::PRIME_X), j, x1, y1);
754            }
755        }
756
757        return (n0 + n1 + n2) * 99.83685446303647;
758    }
759
760    fn single_open_simplex_2_3d(&self, mut seed: i32, x: f32, y: f32, z: f32) -> f32 {
761        // 3D OpenSimplex2 case uses two offset rotated cube grids.
762
763        /*
764         * --- Rotation moved to TransformNoiseCoordinate method ---
765         * const R3: f32 = (FNfloat)(2.0 / 3.0);
766         * r: f32 = (x + y + z) * R3; // Rotation, not skew
767         * x = r - x; y = r - y; z = r - z;
768         */
769
770        let mut i: i32 = fast_round(x);
771        let mut j: i32 = fast_round(y);
772        let mut k: i32 = fast_round(z);
773        let mut x0: f32 = x - i as f32;
774        let mut y0: f32 = y - j as f32;
775        let mut z0: f32 = z - k as f32;
776
777        let mut x_nsign: i32 = (-1.0 - x0) as i32 | 1;
778        let mut y_nsign: i32 = (-1.0 - y0) as i32 | 1;
779        let mut z_nsign: i32 = (-1.0 - z0) as i32 | 1;
780
781        let mut ax0: f32 = x_nsign as f32 * -x0;
782        let mut ay0: f32 = y_nsign as f32 * -y0;
783        let mut az0: f32 = z_nsign as f32 * -z0;
784
785        i = i.wrapping_mul(Self::PRIME_X);
786        j = j.wrapping_mul(Self::PRIME_Y);
787        k = k.wrapping_mul(Self::PRIME_Z);
788
789        let mut value: f32 = 0.0;
790        let mut a: f32 = (0.6 - x0 * x0) - (y0 * y0 + z0 * z0);
791
792        for l in 0..2 {
793            if a > 0.0 {
794                value += (a * a) * (a * a) * Self::grad_coord_3d(seed, i, j, k, x0, y0, z0);
795            }
796
797            let mut b: f32 = a + 1.0;
798            let mut i1: i32 = i;
799            let mut j1: i32 = j;
800            let mut k1: i32 = k;
801            let mut x1: f32 = x0;
802            let mut y1: f32 = y0;
803            let mut z1: f32 = z0;
804
805            if ax0 >= ay0 && ax0 >= az0 {
806                x1 += x_nsign as f32;
807                b -= x_nsign as f32 * 2.0 * x1;
808                i1 -= x_nsign * Self::PRIME_X;
809            } else if ay0 > ax0 && ay0 >= az0 {
810                y1 += y_nsign as f32;
811                b -= y_nsign as f32 * 2.0 * y1;
812                j1 -= y_nsign * Self::PRIME_Y;
813            } else {
814                z1 += z_nsign as f32;
815                b -= z_nsign as f32 * 2.0 * z1;
816                k1 -= z_nsign * Self::PRIME_Z;
817            }
818
819            if b > 0.0 {
820                value += (b * b) * (b * b) * Self::grad_coord_3d(seed, i1, j1, k1, x1, y1, z1);
821            }
822
823            if l == 1 {
824                break;
825            }
826
827            ax0 = 0.5 - ax0;
828            ay0 = 0.5 - ay0;
829            az0 = 0.5 - az0;
830
831            x0 = x_nsign as f32 * ax0;
832            y0 = y_nsign as f32 * ay0;
833            z0 = z_nsign as f32 * az0;
834
835            a += (0.75 - ax0) - (ay0 + az0);
836
837            i += (x_nsign >> 1) & Self::PRIME_X;
838            j += (y_nsign >> 1) & Self::PRIME_Y;
839            k += (z_nsign >> 1) & Self::PRIME_Z;
840
841            x_nsign = -x_nsign;
842            y_nsign = -y_nsign;
843            z_nsign = -z_nsign;
844
845            seed = !seed;
846        }
847
848        return value * 32.69428253173828125;
849    }
850
851    // OpenSimplex2S Noise
852
853    fn single_open_simplex2s_2d(&self, seed: i32, x: f32, y: f32) -> f32 {
854        // 2D OpenSimplex2S case is a modified 2D simplex noise.
855
856        const SQRT3: f32 = 1.7320508075688772935274463415059;
857        const G2: f32 = (3.0 - SQRT3) / 6.0;
858
859        /*
860         * --- Skew moved to TransformNoiseCoordinate method ---
861         * const F2: f32 = 0.5f * (SQRT3 - 1);
862         * s: f32 = (x + y) * F2;
863         * x += s; y += s;
864         */
865
866        let mut i: i32 = fast_floor(x);
867        let mut j: i32 = fast_floor(y);
868        let xi: f32 = x - i as f32;
869        let yi: f32 = y - j as f32;
870
871        i = i.wrapping_mul(Self::PRIME_X);
872        j = j.wrapping_mul(Self::PRIME_Y);
873        let i1: i32 = i.wrapping_add(Self::PRIME_X);
874        let j1: i32 = j.wrapping_add(Self::PRIME_Y);
875
876        let t: f32 = (xi + yi) * G2;
877        let x0: f32 = xi - t;
878        let y0: f32 = yi - t;
879
880        let a0: f32 = (2.0 / 3.0) - x0 * x0 - y0 * y0;
881        let mut value: f32 = (a0 * a0) * (a0 * a0) * Self::grad_coord_2d(seed, i, j, x0, y0);
882
883        let a1: f32 = (2.0 * (1.0 - 2.0 * G2) * (1.0 / G2 - 2.0)) * t
884            + ((-2.0 * (1.0 - 2.0 * G2) * (1.0 - 2.0 * G2)) + a0);
885        let x1: f32 = x0 - (1.0 - 2.0 * G2);
886        let y1: f32 = y0 - (1.0 - 2.0 * G2);
887        value += (a1 * a1) * (a1 * a1) * Self::grad_coord_2d(seed, i1, j1, x1, y1);
888
889        // Nested conditionals were faster than compact bit logic/arithmetic.
890        let xmyi: f32 = xi - yi;
891        if t > G2 {
892            if xi + xmyi > 1.0 {
893                let x2: f32 = x0 + (3.0 * G2 - 2.0);
894                let y2: f32 = y0 + (3.0 * G2 - 1.0);
895                let a2: f32 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
896                if a2 > 0.0 {
897                    value += (a2 * a2)
898                        * (a2 * a2)
899                        * Self::grad_coord_2d(
900                            seed,
901                            i.wrapping_add(Self::PRIME_X << 1),
902                            j.wrapping_add(Self::PRIME_Y),
903                            x2,
904                            y2,
905                        );
906                }
907            } else {
908                let x2: f32 = x0 + G2;
909                let y2: f32 = y0 + (G2 - 1.0);
910                let a2: f32 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
911                if a2 > 0.0 {
912                    value += (a2 * a2)
913                        * (a2 * a2)
914                        * Self::grad_coord_2d(seed, i, j.wrapping_add(Self::PRIME_Y), x2, y2);
915                }
916            }
917
918            if yi - xmyi > 1.0 {
919                let x3: f32 = x0 + (3.0 * G2 - 1.0);
920                let y3: f32 = y0 + (3.0 * G2 - 2.0);
921                let a3: f32 = (2.0 / 3.0) - x3 * x3 - y3 * y3;
922                if a3 > 0.0 {
923                    value += (a3 * a3)
924                        * (a3 * a3)
925                        * Self::grad_coord_2d(
926                            seed,
927                            i.wrapping_add(Self::PRIME_X),
928                            j.wrapping_add(Self::PRIME_Y << 1),
929                            x3,
930                            y3,
931                        );
932                }
933            } else {
934                let x3: f32 = x0 + (G2 - 1.0);
935                let y3: f32 = y0 + G2;
936                let a3: f32 = (2.0 / 3.0) - x3 * x3 - y3 * y3;
937                if a3 > 0.0 {
938                    value += (a3 * a3)
939                        * (a3 * a3)
940                        * Self::grad_coord_2d(seed, i.wrapping_add(Self::PRIME_X), j, x3, y3);
941                }
942            }
943        } else {
944            if xi + xmyi < 0.0 {
945                let x2: f32 = x0 + (1.0 - G2);
946                let y2: f32 = y0 - G2;
947                let a2: f32 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
948                if a2 > 0.0 {
949                    value += (a2 * a2)
950                        * (a2 * a2)
951                        * Self::grad_coord_2d(seed, i.wrapping_sub(Self::PRIME_X), j, x2, y2);
952                }
953            } else {
954                let x2: f32 = x0 + (G2 - 1.0);
955                let y2: f32 = y0 + G2;
956                let a2: f32 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
957                if a2 > 0.0 {
958                    value += (a2 * a2)
959                        * (a2 * a2)
960                        * Self::grad_coord_2d(seed, i.wrapping_add(Self::PRIME_X), j, x2, y2);
961                }
962            }
963
964            if yi < xmyi {
965                let x2: f32 = x0 - G2;
966                let y2: f32 = y0 - (G2 - 1.0);
967                let a2: f32 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
968                if a2 > 0.0 {
969                    value += (a2 * a2)
970                        * (a2 * a2)
971                        * Self::grad_coord_2d(seed, i, j.wrapping_sub(Self::PRIME_Y), x2, y2);
972                }
973            } else {
974                let x2: f32 = x0 + G2;
975                let y2: f32 = y0 + (G2 - 1.0);
976                let a2: f32 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
977                if a2 > 0.0 {
978                    value += (a2 * a2)
979                        * (a2 * a2)
980                        * Self::grad_coord_2d(seed, i, j.wrapping_add(Self::PRIME_Y), x2, y2);
981                }
982            }
983        }
984
985        return value * 18.24196194486065;
986    }
987
988    fn single_open_simplex_2s_3d(&self, seed: i32, x: f32, y: f32, z: f32) -> f32 {
989        // 3D OpenSimplex2S case uses two offset rotated cube grids.
990
991        /*
992         * --- Rotation moved to TransformNoiseCoordinate method ---
993         * const R3: f32 = (FNfloat)(2.0 / 3.0);
994         * r: f32 = (x + y + z) * R3; // Rotation, not skew
995         * x = r - x; y = r - y; z = r - z;
996         */
997
998        let mut i: i32 = fast_floor(x);
999        let mut j: i32 = fast_floor(y);
1000        let mut k: i32 = fast_floor(z);
1001        let xi: f32 = x - i as f32;
1002        let yi: f32 = y - j as f32;
1003        let zi: f32 = z - k as f32;
1004
1005        i = i.wrapping_mul(Self::PRIME_X);
1006        j = j.wrapping_mul(Self::PRIME_Y);
1007        k = k.wrapping_mul(Self::PRIME_Z);
1008        let seed2: i32 = seed.wrapping_add(1293373);
1009
1010        let x_n_mask: i32 = (-0.5 - xi) as i32;
1011        let y_n_mask: i32 = (-0.5 - yi) as i32;
1012        let z_n_mask: i32 = (-0.5 - zi) as i32;
1013
1014        let x0: f32 = xi + x_n_mask as f32;
1015        let y0: f32 = yi + y_n_mask as f32;
1016        let z0: f32 = zi + z_n_mask as f32;
1017        let a0: f32 = 0.75 - x0 * x0 - y0 * y0 - z0 * z0;
1018        let mut value: f32 = (a0 * a0)
1019            * (a0 * a0)
1020            * Self::grad_coord_3d(
1021                seed,
1022                i + (x_n_mask & Self::PRIME_X),
1023                j + (y_n_mask & Self::PRIME_Y),
1024                k + (z_n_mask & Self::PRIME_Z),
1025                x0,
1026                y0,
1027                z0,
1028            );
1029
1030        let x1: f32 = xi - 0.5;
1031        let y1: f32 = yi - 0.5;
1032        let z1: f32 = zi - 0.5;
1033        let a1: f32 = 0.75 - x1 * x1 - y1 * y1 - z1 * z1;
1034        value += (a1 * a1)
1035            * (a1 * a1)
1036            * Self::grad_coord_3d(
1037                seed2,
1038                i.wrapping_add(Self::PRIME_X),
1039                j.wrapping_add(Self::PRIME_Y),
1040                k.wrapping_add(Self::PRIME_Z),
1041                x1,
1042                y1,
1043                z1,
1044            );
1045
1046        let x_a_flip_mask_0: f32 = ((x_n_mask | 1) << 1) as f32 * x1;
1047        let y_a_flip_mask_0: f32 = ((y_n_mask | 1) << 1) as f32 * y1;
1048        let z_a_flip_mask_0: f32 = ((z_n_mask | 1) << 1) as f32 * z1;
1049        let x_a_flip_mask_1: f32 = (-2 - (x_n_mask << 2)) as f32 * x1 - 1.0;
1050        let y_a_flip_mask_1: f32 = (-2 - (y_n_mask << 2)) as f32 * y1 - 1.0;
1051        let z_a_flip_mask_1: f32 = (-2 - (z_n_mask << 2)) as f32 * z1 - 1.0;
1052
1053        let mut skip_5 = false;
1054        let a2: f32 = x_a_flip_mask_0 + a0;
1055        if a2 > 0.0 {
1056            let x2: f32 = x0 - (x_n_mask | 1) as f32;
1057            let y2: f32 = y0;
1058            let z2: f32 = z0;
1059            value += (a2 * a2)
1060                * (a2 * a2)
1061                * Self::grad_coord_3d(
1062                    seed,
1063                    i + (!x_n_mask & Self::PRIME_X),
1064                    j + (y_n_mask & Self::PRIME_Y),
1065                    k + (z_n_mask & Self::PRIME_Z),
1066                    x2,
1067                    y2,
1068                    z2,
1069                );
1070        } else {
1071            let a3: f32 = y_a_flip_mask_0 + z_a_flip_mask_0 + a0;
1072            if a3 > 0.0 {
1073                let x3: f32 = x0;
1074                let y3: f32 = y0 - (y_n_mask | 1) as f32;
1075                let z3: f32 = z0 - (z_n_mask | 1) as f32;
1076                value += (a3 * a3)
1077                    * (a3 * a3)
1078                    * Self::grad_coord_3d(
1079                        seed,
1080                        i + (x_n_mask & Self::PRIME_X),
1081                        j + (!y_n_mask & Self::PRIME_Y),
1082                        k + (!z_n_mask & Self::PRIME_Z),
1083                        x3,
1084                        y3,
1085                        z3,
1086                    );
1087            }
1088
1089            let a4: f32 = x_a_flip_mask_1 + a1;
1090            if a4 > 0.0 {
1091                let x4: f32 = (x_n_mask | 1) as f32 + x1;
1092                let y4: f32 = y1;
1093                let z4: f32 = z1;
1094                value += (a4 * a4)
1095                    * (a4 * a4)
1096                    * Self::grad_coord_3d(
1097                        seed2,
1098                        i + (x_n_mask & (Self::PRIME_X * 2)),
1099                        j.wrapping_add(Self::PRIME_Y),
1100                        k.wrapping_add(Self::PRIME_Z),
1101                        x4,
1102                        y4,
1103                        z4,
1104                    );
1105                skip_5 = true;
1106            }
1107        }
1108
1109        let mut skip_9 = false;
1110        let a6: f32 = y_a_flip_mask_0 + a0;
1111        if a6 > 0.0 {
1112            let x6: f32 = x0;
1113            let y6: f32 = y0 - (y_n_mask | 1) as f32;
1114            let z6: f32 = z0;
1115            value += (a6 * a6)
1116                * (a6 * a6)
1117                * Self::grad_coord_3d(
1118                    seed,
1119                    i + (x_n_mask & Self::PRIME_X),
1120                    j + (!y_n_mask & Self::PRIME_Y),
1121                    k + (z_n_mask & Self::PRIME_Z),
1122                    x6,
1123                    y6,
1124                    z6,
1125                );
1126        } else {
1127            let a7: f32 = x_a_flip_mask_0 + z_a_flip_mask_0 + a0;
1128            if a7 > 0.0 {
1129                let x7: f32 = x0 - (x_n_mask | 1) as f32;
1130                let y7: f32 = y0;
1131                let z7: f32 = z0 - (z_n_mask | 1) as f32;
1132                value += (a7 * a7)
1133                    * (a7 * a7)
1134                    * Self::grad_coord_3d(
1135                        seed,
1136                        i + (!x_n_mask & Self::PRIME_X),
1137                        j + (y_n_mask & Self::PRIME_Y),
1138                        k + (!z_n_mask & Self::PRIME_Z),
1139                        x7,
1140                        y7,
1141                        z7,
1142                    );
1143            }
1144
1145            let a8: f32 = y_a_flip_mask_1 + a1;
1146            if a8 > 0.0 {
1147                let x8: f32 = x1;
1148                let y8: f32 = (y_n_mask | 1) as f32 + y1;
1149                let z8: f32 = z1;
1150                value += (a8 * a8)
1151                    * (a8 * a8)
1152                    * Self::grad_coord_3d(
1153                        seed2,
1154                        i.wrapping_add(Self::PRIME_X),
1155                        j + (y_n_mask & (Self::PRIME_Y << 1)),
1156                        k.wrapping_add(Self::PRIME_Z),
1157                        x8,
1158                        y8,
1159                        z8,
1160                    );
1161                skip_9 = true;
1162            }
1163        }
1164
1165        let mut skip_d = false;
1166        let a_a: f32 = z_a_flip_mask_0 + a0;
1167        if a_a > 0.0 {
1168            let x_a: f32 = x0;
1169            let y_a: f32 = y0;
1170            let z_a: f32 = z0 - (z_n_mask | 1) as f32;
1171            value += (a_a * a_a)
1172                * (a_a * a_a)
1173                * Self::grad_coord_3d(
1174                    seed,
1175                    i + (x_n_mask & Self::PRIME_X),
1176                    j + (y_n_mask & Self::PRIME_Y),
1177                    k + (!z_n_mask & Self::PRIME_Z),
1178                    x_a,
1179                    y_a,
1180                    z_a,
1181                );
1182        } else {
1183            let a_b: f32 = x_a_flip_mask_0 + y_a_flip_mask_0 + a0;
1184            if a_b > 0.0 {
1185                let x_b: f32 = x0 - (x_n_mask | 1) as f32;
1186                let y_b: f32 = y0 - (y_n_mask | 1) as f32;
1187                let z_b: f32 = z0;
1188                value += (a_b * a_b)
1189                    * (a_b * a_b)
1190                    * Self::grad_coord_3d(
1191                        seed,
1192                        i + (!x_n_mask & Self::PRIME_X),
1193                        j + (!y_n_mask & Self::PRIME_Y),
1194                        k + (z_n_mask & Self::PRIME_Z),
1195                        x_b,
1196                        y_b,
1197                        z_b,
1198                    );
1199            }
1200
1201            let a_c: f32 = z_a_flip_mask_1 + a1;
1202            if a_c > 0.0 {
1203                let x_c: f32 = x1;
1204                let y_c: f32 = y1;
1205                let z_c: f32 = (z_n_mask | 1) as f32 + z1;
1206                value += (a_c * a_c)
1207                    * (a_c * a_c)
1208                    * Self::grad_coord_3d(
1209                        seed2,
1210                        i.wrapping_add(Self::PRIME_X),
1211                        j.wrapping_add(Self::PRIME_Y),
1212                        k + (z_n_mask & (Self::PRIME_Z << 1)),
1213                        x_c,
1214                        y_c,
1215                        z_c,
1216                    );
1217                skip_d = true;
1218            }
1219        }
1220
1221        if !skip_5 {
1222            let a5: f32 = y_a_flip_mask_1 + z_a_flip_mask_1 + a1;
1223            if a5 > 0.0 {
1224                let x5: f32 = x1;
1225                let y5: f32 = (y_n_mask | 1) as f32 + y1;
1226                let z5: f32 = (z_n_mask | 1) as f32 + z1;
1227                value += (a5 * a5)
1228                    * (a5 * a5)
1229                    * Self::grad_coord_3d(
1230                        seed2,
1231                        i.wrapping_add(Self::PRIME_X),
1232                        j + (y_n_mask & (Self::PRIME_Y << 1)),
1233                        k + (z_n_mask & (Self::PRIME_Z << 1)),
1234                        x5,
1235                        y5,
1236                        z5,
1237                    );
1238            }
1239        }
1240
1241        if !skip_9 {
1242            let a9: f32 = x_a_flip_mask_1 + z_a_flip_mask_1 + a1;
1243            if a9 > 0.0 {
1244                let x9: f32 = (x_n_mask | 1) as f32 + x1;
1245                let y9: f32 = y1;
1246                let z9: f32 = (z_n_mask | 1) as f32 + z1;
1247                value += (a9 * a9)
1248                    * (a9 * a9)
1249                    * Self::grad_coord_3d(
1250                        seed2,
1251                        i + (x_n_mask & (Self::PRIME_X * 2)),
1252                        j.wrapping_add(Self::PRIME_Y),
1253                        k + (z_n_mask & (Self::PRIME_Z << 1)),
1254                        x9,
1255                        y9,
1256                        z9,
1257                    );
1258            }
1259        }
1260
1261        if !skip_d {
1262            let a_d: f32 = x_a_flip_mask_1 + y_a_flip_mask_1 + a1;
1263            if a_d > 0.0 {
1264                let x_d: f32 = (x_n_mask | 1) as f32 + x1;
1265                let y_d: f32 = (y_n_mask | 1) as f32 + y1;
1266                let z_d: f32 = z1;
1267                value += (a_d * a_d)
1268                    * (a_d * a_d)
1269                    * Self::grad_coord_3d(
1270                        seed2,
1271                        i + (x_n_mask & (Self::PRIME_X << 1)),
1272                        j + (y_n_mask & (Self::PRIME_Y << 1)),
1273                        k.wrapping_add(Self::PRIME_Z),
1274                        x_d,
1275                        y_d,
1276                        z_d,
1277                    );
1278            }
1279        }
1280
1281        return value * 9.046026385208288;
1282    }
1283
1284    fn single_cellular_2d(&self, seed: i32, x: f32, y: f32) -> f32 {
1285        let xr: i32 = fast_round(x);
1286        let yr: i32 = fast_round(y);
1287
1288        let mut distance0: f32 = 1e10;
1289        let mut distance1: f32 = 1e10;
1290        let mut closest_hash: i32 = 0;
1291
1292        let cellular_jitter: f32 = 0.43701595 * self.cellular_jitter_modifier;
1293
1294        let mut x_primed: i32 = (xr - 1) * Self::PRIME_X;
1295        let y_primed_base: i32 = (yr - 1) * Self::PRIME_Y;
1296
1297        match self.cellular_distance_function {
1298            CellularDistanceFunction::Manhattan => {
1299                for xi in (xr - 1)..=(xr + 1) {
1300                    let mut y_primed: i32 = y_primed_base;
1301
1302                    for yi in (yr - 1)..=(yr + 1) {
1303                        let hash: i32 = Self::hash_2d(seed, x_primed, y_primed);
1304                        let idx: i32 = hash & (255 << 1);
1305
1306                        let vec_x: f32 =
1307                            (xi as f32 - x) + lookup::RAND_VECS_2D[idx as usize] * cellular_jitter;
1308                        let vec_y: f32 = (yi as f32 - y)
1309                            + lookup::RAND_VECS_2D[(idx | 1) as usize] * cellular_jitter;
1310
1311                        let new_distance: f32 = fast_abs(vec_x) + fast_abs(vec_y);
1312
1313                        distance1 = fast_max(fast_min(distance1, new_distance), distance0);
1314                        if new_distance < distance0 {
1315                            distance0 = new_distance;
1316                            closest_hash = hash;
1317                        }
1318                        y_primed += Self::PRIME_Y;
1319                    }
1320                    x_primed += Self::PRIME_X;
1321                }
1322            }
1323            CellularDistanceFunction::Hybrid => {
1324                for xi in (xr - 1)..=(xr + 1) {
1325                    let mut y_primed: i32 = y_primed_base;
1326
1327                    for yi in (yr - 1)..=(yr + 1) {
1328                        let hash: i32 = Self::hash_2d(seed, x_primed, y_primed);
1329                        let idx: i32 = hash & (255 << 1);
1330
1331                        let vec_x: f32 =
1332                            (xi as f32 - x) + lookup::RAND_VECS_2D[idx as usize] * cellular_jitter;
1333                        let vec_y: f32 = (yi as f32 - y)
1334                            + lookup::RAND_VECS_2D[(idx | 1) as usize] * cellular_jitter;
1335
1336                        let new_distance: f32 =
1337                            (fast_abs(vec_x) + fast_abs(vec_y)) + (vec_x * vec_x + vec_y * vec_y);
1338
1339                        distance1 = fast_max(fast_min(distance1, new_distance), distance0);
1340                        if new_distance < distance0 {
1341                            distance0 = new_distance;
1342                            closest_hash = hash;
1343                        }
1344                        y_primed += Self::PRIME_Y;
1345                    }
1346                    x_primed += Self::PRIME_X;
1347                }
1348            }
1349            _ => {
1350                for xi in (xr - 1)..=(xr + 1) {
1351                    let mut y_primed: i32 = y_primed_base;
1352
1353                    for yi in (yr - 1)..=(yr + 1) {
1354                        let hash: i32 = Self::hash_2d(seed, x_primed, y_primed);
1355                        let idx: i32 = hash & (255 << 1);
1356
1357                        let vec_x: f32 =
1358                            (xi as f32 - x) + lookup::RAND_VECS_2D[idx as usize] * cellular_jitter;
1359                        let vec_y: f32 = (yi as f32 - y)
1360                            + lookup::RAND_VECS_2D[(idx | 1) as usize] * cellular_jitter;
1361
1362                        let new_distance: f32 = vec_x * vec_x + vec_y * vec_y;
1363
1364                        distance1 = fast_max(fast_min(distance1, new_distance), distance0);
1365                        if new_distance < distance0 {
1366                            distance0 = new_distance;
1367                            closest_hash = hash;
1368                        }
1369                        y_primed += Self::PRIME_Y;
1370                    }
1371                    x_primed += Self::PRIME_X;
1372                }
1373            }
1374        }
1375
1376        if self.cellular_distance_function == CellularDistanceFunction::Euclidean
1377            && self.cellular_return_type >= CellularReturnType::Distance
1378        {
1379            distance0 = fast_sqrt(distance0);
1380
1381            if self.cellular_return_type >= CellularReturnType::Distance2 {
1382                distance1 = fast_sqrt(distance1);
1383            }
1384        }
1385
1386        match self.cellular_return_type {
1387            CellularReturnType::CellValue => closest_hash as f32 * (1.0 / 2147483648.0),
1388            CellularReturnType::Distance => distance0 - 1.0,
1389            CellularReturnType::Distance2 => distance1 - 1.0,
1390            CellularReturnType::Distance2Add => (distance1 + distance0) * 0.5 - 1.0,
1391            CellularReturnType::Distance2Sub => distance1 - distance0 - 1.0,
1392            CellularReturnType::Distance2Mul => distance1 * distance0 * 0.5 - 1.0,
1393            CellularReturnType::Distance2Div => distance0 / distance1 - 1.0,
1394        }
1395    }
1396
1397    fn single_cellular_3d(&self, seed: i32, x: f32, y: f32, z: f32) -> f32 {
1398        let xr: i32 = fast_round(x);
1399        let yr: i32 = fast_round(y);
1400        let zr: i32 = fast_round(z);
1401
1402        let mut distance0: f32 = 1e10;
1403        let mut distance1: f32 = 1e10;
1404        let mut closest_hash: i32 = 0;
1405
1406        let cellular_jitter: f32 = 0.39614353 * self.cellular_jitter_modifier;
1407
1408        let mut x_primed: i32 = (xr - 1) * Self::PRIME_X;
1409        let y_primed_base: i32 = (yr - 1) * Self::PRIME_Y;
1410        let z_primed_base: i32 = (zr - 1) * Self::PRIME_Z;
1411
1412        match self.cellular_distance_function {
1413            CellularDistanceFunction::Euclidean | CellularDistanceFunction::EuclideanSq => {
1414                for xi in (xr - 1)..=(xr + 1) {
1415                    let mut y_primed: i32 = y_primed_base;
1416
1417                    for yi in (yr - 1)..=(yr + 1) {
1418                        let mut z_primed: i32 = z_primed_base;
1419
1420                        for zi in (zr - 1)..=(zr + 1) {
1421                            let hash: i32 = Self::hash_3d(seed, x_primed, y_primed, z_primed);
1422                            let idx: i32 = hash & (255 << 2);
1423
1424                            let vec_x: f32 = (xi as f32 - x)
1425                                + lookup::RAND_VECS_3D[idx as usize] * cellular_jitter;
1426                            let vec_y: f32 = (yi as f32 - y)
1427                                + lookup::RAND_VECS_3D[(idx | 1) as usize] * cellular_jitter;
1428                            let vec_z: f32 = (zi as f32 - z)
1429                                + lookup::RAND_VECS_3D[(idx | 2) as usize] * cellular_jitter;
1430
1431                            let new_distance: f32 = vec_x * vec_x + vec_y * vec_y + vec_z * vec_z;
1432
1433                            distance1 = fast_max(fast_min(distance1, new_distance), distance0);
1434                            if new_distance < distance0 {
1435                                distance0 = new_distance;
1436                                closest_hash = hash;
1437                            }
1438                            z_primed += Self::PRIME_Z;
1439                        }
1440                        y_primed += Self::PRIME_Y;
1441                    }
1442                    x_primed += Self::PRIME_X;
1443                }
1444            }
1445            CellularDistanceFunction::Manhattan => {
1446                for xi in (xr - 1)..=(xr + 1) {
1447                    let mut y_primed: i32 = y_primed_base;
1448
1449                    for yi in (yr - 1)..=(yr + 1) {
1450                        let mut z_primed: i32 = z_primed_base;
1451
1452                        for zi in (zr - 1)..=(zr + 1) {
1453                            let hash: i32 = Self::hash_3d(seed, x_primed, y_primed, z_primed);
1454                            let idx: i32 = hash & (255 << 2);
1455
1456                            let vec_x: f32 = (xi as f32 - x)
1457                                + lookup::RAND_VECS_3D[idx as usize] * cellular_jitter;
1458                            let vec_y: f32 = (yi as f32 - y)
1459                                + lookup::RAND_VECS_3D[(idx | 1) as usize] * cellular_jitter;
1460                            let vec_z: f32 = (zi as f32 - z)
1461                                + lookup::RAND_VECS_3D[(idx | 2) as usize] * cellular_jitter;
1462
1463                            let new_distance: f32 =
1464                                fast_abs(vec_x) + fast_abs(vec_y) + fast_abs(vec_z);
1465
1466                            distance1 = fast_max(fast_min(distance1, new_distance), distance0);
1467                            if new_distance < distance0 {
1468                                distance0 = new_distance;
1469                                closest_hash = hash;
1470                            }
1471                            z_primed += Self::PRIME_Z;
1472                        }
1473                        y_primed += Self::PRIME_Y;
1474                    }
1475                    x_primed += Self::PRIME_X;
1476                }
1477            }
1478            CellularDistanceFunction::Hybrid => {
1479                for xi in (xr - 1)..=(xr + 1) {
1480                    let mut y_primed: i32 = y_primed_base;
1481
1482                    for yi in (yr - 1)..=(yr + 1) {
1483                        let mut z_primed: i32 = z_primed_base;
1484
1485                        for zi in (zr - 1)..=(zr + 1) {
1486                            let hash: i32 = Self::hash_3d(seed, x_primed, y_primed, z_primed);
1487                            let idx: i32 = hash & (255 << 2);
1488
1489                            let vec_x: f32 = (xi as f32 - x)
1490                                + lookup::RAND_VECS_3D[idx as usize] * cellular_jitter;
1491                            let vec_y: f32 = (yi as f32 - y)
1492                                + lookup::RAND_VECS_3D[(idx | 1) as usize] * cellular_jitter;
1493                            let vec_z: f32 = (zi as f32 - z)
1494                                + lookup::RAND_VECS_3D[(idx | 2) as usize] * cellular_jitter;
1495
1496                            let new_distance: f32 =
1497                                (fast_abs(vec_x) + fast_abs(vec_y) + fast_abs(vec_z))
1498                                    + (vec_x * vec_x + vec_y * vec_y + vec_z * vec_z);
1499
1500                            distance1 = fast_max(fast_min(distance1, new_distance), distance0);
1501                            if new_distance < distance0 {
1502                                distance0 = new_distance;
1503                                closest_hash = hash;
1504                            }
1505                            z_primed += Self::PRIME_Z;
1506                        }
1507                        y_primed += Self::PRIME_Y;
1508                    }
1509                    x_primed += Self::PRIME_X;
1510                }
1511            }
1512        };
1513
1514        if self.cellular_distance_function == CellularDistanceFunction::Euclidean
1515            && self.cellular_return_type >= CellularReturnType::Distance
1516        {
1517            distance0 = fast_sqrt(distance0);
1518
1519            if self.cellular_return_type >= CellularReturnType::Distance2 {
1520                distance1 = fast_sqrt(distance1);
1521            }
1522        }
1523
1524        match self.cellular_return_type {
1525            CellularReturnType::CellValue => closest_hash as f32 * (1.0 / 2147483648.0),
1526            CellularReturnType::Distance => distance0 - 1.0,
1527            CellularReturnType::Distance2 => distance1 - 1.0,
1528            CellularReturnType::Distance2Add => (distance1 + distance0) * 0.5 - 1.0,
1529            CellularReturnType::Distance2Sub => distance1 - distance0 - 1.0,
1530            CellularReturnType::Distance2Mul => distance1 * distance0 * 0.5 - 1.0,
1531            CellularReturnType::Distance2Div => distance0 / distance1 - 1.0,
1532        }
1533    }
1534
1535    fn single_perlin_2d(&self, seed: i32, x: f32, y: f32) -> f32 {
1536        let mut x0: i32 = fast_floor(x);
1537        let mut y0: i32 = fast_floor(y);
1538
1539        let xd0: f32 = x - x0 as f32;
1540        let yd0: f32 = y - y0 as f32;
1541        let xd1: f32 = xd0 - 1.0;
1542        let yd1: f32 = yd0 - 1.0;
1543
1544        let xs: f32 = interp_quintic(xd0);
1545        let ys: f32 = interp_quintic(yd0);
1546
1547        x0 *= Self::PRIME_X;
1548        y0 *= Self::PRIME_Y;
1549        let x1: i32 = x0.wrapping_add(Self::PRIME_X);
1550        let y1: i32 = y0.wrapping_add(Self::PRIME_Y);
1551
1552        let xf0: f32 = lerp(
1553            Self::grad_coord_2d(seed, x0, y0, xd0, yd0),
1554            Self::grad_coord_2d(seed, x1, y0, xd1, yd0),
1555            xs,
1556        );
1557        let xf1: f32 = lerp(
1558            Self::grad_coord_2d(seed, x0, y1, xd0, yd1),
1559            Self::grad_coord_2d(seed, x1, y1, xd1, yd1),
1560            xs,
1561        );
1562
1563        return lerp(xf0, xf1, ys) * 1.4247691104677813;
1564    }
1565
1566    fn single_perlin_3d(&self, seed: i32, x: f32, y: f32, z: f32) -> f32 {
1567        let mut x0: i32 = fast_floor(x);
1568        let mut y0: i32 = fast_floor(y);
1569        let mut z0: i32 = fast_floor(z);
1570
1571        let xd0: f32 = x - x0 as f32;
1572        let yd0: f32 = y - y0 as f32;
1573        let zd0: f32 = z - z0 as f32;
1574        let xd1: f32 = xd0 - 1.0;
1575        let yd1: f32 = yd0 - 1.0;
1576        let zd1: f32 = zd0 - 1.0;
1577
1578        let xs: f32 = interp_quintic(xd0);
1579        let ys: f32 = interp_quintic(yd0);
1580        let zs: f32 = interp_quintic(zd0);
1581
1582        x0 *= Self::PRIME_X;
1583        y0 *= Self::PRIME_Y;
1584        z0 *= Self::PRIME_Z;
1585        let x1: i32 = x0.wrapping_add(Self::PRIME_X);
1586        let y1: i32 = y0.wrapping_add(Self::PRIME_Y);
1587        let z1: i32 = z0.wrapping_add(Self::PRIME_Z);
1588
1589        let xf00: f32 = lerp(
1590            Self::grad_coord_3d(seed, x0, y0, z0, xd0, yd0, zd0),
1591            Self::grad_coord_3d(seed, x1, y0, z0, xd1, yd0, zd0),
1592            xs,
1593        );
1594        let xf10: f32 = lerp(
1595            Self::grad_coord_3d(seed, x0, y1, z0, xd0, yd1, zd0),
1596            Self::grad_coord_3d(seed, x1, y1, z0, xd1, yd1, zd0),
1597            xs,
1598        );
1599        let xf01: f32 = lerp(
1600            Self::grad_coord_3d(seed, x0, y0, z1, xd0, yd0, zd1),
1601            Self::grad_coord_3d(seed, x1, y0, z1, xd1, yd0, zd1),
1602            xs,
1603        );
1604        let xf11: f32 = lerp(
1605            Self::grad_coord_3d(seed, x0, y1, z1, xd0, yd1, zd1),
1606            Self::grad_coord_3d(seed, x1, y1, z1, xd1, yd1, zd1),
1607            xs,
1608        );
1609
1610        let yf0: f32 = lerp(xf00, xf10, ys);
1611        let yf1: f32 = lerp(xf01, xf11, ys);
1612
1613        return lerp(yf0, yf1, zs) * 0.964921414852142333984375;
1614    }
1615
1616    fn single_value_cubic_2d(&self, seed: i32, x: f32, y: f32) -> f32 {
1617        let mut x1: i32 = fast_floor(x);
1618        let mut y1: i32 = fast_floor(y);
1619
1620        let xs: f32 = x - x1 as f32;
1621        let ys: f32 = y - y1 as f32;
1622
1623        x1 *= Self::PRIME_X;
1624        y1 *= Self::PRIME_Y;
1625        let x0: i32 = x1.wrapping_sub(Self::PRIME_X);
1626        let y0: i32 = y1.wrapping_sub(Self::PRIME_Y);
1627        let x2: i32 = x1.wrapping_add(Self::PRIME_X);
1628        let y2: i32 = y1.wrapping_add(Self::PRIME_Y);
1629        let x3: i32 = x1 + ((Self::PRIME_X as i64) << 1) as i32;
1630        let y3: i32 = y1 + ((Self::PRIME_Y as i64) << 1) as i32;
1631
1632        return cubic_lerp(
1633            cubic_lerp(
1634                Self::val_coord_2d(seed, x0, y0),
1635                Self::val_coord_2d(seed, x1, y0),
1636                Self::val_coord_2d(seed, x2, y0),
1637                Self::val_coord_2d(seed, x3, y0),
1638                xs,
1639            ),
1640            cubic_lerp(
1641                Self::val_coord_2d(seed, x0, y1),
1642                Self::val_coord_2d(seed, x1, y1),
1643                Self::val_coord_2d(seed, x2, y1),
1644                Self::val_coord_2d(seed, x3, y1),
1645                xs,
1646            ),
1647            cubic_lerp(
1648                Self::val_coord_2d(seed, x0, y2),
1649                Self::val_coord_2d(seed, x1, y2),
1650                Self::val_coord_2d(seed, x2, y2),
1651                Self::val_coord_2d(seed, x3, y2),
1652                xs,
1653            ),
1654            cubic_lerp(
1655                Self::val_coord_2d(seed, x0, y3),
1656                Self::val_coord_2d(seed, x1, y3),
1657                Self::val_coord_2d(seed, x2, y3),
1658                Self::val_coord_2d(seed, x3, y3),
1659                xs,
1660            ),
1661            ys,
1662        ) * (1.0 / (1.5 * 1.5));
1663    }
1664
1665    fn single_value_cubic_3d(&self, seed: i32, x: f32, y: f32, z: f32) -> f32 {
1666        let mut x1: i32 = fast_floor(x);
1667        let mut y1: i32 = fast_floor(y);
1668        let mut z1: i32 = fast_floor(z);
1669
1670        let xs: f32 = x - x1 as f32;
1671        let ys: f32 = y - y1 as f32;
1672        let zs: f32 = z - z1 as f32;
1673
1674        x1 *= Self::PRIME_X;
1675        y1 *= Self::PRIME_Y;
1676        z1 *= Self::PRIME_Z;
1677
1678        let x0: i32 = x1.wrapping_sub(Self::PRIME_X);
1679        let y0: i32 = y1.wrapping_sub(Self::PRIME_Y);
1680        let z0: i32 = z1.wrapping_sub(Self::PRIME_Z);
1681        let x2: i32 = x1.wrapping_add(Self::PRIME_X);
1682        let y2: i32 = y1.wrapping_add(Self::PRIME_Y);
1683        let z2: i32 = z1.wrapping_add(Self::PRIME_Z);
1684        let x3: i32 = x1 + ((Self::PRIME_X as i64) << 1) as i32;
1685        let y3: i32 = y1 + ((Self::PRIME_Y as i64) << 1) as i32;
1686        let z3: i32 = z1 + ((Self::PRIME_Z as i64) << 1) as i32;
1687
1688        return cubic_lerp(
1689            cubic_lerp(
1690                cubic_lerp(
1691                    Self::val_coord_3d(seed, x0, y0, z0),
1692                    Self::val_coord_3d(seed, x1, y0, z0),
1693                    Self::val_coord_3d(seed, x2, y0, z0),
1694                    Self::val_coord_3d(seed, x3, y0, z0),
1695                    xs,
1696                ),
1697                cubic_lerp(
1698                    Self::val_coord_3d(seed, x0, y1, z0),
1699                    Self::val_coord_3d(seed, x1, y1, z0),
1700                    Self::val_coord_3d(seed, x2, y1, z0),
1701                    Self::val_coord_3d(seed, x3, y1, z0),
1702                    xs,
1703                ),
1704                cubic_lerp(
1705                    Self::val_coord_3d(seed, x0, y2, z0),
1706                    Self::val_coord_3d(seed, x1, y2, z0),
1707                    Self::val_coord_3d(seed, x2, y2, z0),
1708                    Self::val_coord_3d(seed, x3, y2, z0),
1709                    xs,
1710                ),
1711                cubic_lerp(
1712                    Self::val_coord_3d(seed, x0, y3, z0),
1713                    Self::val_coord_3d(seed, x1, y3, z0),
1714                    Self::val_coord_3d(seed, x2, y3, z0),
1715                    Self::val_coord_3d(seed, x3, y3, z0),
1716                    xs,
1717                ),
1718                ys,
1719            ),
1720            cubic_lerp(
1721                cubic_lerp(
1722                    Self::val_coord_3d(seed, x0, y0, z1),
1723                    Self::val_coord_3d(seed, x1, y0, z1),
1724                    Self::val_coord_3d(seed, x2, y0, z1),
1725                    Self::val_coord_3d(seed, x3, y0, z1),
1726                    xs,
1727                ),
1728                cubic_lerp(
1729                    Self::val_coord_3d(seed, x0, y1, z1),
1730                    Self::val_coord_3d(seed, x1, y1, z1),
1731                    Self::val_coord_3d(seed, x2, y1, z1),
1732                    Self::val_coord_3d(seed, x3, y1, z1),
1733                    xs,
1734                ),
1735                cubic_lerp(
1736                    Self::val_coord_3d(seed, x0, y2, z1),
1737                    Self::val_coord_3d(seed, x1, y2, z1),
1738                    Self::val_coord_3d(seed, x2, y2, z1),
1739                    Self::val_coord_3d(seed, x3, y2, z1),
1740                    xs,
1741                ),
1742                cubic_lerp(
1743                    Self::val_coord_3d(seed, x0, y3, z1),
1744                    Self::val_coord_3d(seed, x1, y3, z1),
1745                    Self::val_coord_3d(seed, x2, y3, z1),
1746                    Self::val_coord_3d(seed, x3, y3, z1),
1747                    xs,
1748                ),
1749                ys,
1750            ),
1751            cubic_lerp(
1752                cubic_lerp(
1753                    Self::val_coord_3d(seed, x0, y0, z2),
1754                    Self::val_coord_3d(seed, x1, y0, z2),
1755                    Self::val_coord_3d(seed, x2, y0, z2),
1756                    Self::val_coord_3d(seed, x3, y0, z2),
1757                    xs,
1758                ),
1759                cubic_lerp(
1760                    Self::val_coord_3d(seed, x0, y1, z2),
1761                    Self::val_coord_3d(seed, x1, y1, z2),
1762                    Self::val_coord_3d(seed, x2, y1, z2),
1763                    Self::val_coord_3d(seed, x3, y1, z2),
1764                    xs,
1765                ),
1766                cubic_lerp(
1767                    Self::val_coord_3d(seed, x0, y2, z2),
1768                    Self::val_coord_3d(seed, x1, y2, z2),
1769                    Self::val_coord_3d(seed, x2, y2, z2),
1770                    Self::val_coord_3d(seed, x3, y2, z2),
1771                    xs,
1772                ),
1773                cubic_lerp(
1774                    Self::val_coord_3d(seed, x0, y3, z2),
1775                    Self::val_coord_3d(seed, x1, y3, z2),
1776                    Self::val_coord_3d(seed, x2, y3, z2),
1777                    Self::val_coord_3d(seed, x3, y3, z2),
1778                    xs,
1779                ),
1780                ys,
1781            ),
1782            cubic_lerp(
1783                cubic_lerp(
1784                    Self::val_coord_3d(seed, x0, y0, z3),
1785                    Self::val_coord_3d(seed, x1, y0, z3),
1786                    Self::val_coord_3d(seed, x2, y0, z3),
1787                    Self::val_coord_3d(seed, x3, y0, z3),
1788                    xs,
1789                ),
1790                cubic_lerp(
1791                    Self::val_coord_3d(seed, x0, y1, z3),
1792                    Self::val_coord_3d(seed, x1, y1, z3),
1793                    Self::val_coord_3d(seed, x2, y1, z3),
1794                    Self::val_coord_3d(seed, x3, y1, z3),
1795                    xs,
1796                ),
1797                cubic_lerp(
1798                    Self::val_coord_3d(seed, x0, y2, z3),
1799                    Self::val_coord_3d(seed, x1, y2, z3),
1800                    Self::val_coord_3d(seed, x2, y2, z3),
1801                    Self::val_coord_3d(seed, x3, y2, z3),
1802                    xs,
1803                ),
1804                cubic_lerp(
1805                    Self::val_coord_3d(seed, x0, y3, z3),
1806                    Self::val_coord_3d(seed, x1, y3, z3),
1807                    Self::val_coord_3d(seed, x2, y3, z3),
1808                    Self::val_coord_3d(seed, x3, y3, z3),
1809                    xs,
1810                ),
1811                ys,
1812            ),
1813            zs,
1814        ) * (1.0 / (1.5 * 1.5 * 1.5));
1815    }
1816
1817    fn single_value_2d(&self, seed: i32, x: f32, y: f32) -> f32 {
1818        let mut x0: i32 = fast_floor(x);
1819        let mut y0: i32 = fast_floor(y);
1820
1821        let xs: f32 = interp_hermite(x - x0 as f32);
1822        let ys: f32 = interp_hermite(y - y0 as f32);
1823
1824        x0 *= Self::PRIME_X;
1825        y0 *= Self::PRIME_Y;
1826        let x1: i32 = x0.wrapping_add(Self::PRIME_X);
1827        let y1: i32 = y0.wrapping_add(Self::PRIME_Y);
1828
1829        let xf0: f32 = lerp(
1830            Self::val_coord_2d(seed, x0, y0),
1831            Self::val_coord_2d(seed, x1, y0),
1832            xs,
1833        );
1834        let xf1: f32 = lerp(
1835            Self::val_coord_2d(seed, x0, y1),
1836            Self::val_coord_2d(seed, x1, y1),
1837            xs,
1838        );
1839
1840        return lerp(xf0, xf1, ys);
1841    }
1842
1843    fn single_value_3d(&self, seed: i32, x: f32, y: f32, z: f32) -> f32 {
1844        let mut x0: i32 = fast_floor(x);
1845        let mut y0: i32 = fast_floor(y);
1846        let mut z0: i32 = fast_floor(z);
1847
1848        let xs: f32 = interp_hermite(x - x0 as f32);
1849        let ys: f32 = interp_hermite(y - y0 as f32);
1850        let zs: f32 = interp_hermite(z - z0 as f32);
1851
1852        x0 *= Self::PRIME_X;
1853        y0 *= Self::PRIME_Y;
1854        z0 *= Self::PRIME_Z;
1855        let x1: i32 = x0.wrapping_add(Self::PRIME_X);
1856        let y1: i32 = y0.wrapping_add(Self::PRIME_Y);
1857        let z1: i32 = z0.wrapping_add(Self::PRIME_Z);
1858
1859        let xf00: f32 = lerp(
1860            Self::val_coord_3d(seed, x0, y0, z0),
1861            Self::val_coord_3d(seed, x1, y0, z0),
1862            xs,
1863        );
1864        let xf10: f32 = lerp(
1865            Self::val_coord_3d(seed, x0, y1, z0),
1866            Self::val_coord_3d(seed, x1, y1, z0),
1867            xs,
1868        );
1869        let xf01: f32 = lerp(
1870            Self::val_coord_3d(seed, x0, y0, z1),
1871            Self::val_coord_3d(seed, x1, y0, z1),
1872            xs,
1873        );
1874        let xf11: f32 = lerp(
1875            Self::val_coord_3d(seed, x0, y1, z1),
1876            Self::val_coord_3d(seed, x1, y1, z1),
1877            xs,
1878        );
1879
1880        let yf0: f32 = lerp(xf00, xf10, ys);
1881        let yf1: f32 = lerp(xf01, xf11, ys);
1882
1883        return lerp(yf0, yf1, zs);
1884    }
1885
1886    fn do_single_domain_warp_2d(
1887        &self,
1888        seed: i32,
1889        amp: f32,
1890        freq: f32,
1891        x: f32,
1892        y: f32,
1893        xr: &mut f32,
1894        yr: &mut f32,
1895    ) {
1896        match self.domain_warp_type {
1897            DomainWarpType::OpenSimplex2 => self.single_domain_warp_simplex_gradient_2d(
1898                seed,
1899                amp * 38.283687591552734375,
1900                freq,
1901                x,
1902                y,
1903                xr,
1904                yr,
1905                false,
1906            ),
1907            DomainWarpType::OpenSimplex2Reduced => self.single_domain_warp_simplex_gradient_2d(
1908                seed,
1909                amp * 16.0,
1910                freq,
1911                x,
1912                y,
1913                xr,
1914                yr,
1915                true,
1916            ),
1917            DomainWarpType::BasicGrid => {
1918                self.single_domain_warp_basic_grid_2d(seed, amp, freq, x, y, xr, yr)
1919            }
1920        }
1921    }
1922
1923    fn do_single_domain_warp_3d(
1924        &self,
1925        seed: i32,
1926        amp: f32,
1927        freq: f32,
1928        x: f32,
1929        y: f32,
1930        z: f32,
1931        xr: &mut f32,
1932        yr: &mut f32,
1933        zr: &mut f32,
1934    ) {
1935        match self.domain_warp_type {
1936            DomainWarpType::OpenSimplex2 => self.single_domain_warp_open_simplex2_gradient_3d(
1937                seed,
1938                amp * 32.69428253173828125,
1939                freq,
1940                x,
1941                y,
1942                z,
1943                xr,
1944                yr,
1945                zr,
1946                false,
1947            ),
1948            DomainWarpType::OpenSimplex2Reduced => self
1949                .single_domain_warp_open_simplex2_gradient_3d(
1950                    seed,
1951                    amp * 7.71604938271605,
1952                    freq,
1953                    x,
1954                    y,
1955                    z,
1956                    xr,
1957                    yr,
1958                    zr,
1959                    true,
1960                ),
1961            DomainWarpType::BasicGrid => {
1962                self.single_domain_warp_basic_grid_3d(seed, amp, freq, x, y, z, xr, yr, zr)
1963            }
1964        }
1965    }
1966
1967    fn domain_warp_single_2d(&self, x: &mut f32, y: &mut f32) {
1968        let seed: i32 = self.seed;
1969        let amp: f32 = self.domain_warp_amp * self.fractal_bounding;
1970        let freq: f32 = self.frequency;
1971
1972        let mut xs: f32 = *x;
1973        let mut ys: f32 = *y;
1974        self.transform_domain_warp_coordinate_2d(&mut xs, &mut ys);
1975
1976        self.do_single_domain_warp_2d(seed, amp, freq, xs, ys, x, y);
1977    }
1978
1979    fn domain_warp_single_3d(&self, x: &mut f32, y: &mut f32, z: &mut f32) {
1980        let seed: i32 = self.seed;
1981        let amp: f32 = self.domain_warp_amp * self.fractal_bounding;
1982        let freq: f32 = self.frequency;
1983
1984        let mut xs: f32 = *x;
1985        let mut ys: f32 = *y;
1986        let mut zs: f32 = *z;
1987        self.transform_domain_warp_coordinate_3d(&mut xs, &mut ys, &mut zs);
1988
1989        self.do_single_domain_warp_3d(seed, amp, freq, xs, ys, zs, x, y, z);
1990    }
1991
1992    fn domain_warp_fractal_progressive_2d(&self, x: &mut f32, y: &mut f32) {
1993        let mut seed: i32 = self.seed;
1994        let mut amp: f32 = self.domain_warp_amp * self.fractal_bounding;
1995        let mut freq: f32 = self.frequency;
1996
1997        for _ in 0..self.octaves {
1998            let mut xs: f32 = *x;
1999            let mut ys: f32 = *y;
2000            self.transform_domain_warp_coordinate_2d(&mut xs, &mut ys);
2001
2002            self.do_single_domain_warp_2d(seed, amp, freq, xs, ys, x, y);
2003
2004            seed += 1;
2005            amp *= self.gain;
2006            freq *= self.lacunarity;
2007        }
2008    }
2009
2010    fn domain_warp_fractal_progressive_3d(&self, x: &mut f32, y: &mut f32, z: &mut f32) {
2011        let mut seed: i32 = self.seed;
2012        let mut amp: f32 = self.domain_warp_amp * self.fractal_bounding;
2013        let mut freq: f32 = self.frequency;
2014
2015        for _ in 0..self.octaves {
2016            let mut xs: f32 = *x;
2017            let mut ys: f32 = *y;
2018            let mut zs: f32 = *z;
2019            self.transform_domain_warp_coordinate_3d(&mut xs, &mut ys, &mut zs);
2020
2021            self.do_single_domain_warp_3d(seed, amp, freq, xs, ys, zs, x, y, z);
2022
2023            seed += 1;
2024            amp *= self.gain;
2025            freq *= self.lacunarity;
2026        }
2027    }
2028
2029    fn domain_warp_fractal_independent_2d(&self, x: &mut f32, y: &mut f32) {
2030        let mut xs: f32 = *x;
2031        let mut ys: f32 = *y;
2032        self.transform_domain_warp_coordinate_2d(&mut xs, &mut ys);
2033
2034        let mut seed: i32 = self.seed;
2035        let mut amp: f32 = self.domain_warp_amp * self.fractal_bounding;
2036        let mut freq: f32 = self.frequency;
2037
2038        for _ in 0..self.octaves {
2039            self.do_single_domain_warp_2d(seed, amp, freq, xs, ys, x, y);
2040
2041            seed += 1;
2042            amp *= self.gain;
2043            freq *= self.lacunarity;
2044        }
2045    }
2046
2047    fn domain_warp_fractal_independent_3d(&self, x: &mut f32, y: &mut f32, z: &mut f32) {
2048        let mut xs: f32 = *x;
2049        let mut ys: f32 = *y;
2050        let mut zs: f32 = *z;
2051        self.transform_domain_warp_coordinate_3d(&mut xs, &mut ys, &mut zs);
2052
2053        let mut seed: i32 = self.seed;
2054        let mut amp: f32 = self.domain_warp_amp * self.fractal_bounding;
2055        let mut freq: f32 = self.frequency;
2056
2057        for _ in 0..self.octaves {
2058            self.do_single_domain_warp_3d(seed, amp, freq, xs, ys, zs, x, y, z);
2059
2060            seed += 1;
2061            amp *= self.gain;
2062            freq *= self.lacunarity;
2063        }
2064    }
2065
2066    fn single_domain_warp_basic_grid_2d(
2067        &self,
2068        seed: i32,
2069        warp_amp: f32,
2070        frequency: f32,
2071        x: f32,
2072        y: f32,
2073        xr: &mut f32,
2074        yr: &mut f32,
2075    ) {
2076        let xf: f32 = x * frequency;
2077        let yf: f32 = y * frequency;
2078
2079        let mut x0: i32 = fast_floor(xf);
2080        let mut y0: i32 = fast_floor(yf);
2081
2082        let xs: f32 = interp_hermite(xf - x0 as f32);
2083        let ys: f32 = interp_hermite(yf - y0 as f32);
2084
2085        x0 *= Self::PRIME_X;
2086        y0 *= Self::PRIME_Y;
2087        let x1: i32 = x0.wrapping_add(Self::PRIME_X);
2088        let y1: i32 = y0.wrapping_add(Self::PRIME_Y);
2089
2090        let mut hash0: i32 = Self::hash_2d(seed, x0, y0) & (255 << 1);
2091        let mut hash1: i32 = Self::hash_2d(seed, x1, y0) & (255 << 1);
2092
2093        let lx0x: f32 = lerp(
2094            lookup::RAND_VECS_2D[hash0 as usize],
2095            lookup::RAND_VECS_2D[hash1 as usize],
2096            xs,
2097        );
2098        let ly0x: f32 = lerp(
2099            lookup::RAND_VECS_2D[(hash0 | 1) as usize],
2100            lookup::RAND_VECS_2D[(hash1 | 1) as usize],
2101            xs,
2102        );
2103
2104        hash0 = Self::hash_2d(seed, x0, y1) & (255 << 1);
2105        hash1 = Self::hash_2d(seed, x1, y1) & (255 << 1);
2106
2107        let lx1x: f32 = lerp(
2108            lookup::RAND_VECS_2D[hash0 as usize],
2109            lookup::RAND_VECS_2D[hash1 as usize],
2110            xs,
2111        );
2112        let ly1x: f32 = lerp(
2113            lookup::RAND_VECS_2D[(hash0 | 1) as usize],
2114            lookup::RAND_VECS_2D[(hash1 | 1) as usize],
2115            xs,
2116        );
2117
2118        *xr += lerp(lx0x, lx1x, ys) * warp_amp;
2119        *yr += lerp(ly0x, ly1x, ys) * warp_amp;
2120    }
2121
2122    fn single_domain_warp_basic_grid_3d(
2123        &self,
2124        seed: i32,
2125        warp_amp: f32,
2126        frequency: f32,
2127        x: f32,
2128        y: f32,
2129        z: f32,
2130        xr: &mut f32,
2131        yr: &mut f32,
2132        zr: &mut f32,
2133    ) {
2134        let xf: f32 = x * frequency;
2135        let yf: f32 = y * frequency;
2136        let zf: f32 = z * frequency;
2137
2138        let mut x0: i32 = fast_floor(xf);
2139        let mut y0: i32 = fast_floor(yf);
2140        let mut z0: i32 = fast_floor(zf);
2141
2142        let xs: f32 = interp_hermite(xf - x0 as f32);
2143        let ys: f32 = interp_hermite(yf - y0 as f32);
2144        let zs: f32 = interp_hermite(zf - z0 as f32);
2145
2146        x0 *= Self::PRIME_X;
2147        y0 *= Self::PRIME_Y;
2148        z0 *= Self::PRIME_Z;
2149        let x1: i32 = x0.wrapping_add(Self::PRIME_X);
2150        let y1: i32 = y0.wrapping_add(Self::PRIME_Y);
2151        let z1: i32 = z0.wrapping_add(Self::PRIME_Z);
2152
2153        let mut hash0: i32 = Self::hash_3d(seed, x0, y0, z0) & (255 << 2);
2154        let mut hash1: i32 = Self::hash_3d(seed, x1, y0, z0) & (255 << 2);
2155
2156        let mut lx0x: f32 = lerp(
2157            lookup::RAND_VECS_3D[hash0 as usize],
2158            lookup::RAND_VECS_3D[hash1 as usize],
2159            xs,
2160        );
2161        let mut ly0x: f32 = lerp(
2162            lookup::RAND_VECS_3D[(hash0 | 1) as usize],
2163            lookup::RAND_VECS_3D[(hash1 | 1) as usize],
2164            xs,
2165        );
2166        let mut lz0x: f32 = lerp(
2167            lookup::RAND_VECS_3D[(hash0 | 2) as usize],
2168            lookup::RAND_VECS_3D[(hash1 | 2) as usize],
2169            xs,
2170        );
2171
2172        hash0 = Self::hash_3d(seed, x0, y1, z0) & (255 << 2);
2173        hash1 = Self::hash_3d(seed, x1, y1, z0) & (255 << 2);
2174
2175        let mut lx1x: f32 = lerp(
2176            lookup::RAND_VECS_3D[hash0 as usize],
2177            lookup::RAND_VECS_3D[hash1 as usize],
2178            xs,
2179        );
2180        let mut ly1x: f32 = lerp(
2181            lookup::RAND_VECS_3D[(hash0 | 1) as usize],
2182            lookup::RAND_VECS_3D[(hash1 | 1) as usize],
2183            xs,
2184        );
2185        let mut lz1x: f32 = lerp(
2186            lookup::RAND_VECS_3D[(hash0 | 2) as usize],
2187            lookup::RAND_VECS_3D[(hash1 | 2) as usize],
2188            xs,
2189        );
2190
2191        let lx0y: f32 = lerp(lx0x, lx1x, ys);
2192        let ly0y: f32 = lerp(ly0x, ly1x, ys);
2193        let lz0y: f32 = lerp(lz0x, lz1x, ys);
2194
2195        hash0 = Self::hash_3d(seed, x0, y0, z1) & (255 << 2);
2196        hash1 = Self::hash_3d(seed, x1, y0, z1) & (255 << 2);
2197
2198        lx0x = lerp(
2199            lookup::RAND_VECS_3D[hash0 as usize],
2200            lookup::RAND_VECS_3D[hash1 as usize],
2201            xs,
2202        );
2203        ly0x = lerp(
2204            lookup::RAND_VECS_3D[(hash0 | 1) as usize],
2205            lookup::RAND_VECS_3D[(hash1 | 1) as usize],
2206            xs,
2207        );
2208        lz0x = lerp(
2209            lookup::RAND_VECS_3D[(hash0 | 2) as usize],
2210            lookup::RAND_VECS_3D[(hash1 | 2) as usize],
2211            xs,
2212        );
2213
2214        hash0 = Self::hash_3d(seed, x0, y1, z1) & (255 << 2);
2215        hash1 = Self::hash_3d(seed, x1, y1, z1) & (255 << 2);
2216
2217        lx1x = lerp(
2218            lookup::RAND_VECS_3D[hash0 as usize],
2219            lookup::RAND_VECS_3D[hash1 as usize],
2220            xs,
2221        );
2222        ly1x = lerp(
2223            lookup::RAND_VECS_3D[(hash0 | 1) as usize],
2224            lookup::RAND_VECS_3D[(hash1 | 1) as usize],
2225            xs,
2226        );
2227        lz1x = lerp(
2228            lookup::RAND_VECS_3D[(hash0 | 2) as usize],
2229            lookup::RAND_VECS_3D[(hash1 | 2) as usize],
2230            xs,
2231        );
2232
2233        *xr += lerp(lx0y, lerp(lx0x, lx1x, ys), zs) * warp_amp;
2234        *yr += lerp(ly0y, lerp(ly0x, ly1x, ys), zs) * warp_amp;
2235        *zr += lerp(lz0y, lerp(lz0x, lz1x, ys), zs) * warp_amp;
2236    }
2237
2238    fn single_domain_warp_simplex_gradient_2d(
2239        &self,
2240        seed: i32,
2241        warp_amp: f32,
2242        frequency: f32,
2243        mut x: f32,
2244        mut y: f32,
2245        xr: &mut f32,
2246        yr: &mut f32,
2247        out_grad_only: bool,
2248    ) {
2249        const SQRT3: f32 = 1.7320508075688772935274463415059;
2250        const G2: f32 = (3.0 - SQRT3) / 6.0;
2251
2252        x *= frequency;
2253        y *= frequency;
2254
2255        /*
2256         * --- Skew moved to TransformNoiseCoordinate method ---
2257         * const F2: f32 = 0.5f * (SQRT3 - 1);
2258         * s: f32 = (x + y) * F2;
2259         * x += s; y += s;
2260         */
2261
2262        let mut i: i32 = fast_floor(x);
2263        let mut j: i32 = fast_floor(y);
2264        let xi: f32 = x - i as f32;
2265        let yi: f32 = y - j as f32;
2266
2267        let t: f32 = (xi + yi) * G2;
2268        let x0: f32 = xi - t;
2269        let y0: f32 = yi - t;
2270
2271        i = i.wrapping_mul(Self::PRIME_X);
2272        j = j.wrapping_mul(Self::PRIME_Y);
2273
2274        let mut vx: f32 = 0.0;
2275        let mut vy: f32 = 0.0;
2276
2277        let a: f32 = 0.5 - x0 * x0 - y0 * y0;
2278        if a > 0.0 {
2279            let aaaa: f32 = (a * a) * (a * a);
2280            let mut xo: f32 = 0.0;
2281            let mut yo: f32 = 0.0;
2282            if out_grad_only {
2283                Self::grad_coord_out_2d(seed, i, j, &mut xo, &mut yo);
2284            } else {
2285                Self::grad_coord_dual_2d(seed, i, j, x0, y0, &mut xo, &mut yo);
2286            }
2287            vx += aaaa * xo;
2288            vy += aaaa * yo;
2289        }
2290
2291        let c: f32 = (2.0 * (1.0 - 2.0 * G2) * (1.0 / G2 - 2.0)) * t
2292            + ((-2.0 * (1.0 - 2.0 * G2) * (1.0 - 2.0 * G2)) + a);
2293        if c > 0.0 {
2294            let x2: f32 = x0 + (2.0 * G2 - 1.0);
2295            let y2: f32 = y0 + (2.0 * G2 - 1.0);
2296            let cccc: f32 = (c * c) * (c * c);
2297            let mut xo: f32 = 0.0;
2298            let mut yo: f32 = 0.0;
2299            if out_grad_only {
2300                Self::grad_coord_out_2d(
2301                    seed,
2302                    i.wrapping_add(Self::PRIME_X),
2303                    j.wrapping_add(Self::PRIME_Y),
2304                    &mut xo,
2305                    &mut yo,
2306                );
2307            } else {
2308                Self::grad_coord_dual_2d(
2309                    seed,
2310                    i.wrapping_add(Self::PRIME_X),
2311                    j.wrapping_add(Self::PRIME_Y),
2312                    x2,
2313                    y2,
2314                    &mut xo,
2315                    &mut yo,
2316                );
2317            }
2318            vx += cccc * xo;
2319            vy += cccc * yo;
2320        }
2321
2322        if y0 > x0 {
2323            let x1: f32 = x0 + G2;
2324            let y1: f32 = y0 + (G2 - 1.0);
2325            let b: f32 = 0.5 - x1 * x1 - y1 * y1;
2326            if b > 0.0 {
2327                let bbbb: f32 = (b * b) * (b * b);
2328                let mut xo: f32 = 0.0;
2329                let mut yo: f32 = 0.0;
2330                if out_grad_only {
2331                    Self::grad_coord_out_2d(
2332                        seed,
2333                        i,
2334                        j.wrapping_add(Self::PRIME_Y),
2335                        &mut xo,
2336                        &mut yo,
2337                    );
2338                } else {
2339                    Self::grad_coord_dual_2d(
2340                        seed,
2341                        i,
2342                        j.wrapping_add(Self::PRIME_Y),
2343                        x1,
2344                        y1,
2345                        &mut xo,
2346                        &mut yo,
2347                    );
2348                }
2349                vx += bbbb * xo;
2350                vy += bbbb * yo;
2351            }
2352        } else {
2353            let x1: f32 = x0 + (G2 - 1.0);
2354            let y1: f32 = y0 + G2;
2355            let b: f32 = 0.5 - x1 * x1 - y1 * y1;
2356            if b > 0.0 {
2357                let bbbb: f32 = (b * b) * (b * b);
2358                let mut xo: f32 = 0.0;
2359                let mut yo: f32 = 0.0;
2360                if out_grad_only {
2361                    Self::grad_coord_out_2d(
2362                        seed,
2363                        i.wrapping_add(Self::PRIME_X),
2364                        j,
2365                        &mut xo,
2366                        &mut yo,
2367                    );
2368                } else {
2369                    Self::grad_coord_dual_2d(
2370                        seed,
2371                        i.wrapping_add(Self::PRIME_X),
2372                        j,
2373                        x1,
2374                        y1,
2375                        &mut xo,
2376                        &mut yo,
2377                    );
2378                }
2379                vx += bbbb * xo;
2380                vy += bbbb * yo;
2381            }
2382        }
2383
2384        *xr += vx * warp_amp;
2385        *yr += vy * warp_amp;
2386    }
2387
2388    fn single_domain_warp_open_simplex2_gradient_3d(
2389        &self,
2390        mut seed: i32,
2391        warp_amp: f32,
2392        frequency: f32,
2393        mut x: f32,
2394        mut y: f32,
2395        mut z: f32,
2396        xr: &mut f32,
2397        yr: &mut f32,
2398        zr: &mut f32,
2399        out_grad_only: bool,
2400    ) {
2401        x *= frequency;
2402        y *= frequency;
2403        z *= frequency;
2404
2405        /*
2406         * --- Rotation moved to TransformDomainWarpCoordinate method ---
2407         * const R3: f32 = (FNfloat)(2.0 / 3.0);
2408         * r: f32 = (x + y + z) * R3; // Rotation, not skew
2409         * x = r - x; y = r - y; z = r - z;
2410         */
2411
2412        let mut i: i32 = fast_round(x);
2413        let mut j: i32 = fast_round(y);
2414        let mut k: i32 = fast_round(z);
2415        let mut x0: f32 = x - i as f32;
2416        let mut y0: f32 = y - j as f32;
2417        let mut z0: f32 = z - k as f32;
2418
2419        let mut xn_sign: i32 = (-x0 - 1.0) as i32 | 1;
2420        let mut yn_sign: i32 = (-y0 - 1.0) as i32 | 1;
2421        let mut zn_sign: i32 = (-z0 - 1.0) as i32 | 1;
2422
2423        let mut ax0: f32 = xn_sign as f32 * -x0;
2424        let mut ay0: f32 = yn_sign as f32 * -y0;
2425        let mut az0: f32 = zn_sign as f32 * -z0;
2426
2427        i = i.wrapping_mul(Self::PRIME_X);
2428        j = j.wrapping_mul(Self::PRIME_Y);
2429        k = k.wrapping_mul(Self::PRIME_Z);
2430
2431        let mut vx: f32 = 0.0;
2432        let mut vy: f32 = 0.0;
2433        let mut vz: f32 = 0.0;
2434
2435        let mut a: f32 = (0.6 - x0 * x0) - (y0 * y0 + z0 * z0);
2436
2437        for l in 0..2 {
2438            if a > 0.0 {
2439                let aaaa: f32 = (a * a) * (a * a);
2440                let mut xo: f32 = 0.0;
2441                let mut yo: f32 = 0.0;
2442                let mut zo: f32 = 0.0;
2443                if out_grad_only {
2444                    Self::grad_coord_out_3d(seed, i, j, k, &mut xo, &mut yo, &mut zo);
2445                } else {
2446                    Self::grad_coord_dual_3d(seed, i, j, k, x0, y0, z0, &mut xo, &mut yo, &mut zo);
2447                }
2448                vx += aaaa * xo;
2449                vy += aaaa * yo;
2450                vz += aaaa * zo;
2451            }
2452
2453            let mut b: f32 = a + 1.0;
2454            let mut i1: i32 = i;
2455            let mut j1: i32 = j;
2456            let mut k1: i32 = k;
2457            let mut x1: f32 = x0;
2458            let mut y1: f32 = y0;
2459            let mut z1: f32 = z0;
2460
2461            if ax0 >= ay0 && ax0 >= az0 {
2462                x1 += xn_sign as f32;
2463                b -= xn_sign as f32 * 2.0 * x1;
2464                i1 -= xn_sign * Self::PRIME_X;
2465            } else if ay0 > ax0 && ay0 >= az0 {
2466                y1 += yn_sign as f32;
2467                b -= yn_sign as f32 * 2.0 * y1;
2468                j1 -= yn_sign * Self::PRIME_Y;
2469            } else {
2470                z1 += zn_sign as f32;
2471                b -= zn_sign as f32 * 2.0 * z1;
2472                k1 -= zn_sign * Self::PRIME_Z;
2473            }
2474
2475            if b > 0.0 {
2476                let bbbb: f32 = (b * b) * (b * b);
2477                let mut xo: f32 = 0.0;
2478                let mut yo: f32 = 0.0;
2479                let mut zo: f32 = 0.0;
2480                if out_grad_only {
2481                    Self::grad_coord_out_3d(seed, i1, j1, k1, &mut xo, &mut yo, &mut zo);
2482                } else {
2483                    Self::grad_coord_dual_3d(
2484                        seed, i1, j1, k1, x1, y1, z1, &mut xo, &mut yo, &mut zo,
2485                    );
2486                }
2487                vx += bbbb * xo;
2488                vy += bbbb * yo;
2489                vz += bbbb * zo;
2490            }
2491
2492            if l == 1 {
2493                break;
2494            }
2495
2496            ax0 = 0.5 - ax0;
2497            ay0 = 0.5 - ay0;
2498            az0 = 0.5 - az0;
2499
2500            x0 = xn_sign as f32 * ax0;
2501            y0 = yn_sign as f32 * ay0;
2502            z0 = zn_sign as f32 * az0;
2503
2504            a += (0.75 - ax0) - (ay0 + az0);
2505
2506            i += (xn_sign >> 1) & Self::PRIME_X;
2507            j += (yn_sign >> 1) & Self::PRIME_Y;
2508            k += (zn_sign >> 1) & Self::PRIME_Z;
2509
2510            xn_sign = -xn_sign;
2511            yn_sign = -yn_sign;
2512            zn_sign = -zn_sign;
2513
2514            seed += 1293373;
2515        }
2516
2517        *xr += vx * warp_amp;
2518        *yr += vy * warp_amp;
2519        *zr += vz * warp_amp;
2520    }
2521}