import java.awt.*; public class bspline extends BufferedApplet { int w = 1, h, I = -1; double S = 8, p[] = new double[(int)S + 4]; Color lightGreen = new Color(160,255,160); public void render(Graphics g) { w = bounds().width; // FETCH APPLET WIDTH,HT h = bounds().height; g.setColor(Color.white); // FILL BACKGROUND g.fillRect(0,0,w,h); g.setColor(Color.green); // DRAW Y=0 AXIS g.drawLine(0,h/2,w,h/2); for (int i = 0 ; i < p.length ; i++) { int x = X2x(i-1); g.setColor(lightGreen); g.drawLine(x,0,x,h); // DRAW VERTICAL STRIPES g.setColor(Color.black); g.fillOval(x-4,Y2y(p[i])-4,8,8); // DRAW KEY HANDLES } g.setColor(Color.red); for (int x = 0 ; x < w ; x++) g.drawLine(x,f2(x),x+1,f2(x+1)); // DRAW THE CURVE g.setColor(Color.blue); for (int x = 0 ; x < w ; x++) g.drawLine(x,f1(x),x+1,f1(x+1)); // DRAW THE CURVE g.setColor(Color.black); for (int x = 0 ; x < w ; x++) { g.drawLine(x,f(x),x+1,f(x+1)); // DRAW THE CURVE g.drawLine(x,f(x)+1,x+1,f(x+1)+1); } } // CONVERT BETWEEN PIXELS (x,y) AND CURVE COORDS (X,Y) double x2X(int x) { return S * x / w; } double y2Y(int y) { return (h/2-y) * S / w; } int X2x(double X) { return (int)(w * X / S); } int Y2y(double Y) { return h/2 - (int)(Y * w / S); } int i_prev = -10000; int f(int x) { // FUNCTION DEFINING CURVE, IN PIXEL COORDS double t = x2X(x); int i = (int)Math.floor(t); t -= i; double A=p[i], B=p[i+1], C=p[i+2], D=p[i+3]; double a = ( -A + 3*B - 3*C + D) / 6; double b = ( 3*A - 6*B + 3*C ) / 6; double c = (-3*A + 3*C ) / 6; double d = ( A + 4*B + C ) / 6; t = ((a * t + b) * t + c) * t + d; return Y2y(t); } int f1(int x) { double t = x2X(x); int i = (int)Math.floor(t); t -= i; double A=p[i], B=p[i+1], C=p[i+2], D=p[i+3]; double b = (-3*A + 9*B - 9*C + 3*D) / 6; double c = ( 6*A -12*B + 6*C ) / 6; double d = (-3*A + 3*C ) / 6; t = (b * t + c) * t + d; return Y2y(t); } int f2(int x) { double t = x2X(x); int i = (int)Math.floor(t); t -= i; double A=p[i], B=p[i+1], C=p[i+2], D=p[i+3]; double c = (-6*A +18*B -18*C + 6*D) / 6; double d = ( 6*A -12*B + 6*C ) / 6; t = c * t + d; return Y2y(t); } /* t=0 t=1 fA(t) = -t3 + 3t2 - 3t + 1 1 0 -3t2 + 6t - 3 -3 0 -6t + 6 6 0 fB(t) = 3t3 - 6t2 + 4 4 1 9t2 - 12t 0 -3 18t - 12 -12 6 fC(t) = -3t3 + 3t2 + 3t + 1 1 4 -9t2 + 6t + 3 3 0 -18t + 6 6 -12 fD(t) = t3 0 1 3t2 0 3 6t 0 6 0 6 -12 1*(0+6)/2 = 3 1*(6-12)/2 = -3 A = width of first segment B = width of second segment assume second derivative f2 at A = 1, and zero area under f2 between 0 and A+B f2 is piecewise linear: t = 0 ... A ... A+B f2(t) = 0 ... 1 ... -A/B-1 A = -B(1+q) => -A/B = 1+q => q = -A/B-1 f1 is piecewise quadratic: Betw 0 and A: f1(t) = tt/(2A) f1(0) = 0 f1(A) = A/2 f2(t) = t/A Betw A and A+B: f (t) = auu + bu f1(0) = A/2 f1'(0) = 1 f1(B) = 0 f'(t) = 2au + b + 1 fB1 = ? + ? t + ? tt fB2 = d - ? t derivative at left of A = derivative at right of B = 0 integral of f2 over A = A(0+2d/A)/2 = d = change in f1 over A integral of f2 over B = B(2d/A+?)/2 = -d = change in f1 over B ? = -2d (1/B + 1/A) f2(0 < t < A) = d t / A f2(A < t < B) = d - 2d (1/B + 1/A) t f1(0 < t < A) = d t2 / A / 2 f1(A < t < B) = d t - (d/B + d/A) t2 */ /* Generalizing to unevenly spaced knots would allow a really useful general purpose utility. If we were to make uneven knots, the interaction might be: Click on a keypoint (except left or rightmost ones) to delete it Click on space between keypoints to create a new keypoint Drag on a vertical line to move it left/right Drag on a keypoint to move it up/down */ int mode; public boolean keyUp(Event e, int key) { mode = key; damage = true; return true; } public boolean mouseDown(Event e, int x, int y) { I = (int)(x2X(x) + .5) + 1; return true; } public boolean mouseDrag(Event e, int x, int y) { if (I >= 0 && I < p.length) { p[I] = y2Y(y); damage = true; } return true; } }