1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use math;
use math::interpolate;
use noise_fns::NoiseFn;
use std;
pub struct Curve<'a, T: 'a> {
pub source: &'a dyn NoiseFn<T>,
control_points: Vec<ControlPoint<f64>>,
}
struct ControlPoint<T> {
input: T,
output: T,
}
impl<'a, T> Curve<'a, T> {
pub fn new(source: &'a dyn NoiseFn<T>) -> Self {
Self {
source,
control_points: Vec::with_capacity(4),
}
}
pub fn add_control_point(mut self, input_value: f64, output_value: f64) -> Self {
if !self
.control_points
.iter()
.any(|x| (x.input - input_value).abs() < std::f64::EPSILON)
{
let insertion_point = self
.control_points
.iter()
.position(|x| x.input >= input_value)
.unwrap_or_else(|| self.control_points.len());
self.control_points.insert(
insertion_point,
ControlPoint {
input: input_value,
output: output_value,
},
);
}
self
}
}
impl<'a, T> NoiseFn<T> for Curve<'a, T> {
fn get(&self, point: T) -> f64 {
assert!(self.control_points.len() >= 4);
let source_value = self.source.get(point);
let index_pos = self
.control_points
.iter()
.position(|x| x.input > source_value)
.unwrap_or_else(|| self.control_points.len());
if index_pos < 2 {
println!(
"index_pos in curve was less than 2! source value was {}",
source_value
);
}
let index_pos = math::clamp(index_pos, 2, self.control_points.len());
let index0 = math::clamp(index_pos - 2, 0, self.control_points.len() - 1);
let index1 = math::clamp(index_pos - 1, 0, self.control_points.len() - 1);
let index2 = math::clamp(index_pos, 0, self.control_points.len() - 1);
let index3 = math::clamp(index_pos + 1, 0, self.control_points.len() - 1);
if index1 == index2 {
return self.control_points[index1].output;
}
let input0 = self.control_points[index1].input;
let input1 = self.control_points[index2].input;
let alpha = (source_value - input0) / (input1 - input0);
interpolate::cubic(
self.control_points[index0].output,
self.control_points[index1].output,
self.control_points[index2].output,
self.control_points[index3].output,
alpha,
)
}
}