1 // curve.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.curve; 23 24 private { 25 import noise.mod.modulebase; 26 import noise.interp; 27 import noise.misc; 28 } 29 30 /// @addtogroup libnoise 31 /// @{ 32 33 /// This structure defines a control point. 34 /// 35 /// Control points are used for defining splines. 36 struct ControlPoint 37 { 38 39 /// The input value. 40 double inputValue; 41 42 /// The output value that is mapped from the input value. 43 double outputValue; 44 45 }; 46 47 /// @addtogroup modules 48 /// @{ 49 50 /// @addtogroup modifiermodules 51 /// @{ 52 53 /// Noise module that maps the output value from a source module onto an 54 /// arbitrary function curve. 55 /// 56 /// @image html modulecurve.png 57 /// 58 /// This noise module maps the output value from the source module onto an 59 /// application-defined curve. This curve is defined by a number of 60 /// <i>control points</i>; each control point has an <i>input value</i> 61 /// that maps to an <i>output value</i>. Refer to the following 62 /// illustration: 63 /// 64 /// @image html curve.png 65 /// 66 /// To add the control points to this curve, call the AddControlPoint() 67 /// method. 68 /// 69 /// Since this curve is a cubic spline, an application must add a minimum 70 /// of four control points to the curve. If this is not done, the 71 /// GetValue() method fails. Each control point can have any input and 72 /// output value, although no two control points can have the same input 73 /// value. There is no limit to the number of control points that can be 74 /// added to the curve. 75 /// 76 /// This noise module requires one source module. 77 class Curve : Mod 78 { 79 80 public: 81 82 /// Constructor. 83 this () 84 { 85 super(this.GetSourceModCount ()); 86 m_pControlPoints = null; 87 m_controlPointCount = 0; 88 } 89 90 /// Destructor. 91 ~this (){} 92 93 /// Adds a control point to the curve. 94 /// 95 /// @param inputValue The input value stored in the control point. 96 /// @param outputValue The output value stored in the control point. 97 /// 98 /// @pre No two control points have the same input value. 99 /// 100 /// @throw new ExceptionInvalidParam An invalid parameter was 101 /// specified; see the preconditions for more information. 102 /// 103 /// It does not matter which order these points are added. 104 void AddControlPoint (double inputValue, double outputValue) 105 { 106 // Find the insertion point for the new control point and insert the new 107 // point at that position. The control point array will remain sorted by 108 // input value. 109 int insertionPos = FindInsertionPos (inputValue); 110 InsertAtPos (insertionPos, inputValue, outputValue); 111 } 112 113 /// Deletes all the control points on the curve. 114 /// 115 /// @post All points on the curve are deleted. 116 void ClearAllControlPoints () 117 { 118 m_pControlPoints = null; 119 m_controlPointCount = 0; 120 } 121 122 /// Returns a pointer to the array of control points on the curve. 123 /// 124 /// @returns A pointer to the array of control points. 125 /// 126 /// Before calling this method, call GetControlPointCount() to 127 /// determine the number of control points in this array. 128 /// 129 /// It is recommended that an application does not store this pointer 130 /// for later use since the pointer to the array may change if the 131 /// application calls another method of this object. 132 const (ControlPoint)[] GetControlPointArray () const 133 { 134 return m_pControlPoints; 135 } 136 137 /// Returns the number of control points on the curve. 138 /// 139 /// @returns The number of control points on the curve. 140 int GetControlPointCount () const 141 { 142 return m_controlPointCount; 143 } 144 145 override int GetSourceModCount () const 146 { 147 return 1; 148 } 149 150 override double GetValue (double x, double y, double z) const 151 { 152 assert (m_pSourceMod[0] !is null); 153 assert (m_controlPointCount >= 4); 154 155 // Get the output value from the source module. 156 double sourceModValue = m_pSourceMod[0].GetValue (x, y, z); 157 158 // Find the first element in the control point array that has an input value 159 // larger than the output value from the source module. 160 int indexPos; 161 for (indexPos = 0; indexPos < m_controlPointCount; indexPos++) { 162 if (sourceModValue < m_pControlPoints[indexPos].inputValue) { 163 break; 164 } 165 } 166 167 // Find the four nearest control points so that we can perform cubic 168 // interpolation. 169 int index0 = ClampValue (indexPos - 2, 0, m_controlPointCount - 1); 170 int index1 = ClampValue (indexPos - 1, 0, m_controlPointCount - 1); 171 int index2 = ClampValue (indexPos , 0, m_controlPointCount - 1); 172 int index3 = ClampValue (indexPos + 1, 0, m_controlPointCount - 1); 173 174 // If some control points are missing (which occurs if the value from the 175 // source module is greater than the largest input value or less than the 176 // smallest input value of the control point array), get the corresponding 177 // output value of the nearest control point and exit now. 178 if (index1 == index2) { 179 return m_pControlPoints[index1].outputValue; 180 } 181 182 // Compute the alpha value used for cubic interpolation. 183 double input0 = m_pControlPoints[index1].inputValue; 184 double input1 = m_pControlPoints[index2].inputValue; 185 double alpha = (sourceModValue - input0) / (input1 - input0); 186 187 // Now perform the cubic interpolation given the alpha value. 188 return CubicInterp ( 189 m_pControlPoints[index0].outputValue, 190 m_pControlPoints[index1].outputValue, 191 m_pControlPoints[index2].outputValue, 192 m_pControlPoints[index3].outputValue, 193 alpha); 194 } 195 196 protected: 197 198 /// Determines the array index in which to insert the control point 199 /// into the internal control point array. 200 /// 201 /// @param inputValue The input value of the control point. 202 /// 203 /// @returns The array index in which to insert the control point. 204 /// 205 /// @pre No two control points have the same input value. 206 /// 207 /// @throw new ExceptionInvalidParam An invalid parameter was 208 /// specified; see the preconditions for more information. 209 /// 210 /// By inserting the control point at the returned array index, this 211 /// class ensures that the control point array is sorted by input 212 /// value. The code that maps a value onto the curve requires a 213 /// sorted control point array. 214 int FindInsertionPos (double inputValue) 215 { 216 int insertionPos; 217 for (insertionPos = 0; insertionPos < m_controlPointCount; insertionPos++) { 218 if (inputValue < m_pControlPoints[insertionPos].inputValue) { 219 // We found the array index in which to insert the new control point. 220 // Exit now. 221 break; 222 } else if (inputValue == m_pControlPoints[insertionPos].inputValue) { 223 // Each control point is required to contain a unique input value, so 224 // throw new an exception. 225 throw new ExceptionInvalidParam (); 226 } 227 } 228 return insertionPos; 229 } 230 231 /// Inserts the control point at the specified position in the 232 /// internal control point array. 233 /// 234 /// @param insertionPos The zero-based array position in which to 235 /// insert the control point. 236 /// @param inputValue The input value stored in the control point. 237 /// @param outputValue The output value stored in the control point. 238 /// 239 /// To make room for this new control point, this method reallocates 240 /// the control point array and shifts all control points occurring 241 /// after the insertion position up by one. 242 /// 243 /// Because the curve mapping algorithm used by this noise module 244 /// requires that all control points in the array must be sorted by 245 /// input value, the new control point should be inserted at the 246 /// position in which the order is still preserved. 247 void InsertAtPos (int insertionPos, double inputValue, 248 double outputValue) 249 { 250 // Make room for the new control point at the specified position within the 251 // control point array. The position is determined by the input value of 252 // the control point; the control points must be sorted by input value 253 // within that array. 254 ControlPoint[] newControlPoints = new ControlPoint[m_controlPointCount + 1]; 255 for (int i = 0; i < m_controlPointCount; i++) { 256 if (i < insertionPos) { 257 newControlPoints[i] = m_pControlPoints[i]; 258 } else { 259 newControlPoints[i + 1] = m_pControlPoints[i]; 260 } 261 } 262 m_pControlPoints = newControlPoints; 263 ++m_controlPointCount; 264 265 // Now that we've made room for the new control point within the array, add 266 // the new control point. 267 m_pControlPoints[insertionPos].inputValue = inputValue ; 268 m_pControlPoints[insertionPos].outputValue = outputValue; 269 } 270 271 /// Number of control points on the curve. 272 int m_controlPointCount; 273 274 /// Array that stores the control points. 275 ControlPoint[] m_pControlPoints; 276 277 }; 278 279 /// @} 280 281 /// @} 282 283 /// @}