diff options
Diffstat (limited to 'engine/src/core/com/jme3/math/Spline.java')
-rw-r--r-- | engine/src/core/com/jme3/math/Spline.java | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/engine/src/core/com/jme3/math/Spline.java b/engine/src/core/com/jme3/math/Spline.java new file mode 100644 index 0000000..b28a797 --- /dev/null +++ b/engine/src/core/com/jme3/math/Spline.java @@ -0,0 +1,447 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.math; + +import com.jme3.export.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * + * @author Nehon + */ +public class Spline implements Savable { + + public enum SplineType { + Linear, + CatmullRom, + Bezier, + Nurb + } + + private List<Vector3f> controlPoints = new ArrayList<Vector3f>(); + private List<Float> knots; //knots of NURBS spline + private float[] weights; //weights of NURBS spline + private int basisFunctionDegree; //degree of NURBS spline basis function (computed automatically) + private boolean cycle; + private List<Float> segmentsLength; + private float totalLength; + private List<Vector3f> CRcontrolPoints; + private float curveTension = 0.5f; + private SplineType type = SplineType.CatmullRom; + + public Spline() { + } + + /** + * Create a spline + * @param splineType the type of the spline @see {SplineType} + * @param controlPoints an array of vector to use as control points of the spline + * If the type of the curve is Bezier curve the control points should be provided + * in the appropriate way. Each point 'p' describing control position in the scene + * should be surrounded by two handler points. This applies to every point except + * for the border points of the curve, who should have only one handle point. + * The pattern should be as follows: + * P0 - H0 : H1 - P1 - H1 : ... : Hn - Pn + * + * n is the amount of 'P' - points. + * @param curveTension the tension of the spline + * @param cycle true if the spline cycle. + */ + public Spline(SplineType splineType, Vector3f[] controlPoints, float curveTension, boolean cycle) { + if(splineType==SplineType.Nurb) { + throw new IllegalArgumentException("To create NURBS spline use: 'public Spline(Vector3f[] controlPoints, float[] weights, float[] nurbKnots)' constructor!"); + } + for (int i = 0; i < controlPoints.length; i++) { + Vector3f vector3f = controlPoints[i]; + this.controlPoints.add(vector3f); + } + type = splineType; + this.curveTension = curveTension; + this.cycle = cycle; + this.computeTotalLentgh(); + } + + /** + * Create a spline + * @param splineType the type of the spline @see {SplineType} + * @param controlPoints a list of vector to use as control points of the spline + * If the type of the curve is Bezier curve the control points should be provided + * in the appropriate way. Each point 'p' describing control position in the scene + * should be surrounded by two handler points. This applies to every point except + * for the border points of the curve, who should have only one handle point. + * The pattern should be as follows: + * P0 - H0 : H1 - P1 - H1 : ... : Hn - Pn + * + * n is the amount of 'P' - points. + * @param curveTension the tension of the spline + * @param cycle true if the spline cycle. + */ + public Spline(SplineType splineType, List<Vector3f> controlPoints, float curveTension, boolean cycle) { + if(splineType==SplineType.Nurb) { + throw new IllegalArgumentException("To create NURBS spline use: 'public Spline(Vector3f[] controlPoints, float[] weights, float[] nurbKnots)' constructor!"); + } + type = splineType; + this.controlPoints.addAll(controlPoints); + this.curveTension = curveTension; + this.cycle = cycle; + this.computeTotalLentgh(); + } + + /** + * Create a NURBS spline. A spline type is automatically set to SplineType.Nurb. + * The cycle is set to <b>false</b> by default. + * @param controlPoints a list of vector to use as control points of the spline + * @param nurbKnots the nurb's spline knots + */ + public Spline(List<Vector4f> controlPoints, List<Float> nurbKnots) { + //input data control + for(int i=0;i<nurbKnots.size()-1;++i) { + if(nurbKnots.get(i)>nurbKnots.get(i+1)) { + throw new IllegalArgumentException("The knots values cannot decrease!"); + } + } + + //storing the data + type = SplineType.Nurb; + this.weights = new float[controlPoints.size()]; + this.knots = nurbKnots; + this.basisFunctionDegree = nurbKnots.size() - weights.length; + for(int i=0;i<controlPoints.size();++i) { + Vector4f controlPoint = controlPoints.get(i); + this.controlPoints.add(new Vector3f(controlPoint.x, controlPoint.y, controlPoint.z)); + this.weights[i] = controlPoint.w; + } + CurveAndSurfaceMath.prepareNurbsKnots(knots, basisFunctionDegree); + this.computeTotalLentgh(); + } + + private void initCatmullRomWayPoints(List<Vector3f> list) { + if (CRcontrolPoints == null) { + CRcontrolPoints = new ArrayList<Vector3f>(); + } else { + CRcontrolPoints.clear(); + } + int nb = list.size() - 1; + + if (cycle) { + CRcontrolPoints.add(list.get(list.size() - 2)); + } else { + CRcontrolPoints.add(list.get(0).subtract(list.get(1).subtract(list.get(0)))); + } + + for (Iterator<Vector3f> it = list.iterator(); it.hasNext();) { + Vector3f vector3f = it.next(); + CRcontrolPoints.add(vector3f); + } + if (cycle) { + CRcontrolPoints.add(list.get(1)); + } else { + CRcontrolPoints.add(list.get(nb).add(list.get(nb).subtract(list.get(nb - 1)))); + } + + } + + /** + * Adds a controlPoint to the spline + * @param controlPoint a position in world space + */ + public void addControlPoint(Vector3f controlPoint) { + if (controlPoints.size() > 2 && this.cycle) { + controlPoints.remove(controlPoints.size() - 1); + } + controlPoints.add(controlPoint); + if (controlPoints.size() >= 2 && this.cycle) { + controlPoints.add(controlPoints.get(0)); + } + if (controlPoints.size() > 1) { + this.computeTotalLentgh(); + } + } + + /** + * remove the controlPoint from the spline + * @param controlPoint the controlPoint to remove + */ + public void removeControlPoint(Vector3f controlPoint) { + controlPoints.remove(controlPoint); + if (controlPoints.size() > 1) { + this.computeTotalLentgh(); + } + } + + public void clearControlPoints(){ + controlPoints.clear(); + totalLength = 0; + } + + /** + * This method computes the total length of the curve. + */ + private void computeTotalLentgh() { + totalLength = 0; + float l = 0; + if (segmentsLength == null) { + segmentsLength = new ArrayList<Float>(); + } else { + segmentsLength.clear(); + } + if (type == SplineType.Linear) { + if (controlPoints.size() > 1) { + for (int i = 0; i < controlPoints.size() - 1; i++) { + l = controlPoints.get(i + 1).subtract(controlPoints.get(i)).length(); + segmentsLength.add(l); + totalLength += l; + } + } + } else if(type == SplineType.Bezier) { + this.computeBezierLength(); + } else if(type == SplineType.Nurb) { + this.computeNurbLength(); + } else { + this.initCatmullRomWayPoints(controlPoints); + this.computeCatmulLength(); + } + } + + /** + * This method computes the Catmull Rom curve length. + */ + private void computeCatmulLength() { + float l = 0; + if (controlPoints.size() > 1) { + for (int i = 0; i < controlPoints.size() - 1; i++) { + l = FastMath.getCatmullRomP1toP2Length(CRcontrolPoints.get(i), + CRcontrolPoints.get(i + 1), CRcontrolPoints.get(i + 2), CRcontrolPoints.get(i + 3), 0, 1, curveTension); + segmentsLength.add(l); + totalLength += l; + } + } + } + + /** + * This method calculates the Bezier curve length. + */ + private void computeBezierLength() { + float l = 0; + if (controlPoints.size() > 1) { + for (int i = 0; i < controlPoints.size() - 1; i+=3) { + l = FastMath.getBezierP1toP2Length(controlPoints.get(i), + controlPoints.get(i + 1), controlPoints.get(i + 2), controlPoints.get(i + 3)); + segmentsLength.add(l); + totalLength += l; + } + } + } + + /** + * This method calculates the NURB curve length. + */ + private void computeNurbLength() { + //TODO: implement + } + + /** + * Iterpolate a position on the spline + * @param value a value from 0 to 1 that represent the postion between the curent control point and the next one + * @param currentControlPoint the current control point + * @param store a vector to store the result (use null to create a new one that will be returned by the method) + * @return the position + */ + public Vector3f interpolate(float value, int currentControlPoint, Vector3f store) { + if (store == null) { + store = new Vector3f(); + } + switch (type) { + case CatmullRom: + FastMath.interpolateCatmullRom(value, curveTension, CRcontrolPoints.get(currentControlPoint), CRcontrolPoints.get(currentControlPoint + 1), CRcontrolPoints.get(currentControlPoint + 2), CRcontrolPoints.get(currentControlPoint + 3), store); + break; + case Linear: + FastMath.interpolateLinear(value, controlPoints.get(currentControlPoint), controlPoints.get(currentControlPoint + 1), store); + break; + case Bezier: + FastMath.interpolateBezier(value, controlPoints.get(currentControlPoint), controlPoints.get(currentControlPoint + 1), controlPoints.get(currentControlPoint + 2), controlPoints.get(currentControlPoint + 3), store); + break; + case Nurb: + CurveAndSurfaceMath.interpolateNurbs(value, this, store); + break; + default: + break; + } + return store; + } + + /** + * returns the curve tension + */ + public float getCurveTension() { + return curveTension; + } + + /** + * sets the curve tension + * + * @param curveTension the tension + */ + public void setCurveTension(float curveTension) { + this.curveTension = curveTension; + if(type==SplineType.CatmullRom) { + this.computeTotalLentgh(); + } + } + + /** + * returns true if the spline cycle + */ + public boolean isCycle() { + return cycle; + } + + /** + * set to true to make the spline cycle + * @param cycle + */ + public void setCycle(boolean cycle) { + if(type!=SplineType.Nurb) { + if (controlPoints.size() >= 2) { + if (this.cycle && !cycle) { + controlPoints.remove(controlPoints.size() - 1); + } + if (!this.cycle && cycle) { + controlPoints.add(controlPoints.get(0)); + } + this.cycle = cycle; + this.computeTotalLentgh(); + } else { + this.cycle = cycle; + } + } + } + + /** + * return the total lenght of the spline + */ + public float getTotalLength() { + return totalLength; + } + + /** + * return the type of the spline + */ + public SplineType getType() { + return type; + } + + /** + * Sets the type of the spline + * @param type + */ + public void setType(SplineType type) { + this.type = type; + this.computeTotalLentgh(); + } + + /** + * returns this spline control points + */ + public List<Vector3f> getControlPoints() { + return controlPoints; + } + + /** + * returns a list of float representing the segments lenght + */ + public List<Float> getSegmentsLength() { + return segmentsLength; + } + + //////////// NURBS getters ///////////////////// + + /** + * This method returns the minimum nurb curve knot value. Check the nurb type before calling this method. It the curve is not of a Nurb + * type - NPE will be thrown. + * @return the minimum nurb curve knot value + */ + public float getMinNurbKnot() { + return knots.get(basisFunctionDegree - 1); + } + + /** + * This method returns the maximum nurb curve knot value. Check the nurb type before calling this method. It the curve is not of a Nurb + * type - NPE will be thrown. + * @return the maximum nurb curve knot value + */ + public float getMaxNurbKnot() { + return knots.get(weights.length); + } + + /** + * This method returns NURBS' spline knots. + * @return NURBS' spline knots + */ + public List<Float> getKnots() { + return knots; + } + + /** + * This method returns NURBS' spline weights. + * @return NURBS' spline weights + */ + public float[] getWeights() { + return weights; + } + + /** + * This method returns NURBS' spline basis function degree. + * @return NURBS' spline basis function degree + */ + public int getBasisFunctionDegree() { + return basisFunctionDegree; + } + + @Override + public void write(JmeExporter ex) throws IOException { + OutputCapsule oc = ex.getCapsule(this); + oc.writeSavableArrayList((ArrayList) controlPoints, "controlPoints", null); + oc.write(type, "type", SplineType.CatmullRom); + float list[] = new float[segmentsLength.size()]; + for (int i = 0; i < segmentsLength.size(); i++) { + list[i] = segmentsLength.get(i); + } + oc.write(list, "segmentsLength", null); + + oc.write(totalLength, "totalLength", 0); + oc.writeSavableArrayList((ArrayList) CRcontrolPoints, "CRControlPoints", null); + oc.write(curveTension, "curveTension", 0.5f); + oc.write(cycle, "cycle", false); + oc.writeSavableArrayList((ArrayList<Float>)knots, "knots", null); + oc.write(weights, "weights", null); + oc.write(basisFunctionDegree, "basisFunctionDegree", 0); + } + + @Override + public void read(JmeImporter im) throws IOException { + InputCapsule in = im.getCapsule(this); + + controlPoints = (ArrayList<Vector3f>) in.readSavableArrayList("wayPoints", null); + float list[] = in.readFloatArray("segmentsLength", null); + if (list != null) { + segmentsLength = new ArrayList<Float>(); + for (int i = 0; i < list.length; i++) { + segmentsLength.add(new Float(list[i])); + } + } + type = in.readEnum("pathSplineType", SplineType.class, SplineType.CatmullRom); + totalLength = in.readFloat("totalLength", 0); + CRcontrolPoints = (ArrayList<Vector3f>) in.readSavableArrayList("CRControlPoints", null); + curveTension = in.readFloat("curveTension", 0.5f); + cycle = in.readBoolean("cycle", false); + knots = in.readSavableArrayList("knots", null); + weights = in.readFloatArray("weights", null); + basisFunctionDegree = in.readInt("basisFunctionDegree", 0); + } +} |