1package com.jme3.math;
2
3import com.jme3.math.Spline.SplineType;
4import java.util.List;
5
6/**
7 * This class offers methods to help with curves and surfaces calculations.
8 * @author Marcin Roguski (Kealthas)
9 */
10public class CurveAndSurfaceMath {
11	private static final float KNOTS_MINIMUM_DELTA = 0.0001f;
12
13	/**
14	 * A private constructor is defined to avoid instatiation of this class.
15	 */
16	private CurveAndSurfaceMath() {}
17
18	/**
19	 * This method interpolates tha data for the nurbs curve.
20	 * @param u
21	 *            the u value
22	 * @param nurbSpline
23	 *            the nurbs spline definition
24	 * @param store
25	 *            the resulting point in 3D space
26	 */
27	public static void interpolateNurbs(float u, Spline nurbSpline, Vector3f store) {
28		if (nurbSpline.getType() != SplineType.Nurb) {
29			throw new IllegalArgumentException("Given spline is not of a NURB type!");
30		}
31		List<Vector3f> controlPoints = nurbSpline.getControlPoints();
32		float[] weights = nurbSpline.getWeights();
33		List<Float> knots = nurbSpline.getKnots();
34		int controlPointAmount = controlPoints.size();
35
36		store.set(Vector3f.ZERO);
37		float delimeter = 0;
38		for (int i = 0; i < controlPointAmount; ++i) {
39			float val = weights[i] * CurveAndSurfaceMath.computeBaseFunctionValue(i, nurbSpline.getBasisFunctionDegree(), u, knots);
40			store.addLocal(nurbSpline.getControlPoints().get(i)
41					.mult(val));
42			delimeter += val;
43		}
44		store.divideLocal(delimeter);
45	}
46
47	/**
48	 * This method interpolates tha data for the nurbs surface.
49	 *
50	 * @param u
51	 *            the u value
52	 * @param v
53	 *            the v value
54	 * @param controlPoints
55	 *            the nurbs' control points
56	 * @param knots
57	 *            the nurbs' knots
58	 * @param basisUFunctionDegree
59	 *            the degree of basis U function
60	 * @param basisVFunctionDegree
61	 *            the degree of basis V function
62	 * @param store
63	 *            the resulting point in 3D space
64	 */
65	public static void interpolate(float u, float v, List<List<Vector4f>> controlPoints, List<Float>[] knots,
66			int basisUFunctionDegree, int basisVFunctionDegree, Vector3f store) {
67		store.set(Vector3f.ZERO);
68		float delimeter = 0;
69		int vControlPointsAmount = controlPoints.size();
70		int uControlPointsAmount = controlPoints.get(0).size();
71		for (int i = 0; i < vControlPointsAmount; ++i) {
72			for (int j = 0; j < uControlPointsAmount; ++j) {
73				Vector4f controlPoint = controlPoints.get(i).get(j);
74				float val = controlPoint.w
75								* CurveAndSurfaceMath.computeBaseFunctionValue(i, basisVFunctionDegree, v, knots[1])
76								* CurveAndSurfaceMath.computeBaseFunctionValue(j, basisUFunctionDegree, u, knots[0]);
77				store.addLocal(controlPoint.x * val, controlPoint.y * val, controlPoint.z * val);
78				delimeter += val;
79			}
80		}
81		store.divideLocal(delimeter);
82	}
83
84	/**
85	 * This method prepares the knots to be used. If the knots represent non-uniform B-splines (first and last knot values are being
86	 * repeated) it leads to NaN results during calculations. This method adds a small number to each of such knots to avoid NaN's.
87	 * @param knots
88	 *            the knots to be prepared to use
89	 * @param basisFunctionDegree
90	 *            the degree of basis function
91	 */
92	// TODO: improve this; constant delta may lead to errors if the difference between tha last repeated
93	// point and the following one is lower than it
94	public static void prepareNurbsKnots(List<Float> knots, int basisFunctionDegree) {
95		float delta = KNOTS_MINIMUM_DELTA;
96		float prevValue = knots.get(0).floatValue();
97		for(int i=1;i<knots.size();++i) {
98			float value = knots.get(i).floatValue();
99			if(value<=prevValue) {
100				value += delta;
101				knots.set(i, Float.valueOf(value));
102				delta += KNOTS_MINIMUM_DELTA;
103			} else {
104				delta = KNOTS_MINIMUM_DELTA;//reset the delta's value
105			}
106
107			prevValue = value;
108		}
109	}
110
111	/**
112	 * This method computes the base function value for the NURB curve.
113	 * @param i
114	 *            the knot index
115	 * @param k
116	 *            the base function degree
117	 * @param t
118	 *            the knot value
119	 * @param knots
120	 *            the knots' values
121	 * @return the base function value
122	 */
123	private static float computeBaseFunctionValue(int i, int k, float t, List<Float> knots) {
124		if (k == 1) {
125			return knots.get(i) <= t && t < knots.get(i + 1) ? 1.0f : 0.0f;
126		} else {
127			return (t - knots.get(i)) / (knots.get(i + k - 1) - knots.get(i)) *
128					CurveAndSurfaceMath.computeBaseFunctionValue(i, k - 1, t, knots)
129					+ (knots.get(i + k) - t) / (knots.get(i + k) - knots.get(i + 1)) *
130					CurveAndSurfaceMath.computeBaseFunctionValue(i + 1, k - 1, t, knots);
131		}
132	}
133}
134