package org.archstudio.swtutils;
import org.eclipse.swt.graphics.Point;
/**
* A B-Spline calculator.
*
* Ported by E. M. Dashofy from C++ code released into the public domain with the following notice:
*
* <code>Copyright 1994 by Keith Vertanen (vertankd@cda.mrs.umn.edu)</code> <br>
* <code>Released to the public domain (your mileage may vary)</code>
*
* @author Eric M. Dashofy <a href="mailto:edashofy@ics.uci.edu">edashofy@ics.uci.edu</a>
*/
public class BSpline {
public static final int DEFAULT_DEGREE = 4;
private static class point {
double x;
double y;
double z;
};
/**
* Calculate the points of a B-spline given an array of control points and some other parameters.
*
* @param controlPoints
* A set of control points for the B-Spline
* @param splinePointsPerMidpoint
* How many points to add per midpoint (should be an even number), the more you put, the curvier the
* spline.
* @return An ordered set of line segment endpoints that should be drawn to render the B-Spline
*/
public static Point[] bspline(Point[] controlPoints, int splinePointsPerMidpoint) {
if (controlPoints.length <= 2) { //A bspline with 2 control points is called a line :)
return controlPoints;
}
int n = controlPoints.length - 1;
int t = DEFAULT_DEGREE;
if (n + 2 <= t) {
t = n + 1;
}
point[] control = new point[controlPoints.length];
for (int i = 0; i < controlPoints.length; i++) {
point p = new point();
p.x = controlPoints[i].x;
p.y = controlPoints[i].y;
p.z = 0.0d;
control[i] = p;
}
int numPoints = controlPoints.length;
int numMidPoints = controlPoints.length - 2;
if (numMidPoints > 0) {
numPoints += numMidPoints * splinePointsPerMidpoint; //Add four additional control points for each midpoint.
}
//numPoints += 4; //Two additional for each endpoint
point[] output = new point[numPoints];
for (int i = 0; i < output.length; i++) {
output[i] = new point();
}
//Call bspline function
bspline(n, t, control, output, output.length);
Point[] outputPoints = new Point[output.length];
for (int i = 0; i < output.length; i++) {
outputPoints[i] = new Point((int) output[i].x, (int) output[i].y);
}
return outputPoints;
}
/**
* Calculate the points of a B-spline given an array of control points and some other parameters.
*
* @param n
* the number of control points minus 1
* @param t
* the degree of the polynomial plus 1
* @param control
* control point array made up of point stucture
* @param output
* array in which the calculate spline points are to be put
* @param num_output
* how many points on the spline are to be calculated
*/
private static void bspline(int n, int t, point[] control, point[] output, int num_output) {
int[] u;
double increment, interval;
point calcxyz = new point();
int output_index;
u = new int[n + t + 1];
compute_intervals(u, n, t);
increment = (double) (n - t + 2) / (num_output - 1); // how much parameter goes up each time
interval = 0;
for (output_index = 0; output_index < num_output - 1; output_index++) {
compute_point(u, n, t, interval, control, calcxyz);
output[output_index].x = calcxyz.x;
output[output_index].y = calcxyz.y;
output[output_index].z = calcxyz.z;
interval = interval + increment; // increment our parameter
}
output[num_output - 1].x = control[n].x; // put in the last point
output[num_output - 1].y = control[n].y;
output[num_output - 1].z = control[n].z;
}
/*
* void main() { int *u; int n,t,i; n=7; // number of control points = n+1 t=4; // degree of polynomial = t-1
*
* point[] pts; // allocate our control point array pts = new point[n+1];
*
* // randomize(); // for (i=0; i<=n; i++) // assign the control points randomly // { //
* (pts[i].x)=random(100)+(i*600/n); // (pts[i].y)=random(500); // (pts[i].z)=random(500); // }
*
* pts[0].x=10; pts[0].y=100; pts[0].z=0; pts[1].x=200; pts[1].y=100; pts[1].z=0; pts[2].x=345; pts[2].y=300;
* pts[2].z=0; pts[3].x=400; pts[3].y=250; pts[3].z=0; pts[4].x=500; pts[4].y=550; pts[4].z=0; pts[5].x=550;
* pts[5].y=150; pts[5].z=0; pts[6].x=570; pts[6].y=50; pts[6].z=0; pts[7].x=600; pts[7].y=100; pts[7].z=0;
*
* int resolution = 100; // how many points our in our output array point *out_pts; out_pts = new point[resolution];
*
* bspline(n, t, pts, out_pts, resolution); if (set_graph()) { setcolor(69); for (i=0; i<=n; i++)
* circle(pts[i].x,pts[i].y,2); // put circles at control points circle(pts[0].x,pts[0].y,0); // drop the pen down
* at first control point for (i=0; i<resolution; i++) { setcolor(i); // have a little fun with the colors
* putpixel(out_pts[i].x,out_pts[i].y,WHITE); } } }
*/
private static double blend(int k, int t, int[] u, double v) // calculate the blending value
{
double value;
if (t == 1) // base case for the recursion
{
if (u[k] <= v && v < u[k + 1]) {
value = 1;
}
else {
value = 0;
}
}
else {
if (u[k + t - 1] == u[k] && u[k + t] == u[k + 1]) {
value = 0;
}
else if (u[k + t - 1] == u[k]) {
// if a term's denominator is zero,use just the other
value = (u[k + t] - v) / (u[k + t] - u[k + 1]) * blend(k + 1, t - 1, u, v);
}
else if (u[k + t] == u[k + 1]) {
value = (v - u[k]) / (u[k + t - 1] - u[k]) * blend(k, t - 1, u, v);
}
else {
value = (v - u[k]) / (u[k + t - 1] - u[k]) * blend(k, t - 1, u, v) + (u[k + t] - v)
/ (u[k + t] - u[k + 1]) * blend(k + 1, t - 1, u, v);
}
}
return value;
}
private static void compute_intervals(int[] u, int n, int t) // figure out the knots
{
int j;
for (j = 0; j <= n + t; j++) {
if (j < t) {
u[j] = 0;
}
else if (t <= j && j <= n) {
u[j] = j - t + 1;
}
else if (j > n) {
u[j] = n - t + 2; // if n-t=-2 then we're screwed, everything goes to 0
}
}
}
private static void compute_point(int[] u, int n, int t, double v, point[] control, point output) {
int k;
double temp;
// initialize the variables that will hold our outputted point
output.x = 0;
output.y = 0;
output.z = 0;
for (k = 0; k <= n; k++) {
temp = blend(k, t, u, v); // same blend is used for each dimension coordinate
output.x = output.x + control[k].x * temp;
output.y = output.y + control[k].y * temp;
output.z = output.z + control[k].z * temp;
}
}
}