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 /// @}