1 // terrace.h 2 // 3 // Copyright (C) 2003, 2004 Jason Bevins 4 // 5 // This library is free software; you can redistribute it and/or modify it 6 // under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation; either version 2.1 of the License, or (at 8 // your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, but WITHOUT 11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 13 // License (COPYING.txt) for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with this library; if not, write to the Free Software Foundation, 17 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 // 19 // The developer's email is jlbezigvins@gmzigail.com (for great email, take 20 // off every 'zig'.) 21 // 22 module noise.mod.terrace; 23 24 private { 25 import noise.mod.modulebase; 26 import noise.misc; 27 import noise.interp; 28 } 29 30 /// @addtogroup libnoise 31 /// @{ 32 33 /// @addtogroup modules 34 /// @{ 35 36 /// @addtogroup modifiermodules 37 /// @{ 38 39 /// Noise module that maps the output value from a source module onto a 40 /// terrace-forming curve. 41 /// 42 /// @image html moduleterrace.png 43 /// 44 /// This noise module maps the output value from the source module onto a 45 /// terrace-forming curve. The start of this curve has a slope of zero; 46 /// its slope then smoothly increases. This curve also contains 47 /// <i>control points</i> which resets the slope to zero at that point, 48 /// producing a "terracing" effect. Refer to the following illustration: 49 /// 50 /// @image html terrace.png 51 /// 52 /// To add a control point to this noise module, call the 53 /// AddControlPoint() method. 54 /// 55 /// An application must add a minimum of two control points to the curve. 56 /// If this is not done, the GetValue() method fails. The control points 57 /// can have any value, although no two control points can have the same 58 /// value. There is no limit to the number of control points that can be 59 /// added to the curve. 60 /// 61 /// This noise module clamps the output value from the source module if 62 /// that value is less than the value of the lowest control point or 63 /// greater than the value of the highest control point. 64 /// 65 /// This noise module is often used to generate terrain features such as 66 /// your stereotypical desert canyon. 67 /// 68 /// This noise module requires one source module. 69 class Terrace : Mod 70 { 71 72 public: 73 74 /// Constructor. 75 this() 76 { 77 super(this.GetSourceModCount ()); 78 m_controlPointCount = 0; 79 m_invertTerraces = false; 80 m_pControlPoints = null; 81 } 82 83 /// Destructor. 84 ~this(){} 85 86 /// Adds a control point to the terrace-forming curve. 87 /// 88 /// @param value The value of the control point to add. 89 /// 90 /// @pre No two control points have the same value. 91 /// 92 /// @throw new ExceptionInvalidParam An invalid parameter was 93 /// specified; see the preconditions for more information. 94 /// 95 /// Two or more control points define the terrace-forming curve. The 96 /// start of this curve has a slope of zero; its slope then smoothly 97 /// increases. At the control points, its slope resets to zero. 98 /// 99 /// It does not matter which order these points are added. 100 void AddControlPoint (double value) 101 { 102 // Find the insertion point for the new control point and insert the new 103 // point at that position. The control point array will remain sorted by 104 // value. 105 int insertionPos = FindInsertionPos (value); 106 InsertAtPos (insertionPos, value); 107 } 108 109 /// Deletes all the control points on the terrace-forming curve. 110 /// 111 /// @post All control points on the terrace-forming curve are deleted. 112 void ClearAllControlPoints () 113 { 114 m_pControlPoints = null; 115 m_controlPointCount = 0; 116 } 117 118 /// Returns a pointer to the array of control points on the 119 /// terrace-forming curve. 120 /// 121 /// @returns A pointer to the array of control points in this noise 122 /// module. 123 /// 124 /// Two or more control points define the terrace-forming curve. The 125 /// start of this curve has a slope of zero; its slope then smoothly 126 /// increases. At the control points, its slope resets to zero. 127 /// 128 /// Before calling this method, call GetControlPointCount() to 129 /// determine the number of control points in this array. 130 /// 131 /// It is recommended that an application does not store this pointer 132 /// for later use since the pointer to the array may change if the 133 /// application calls another method of this object. 134 const (double)[] GetControlPointArray () const 135 { 136 return m_pControlPoints; 137 } 138 139 /// Returns the number of control points on the terrace-forming curve. 140 /// 141 /// @returns The number of control points on the terrace-forming 142 /// curve. 143 int GetControlPointCount () const 144 { 145 return m_controlPointCount; 146 } 147 148 override int GetSourceModCount () const 149 { 150 return 1; 151 } 152 153 /// Enables or disables the inversion of the terrace-forming curve 154 /// between the control points. 155 /// 156 /// @param invert Specifies whether to invert the curve between the 157 /// control points. 158 void InvertTerraces (bool invert = true) 159 { 160 m_invertTerraces = invert; 161 } 162 163 /// Determines if the terrace-forming curve between the control 164 /// points is inverted. 165 /// 166 /// @returns 167 /// - @a true if the curve between the control points is inverted. 168 /// - @a false if the curve between the control points is not 169 /// inverted. 170 bool IsTerracesInverted () const 171 { 172 return m_invertTerraces; 173 } 174 175 override double GetValue (double x, double y, double z) const 176 { 177 assert (m_pSourceMod[0] !is null); 178 assert (m_controlPointCount >= 2); 179 180 // Get the output value from the source module. 181 double sourceModValue = m_pSourceMod[0].GetValue (x, y, z); 182 183 // Find the first element in the control point array that has a value 184 // larger than the output value from the source module. 185 int indexPos; 186 for (indexPos = 0; indexPos < m_controlPointCount; indexPos++) { 187 if (sourceModValue < m_pControlPoints[indexPos]) { 188 break; 189 } 190 } 191 192 // Find the two nearest control points so that we can map their values 193 // onto a quadratic curve. 194 int index0 = ClampValue (indexPos - 1, 0, m_controlPointCount - 1); 195 int index1 = ClampValue (indexPos , 0, m_controlPointCount - 1); 196 197 // If some control points are missing (which occurs if the output value from 198 // the source module is greater than the largest value or less than the 199 // smallest value of the control point array), get the value of the nearest 200 // control point and exit now. 201 if (index0 == index1) { 202 return m_pControlPoints[index1]; 203 } 204 205 // Compute the alpha value used for linear interpolation. 206 double value0 = m_pControlPoints[index0]; 207 double value1 = m_pControlPoints[index1]; 208 double alpha = (sourceModValue - value0) / (value1 - value0); 209 if (m_invertTerraces) { 210 alpha = 1.0 - alpha; 211 SwapValues (value0, value1); 212 } 213 214 // Squaring the alpha produces the terrace effect. 215 alpha *= alpha; 216 217 // Now perform the linear interpolation given the alpha value. 218 return LinearInterp (value0, value1, alpha); 219 } 220 221 222 /// Creates a number of equally-spaced control points that range from 223 /// -1 to +1. 224 /// 225 /// @param controlPointCount The number of control points to generate. 226 /// 227 /// @pre The number of control points must be greater than or equal to 228 /// 2. 229 /// 230 /// @post The previous control points on the terrace-forming curve are 231 /// deleted. 232 /// 233 /// @throw new ExceptionInvalidParam An invalid parameter was 234 /// specified; see the preconditions for more information. 235 /// 236 /// Two or more control points define the terrace-forming curve. The 237 /// start of this curve has a slope of zero; its slope then smoothly 238 /// increases. At the control points, its slope resets to zero. 239 void MakeControlPoints (int controlPointCount) 240 { 241 if (controlPointCount < 2) { 242 throw new ExceptionInvalidParam (); 243 } 244 245 ClearAllControlPoints (); 246 247 double terraceStep = 2.0 / (cast(double)controlPointCount - 1.0); 248 double curValue = -1.0; 249 for (int i = 0; i < cast(int)controlPointCount; i++) { 250 AddControlPoint (curValue); 251 curValue += terraceStep; 252 } 253 } 254 255 protected: 256 257 /// Determines the array index in which to insert the control point 258 /// into the internal control point array. 259 /// 260 /// @param value The value of the control point. 261 /// 262 /// @returns The array index in which to insert the control point. 263 /// 264 /// @pre No two control points have the same value. 265 /// 266 /// @throw new ExceptionInvalidParam An invalid parameter was 267 /// specified; see the preconditions for more information. 268 /// 269 /// By inserting the control point at the returned array index, this 270 /// class ensures that the control point array is sorted by value. 271 /// The code that maps a value onto the curve requires a sorted 272 /// control point array. 273 int FindInsertionPos (double value) 274 { 275 int insertionPos; 276 for (insertionPos = 0; insertionPos < m_controlPointCount; insertionPos++) { 277 if (value < m_pControlPoints[insertionPos]) { 278 // We found the array index in which to insert the new control point. 279 // Exit now. 280 break; 281 } else if (value == m_pControlPoints[insertionPos]) { 282 // Each control point is required to contain a unique value, so throw 283 // an exception. 284 throw new ExceptionInvalidParam (); 285 } 286 } 287 return insertionPos; 288 } 289 290 /// Inserts the control point at the specified position in the 291 /// internal control point array. 292 /// 293 /// @param insertionPos The zero-based array position in which to 294 /// insert the control point. 295 /// @param value The value of the control point. 296 /// 297 /// To make room for this new control point, this method reallocates 298 /// the control point array and shifts all control points occurring 299 /// after the insertion position up by one. 300 /// 301 /// Because the curve mapping algorithm in this noise module requires 302 /// that all control points in the array be sorted by value, the new 303 /// control point should be inserted at the position in which the 304 /// order is still preserved. 305 void InsertAtPos (int insertionPos, double value) 306 { 307 // Make room for the new control point at the specified position within 308 // the control point array. The position is determined by the value of 309 // the control point; the control points must be sorted by value within 310 // that array. 311 double[] newControlPoints = new double[m_controlPointCount + 1]; 312 for (int i = 0; i < m_controlPointCount; i++) { 313 if (i < insertionPos) { 314 newControlPoints[i] = m_pControlPoints[i]; 315 } else { 316 newControlPoints[i + 1] = m_pControlPoints[i]; 317 } 318 } 319 m_pControlPoints = newControlPoints; 320 ++m_controlPointCount; 321 322 // Now that we've made room for the new control point within the array, 323 // add the new control point. 324 m_pControlPoints[insertionPos] = value; 325 } 326 327 /// Number of control points stored in this noise module. 328 int m_controlPointCount; 329 330 /// Determines if the terrace-forming curve between all control points 331 /// is inverted. 332 bool m_invertTerraces; 333 334 /// Array that stores the control points. 335 double[] m_pControlPoints; 336 337 }; 338 339 /// @} 340 341 /// @} 342 343 /// @}