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