/*
* 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 Curve extends ModuleBase
{
/// Noise module that maps the output value from a source module onto an
/// arbitrary function curve.
///
/// This noise module maps the output value from the source module onto an
/// application-defined curve. This curve is defined by a number of
/// control points; each control point has an input value
/// that maps to an output value.
///
/// To add the control points to this curve, call the addControlPoint()
/// method. Note that the class ControlPoint follows the class Curve in
/// this file.
///
/// Since this curve is a cubic spline, an application must add a minimum
/// of four control points to the curve. If this is not done, the
/// getValue() method fails. Each control point can have any input and
/// output value, although no two control points can have the same input
/// value. There is no limit to the number of control points that can be
/// added to the curve.
///
/// This noise module requires one source module
int controlPointCount;
ControlPoint[] controlPoints;
public Curve (ModuleBase sourceModule) throws ExceptionInvalidParam
{
super(1);
setSourceModule(0, sourceModule);
controlPointCount = 0;
controlPoints= new ControlPoint[1];
controlPoints[0] = new ControlPoint(0.0, 0.0);
}
public void addControlPoint (double inputValue, double outputValue)
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
// input value.
int insertionPos = findInsertionPos(inputValue);
insertAtPos (insertionPos, inputValue, outputValue);
}
public void clearAllControlPoints ()
{
controlPoints = null;
controlPointCount = 0;
}
public int findInsertionPos (double inputValue) throws ExceptionInvalidParam
{
int insertionPos;
for (insertionPos = 0; insertionPos < controlPointCount; insertionPos++)
{
if (inputValue < controlPoints[insertionPos].inputValue)
// We found the array index in which to insert the new control point.
// Exit now.
break;
else if (inputValue == controlPoints[insertionPos].inputValue)
// Each control point is required to contain a unique input value, so
// throw an exception.
throw new ExceptionInvalidParam("Invalid Parameter in Curve");
}
return insertionPos;
}
public double getValue (double x, double y, double z)
{
assert (sourceModules[0] != null);
assert (controlPointCount >= 4);
// 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 an input value
// larger than the output value from the source module.
int indexPos;
for (indexPos = 0; indexPos < controlPointCount; indexPos++)
{
if (sourceModuleValue < controlPoints[indexPos].inputValue)
break;
}
// Find the four nearest control points so that we can perform cubic
// interpolation.
int index0 = Misc.ClampValue (indexPos - 2, 0, controlPointCount - 1);
int index1 = Misc.ClampValue (indexPos - 1, 0, controlPointCount - 1);
int index2 = Misc.ClampValue (indexPos , 0, controlPointCount - 1);
int index3 = Misc.ClampValue (indexPos + 1, 0, controlPointCount - 1);
// If some control points are missing (which occurs if the value from the
// source module is greater than the largest input value or less than the
// smallest input value of the control point array), get the corresponding
// output value of the nearest control point and exit now.
if (index1 == index2) {
return controlPoints[index1].outputValue;
}
// Compute the alpha value used for cubic interpolation.
double input0 = controlPoints[index1].inputValue;
double input1 = controlPoints[index2].inputValue;
double alpha = (sourceModuleValue - input0) / (input1 - input0);
// Now perform the cubic interpolation given the alpha value.
return Interp.cubicInterp(
controlPoints[index0].outputValue,
controlPoints[index1].outputValue,
controlPoints[index2].outputValue,
controlPoints[index3].outputValue,
alpha);
}
public void insertAtPos (int insertionPos, double inputValue,
double outputValue)
{
// Make room for the new control point at the specified position within the
// control point array. The position is determined by the input value of
// the control point; the control points must be sorted by input value
// within that array.
ControlPoint[] newControlPoints = new ControlPoint[controlPointCount + 1];
for (int t = 0; t < (controlPointCount + 1); t++)
newControlPoints[t] = new ControlPoint();
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].inputValue = inputValue;
controlPoints[insertionPos].outputValue = outputValue;
}
}
/// This class defines a control point.
///
/// Control points are used for defining splines.
class ControlPoint
{
/// The input value.
double inputValue;
/// The output value that is mapped from the input value.
double outputValue;
ControlPoint()
{
inputValue = 0.0;
outputValue = 0.0;
}
ControlPoint(double inputValue, double outputValue)
{
this.inputValue = inputValue;
this.outputValue = outputValue;
}
}