/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package feuille.drawing.bspline;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
/**
* Trouvé sur le projet http://code.google.com/p/curvecreator/
* @author The Wingate 2940
*/
public class BSplineCurve extends Curve {
protected List<Double> knots = new ArrayList<Double>();
protected int degree = 3;
public BSplineCurve(Point2D startPoint) {
addControlPoint(startPoint);
updateKnots();
}
public BSplineCurve(BezierCurve bezierCurve) {
getControlPoints().addAll(bezierCurve.getControlPoints());
degree = bezierCurve.getControlPoints().size() - 1;
updateKnots();
for (int i = 0; i < knots.size(); i++) {
knots.set(i, new Double(i / (degree + 1)));
}
}
public BSplineCurve(List<Point2D> controlPoints, List<Double> knots, int degree) throws IllegalArgumentException {
setControlPoints(controlPoints);
this.degree = degree;
this.knots = knots;
if(!isValid()) {
throw new IllegalArgumentException("B-Spline curve is not valid.");
}
}
@Override
public final boolean isValid() {
return getControlPoints().size() > degree && knots.size() == getControlPoints().size() + degree + 1;
}
@Override
public void paintCurve(Graphics2D g, int quality, boolean adaptive) {
if(adaptive) {
paintAdaptive(g, quality);
} else {
paintUniform(g, quality);
}
}
@Override
public final void addControlPoint(Point2D point) {
getControlPoints().add(point);
updateKnots();
}
@Override
public void removeControlPoint(Point2D point) {
getControlPoints().remove(point);
updateKnots();
}
private void paintUniform(Graphics2D g, int quality) {
int numPoints = quality * 2 * getControlPoints().size();
Point2D[] points = new Point2D[numPoints];
double minU = knots.get(degree);
double maxU = knots.get(knots.size() - 1 - degree);
if (minU >= maxU) {
return;
}
for (int currentPoint = 0; currentPoint < points.length; currentPoint++) {
double u = (((maxU - minU) / (points.length - 0.9999999)) * currentPoint) + minU;
double N[] = getBasis(u, getControlPoints().size() - 1, degree);
double x = 0, y = 0;
for (int i = 0; i < getControlPoints().size(); i++) {
Point2D control = getControlPoints().get(i);
x += control.getX() * N[i];
y += control.getY() * N[i];
}
points[currentPoint] = new Point2D.Double(x, y);
}
for (int i = 0; i < points.length - 1; i++) {
Line2D l = new Line2D.Double(points[i], points[i + 1]);
g.draw(l);
}
}
private void paintAdaptive(Graphics2D g, int quality) {
for (int i = degree; i < getControlPoints().size(); i++) {
extractBezierCurve(i).paintCurve(g, quality, true);
}
}
public List<BezierCurve> extractAllBezierCurves() {
List<BezierCurve> curves = new ArrayList<BezierCurve>();
for (int i = degree; i < getControlPoints().size(); i++) {
curves.add(extractBezierCurve(i));
}
return curves;
}
private BezierCurve extractBezierCurve(int index) {
ArrayList<Double> tempKnots = new ArrayList<Double>();
ArrayList<Point2D> tempControlPoints = new ArrayList<Point2D>();
for (int i = index - degree; i <= index; i++) {
Point2D cp = getControlPoints().get(i);
tempControlPoints.add(new Point2D.Double(cp.getX(), cp.getY()));
}
for (int i = index - degree; i <= index + degree + 1; i++) {
double kn = knots.get(i);
tempKnots.add(kn);
}
for (;;) {
int j = 0;
for (int i = degree - 1; i > 0; i--) {
if (tempKnots.get(i) < tempKnots.get(degree)) {
j = i;
break;
}
j = 0;
}
if (j == 0) {
break;
}
for (int i = 0; i < j; i++) {
double knotKI1 = tempKnots.get(degree + i + 1);
double knotK = tempKnots.get(degree);
double knotI1 = tempKnots.get(i + 1);
Point2D cpI = tempControlPoints.get(i);
Point2D cpI1 = tempControlPoints.get(i + 1);
cpI.setLocation(
((knotKI1 - knotK) / (knotKI1 - knotI1)) * cpI.getX() + ((knotK - knotI1) / (knotKI1 - knotI1)) * cpI1.getX(),
((knotKI1 - knotK) / (knotKI1 - knotI1)) * cpI.getY() + ((knotK - knotI1) / (knotKI1 - knotI1)) * cpI1.getY());
}
for (int i = 0; i < j; i++) {
tempKnots.set(i, tempKnots.get(i + 1));
}
tempKnots.set(j, tempKnots.get(degree));
}
for (;;) {
int j = 0;
for (int i = degree + 2; i < degree * 2 + 1; i++) {
if (tempKnots.get(i) > tempKnots.get(degree + 1)) {
j = i;
break;
}
j = 0;
}
if (j == 0) {
break;
}
for (int i = degree; i >= j - degree; i--) {
double knotKI = tempKnots.get(degree + i);
double knotK1 = tempKnots.get(degree + 1);
double knotI = tempKnots.get(i);
Point2D cpIm1 = tempControlPoints.get(i - 1);
Point2D cpI = tempControlPoints.get(i);
cpI.setLocation(
((knotKI - knotK1) / (knotKI - knotI)) * cpIm1.getX() + ((knotK1 - knotI) / (knotKI - knotI)) * cpI.getX(),
((knotKI - knotK1) / (knotKI - knotI)) * cpIm1.getY() + ((knotK1 - knotI) / (knotKI - knotI)) * cpI.getY());
}
for (int i = degree * 2 + 1; i > j; i--) {
tempKnots.set(i, tempKnots.get(i - 1));
}
tempKnots.set(j, tempKnots.get(degree + 1));
}
return new BezierCurve(tempControlPoints);
}
private double[] getBasis(double u, int n, int degree) {
double[] basis = new double[n + 2];
for (int i = 0; i < basis.length; i++) {
basis[i] = (u >= knots.get(i) && u < knots.get(i + 1)) ? 1 : 0;
}
for (int k = 1; k <= degree; k++) {
for (int i = 0; i <= n; i++) {
double a = (u - knots.get(i)) / (knots.get(i + k) - knots.get(i));
double b = (knots.get(i + k + 1) - u) / (knots.get(i + k + 1) - knots.get(i + 1));
basis[i] = basis[i] * a + basis[i + 1] * b;
}
}
return basis;
}
private void updateKnots() {
knots = new ArrayList<Double>(getControlPoints().size() + degree + 1);
for (int i = 0; i < getControlPoints().size() + degree + 1; i++) {
knots.add(new Double(i));
}
}
public int getDegree() {
return degree;
}
public List<Double> getKnots() {
return knots;
}
public void setDegree(int degree) {
this.degree = degree;
}
public void setKnots(List<Double> knots) {
this.knots = knots;
}
}