aboutsummaryrefslogtreecommitdiff
path: root/engine/src/core/com/jme3/math/Spline.java
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/core/com/jme3/math/Spline.java')
-rw-r--r--engine/src/core/com/jme3/math/Spline.java447
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);
+ }
+}