/*******************************************************************************
* Copyright 2012 Geoscience Australia
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package au.gov.ga.earthsci.common.math.interpolation;
import gov.nasa.worldwind.cache.BasicMemoryCache;
import au.gov.ga.earthsci.common.math.bezier.Bezier;
import au.gov.ga.earthsci.common.math.vector.Vector;
import au.gov.ga.earthsci.common.util.Validate;
/**
* An implementation of the {@link Interpolator} interface that performs
* interpolation using cubic bezier curves.
* <p/>
* This interpolator needs to be primed with the four control points needed to
* define the bezier curve to use for interpolation
*
* @author James Navin (james.navin@ga.gov.au)
*/
public class BezierInterpolator<V extends Vector<V>> implements Interpolator<V>
{
/** A cache for computed beziers */
private static final int DEFAULT_CACHE_SIZE = 30000000;
private static final BasicMemoryCache BEZIER_CACHE = new BasicMemoryCache((int) 0.85 * DEFAULT_CACHE_SIZE,
DEFAULT_CACHE_SIZE);
// The four points needed to describe the cubic bezier control curve
/**
* The beginning value of the bezier. The curve will pass through this
* point.
*/
private V begin;
/** The control point to use when exiting the beginning point. */
private V out;
/** The control point to use when entering the end point. */
private V in;
/** The end value of the bezier. The curve will pass through this point. */
private V end;
/** The bezier curve to use to calculate the interpolation */
private Bezier<V> bezier;
/**
* Constructor. Initialises the control points.
*/
public BezierInterpolator(V begin, V out, V in, V end)
{
setControlPoints(begin, out, in, end);
}
@Override
public V computeValue(double percent)
{
if (bezier == null)
{
bezier = getBezier();
}
return bezier.pointAt(percent);
}
/**
* @return A bezier to use from the current control points
*/
@SuppressWarnings("unchecked")
private Bezier<V> getBezier()
{
String cacheKey = createCacheKey();
Bezier<V> result = (Bezier<V>) BEZIER_CACHE.getObject(cacheKey);
if (result != null)
{
return result;
}
result = new Bezier<V>(begin, out, in, end);
BEZIER_CACHE.add(cacheKey, result, result.getNumSubdivisions() * (Double.SIZE / 8));
return result;
}
/**
* @return A cache key to use for the current bezier values
*/
private String createCacheKey()
{
return "[" + begin + out + in + end + "]"; //$NON-NLS-1$//$NON-NLS-2$
}
/**
* Set the four control points needed to define the bezier curve
*
* @param begin
* The beginning value of the bezier. The curve will pass through
* this point.
* @param out
* The control point to use when exiting the beginning point
* @param in
* The control point to use when entering the end point
* @param end
* The end value of the bezier. The curve will pass through this
* point.
*/
public void setControlPoints(V begin, V out, V in, V end)
{
setBegin(begin);
setEnd(end);
setOut(out);
setIn(in);
}
/**
* @return The beginning value of the bezier. The curve will pass through
* this point.
*/
public V getBegin()
{
return begin;
}
/**
* @param begin
* the begin value to set
*/
public void setBegin(V begin)
{
Validate.notNull(begin, "A begin value is required"); //$NON-NLS-1$
if (this.begin == null || !this.begin.equals(begin))
{
this.begin = begin;
this.bezier = null;
}
}
/**
* @return The control point to use when exiting the beginning point
*/
public V getOut()
{
return out;
}
/**
* @param out
* the out value to set
*/
public void setOut(V out)
{
Validate.notNull(out, "An out value is required"); //$NON-NLS-1$
if (this.out == null || !this.out.equals(out))
{
this.out = out;
this.bezier = null;
}
}
/**
* @return The control point to use when entering the end point
*/
public V getIn()
{
return in;
}
/**
* @param in
* the in value to set
*/
public void setIn(V in)
{
Validate.notNull(in, "An in value is required"); //$NON-NLS-1$
if (this.in == null || !this.in.equals(in))
{
this.in = in;
this.bezier = null;
}
}
/**
* @return The end value of the bezier. The curve will pass through this
* point.
*/
public V getEnd()
{
return end;
}
/**
* @param end
* the end value to set
*/
public void setEnd(V end)
{
Validate.notNull(end, "An end value is required"); //$NON-NLS-1$
if (this.end == null || !this.end.equals(end))
{
this.end = end;
this.bezier = null;
}
}
}