noise/noise_fns/selectors/
select.rs

1use crate::{
2    math::{interpolate, s_curve::cubic::Cubic},
3    noise_fns::NoiseFn,
4};
5use core::marker::PhantomData;
6
7/// Noise function that outputs the value selected from one of two source
8/// functions chosen by the output value from a control function.
9#[derive(Clone, Debug)]
10pub struct Select<T, Source1, Source2, Control, const DIM: usize>
11where
12    Source1: NoiseFn<T, DIM>,
13    Source2: NoiseFn<T, DIM>,
14    Control: NoiseFn<T, DIM>,
15{
16    /// Outputs a value.
17    pub source1: Source1,
18
19    /// Outputs a value.
20    pub source2: Source2,
21
22    /// Determines the value to select. If the output value from
23    /// the control function is within a range of values know as the _selection
24    /// range_, this noise function outputs the value from `source2`.
25    /// Otherwise, this noise function outputs the value from `source1`.
26    pub control: Control,
27
28    /// Bounds of the selection range. Default is 0.0 to 1.0.
29    pub bounds: (f64, f64),
30
31    /// Edge falloff value. Default is 0.0.
32    pub falloff: f64,
33
34    phantom: PhantomData<T>,
35}
36
37impl<T, Source1, Source2, Control, const DIM: usize> Select<T, Source1, Source2, Control, DIM>
38where
39    Source1: NoiseFn<T, DIM>,
40    Source2: NoiseFn<T, DIM>,
41    Control: NoiseFn<T, DIM>,
42{
43    pub fn new(source1: Source1, source2: Source2, control: Control) -> Self {
44        Select {
45            source1,
46            source2,
47            control,
48            bounds: (0.0, 1.0),
49            falloff: 0.0,
50            phantom: PhantomData,
51        }
52    }
53
54    pub fn set_bounds(self, lower_bound: f64, upper_bound: f64) -> Self {
55        Select {
56            bounds: (lower_bound, upper_bound),
57            ..self
58        }
59    }
60
61    pub fn set_falloff(self, falloff: f64) -> Self {
62        Select { falloff, ..self }
63    }
64}
65
66impl<T, Source1, Source2, Control, const DIM: usize> NoiseFn<T, DIM>
67    for Select<T, Source1, Source2, Control, DIM>
68where
69    T: Copy,
70    Source1: NoiseFn<T, DIM>,
71    Source2: NoiseFn<T, DIM>,
72    Control: NoiseFn<T, DIM>,
73{
74    fn get(&self, point: [T; DIM]) -> f64 {
75        let control_value = self.control.get(point);
76        let (lower, upper) = self.bounds;
77
78        if self.falloff > 0.0 {
79            match () {
80                _ if control_value < (lower - self.falloff) => self.source1.get(point),
81                _ if control_value < (lower + self.falloff) => {
82                    let lower_curve = lower - self.falloff;
83                    let upper_curve = lower + self.falloff;
84                    let alpha =
85                        ((control_value - lower_curve) / (upper_curve - lower_curve)).map_cubic();
86
87                    interpolate::linear(self.source1.get(point), self.source2.get(point), alpha)
88                }
89                _ if control_value < (upper - self.falloff) => self.source2.get(point),
90                _ if control_value < (upper + self.falloff) => {
91                    let lower_curve = upper - self.falloff;
92                    let upper_curve = upper + self.falloff;
93                    let alpha =
94                        ((control_value - lower_curve) / (upper_curve - lower_curve)).map_cubic();
95
96                    interpolate::linear(self.source2.get(point), self.source1.get(point), alpha)
97                }
98                _ => self.source1.get(point),
99            }
100        } else if control_value < lower || control_value > upper {
101            self.source1.get(point)
102        } else {
103            self.source2.get(point)
104        }
105    }
106}