/*
* Copyright (C) 2003, 2004 Jason Bevins (original libnoise code)
* Copyright 2010 Thomas J. Hodge (java port of libnoise)
*
* This file is part of libnoiseforjava.
*
* libnoiseforjava is a Java port of the C++ library libnoise, which may be found at
* http://libnoise.sourceforge.net/. libnoise was developed by Jason Bevins, who may be
* contacted at jlbezigvins@gmzigail.com (for great email, take off every 'zig').
* Porting to Java was done by Thomas Hodge, who may be contacted at
* libnoisezagforjava@gzagmail.com (remove every 'zag').
*
* libnoiseforjava is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* libnoiseforjava is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* libnoiseforjava. If not, see .
*
*/
package libnoiseforjava.module;
import libnoiseforjava.Interp;
import libnoiseforjava.Misc;
import libnoiseforjava.exception.ExceptionInvalidParam;
public class Terrace extends ModuleBase
{
/// Noise module that maps the output value from a source module onto a
/// terrace-forming curve.
///
/// This noise module maps the output value from the source module onto a
/// terrace-forming curve. The start of this curve has a slope of zero;
/// its slope then smoothly increases. This curve also contains
/// control points which resets the slope to zero at that point,
/// producing a "terracing" effect. Refer to the following illustration:
///
/// @image html terrace.png
///
/// To add a control point to this noise module, call the
/// addControlPoint() method.
///
/// An application must add a minimum of two control points to the curve.
/// If this is not done, the getValue() method fails. 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.
///
/// This noise module clamps the output value from the source module 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 module is often used to generate terrain features such as
/// your stereotypical desert canyon.
///
/// This noise module requires one source module.
/// Number of control points stored in this noise module.
int controlPointCount;
/// Determines if the terrace-forming curve between all control points
/// is inverted.
boolean invertTerraces;
/// Array that stores the control points.
double [] controlPoints;
public Terrace (ModuleBase sourceModule) throws ExceptionInvalidParam
{
super(1);
setSourceModule(0, sourceModule);
controlPointCount = 0;
invertTerraces = false;
controlPoints = new double [0];
}
/// Adds a control point to the terrace-forming curve.
///
/// @param value The value of the control point to add.
///
/// @pre No two control points have the same value.
///
/// @throw ExceptionInvalidParam An invalid parameter was
/// specified; see the preconditions for more information.
///
/// Two or more control points define the terrace-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.
public void addControlPoint (double value) throws ExceptionInvalidParam
{
// Find the insertion point for the new control point and insert the new
// point at that position. The control point array will remain sorted by
// value.
int insertionPos = findInsertionPos (value);
insertAtPos (insertionPos, value);
}
/// Deletes all the control points on the terrace-forming curve.
///
/// @post All control points on the terrace-forming curve are deleted.
public void clearAllControlPoints ()
{
controlPoints = null;
controlPointCount = 0;
}
/// Determines the array index in which to insert the control point
/// into the internal control point array.
///
/// @param value The value of the control point.
///
/// @returns The array index in which to insert the control point.
///
/// @pre No two control points have the same value.
///
/// @throw ExceptionInvalidParam An invalid parameter was
/// specified; see the preconditions for more information.
///
/// By inserting the control point at the returned array index, this
/// class ensures that the control point array is sorted by value.
/// The code that maps a value onto the curve requires a sorted
/// control point array.
public int findInsertionPos (double value) throws ExceptionInvalidParam
{
int insertionPos;
for (insertionPos = 0; insertionPos < controlPointCount; insertionPos++)
{
if (value < controlPoints[insertionPos])
// We found the array index in which to insert the new control point.
// Exit now.
break;
else if (value == controlPoints[insertionPos])
// Each control point is required to contain a unique value, so throw
// an exception.
throw new ExceptionInvalidParam ("Invalid Parameter in Terrace Noise Moduled");
}
return insertionPos;
}
public double getValue (double x, double y, double z)
{
assert (sourceModules[0] != null);
assert (controlPointCount >= 2);
// Get the output value from the source module.
double sourceModuleValue = sourceModules[0].getValue (x, y, z);
// Find the first element in the control point array that has a value
// larger than the output value from the source module.
int indexPos;
for (indexPos = 0; indexPos < controlPointCount; indexPos++)
{
if (sourceModuleValue < controlPoints[indexPos])
break;
}
// Find the two nearest control points so that we can map their values
// onto a quadratic curve.
int index0 = Misc.ClampValue (indexPos - 1, 0, controlPointCount - 1);
int index1 = Misc.ClampValue (indexPos, 0, controlPointCount - 1);
// If some control points are missing (which occurs if the output value from
// the source module is greater than the largest value or less than the
// smallest value of the control point array), get the value of the nearest
// control point and exit now.
if (index0 == index1)
return controlPoints[index1];
// Compute the alpha value used for linear interpolation.
double value0 = controlPoints[index0];
double value1 = controlPoints[index1];
double alpha = (sourceModuleValue - value0) / (value1 - value0);
if (invertTerraces)
{
alpha = 1.0 - alpha;
double tempValue = value0;
value0 = value1;
value1 = tempValue;
}
// Squaring the alpha produces the terrace effect.
alpha *= alpha;
// Now perform the linear interpolation given the alpha value.
return Interp.linearInterp (value0, value1, alpha);
}
/// Inserts the control point at the specified position in the
/// internal control point array.
///
/// @param insertionPos The zero-based array position in which to
/// insert the control point.
/// @param value The value of the control point.
///
/// To make room for this new control point, this method reallocates
/// the control point array and shifts all control points occurring
/// after the insertion position up by one.
///
/// Because the curve mapping algorithm in this noise module requires
/// that all control points in the array be sorted by value, the new
/// control point should be inserted at the position in which the
/// order is still preserved.
public void insertAtPos (int insertionPos, double value)
{
// Make room for the new control point at the specified position within
// the control point array. The position is determined by the value of
// the control point; the control points must be sorted by value within
// that array.
double[] newControlPoints = new double[controlPointCount + 1];
for (int i = 0; i < controlPointCount; i++)
{
if (i < insertionPos)
newControlPoints[i] = controlPoints[i];
else
newControlPoints[i + 1] = controlPoints[i];
}
controlPoints = newControlPoints;
++controlPointCount;
// Now that we've made room for the new control point within the array,
// add the new control point.
controlPoints[insertionPos] = value;
}
/// Creates a number of equally-spaced control points that range from
/// -1 to +1.
///
/// @param controlPointCount The number of control points to generate.
///
/// @pre The number of control points must be greater than or equal to
/// 2.
///
/// @post The previous control points on the terrace-forming curve are
/// deleted.
///
/// @throw ExceptionInvalidParam An invalid parameter was
/// specified; see the preconditions for more information.
///
/// Two or more control points define the terrace-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.
void makeControlPoints (int controlPointCount) throws ExceptionInvalidParam
{
if (controlPointCount < 2)
throw new ExceptionInvalidParam ("Invalid Parameter in Terrace Noise Module");
clearAllControlPoints ();
double terraceStep = 2.0 / ((double)controlPointCount - 1.0);
double curValue = -1.0;
for (int i = 0; i < (int)controlPointCount; i++)
{
addControlPoint (curValue);
curValue += terraceStep;
}
}
/// Returns a pointer to the array of control points on the
/// terrace-forming curve.
///
/// @returns A pointer to the array of control points in this noise
/// module.
///
/// Two or more control points define the terrace-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.
///
/// Before calling this method, call getControlPointCount() to
/// determine the number of control points in this array.
///
/// It is recommended that an application does not store this pointer
/// for later use since the pointer to the array may change if the
/// application calls another method of this object.
public double[] getControlPointArray ()
{
return controlPoints;
}
/// Returns the number of control points on the terrace-forming curve.
///
/// @returns The number of control points on the terrace-forming
/// curve.
public int getControlPointCount ()
{
return controlPointCount;
}
/// Enables or disables the inversion of the terrace-forming curve
/// between the control points.
///
/// @param invert Specifies whether to invert the curve between the
/// control points.
public void invertTerraces (boolean invert)
{
if (invert)
invertTerraces = invert;
}
/// Determines if the terrace-forming curve between the control
/// points is inverted.
///
/// @returns
/// - @a true if the curve between the control points is inverted.
/// - @a false if the curve between the control points is not
/// inverted.
public boolean isTerracesInverted ()
{
return invertTerraces;
}
}