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 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
use crate::util::{clamp, linear_interpolate, FloatIterator}; use noise::NoiseFn; /// Noise function that maps the output value from the source function onto a /// levels-forming curve. /// /// This noise function maps the output value from the source function onto a /// levels-forming curve. The start of the curve has a slode of zero; it's /// slope then smoothly increases. This curve also contains _control points_ /// which resets the slope to zero at that point, producing a "terracing" /// effect. /// /// To add control points to the curve, use the `add_control_point` method. /// /// An application must add a minimum of two control points to the curve. If /// there are less than two control points, the get() method panics. The /// control points can have any value, although no two control points can /// have the same value. There is no limit to the number of control points /// that can be added to the curve. /// /// The noise function clamps the output value from the source function if that /// value is less than the value of the lowest control point or greater than /// the value of the highest control point. /// /// This noise function is often used to generate terrain features such as the /// stereotypical desert canyon. pub struct Levels<'a, T> { /// Outputs a value. pub source: &'a dyn NoiseFn<T>, /// Determines if the levels-forming curve between all control points is /// inverted. pub invert_levelss: bool, /// Vec that stores the control points. control_points: Vec<f64>, } impl<'a, T> Levels<'a, T> { pub fn new(source: &'a dyn NoiseFn<T>) -> Self { Levels { source, invert_levelss: false, control_points: Vec::with_capacity(2), } } /// Adds a control point to the levels-forming curve. /// /// Two or more control points define the levels-forming curve. The start /// of this curve has a slope of zero; its slope then smoothly increases. /// At the control points, its slope resets to zero. /// /// It does not matter which order these points are added in. pub fn add_control_point(mut self, control_point: f64) -> Self { // check to see if the vector already contains the input point. if !self .control_points .iter() .any(|&x| (x - control_point).abs() < std::f64::EPSILON) { // it doesn't, so find the correct position to insert the new // control point. let insertion_point = self .control_points .iter() .position(|&x| x >= control_point) .unwrap_or_else(|| self.control_points.len()); // add the new control point at the correct position. self.control_points.insert(insertion_point, control_point); } // create new Levels with updated control_points vector Levels { ..self } } pub fn add_control_points(mut self, min: f64, max: f64, step: f64) -> Self { let control_points = FloatIterator::new_with_step(min, max, step); for control_point in control_points { // check to see if the vector already contains the input point. if !self .control_points .iter() .any(|&x| (x - control_point).abs() < std::f64::EPSILON) { // it doesn't, so find the correct position to insert the new // control point. let insertion_point = self .control_points .iter() .position(|&x| x >= control_point) .unwrap_or_else(|| self.control_points.len()); // add the new control point at the correct position. self.control_points.insert(insertion_point, control_point); } } // create new Levels with updated control_points vector Levels { ..self } } /// Enables or disables the inversion of the terrain-forming curve between /// the control points. pub fn invert_levels(self, invert_levelss: bool) -> Self { Levels { invert_levelss, ..self } } } impl<'a, T> NoiseFn<T> for Levels<'a, T> { fn get(&self, point: T) -> f64 { // confirm that there's at least 2 control points in the vector. assert!(self.control_points.len() >= 2); // get output value from the source function let source_value = self.source.get(point); // Find the first element in the control point array that has a input // value larger than the output value from the source function let index_pos = self .control_points .iter() .position(|&x| x >= source_value) .unwrap_or_else(|| self.control_points.len()); // Find the two nearest control points so that we can map their values // onto a quadratic curve. let index0 = clamp_index(index_pos as isize - 1, 0, self.control_points.len() - 1); let index1 = clamp_index(index_pos as isize, 0, self.control_points.len() - 1); // If some control points are missing (which occurs if the value from // the source function is greater than the largest input value or less // than the smallest input value of the control point array), get the // corresponding output value of the nearest control point and exit. if index0 == index1 { return self.control_points[index1]; } // Compute the alpha value used for cubic interpolation let mut input0 = self.control_points[index0]; let mut input1 = self.control_points[index1]; let mut alpha = (source_value - input0) / (input1 - input0); if self.invert_levelss { alpha = 1.0 - alpha; std::mem::swap(&mut input0, &mut input1); } // Squaring the alpha produces the levels effect. alpha *= alpha; // Now perform the cubic interpolation and return. linear_interpolate(input0, input1, alpha) } } fn clamp_index(index: isize, min: usize, max: usize) -> usize { clamp(index, min as isize, max as isize) as usize }