package rajawali.animation; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import rajawali.math.Number3D; /** * Derived from http://www.cse.unsw.edu.au/~lambert/splines/source.html * * @author dennis.ippel * */ public class CatmullRomPath3D implements ISpline { protected static final int EPSILON = 36; protected static final float DELTA = .00001f; protected List<Number3D> mPoints; protected int mNumPoints; protected int mSelectedIndex = -1; protected Number3D mCurrentTangent; protected Number3D mCurrentPoint; protected boolean mCalculateTangents; public CatmullRomPath3D() { mPoints = Collections.synchronizedList(new CopyOnWriteArrayList<Number3D>()); mCurrentTangent = new Number3D(); mCurrentPoint = new Number3D(); } public void addPoint(Number3D point) { mPoints.add(point); mNumPoints++; } public int getNumPoints() { return mNumPoints; } public Number3D getPoint(int index) { return mPoints.get(index); } public Number3D calculatePoint(float t) { if(mCalculateTangents) { float prevt = t == 0 ? t + DELTA : t - DELTA; float nextt = t == 1 ? t - DELTA : t + DELTA; mCurrentTangent = p(prevt); Number3D nextp = p(nextt); mCurrentTangent.subtract(nextp); mCurrentTangent.multiply(.5f); mCurrentTangent.normalize(); } return p(t); } protected Number3D p(float t) { int currentIndex = 2 + (int)Math.floor((t == 1 ? t - DELTA : t) * (mNumPoints-3)); float tdivnum = (t * (mNumPoints - 3)) - (currentIndex - 2); mCurrentPoint.setAll(0, 0, 0); // Limit the bounds for AccelerateDecelerateInterpolator currentIndex = Math.max(currentIndex, 2); currentIndex = Math.min(currentIndex, mPoints.size() - 2); for (int j = -2; j <= 1; j++) { float b = b(j, tdivnum); Number3D p = mPoints.get(currentIndex+j); mCurrentPoint.x += b * p.x; mCurrentPoint.y += b * p.y; mCurrentPoint.z += b * p.z; } return mCurrentPoint.clone(); } protected float b(int i, float t) { switch (i) { case -2: return ((-t + 2) * t - 1) * t / 2f; case -1: return (((3 * t - 5) * t) * t + 2) / 2f; case 0: return ((-3 * t + 4) * t + 1) * t / 2f; case 1: return ((t - 1) * t * t) / 2f; } return 0; } public Number3D getCurrentTangent() { return mCurrentTangent; } public int selectPoint(Number3D point) { float minDist = Float.MAX_VALUE; mSelectedIndex = -1; for (int i = 0; i < mNumPoints; i++) { Number3D p = mPoints.get(i); float distance = sqrt(p.x - point.x) + sqrt(p.y - point.y) + sqrt(p.z - point.z); if (distance < minDist && distance < EPSILON) { minDist = distance; mSelectedIndex = i; } } return mSelectedIndex; } protected float sqrt(float value) { return value * value; } public void setCalculateTangents(boolean calculateTangents) { this.mCalculateTangents = calculateTangents; } }