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