terrain_forge/noise/
modifiers.rs1use super::NoiseSource;
2
3pub struct Scale<S: NoiseSource> {
5 pub(crate) source: S,
6 pub(crate) factor: f64,
7}
8
9impl<S: NoiseSource> NoiseSource for Scale<S> {
10 fn sample(&self, x: f64, y: f64) -> f64 {
11 self.source.sample(x, y) * self.factor
12 }
13}
14
15pub struct Offset<S: NoiseSource> {
17 pub(crate) source: S,
18 pub(crate) amount: f64,
19}
20
21impl<S: NoiseSource> NoiseSource for Offset<S> {
22 fn sample(&self, x: f64, y: f64) -> f64 {
23 self.source.sample(x, y) + self.amount
24 }
25}
26
27pub struct Clamp<S: NoiseSource> {
29 pub(crate) source: S,
30 pub(crate) min: f64,
31 pub(crate) max: f64,
32}
33
34impl<S: NoiseSource> NoiseSource for Clamp<S> {
35 fn sample(&self, x: f64, y: f64) -> f64 {
36 self.source.sample(x, y).clamp(self.min, self.max)
37 }
38}
39
40pub struct Abs<S: NoiseSource> {
42 pub(crate) source: S,
43}
44
45impl<S: NoiseSource> NoiseSource for Abs<S> {
46 fn sample(&self, x: f64, y: f64) -> f64 {
47 self.source.sample(x, y).abs()
48 }
49}
50
51pub struct Blend<A: NoiseSource, B: NoiseSource, C: NoiseSource> {
53 pub source_a: A,
54 pub source_b: B,
55 pub control: C,
56}
57
58impl<A: NoiseSource, B: NoiseSource, C: NoiseSource> Blend<A, B, C> {
59 pub fn new(source_a: A, source_b: B, control: C) -> Self {
60 Self {
61 source_a,
62 source_b,
63 control,
64 }
65 }
66}
67
68impl<A: NoiseSource, B: NoiseSource, C: NoiseSource> NoiseSource for Blend<A, B, C> {
69 fn sample(&self, x: f64, y: f64) -> f64 {
70 let a = self.source_a.sample(x, y);
71 let b = self.source_b.sample(x, y);
72 let t = (self.control.sample(x, y) + 1.0) * 0.5; a + t * (b - a)
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use crate::noise::{NoiseExt, Perlin};
81
82 #[test]
83 fn scale_modifier() {
84 let noise = Perlin::new(42).scale(2.0);
85 let base = Perlin::new(42);
86 let v1 = noise.sample(1.0, 1.0);
87 let v2 = base.sample(1.0, 1.0) * 2.0;
88 assert!((v1 - v2).abs() < 1e-10);
89 }
90
91 #[test]
92 fn offset_modifier() {
93 let noise = Perlin::new(42).offset(0.5);
94 let base = Perlin::new(42);
95 let v1 = noise.sample(1.0, 1.0);
96 let v2 = base.sample(1.0, 1.0) + 0.5;
97 assert!((v1 - v2).abs() < 1e-10);
98 }
99
100 #[test]
101 fn clamp_modifier() {
102 let noise = Perlin::new(42).clamp(-0.5, 0.5);
103 for i in 0..50 {
104 for j in 0..50 {
105 let v = noise.sample(i as f64 * 0.1, j as f64 * 0.1);
106 assert!((-0.5..=0.5).contains(&v));
107 }
108 }
109 }
110
111 #[test]
112 fn abs_modifier() {
113 let noise = Perlin::new(42).abs();
114 for i in 0..50 {
115 for j in 0..50 {
116 let v = noise.sample(i as f64 * 0.1, j as f64 * 0.1);
117 assert!(v >= 0.0);
118 }
119 }
120 }
121
122 #[test]
123 fn chained_modifiers() {
124 let noise = Perlin::new(42).scale(0.5).offset(0.5).clamp(0.0, 1.0);
125
126 for i in 0..50 {
127 for j in 0..50 {
128 let v = noise.sample(i as f64 * 0.1, j as f64 * 0.1);
129 assert!((0.0..=1.0).contains(&v));
130 }
131 }
132 }
133}