/**
* Copyright (c) 2005-2006, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of the TimingFramework project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jdesktop.animation.timing.interpolation;
import java.awt.Color;
import java.awt.geom.Arc2D;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Dimension2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
/**
* This class is used by KeyValues to calculate intermediate values for
* specific types.
* This class has built-in support for the following data types:
* <ul>
* <li> java.lang.Byte
* <li> java.lang.Short
* <li> java.lang.Integer
* <li> java.lang.Long
* <li> java.lang.Float
* <li> java.lang.Double
* <li> java.awt.Color
* <li> java.awt.geom.Point2D
* <li> java.awt.geom.Line2D
* <li> java.awt.geom.Dimension2D
* <li> java.awt.geom.Rectangle2D
* <li> java.awt.geom.RoundRectangle2D
* <li> java.awt.geom.Ellipse2D
* <li> java.awt.geom.Arc2D
* <li> java.awt.geom.QuadCurve2D
* <li> java.awt.geom.CubicCurve2D
* </ul>
*
* @author Chet
*/
public abstract class Evaluator<T> {
/**
* HashMap that holds all registered evaluators
*/
private static final Map<Class<?>, Class<? extends Evaluator>>
impls = new HashMap<Class<?>,
Class<? extends Evaluator>>();
/**
* Static registration of pre-defined evaluators
*/
static {
impls.put(Byte.class, EvaluatorByte.class);
impls.put(Short.class, EvaluatorShort.class);
impls.put(Integer.class, EvaluatorInteger.class);
impls.put(Long.class, EvaluatorLong.class);
impls.put(Float.class, EvaluatorFloat.class);
impls.put(Double.class, EvaluatorDouble.class);
impls.put(Color.class, EvaluatorColor.class);
impls.put(Point2D.class, EvaluatorPoint2D.class);
impls.put(Line2D.class, EvaluatorLine2D.class);
impls.put(Dimension2D.class, EvaluatorDimension2D.class);
impls.put(Rectangle2D.class, EvaluatorRectangle2D.class);
impls.put(RoundRectangle2D.class, EvaluatorRoundRectangle2D.class);
impls.put(Ellipse2D.class, EvaluatorEllipse2D.class);
impls.put(Arc2D.class, EvaluatorArc2D.class);
impls.put(QuadCurve2D.class, EvaluatorQuadCurve2D.class);
impls.put(CubicCurve2D.class, EvaluatorCubicCurve2D.class);
}
private static void register(Class<?> type,
Class<? extends Evaluator> impl)
{
impls.put(type, impl);
}
private static void deregister(Class<?> type) {
impls.remove(type);
}
static <T> Evaluator<T> create(Class<?> type) {
Class<? extends Evaluator> interpClass = null;
for (Class<?> klass : impls.keySet()) {
if (klass.isAssignableFrom(type)) {
interpClass = impls.get(klass);
break;
}
}
if (interpClass == null) {
throw new IllegalArgumentException("No Evaluator" +
" can be found for type " + type + "; consider using" +
" different types for your values or supplying a custom" +
" Evaluator");
}
try {
Constructor<? extends Evaluator> ctor =
interpClass.getConstructor();
return (Evaluator<T>)ctor.newInstance();
} catch (Exception e) {
throw new IllegalArgumentException("Problem constructing " +
"appropriate Evaluator for type " + type +
":", e);
}
}
/**
* Abstract method to evaluate between two boundary values. Built-in
* implementations all use linear parametric evaluation:
* <pre>
* v = v0 + (v1 - v0) * fraction
* </pre>
* Extenders of Evaluator will need to override this method
* and do something similar for their own types. Note that this
* mechanism may be used to create non-linear interpolators for
* specific value types, although it may besimpler to just use
* the linear/parametric interpolation
* technique here and perform non-linear interpolation through
* custom Interpolators rather than perform custom calculations in
* this method; the point of this class is to allow calculations with
* new/unknown types, not to provide another mechanism for non-linear
* interpolation.
*/
public abstract T evaluate(T v0, T v1, float fraction);
}
class EvaluatorByte extends Evaluator<Byte> {
public EvaluatorByte() {}
public Byte evaluate(Byte v0, Byte v1,
float fraction)
{
return (byte)(v0 + (byte)((v1 - v0) * fraction));
}
}
class EvaluatorShort extends Evaluator<Short> {
public EvaluatorShort() {}
public Short evaluate(Short v0, Short v1,
float fraction)
{
return (short)(v0 + (short)((v1 - v0) * fraction));
}
}
class EvaluatorInteger extends Evaluator<Integer> {
public EvaluatorInteger() {}
public Integer evaluate(Integer v0, Integer v1,
float fraction)
{
return v0 + (int)((v1 - v0) * fraction);
}
}
class EvaluatorLong extends Evaluator<Long> {
public EvaluatorLong() {}
public Long evaluate(Long v0, Long v1,
float fraction)
{
return v0 + (long)((v1 - v0) * fraction);
}
}
class EvaluatorFloat extends Evaluator<Float> {
public EvaluatorFloat() {}
public Float evaluate(Float v0, Float v1,
float fraction)
{
return v0 + ((v1 - v0) * fraction);
}
}
class EvaluatorDouble extends Evaluator<Double> {
public EvaluatorDouble() {}
public Double evaluate(Double v0, Double v1,
float fraction)
{
return v0 + ((v1 - v0) * fraction);
}
}
class EvaluatorColor extends Evaluator<Color> {
public EvaluatorColor() {}
public Color evaluate(Color v0, Color v1,
float fraction)
{
int r = v0.getRed() +
(int)((v1.getRed() - v0.getRed()) * fraction + 0.5f);
int g = v0.getGreen() +
(int)((v1.getGreen() - v0.getGreen()) * fraction + 0.5f);
int b = v0.getBlue() +
(int)((v1.getBlue() - v0.getBlue()) * fraction + 0.5f);
int a = v0.getAlpha() +
(int)((v1.getAlpha() - v0.getAlpha()) * fraction + 0.5f);
Color value = new Color(r, g, b, a);
return value;
}
}
class EvaluatorPoint2D extends Evaluator<Point2D> {
// REMIND: apply this technique to other classes...
private Point2D value;
public EvaluatorPoint2D() {}
public Point2D evaluate(Point2D v0, Point2D v1,
float fraction)
{
if (value == null) {
// TODO: Note that future calls to this Evaluator may
// use a different subclass of Point2D, so the precision of the
// result may vary because we are caching a clone of
// the first instance we received
value = (Point2D)v0.clone();
}
double x = v0.getX() + ((v1.getX() - v0.getX()) * fraction);
double y = v0.getY() + ((v1.getY() - v0.getY()) * fraction);
value.setLocation(x, y);
return value;
}
}
class EvaluatorLine2D extends Evaluator<Line2D> {
public EvaluatorLine2D() {}
public Line2D evaluate(Line2D v0, Line2D v1,
float fraction)
{
double x1 = v0.getX1() + ((v1.getX1() - v0.getX1()) * fraction);
double y1 = v0.getY1() + ((v1.getY1() - v0.getY1()) * fraction);
double x2 = v0.getX2() + ((v1.getX2() - v0.getX2()) * fraction);
double y2 = v0.getY2() + ((v1.getY2() - v0.getY2()) * fraction);
Line2D value = (Line2D)v0.clone();
value.setLine(x1, y1, x2, y2);
return value;
}
}
class EvaluatorDimension2D extends Evaluator<Dimension2D> {
public EvaluatorDimension2D() {}
public Dimension2D evaluate(Dimension2D v0, Dimension2D v1,
float fraction)
{
double w = v0.getWidth() +
((v1.getWidth() - v0.getWidth()) * fraction);
double h = v0.getHeight() +
((v1.getHeight() - v0.getHeight()) * fraction);
Dimension2D value = (Dimension2D)v0.clone();
value.setSize(w, h);
return value;
}
}
class EvaluatorRectangle2D extends Evaluator<Rectangle2D> {
public EvaluatorRectangle2D() {}
public Rectangle2D evaluate(Rectangle2D v0, Rectangle2D v1,
float fraction)
{
double x = v0.getX() + ((v1.getX() - v0.getX()) * fraction);
double y = v0.getY() + ((v1.getY() - v0.getY()) * fraction);
double w = v0.getWidth() +
((v1.getWidth() - v0.getWidth()) * fraction);
double h = v0.getHeight() +
((v1.getHeight() - v0.getHeight()) * fraction);
Rectangle2D value = (Rectangle2D)v0.clone();
value.setRect(x, y, w, h);
return value;
}
}
class EvaluatorRoundRectangle2D extends Evaluator<RoundRectangle2D> {
public EvaluatorRoundRectangle2D() {}
public RoundRectangle2D evaluate(RoundRectangle2D v0,
RoundRectangle2D v1,
float fraction)
{
double x = v0.getX() + ((v1.getX() - v0.getX()) * fraction);
double y = v0.getY() + ((v1.getY() - v0.getY()) * fraction);
double w = v0.getWidth() +
((v1.getWidth() - v0.getWidth()) * fraction);
double h = v0.getHeight() +
((v1.getHeight() - v0.getHeight()) * fraction);
double arcw = v0.getArcWidth() +
((v1.getArcWidth() - v0.getArcWidth()) * fraction);
double arch = v0.getArcHeight() +
((v1.getArcHeight() - v0.getArcHeight()) * fraction);
RoundRectangle2D value = (RoundRectangle2D)v0.clone();
value.setRoundRect(x, y, w, h, arcw, arch);
return value;
}
}
class EvaluatorEllipse2D extends Evaluator<Ellipse2D> {
public EvaluatorEllipse2D() {}
public Ellipse2D evaluate(Ellipse2D v0, Ellipse2D v1,
float fraction)
{
double x = v0.getX() + ((v1.getX() - v0.getX()) * fraction);
double y = v0.getY() + ((v1.getY() - v0.getY()) * fraction);
double w = v0.getWidth() +
((v1.getWidth() - v0.getWidth()) * fraction);
double h = v0.getHeight() +
((v1.getHeight() - v0.getHeight()) * fraction);
Ellipse2D value = (Ellipse2D)v0.clone();
value.setFrame(x, y, w, h);
return value;
}
}
class EvaluatorArc2D extends Evaluator<Arc2D> {
public EvaluatorArc2D() {}
public Arc2D evaluate(Arc2D v0, Arc2D v1,
float fraction)
{
double x = v0.getX() + ((v1.getX() - v0.getX()) * fraction);
double y = v0.getY() + ((v1.getY() - v0.getY()) * fraction);
double w = v0.getWidth() +
((v1.getWidth() - v0.getWidth()) * fraction);
double h = v0.getHeight() +
((v1.getHeight() - v0.getHeight()) * fraction);
double start = v0.getAngleStart() +
((v1.getAngleStart() - v0.getAngleStart()) * fraction);
double extent = v0.getAngleExtent() +
((v1.getAngleExtent() - v0.getAngleExtent()) * fraction);
Arc2D value = (Arc2D)v0.clone();
value.setArc(x, y, w, h, start, extent, v0.getArcType());
return value;
}
}
class EvaluatorQuadCurve2D extends Evaluator<QuadCurve2D> {
public EvaluatorQuadCurve2D() {}
public QuadCurve2D evaluate(QuadCurve2D v0, QuadCurve2D v1,
float fraction)
{
double x1 = v0.getX1() + ((v1.getX1() - v0.getX1()) * fraction);
double y1 = v0.getY1() + ((v1.getY1() - v0.getY1()) * fraction);
double x2 = v0.getX2() + ((v1.getX2() - v0.getX2()) * fraction);
double y2 = v0.getY2() + ((v1.getY2() - v0.getY2()) * fraction);
double ctrlx = v0.getCtrlX() +
((v1.getCtrlX() - v0.getCtrlX()) * fraction);
double ctrly = v0.getCtrlY() +
((v1.getCtrlY() - v0.getCtrlY()) * fraction);
QuadCurve2D value = (QuadCurve2D)v0.clone();
value.setCurve(x1, y1, ctrlx, ctrly, x2, y2);
return value;
}
}
class EvaluatorCubicCurve2D extends Evaluator<CubicCurve2D> {
public EvaluatorCubicCurve2D() {}
public CubicCurve2D evaluate(CubicCurve2D v0, CubicCurve2D v1,
float fraction)
{
double x1 = v0.getX1() + ((v1.getX1() - v0.getX1()) * fraction);
double y1 = v0.getY1() + ((v1.getY1() - v0.getY1()) * fraction);
double x2 = v0.getX2() + ((v1.getX2() - v0.getX2()) * fraction);
double y2 = v0.getY2() + ((v1.getY2() - v0.getY2()) * fraction);
double ctrlx1 = v0.getCtrlX1() +
((v1.getCtrlX1() - v0.getCtrlX1()) * fraction);
double ctrly1 = v0.getCtrlY1() +
((v1.getCtrlY1() - v0.getCtrlY1()) * fraction);
double ctrlx2 = v0.getCtrlX2() +
((v1.getCtrlX2() - v0.getCtrlX2()) * fraction);
double ctrly2 = v0.getCtrlY2() +
((v1.getCtrlY2() - v0.getCtrlY2()) * fraction);
CubicCurve2D value = (CubicCurve2D)v0.clone();
value.setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
return value;
}
}