/*
* jgeom: Geometry Library fo Java
*
* Copyright (C) 2005 Samuel Gerber
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package toxi.geom.nurbs;
/**
* KnotVector, assembles the knots values of a NURBS and its degree.
*
* @author sg
* @version 1.0
*/
public class KnotVector {
private boolean isOpen;
private final float knots[];
private final int degree;
private final int n;
/**
* Create a Knotvector from the given knot values of the desired degree.
*
* @param knots
* knot values
* @param degree
* degree of Nurbs
*/
public KnotVector(float knots[], int degree)
throws IllegalArgumentException {
this.knots = knots;
this.degree = degree;
n = knots.length - degree - 2;
for (int i = 1; i < knots.length; i++) {
if (knots[i - 1] > knots[i]) {
throw new IllegalArgumentException("Knots not valid knot["
+ (i - 1) + "] > knot[" + i + "]: knot[" + (i - 1)
+ "]=" + knots[i - 1] + " > knot[" + i + "]="
+ knots[i]);
}
}
int m = knots.length - 1;
// Check if it is an open knot vector
isOpen = true;
for (int k = 0; k < degree && isOpen; k++) {
if (knots[k] != knots[k + 1]) {
isOpen = false;
}
}
for (int k = m; k > m - degree && isOpen; k--) {
if (knots[k] != knots[k - 1]) {
isOpen = false;
}
}
}
/**
* Gets the basis function values for the given u value. This function
* calculates firstly the span which is needed in order to calculate the
* basis functions values.
*
* @param u
* Value to calculate basis functions for.
* @return basis function values
*/
public double[] basisFunctions(float u) {
return basisFunctions(findSpan(u), u);
}
/**
* Calculates the basis function values for the given u value, when it's
* already known in which span u lies.
*
* @param span
* The span u lies in
* @param u
* Value to calculate basis functions for.
* @return basis function values
*/
public double[] basisFunctions(int span, float u) {
final int d1 = degree + 1;
double res[] = new double[d1];
double left[] = new double[d1];
double right[] = new double[d1];
res[0] = 1;
for (int j = 1; j < d1; j++) {
left[j] = u - knots[span + 1 - j];
right[j] = knots[span + j] - u;
double saved = 0;
for (int r = 0; r < j; r++) {
double tmp = res[r] / (right[r + 1] + left[j - r]);
res[r] = saved + right[r + 1] * tmp;
saved = left[j - r] * tmp;
}
res[j] = saved;
}
return res;
}
/**
* Calculates the basis functions and its derivatives up to the given grade.
*
* @param u
* Value to calculate basis functions and derivatives for.
* @param grade
* grade to calculate derivations for.
* @return an array of basis function values or derivated basis functions
* values. The first array is the degree of dderivation in the
* second array rhe values are stored.
* Example:
*
* float[]][] f=dersBasisFuns(0.1f, 3);
* float value=f[0][1]; //In value is know the second value of the basis function derived 0 times stored.
*
*
*/
public float[][] derivBasisFunctions(float u, int grade) {
int span = findSpan(u);
return derivBasisFunctions(span, u, grade);
}
/**
* Calculates the basis functions and its derivatives up to the given grade.
*
* @param span
* Span the given value lies in.
* @param u
* Value to calculate basis functions and derivatives for.
* @param grade
* grade to calculate derivations for.
* @return an array of basis function values or derivated basis functions
* values
* @see KnotVector#derivBasisFunctions(float, int)
*/
public float[][] derivBasisFunctions(int span, float u, int grade) {
float[][] ders = new float[grade + 1][degree + 1];
float[][] ndu = new float[degree + 1][degree + 1];
ndu[0][0] = 1.0f;
float[] left = new float[degree + 1];
float[] right = new float[degree + 1];
int j1, j2;
for (int j = 1; j <= degree; j++) {
left[j] = u - knots[span + 1 - j];
right[j] = knots[span + j] - u;
float saved = 0.0f;
for (int r = 0; r < j; r++) {
ndu[j][r] = right[r + 1] + left[j - r];
float temp = ndu[r][j - 1] / ndu[j][r];
ndu[r][j] = saved + right[r + 1] * temp;
saved = left[j - r] * temp;
}
ndu[j][j] = saved;
}
for (int j = 0; j <= degree; j++) {
ders[0][j] = ndu[j][degree];
}
for (int r = 0; r <= degree; r++) {
int s1 = 0;
int s2 = 1;
float[][] a = new float[2][degree + 1];
a[0][0] = 1.0f;
for (int k = 1; k <= grade; k++) {
float d = 0.0f;
final int rk = r - k;
final int pk = degree - k;
final float[] as1 = a[s1];
final float[] as2 = a[s2];
if (r >= k) {
as2[0] = d = as1[0] / ndu[pk + 1][rk];
d *= ndu[rk][pk];
}
if (rk >= -1) {
j1 = 1;
} else {
j1 = -rk;
}
if (r - 1 <= pk) {
j2 = k - 1;
} else {
j2 = degree - r;
}
for (int j = j1; j <= j2; j++) {
as2[j] = (as1[j] - as1[j - 1]) / ndu[pk + 1][rk + j];
d += as2[j] * ndu[rk + j][pk];
}
if (r <= pk) {
as2[k] = -as1[k - 1] / ndu[pk + 1][r];
d += as2[k] * ndu[r][pk];
}
ders[k][r] = d;
int j = s1;
s1 = s2;
s2 = j;
}
}
int r = degree;
for (int k = 1; k <= grade; k++) {
for (int j = 0; j <= degree; j++) {
ders[k][j] *= r;
}
r *= (degree - k);
}
return ders;
}
/**
* Finds the span (Position of corresponding knot values in knot vector) a
* given value belongs to.
*
* @param u
* value to find span for
* @return Position of span.
*/
public int findSpan(float u) {
if (u >= knots[n + 1]) {
return n;
}
int low = degree;
int high = n + 1;
int mid = (low + high) / 2;
while ((u < knots[mid] || u >= knots[mid + 1]) && low < high) {
if (u < knots[mid]) {
high = mid;
} else {
low = mid;
}
mid = (low + high) / 2;
}
return mid;
}
/**
* Get the knot value at a specific index.
*
* @param i
* Index to get knot value for
* @return the knot value
*/
public float get(int i) {
return knots[i];
}
/**
* get the knot values as float array
*
* @return the knot values
*/
public float[] getArray() {
return knots;
}
/**
* Get the degree of the KnotVector
*
* @return Degree of the Knotvector
*/
public int getDegree() {
return degree;
}
/**
* Return the nu
*
* @return Length of the KnotVector
*/
public int getN() {
return n;
}
/**
*
* @return
*/
public int getNumberOfSegments() {
int seg = 0;
float u = knots[0];
for (int i = 1; i < knots.length; i++) {
if (u != knots[i]) {
seg++;
u = knots[i];
}
}
return seg;
}
/**
*
* @return
*/
public synchronized boolean isOpen() {
return isOpen;
}
/**
*
* @return
*/
public int length() {
return knots.length;
}
/**
* Set the knot value at a specific index. After this operation a call to
* isValid may be needed if one is not sure if the KnotVector with the
* changed value is valid for a Nurbs.
*
* @param i
* Index to set knot value
* @param val
* value to set the knot too
*/
public void set(int i, float val) {
knots[i] = val;
}
}