Home | History | Annotate | Download | only in math
      1 /*
      2  * To change this template, choose Tools | Templates
      3  * and open the template in the editor.
      4  */
      5 package com.jme3.math;
      6 
      7 import com.jme3.export.*;
      8 import java.io.IOException;
      9 import java.util.ArrayList;
     10 import java.util.Iterator;
     11 import java.util.List;
     12 
     13 /**
     14  *
     15  * @author Nehon
     16  */
     17 public class Spline implements Savable {
     18 
     19     public enum SplineType {
     20         Linear,
     21         CatmullRom,
     22         Bezier,
     23         Nurb
     24     }
     25 
     26     private List<Vector3f> controlPoints = new ArrayList<Vector3f>();
     27     private List<Float> knots;				//knots of NURBS spline
     28     private float[] weights;				//weights of NURBS spline
     29     private int basisFunctionDegree;		//degree of NURBS spline basis function (computed automatically)
     30     private boolean cycle;
     31     private List<Float> segmentsLength;
     32     private float totalLength;
     33     private List<Vector3f> CRcontrolPoints;
     34     private float curveTension = 0.5f;
     35     private SplineType type = SplineType.CatmullRom;
     36 
     37     public Spline() {
     38     }
     39 
     40     /**
     41      * Create a spline
     42      * @param splineType the type of the spline @see {SplineType}
     43      * @param controlPoints an array of vector to use as control points of the spline
     44      * If the type of the curve is Bezier curve the control points should be provided
     45      * in the appropriate way. Each point 'p' describing control position in the scene
     46      * should be surrounded by two handler points. This applies to every point except
     47      * for the border points of the curve, who should have only one handle point.
     48      * The pattern should be as follows:
     49      * P0 - H0  :  H1 - P1 - H1  :  ...  :  Hn - Pn
     50      *
     51      * n is the amount of 'P' - points.
     52      * @param curveTension the tension of the spline
     53      * @param cycle true if the spline cycle.
     54      */
     55     public Spline(SplineType splineType, Vector3f[] controlPoints, float curveTension, boolean cycle) {
     56     	if(splineType==SplineType.Nurb) {
     57     		throw new IllegalArgumentException("To create NURBS spline use: 'public Spline(Vector3f[] controlPoints, float[] weights, float[] nurbKnots)' constructor!");
     58     	}
     59         for (int i = 0; i < controlPoints.length; i++) {
     60             Vector3f vector3f = controlPoints[i];
     61             this.controlPoints.add(vector3f);
     62         }
     63         type = splineType;
     64         this.curveTension = curveTension;
     65         this.cycle = cycle;
     66         this.computeTotalLentgh();
     67     }
     68 
     69     /**
     70      * Create a spline
     71      * @param splineType the type of the spline @see {SplineType}
     72      * @param controlPoints a list of vector to use as control points of the spline
     73      * If the type of the curve is Bezier curve the control points should be provided
     74      * in the appropriate way. Each point 'p' describing control position in the scene
     75      * should be surrounded by two handler points. This applies to every point except
     76      * for the border points of the curve, who should have only one handle point.
     77      * The pattern should be as follows:
     78      * P0 - H0  :  H1 - P1 - H1  :  ...  :  Hn - Pn
     79      *
     80      * n is the amount of 'P' - points.
     81      * @param curveTension the tension of the spline
     82      * @param cycle true if the spline cycle.
     83      */
     84     public Spline(SplineType splineType, List<Vector3f> controlPoints, float curveTension, boolean cycle) {
     85     	if(splineType==SplineType.Nurb) {
     86     		throw new IllegalArgumentException("To create NURBS spline use: 'public Spline(Vector3f[] controlPoints, float[] weights, float[] nurbKnots)' constructor!");
     87     	}
     88         type = splineType;
     89         this.controlPoints.addAll(controlPoints);
     90         this.curveTension = curveTension;
     91         this.cycle = cycle;
     92         this.computeTotalLentgh();
     93     }
     94 
     95     /**
     96      * Create a NURBS spline. A spline type is automatically set to SplineType.Nurb.
     97      * The cycle is set to <b>false</b> by default.
     98      * @param controlPoints a list of vector to use as control points of the spline
     99 	 * @param nurbKnots the nurb's spline knots
    100      */
    101     public Spline(List<Vector4f> controlPoints, List<Float> nurbKnots) {
    102     	//input data control
    103     	for(int i=0;i<nurbKnots.size()-1;++i) {
    104     		if(nurbKnots.get(i)>nurbKnots.get(i+1)) {
    105     			throw new IllegalArgumentException("The knots values cannot decrease!");
    106     		}
    107     	}
    108 
    109     	//storing the data
    110         type = SplineType.Nurb;
    111         this.weights = new float[controlPoints.size()];
    112         this.knots = nurbKnots;
    113         this.basisFunctionDegree = nurbKnots.size() - weights.length;
    114         for(int i=0;i<controlPoints.size();++i) {
    115         	Vector4f controlPoint = controlPoints.get(i);
    116         	this.controlPoints.add(new Vector3f(controlPoint.x, controlPoint.y, controlPoint.z));
    117         	this.weights[i] = controlPoint.w;
    118         }
    119         CurveAndSurfaceMath.prepareNurbsKnots(knots, basisFunctionDegree);
    120         this.computeTotalLentgh();
    121     }
    122 
    123     private void initCatmullRomWayPoints(List<Vector3f> list) {
    124         if (CRcontrolPoints == null) {
    125             CRcontrolPoints = new ArrayList<Vector3f>();
    126         } else {
    127             CRcontrolPoints.clear();
    128         }
    129         int nb = list.size() - 1;
    130 
    131         if (cycle) {
    132             CRcontrolPoints.add(list.get(list.size() - 2));
    133         } else {
    134             CRcontrolPoints.add(list.get(0).subtract(list.get(1).subtract(list.get(0))));
    135         }
    136 
    137         for (Iterator<Vector3f> it = list.iterator(); it.hasNext();) {
    138             Vector3f vector3f = it.next();
    139             CRcontrolPoints.add(vector3f);
    140         }
    141         if (cycle) {
    142             CRcontrolPoints.add(list.get(1));
    143         } else {
    144             CRcontrolPoints.add(list.get(nb).add(list.get(nb).subtract(list.get(nb - 1))));
    145         }
    146 
    147     }
    148 
    149     /**
    150      * Adds a controlPoint to the spline
    151      * @param controlPoint a position in world space
    152      */
    153     public void addControlPoint(Vector3f controlPoint) {
    154         if (controlPoints.size() > 2 && this.cycle) {
    155             controlPoints.remove(controlPoints.size() - 1);
    156         }
    157         controlPoints.add(controlPoint);
    158         if (controlPoints.size() >= 2 && this.cycle) {
    159             controlPoints.add(controlPoints.get(0));
    160         }
    161         if (controlPoints.size() > 1) {
    162             this.computeTotalLentgh();
    163         }
    164     }
    165 
    166     /**
    167      * remove the controlPoint from the spline
    168      * @param controlPoint the controlPoint to remove
    169      */
    170     public void removeControlPoint(Vector3f controlPoint) {
    171         controlPoints.remove(controlPoint);
    172         if (controlPoints.size() > 1) {
    173             this.computeTotalLentgh();
    174         }
    175     }
    176 
    177     public void clearControlPoints(){
    178         controlPoints.clear();
    179         totalLength = 0;
    180     }
    181 
    182     /**
    183      * This method computes the total length of the curve.
    184      */
    185     private void computeTotalLentgh() {
    186         totalLength = 0;
    187         float l = 0;
    188         if (segmentsLength == null) {
    189             segmentsLength = new ArrayList<Float>();
    190         } else {
    191             segmentsLength.clear();
    192         }
    193         if (type == SplineType.Linear) {
    194             if (controlPoints.size() > 1) {
    195                 for (int i = 0; i < controlPoints.size() - 1; i++) {
    196                     l = controlPoints.get(i + 1).subtract(controlPoints.get(i)).length();
    197                     segmentsLength.add(l);
    198                     totalLength += l;
    199                 }
    200             }
    201         } else if(type == SplineType.Bezier) {
    202         	this.computeBezierLength();
    203         } else if(type == SplineType.Nurb) {
    204         	this.computeNurbLength();
    205         } else {
    206             this.initCatmullRomWayPoints(controlPoints);
    207             this.computeCatmulLength();
    208         }
    209     }
    210 
    211     /**
    212      * This method computes the Catmull Rom curve length.
    213      */
    214     private void computeCatmulLength() {
    215         float l = 0;
    216         if (controlPoints.size() > 1) {
    217             for (int i = 0; i < controlPoints.size() - 1; i++) {
    218                 l = FastMath.getCatmullRomP1toP2Length(CRcontrolPoints.get(i),
    219                         CRcontrolPoints.get(i + 1), CRcontrolPoints.get(i + 2), CRcontrolPoints.get(i + 3), 0, 1, curveTension);
    220                 segmentsLength.add(l);
    221                 totalLength += l;
    222             }
    223         }
    224     }
    225 
    226     /**
    227      * This method calculates the Bezier curve length.
    228      */
    229     private void computeBezierLength() {
    230     	float l = 0;
    231         if (controlPoints.size() > 1) {
    232             for (int i = 0; i < controlPoints.size() - 1; i+=3) {
    233                 l = FastMath.getBezierP1toP2Length(controlPoints.get(i),
    234                 		controlPoints.get(i + 1), controlPoints.get(i + 2), controlPoints.get(i + 3));
    235                 segmentsLength.add(l);
    236                 totalLength += l;
    237             }
    238         }
    239     }
    240 
    241     /**
    242      * This method calculates the NURB curve length.
    243      */
    244     private void computeNurbLength() {
    245     	//TODO: implement
    246     }
    247 
    248     /**
    249      * Iterpolate a position on the spline
    250      * @param value a value from 0 to 1 that represent the postion between the curent control point and the next one
    251      * @param currentControlPoint the current control point
    252      * @param store a vector to store the result (use null to create a new one that will be returned by the method)
    253      * @return the position
    254      */
    255     public Vector3f interpolate(float value, int currentControlPoint, Vector3f store) {
    256         if (store == null) {
    257             store = new Vector3f();
    258         }
    259         switch (type) {
    260             case CatmullRom:
    261                 FastMath.interpolateCatmullRom(value, curveTension, CRcontrolPoints.get(currentControlPoint), CRcontrolPoints.get(currentControlPoint + 1), CRcontrolPoints.get(currentControlPoint + 2), CRcontrolPoints.get(currentControlPoint + 3), store);
    262                 break;
    263             case Linear:
    264                 FastMath.interpolateLinear(value, controlPoints.get(currentControlPoint), controlPoints.get(currentControlPoint + 1), store);
    265                 break;
    266             case Bezier:
    267             	FastMath.interpolateBezier(value, controlPoints.get(currentControlPoint), controlPoints.get(currentControlPoint + 1), controlPoints.get(currentControlPoint + 2), controlPoints.get(currentControlPoint + 3), store);
    268             	break;
    269             case Nurb:
    270             	CurveAndSurfaceMath.interpolateNurbs(value, this, store);
    271             	break;
    272             default:
    273                 break;
    274         }
    275         return store;
    276     }
    277 
    278     /**
    279      * returns the curve tension
    280      */
    281     public float getCurveTension() {
    282         return curveTension;
    283     }
    284 
    285     /**
    286      * sets the curve tension
    287      *
    288      * @param curveTension the tension
    289      */
    290     public void setCurveTension(float curveTension) {
    291         this.curveTension = curveTension;
    292         if(type==SplineType.CatmullRom) {
    293         	this.computeTotalLentgh();
    294         }
    295     }
    296 
    297     /**
    298      * returns true if the spline cycle
    299      */
    300     public boolean isCycle() {
    301         return cycle;
    302     }
    303 
    304     /**
    305      * set to true to make the spline cycle
    306      * @param cycle
    307      */
    308     public void setCycle(boolean cycle) {
    309     	if(type!=SplineType.Nurb) {
    310     		if (controlPoints.size() >= 2) {
    311     			if (this.cycle && !cycle) {
    312     				controlPoints.remove(controlPoints.size() - 1);
    313     			}
    314     			if (!this.cycle && cycle) {
    315     				controlPoints.add(controlPoints.get(0));
    316     			}
    317     			this.cycle = cycle;
    318     			this.computeTotalLentgh();
    319     		} else {
    320     			this.cycle = cycle;
    321     		}
    322     	}
    323     }
    324 
    325     /**
    326      * return the total lenght of the spline
    327      */
    328     public float getTotalLength() {
    329         return totalLength;
    330     }
    331 
    332     /**
    333      * return the type of the spline
    334      */
    335     public SplineType getType() {
    336         return type;
    337     }
    338 
    339     /**
    340      * Sets the type of the spline
    341      * @param type
    342      */
    343     public void setType(SplineType type) {
    344         this.type = type;
    345         this.computeTotalLentgh();
    346     }
    347 
    348     /**
    349      * returns this spline control points
    350      */
    351     public List<Vector3f> getControlPoints() {
    352         return controlPoints;
    353     }
    354 
    355     /**
    356      * returns a list of float representing the segments lenght
    357      */
    358     public List<Float> getSegmentsLength() {
    359         return segmentsLength;
    360     }
    361 
    362     //////////// NURBS getters /////////////////////
    363 
    364 	/**
    365 	 * 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
    366 	 * type - NPE will be thrown.
    367 	 * @return the minimum nurb curve knot value
    368 	 */
    369     public float getMinNurbKnot() {
    370     	return knots.get(basisFunctionDegree - 1);
    371     }
    372 
    373     /**
    374 	 * 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
    375 	 * type - NPE will be thrown.
    376 	 * @return the maximum nurb curve knot value
    377 	 */
    378     public float getMaxNurbKnot() {
    379     	return knots.get(weights.length);
    380     }
    381 
    382     /**
    383      * This method returns NURBS' spline knots.
    384      * @return NURBS' spline knots
    385      */
    386     public List<Float> getKnots() {
    387 		return knots;
    388 	}
    389 
    390     /**
    391      * This method returns NURBS' spline weights.
    392      * @return NURBS' spline weights
    393      */
    394     public float[] getWeights() {
    395 		return weights;
    396 	}
    397 
    398     /**
    399      * This method returns NURBS' spline basis function degree.
    400      * @return NURBS' spline basis function degree
    401      */
    402     public int getBasisFunctionDegree() {
    403 		return basisFunctionDegree;
    404 	}
    405 
    406     @Override
    407     public void write(JmeExporter ex) throws IOException {
    408         OutputCapsule oc = ex.getCapsule(this);
    409         oc.writeSavableArrayList((ArrayList) controlPoints, "controlPoints", null);
    410         oc.write(type, "type", SplineType.CatmullRom);
    411         float list[] = new float[segmentsLength.size()];
    412         for (int i = 0; i < segmentsLength.size(); i++) {
    413             list[i] = segmentsLength.get(i);
    414         }
    415         oc.write(list, "segmentsLength", null);
    416 
    417         oc.write(totalLength, "totalLength", 0);
    418         oc.writeSavableArrayList((ArrayList) CRcontrolPoints, "CRControlPoints", null);
    419         oc.write(curveTension, "curveTension", 0.5f);
    420         oc.write(cycle, "cycle", false);
    421         oc.writeSavableArrayList((ArrayList<Float>)knots, "knots", null);
    422         oc.write(weights, "weights", null);
    423         oc.write(basisFunctionDegree, "basisFunctionDegree", 0);
    424     }
    425 
    426     @Override
    427     public void read(JmeImporter im) throws IOException {
    428         InputCapsule in = im.getCapsule(this);
    429 
    430         controlPoints = (ArrayList<Vector3f>) in.readSavableArrayList("wayPoints", null);
    431         float list[] = in.readFloatArray("segmentsLength", null);
    432         if (list != null) {
    433             segmentsLength = new ArrayList<Float>();
    434             for (int i = 0; i < list.length; i++) {
    435                 segmentsLength.add(new Float(list[i]));
    436             }
    437         }
    438         type = in.readEnum("pathSplineType", SplineType.class, SplineType.CatmullRom);
    439         totalLength = in.readFloat("totalLength", 0);
    440         CRcontrolPoints = (ArrayList<Vector3f>) in.readSavableArrayList("CRControlPoints", null);
    441         curveTension = in.readFloat("curveTension", 0.5f);
    442         cycle = in.readBoolean("cycle", false);
    443         knots = in.readSavableArrayList("knots", null);
    444         weights = in.readFloatArray("weights", null);
    445         basisFunctionDegree = in.readInt("basisFunctionDegree", 0);
    446     }
    447 }
    448