• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Spline()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      */
Spline(SplineType splineType, Vector3f[] controlPoints, float curveTension, boolean cycle)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      */
Spline(SplineType splineType, List<Vector3f> controlPoints, float curveTension, boolean cycle)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      */
Spline(List<Vector4f> controlPoints, List<Float> nurbKnots)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 
initCatmullRomWayPoints(List<Vector3f> list)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      */
addControlPoint(Vector3f controlPoint)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      */
removeControlPoint(Vector3f controlPoint)170     public void removeControlPoint(Vector3f controlPoint) {
171         controlPoints.remove(controlPoint);
172         if (controlPoints.size() > 1) {
173             this.computeTotalLentgh();
174         }
175     }
176 
clearControlPoints()177     public void clearControlPoints(){
178         controlPoints.clear();
179         totalLength = 0;
180     }
181 
182     /**
183      * This method computes the total length of the curve.
184      */
computeTotalLentgh()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      */
computeCatmulLength()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      */
computeBezierLength()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      */
computeNurbLength()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      */
interpolate(float value, int currentControlPoint, Vector3f store)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      */
getCurveTension()281     public float getCurveTension() {
282         return curveTension;
283     }
284 
285     /**
286      * sets the curve tension
287      *
288      * @param curveTension the tension
289      */
setCurveTension(float curveTension)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      */
isCycle()300     public boolean isCycle() {
301         return cycle;
302     }
303 
304     /**
305      * set to true to make the spline cycle
306      * @param cycle
307      */
setCycle(boolean cycle)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      */
getTotalLength()328     public float getTotalLength() {
329         return totalLength;
330     }
331 
332     /**
333      * return the type of the spline
334      */
getType()335     public SplineType getType() {
336         return type;
337     }
338 
339     /**
340      * Sets the type of the spline
341      * @param type
342      */
setType(SplineType type)343     public void setType(SplineType type) {
344         this.type = type;
345         this.computeTotalLentgh();
346     }
347 
348     /**
349      * returns this spline control points
350      */
getControlPoints()351     public List<Vector3f> getControlPoints() {
352         return controlPoints;
353     }
354 
355     /**
356      * returns a list of float representing the segments lenght
357      */
getSegmentsLength()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 	 */
getMinNurbKnot()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 	 */
getMaxNurbKnot()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      */
getKnots()386     public List<Float> getKnots() {
387 		return knots;
388 	}
389 
390     /**
391      * This method returns NURBS' spline weights.
392      * @return NURBS' spline weights
393      */
getWeights()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      */
getBasisFunctionDegree()402     public int getBasisFunctionDegree() {
403 		return basisFunctionDegree;
404 	}
405 
406     @Override
write(JmeExporter ex)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
read(JmeImporter im)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