75 private double[] knots;
86 public BSpline(
final double[] x,
final double[] y,
final int degree) {
87 if (x.length != y.length)
88 throw new IllegalArgumentException(
"The arrays x and y must share the same length");
103 public BSpline(
final double[] x,
final double[] y,
final double[] knots) {
104 if (x.length != y.length)
105 throw new IllegalArgumentException(
"The arrays x and y must share the same length");
106 if (knots.length <= x.length + 1)
107 throw new IllegalArgumentException(
"The number of knots must be at least n+2");
111 this.knots = knots.clone();
139 return knots[knots.length - 1];
157 return knots.clone();
173 if (x.length != y.length)
174 throw new IllegalArgumentException(
"The arrays x and y must share the same length");
175 if (x.length <= degree)
176 throw new IllegalArgumentException(
"The arrays length must be greater than degree");
178 int n = x.length - 1;
180 double[] t =
new double[x.length];
181 for (
int i = 0; i < t.length; i++) {
182 t[i] = (double) i / (t.length - 1);
186 double U[] =
new double[x.length + degree + 1];
187 int m = U.length - 1;
188 for (
int i = 0; i <= degree; i++)
190 for (
int i = 1; i < x.length - degree; i++)
191 U[i + degree] = (
double) i / (x.length - degree);
192 for (
int i = U.length - 1 - degree; i < U.length; i++)
196 double[][] N =
new double[x.length][x.length];
197 for (
int i = 0; i < x.length; i++) {
198 N[i] = computeN(U, degree, t[i], x.length);
202 double[][] D =
new double[x.length][2];
203 for (
int i = 0; i < x.length; i++) {
209 DoubleMatrix2D coltN = DoubleFactory2D.dense.make(N);
210 DoubleMatrix2D coltD = DoubleFactory2D.dense.make(D);
211 DoubleMatrix2D coltP;
212 coltP = Algebra.ZERO.solve(coltN, coltD);
214 return new BSpline(coltP.viewColumn(0).toArray(), coltP.viewColumn(1).toArray(), U);
235 if (x.length != y.length)
236 throw new IllegalArgumentException(
"The arrays x and y must share the same length");
237 if (x.length <= degree)
238 throw new IllegalArgumentException(
"The arrays length must be greater than degree");
241 int n = x.length - 1;
244 double[] t =
new double[x.length];
245 for (
int i = 0; i < t.length; i++) {
246 t[i] = (double) i / n;
250 int m = h + degree + 1;
251 double U[] =
new double[m + 1];
252 for (
int i = 0; i <= degree; i++)
254 for (
int i = 1; i < hp1 - degree; i++)
255 U[i + degree] = (
double) i / (hp1 - degree);
256 for (
int i = m - degree; i <= m; i++)
260 double[][] N =
new double[n + 1][h + 1];
261 for (
int i = 0; i < N.length; i++) {
262 N[i] = computeN(U, degree, t[i], h + 1);
266 double[][] D =
new double[x.length][2];
267 for (
int i = 0; i < x.length; i++) {
273 double[][] tempQ =
new double[x.length][2];
274 for (
int k = 1; k < n; k++) {
275 tempQ[k][0] = D[k][0] - N[k][0] * D[0][0] - N[k][h] * D[D.length - 1][0];
276 tempQ[k][1] = D[k][1] - N[k][0] * D[0][1] - N[k][h] * D[D.length - 1][1];
278 double[][] Q =
new double[h - 1][2];
279 for (
int i = 1; i < h; i++) {
280 for (
int k = 1; k < n; k++) {
281 Q[i - 1][0] += N[k][i] * tempQ[k][0];
282 Q[i - 1][1] += N[k][i] * tempQ[k][1];
287 double[][] N2 =
new double[n - 1][h - 1];
288 for (
int i = 0; i < N2.length; i++) {
289 for (
int j = 0; j < h - 1; j++)
290 N2[i][j] = N[i + 1][j + 1];
294 DoubleMatrix2D coltQ = DoubleFactory2D.dense.make(Q);
295 DoubleMatrix2D coltN = DoubleFactory2D.dense.make(N2);
296 DoubleMatrix2D coltM = Algebra.ZERO.mult(Algebra.ZERO.transpose(coltN), coltN);
297 DoubleMatrix2D coltP = Algebra.ZERO.solve(coltM, coltQ);
298 double[] pxTemp = coltP.viewColumn(0).toArray();
299 double[] pyTemp = coltP.viewColumn(1).toArray();
300 double[] px =
new double[hp1];
301 double[] py =
new double[hp1];
304 px[h] = D[D.length - 1][0];
305 py[h] = D[D.length - 1][1];
306 for (
int i = 0; i < pxTemp.length; i++) {
307 px[i + 1] = pxTemp[i];
308 py[i + 1] = pyTemp[i];
323 double xTemp[] =
new double[this.myX.length - 1];
324 double yTemp[] =
new double[this.myY.length - 1];
325 for (
int i = 0; i < xTemp.length; i++) {
326 xTemp[i] = (myX[i + 1] - myX[i]) * degree / (knots[i + degree + 1] - knots[i + 1]);
327 yTemp[i] = (myY[i + 1] - myY[i]) * degree / (knots[i + degree + 1] - knots[i + 1]);
330 double[] newKnots =
new double[knots.length - 2];
331 for (
int i = 0; i < newKnots.length; i++) {
332 newKnots[i] = knots[i + 1];
336 double xTemp2[] =
new double[this.myX.length - 1];
337 double yTemp2[] =
new double[this.myY.length - 1];
338 for (
int i = 0; i < xTemp.length; i++) {
340 for (
int j = 0; j < xTemp.length; j++) {
341 if (xTemp[i] > xTemp[j])
344 while (xTemp2[k] != 0)
346 xTemp2[k] = xTemp[i];
347 yTemp2[k] = yTemp[i];
350 return new BSpline(xTemp2, yTemp2, newKnots);
396 private void init(
double[] x,
double[] y,
double[] initialKnots) {
397 if (initialKnots ==
null) {
399 knots =
new double[x.length + degree + 1];
400 for (
int i = degree; i < this.knots.length - degree; i++)
401 this.knots[i] = (
double) (i - degree) / (knots.length - (2.0 * degree) - 1);
402 for (
int i = this.knots.length - degree; i <
this.knots.length; i++)
403 this.knots[i] = this.knots[i - 1];
404 for (
int i = degree; i > 0; i--)
405 this.knots[i - 1] = this.knots[i];
412 degree = initialKnots.length - x.length - 1;
424 int iBorneInf = 1, iBorneSup = initialKnots.length - 2;
426 while (areEqual(initialKnots[iBorneInf], initialKnots[0], 1e-10))
428 if (iBorneInf <= degree)
429 iBorneInf = degree - iBorneInf + 1;
434 while (areEqual(initialKnots[iBorneSup], initialKnots[initialKnots.length - 1], 1e-10))
436 if (iBorneSup >= initialKnots.length - 1 - degree)
437 iBorneSup = degree + 1 - (initialKnots.length - 1 - iBorneSup);
442 this.knots =
new double[initialKnots.length + iBorneInf + iBorneSup];
443 myX =
new double[x.length + iBorneInf + iBorneSup];
444 myY =
new double[y.length + iBorneInf + iBorneSup];
445 for (
int i = 0; i < iBorneInf; i++) {
446 this.knots[i] = initialKnots[0];
450 for (
int i = 0; i < initialKnots.length; i++)
451 this.knots[iBorneInf + i] = initialKnots[i];
452 for (
int i = 0; i < x.length; i++) {
453 myX[iBorneInf + i] = x[i];
454 myY[iBorneInf + i] = y[i];
456 for (
int i = 0; i < iBorneSup; i++) {
457 this.knots[this.knots.length - 1 - i] = initialKnots[initialKnots.length - 1];
458 myX[myX.length - 1 - i] = x[x.length - 1];
459 myY[myY.length - 1 - i] = y[y.length - 1];
464 public double evalX(
double u) {
465 int k = Misc.getTimeInterval(knots, 0, knots.length - 1, u);
469 double[][] X =
new double[degree + 1][myX.length];
471 for (
int i = k - degree; i <= k; i++)
474 for (
int j = 1; j <= degree; j++) {
475 for (
int i = k - degree + j; i <= k; i++) {
476 double aij = (u - this.knots[i]) / (this.knots[i + 1 + degree - j] - this.knots[i]);
477 X[j][i] = (1 - aij) * X[j - 1][i - 1] + aij * X[j - 1][i];
483 public double evalY(
double u) {
484 int k = Misc.getTimeInterval(knots, 0, knots.length - 1, u);
488 double[][] Y =
new double[degree + 1][myX.length];
490 for (
int i = k - degree; i <= k; i++)
492 for (
int j = 1; j <= degree; j++) {
493 for (
int i = k - degree + j; i <= k; i++) {
494 double aij = (u - this.knots[i]) / (this.knots[i + 1 + degree - j] - this.knots[i]);
495 Y[j][i] = (1 - aij) * Y[j - 1][i - 1] + aij * Y[j - 1][i];
509 private static boolean areEqual(
double a,
double b,
double tol) {
510 return Math.abs(a - b) < tol;
513 private static double[] computeN(
double[] U,
int degree,
double u,
int np1) {
514 double[] N =
new double[np1];
517 if (areEqual(u, U[0], 1e-10)) {
520 }
else if (areEqual(u, U[U.length - 1], 1e-10)) {
521 N[N.length - 1] = 1.0;
526 int k = Misc.getTimeInterval(U, 0, U.length - 1, u);
529 for (
int d = 1; d <= degree; d++) {
530 N[k - d] = N[k - d + 1] * (U[k + 1] - u) / (U[k + 1] - U[k - d + 1]);
531 for (
int i = k - d + 1; i <= k - 1; i++)
532 N[i] = (u - U[i]) / (U[i + d] - U[i]) * N[i] + ((U[i + d + 1] - u) / (U[i + d + 1] - U[i + 1])) * N[i + 1];
533 N[k] = (u - U[k]) / (U[k + d] - U[k]) * N[k];