/*
bezier.java by Gengbin Zheng
*/
import java.awt.*;
import java.applet.*;
public class bezier extends Applet {
Button draw1Button, draw2Button, modifyButton, deleteButton, clearButton;
myCanvas canvas;
TextField statusBar;
public void init() {
GridBagLayout layout = new GridBagLayout();
setLayout(layout);
GridBagConstraints constraints = new GridBagConstraints();
draw1Button = new Button("Draw Bezier");
draw2Button = new Button("Draw B-Spline");
modifyButton = new Button("Modify");
deleteButton = new Button("Delete curve");
clearButton = new Button("Clear All");
constraints.fill = GridBagConstraints.BOTH;
constraints.weightx = 1;
layout.setConstraints(draw1Button, constraints);
add(draw1Button);
layout.setConstraints(draw2Button, constraints);
add(draw2Button);
layout.setConstraints(modifyButton, constraints);
add(modifyButton);
constraints.gridwidth = GridBagConstraints.RELATIVE;
layout.setConstraints(deleteButton, constraints);
add(deleteButton);
constraints.gridwidth = GridBagConstraints.REMAINDER;
layout.setConstraints(clearButton, constraints);
add(clearButton);
canvas = new myCanvas();
constraints.weighty = 1;
layout.setConstraints(canvas, constraints);
add(canvas);
statusBar = new TextField("Draw Bezier: click to add a point, double click to finish drawing", 45);
statusBar.setEditable(false);
constraints.weighty = 0;
layout.setConstraints(statusBar, constraints);
add(statusBar);
resize(550,450); //Set window size
}
public boolean action(Event evt, Object arg)
{
if (evt.target instanceof Button)
{
HandleButtons(arg);
}
return true;
}
protected void HandleButtons(Object label)
{
String helpMsg;
if (label == "Clear All")
helpMsg = "All curves are cleared.";
else if (label == "Draw Bezier")
helpMsg = "Draw Bezier: click to add a point, double click to finish drawing";
else if (label == "Draw B-Spline")
helpMsg = "Draw B-Spline: click to add a point, double click to finish drawing.";
else if (label == "Modify")
helpMsg = "Modify: select a control point, drag mouse to modify and release to finish.";
else if (label == "Delete curve")
helpMsg = "Delete: select a curve, click to delete.";
else
helpMsg = "";
statusBar.setText(helpMsg);
canvas.HandleButtons(label);
}
}
class myCanvas extends Canvas {
PointList pts[];
int nline;
int curObj;
boolean drawing;
int action;
final int DRAW_BEZIER=1, DRAW_BSPLINE=2, MODIFY=3, DELETE=4;
ErrorFrame errDlg;
// double buffering
Image img = null;
Graphics backg;
public myCanvas() {
pts = new PointList[200];
nline = -1;
drawing = false;
action = DRAW_BEZIER;
errDlg = new ErrorFrame(" Too many points!");
}
void setcursor(boolean working)
{
Cursor curs;
if (working)
curs = new Cursor(Cursor.HAND_CURSOR);
else
curs = new Cursor(Cursor.DEFAULT_CURSOR);
setCursor(curs);
}
public boolean mouseUp(Event evt, int x, int y)
{
if (action == DRAW_BEZIER || action == DRAW_BSPLINE)
{
if (drawing) {
if (!pts[nline].addPoint(x,y))
{
if (!errDlg.isShowing()) errDlg.show();
drawing = false;
nline --;
setcursor(drawing);
}
}
repaint();
}
if (action == MODIFY)
{
if (drawing)
{
drawing = false;
setcursor(drawing);
}
}
if (action == DELETE)
{
if (curObj != -1)
{
for (int i=curObj; i< nline; i++) pts[i] = pts[i+1];
nline--;
repaint();
}
}
return true;
}
public boolean mouseDown(Event evt, int x, int y)
{
if (action == DRAW_BEZIER || action == DRAW_BSPLINE)
{
if (drawing == false)
{
nline ++;
if (action == DRAW_BEZIER) pts[nline] = new bezierLine();
if (action == DRAW_BSPLINE) pts[nline] = new bspline();
pts[nline].addPoint(x,y);
drawing = true;
setcursor(drawing);
}
else {
if (evt.clickCount == 2) {
if (!pts[nline].done())
{
if (!errDlg.isShowing()) errDlg.show();
nline --;
}
drawing = false;
setcursor(drawing);
}
}
}
if (action == MODIFY)
{
if (curObj != -1) {
drawing = true;
setcursor(drawing);
}
}
return true;
}
public boolean mouseMove(Event evt, int x, int y)
{
if (action == DRAW_BEZIER || action == DRAW_BSPLINE)
{
if (drawing)
{
pts[nline].changePoint(x,y);
repaint();
}
}
if (action == MODIFY || action == DELETE)
{
if (drawing == false)
{
int oldObj = curObj;
curObj = -1;
for (int i=0; i<=nline; i++)
{
if (pts[i].inRegion(x,y) != -1)
{
curObj = i;
break;
}
}
if (oldObj != curObj) repaint();
}
}
return true;
}
public boolean mouseDrag(Event evt, int x, int y)
{
if (action == MODIFY)
{
if (drawing == true) {
pts[curObj].changeModPoint(x,y);
if (!pts[curObj].createFinal())
{
if (!errDlg.isShowing()) errDlg.show();
nline --;
}
repaint();
}
}
return true;
}
public void HandleButtons(Object label)
{
if (drawing)
{
drawing = false;
setcursor(drawing);
}
if (label == "Clear All")
{
nline = -1;
repaint();
return;
}
if (action == DRAW_BEZIER || action == DRAW_BSPLINE)
{
if (drawing) pts[nline].done();
}
if (label == "Draw Bezier")
{
action = DRAW_BEZIER;
for (int i=0; i<=nline; i++)
pts[i].setShow(false);
repaint();
}
else if (label == "Draw B-Spline")
{
action = DRAW_BSPLINE;
for (int i=0; i<=nline; i++)
pts[i].setShow(false);
repaint();
}
else if (label == "Modify")
{
action = MODIFY;
for (int i=0; i<=nline; i++)
pts[i].setShow(true);
repaint();
}
else if (label == "Delete curve")
{
action = DELETE;
for (int i=0; i<=nline; i++)
pts[i].setShow(true);
repaint();
}
}
public void paint(Graphics g)
{
update(g);
}
public void update(Graphics g) { //Don't bother
int i,n;
Dimension d=size();
if (img == null)
{
img = createImage(d.width, d.height);
backg = img.getGraphics();
}
backg.setColor(new Color(255,255,255)); //Set color for background
backg.fillRect(0,0, d.width, d.height); //Draw Backround
// draw border
backg.setColor(new Color(0,0,0));
backg.drawRect(1,1,d.width-3,d.height-3);
for (n=0; n <= nline; n++)
pts[n].draw(backg);
g.drawImage(img, 0, 0, this);
}
}
class ErrorFrame extends Frame
{
Label label;
Button button;
String errMsg;
ErrorFrame(String msg)
{
super("Error!");
errMsg = msg;
BorderLayout layout = new BorderLayout();
setLayout(layout);
label = new Label(errMsg);
add("North", label);
button = new Button("Ok");
add("South", button);
resize(200,100);
}
public boolean action(Event evt, Object arg)
{
if (arg == "Ok")
dispose();
return true;
}
}
class PointList {
Point pt[];
int num;
int x,y,z; // color
boolean showLine;
int curPt;
final int MAXCNTL = 50;
final int range = 5;
PointList() {
num = 0;
curPt = -1;
pt = new Point[MAXCNTL];
x = (int)(Math.random() * 255);
y = (int)(Math.random() * 255);
z = (int)(Math.random() * 255);
}
boolean addPoint(int x, int y)
{
if (num == MAXCNTL) return false;
pt[num] = new Point(x,y);
num++;
return true;
}
void changePoint(int x, int y)
{
pt[num-1].x = x;
pt[num-1].y = y;
}
void changeModPoint(int x, int y)
{
pt[curPt].x = x;
pt[curPt].y = y;
}
boolean createFinal()
{
return true;
}
boolean done()
{
return true;
}
void setShow(boolean show)
{
showLine = show;
}
int inRegion(int x, int y)
{
int i;
for (i=0; i<num; i++)
if (Math.abs(pt[i].x-x) < range && Math.abs(pt[i].y-y) < range)
{
curPt = i;
return i;
}
curPt = -1;
return -1;
}
void draw(Graphics g)
{
int i;
int l = 3;
for (i=0; i< num-1; i++)
{
g.drawLine(pt[i].x-l, pt[i].y, pt[i].x+l, pt[i].y);
g.drawLine(pt[i].x, pt[i].y-l, pt[i].x, pt[i].y+l);
drawDashLine(g, pt[i].x,pt[i].y,pt[i+1].x,pt[i+1].y); //Draw segment
}
g.drawLine(pt[i].x-l, pt[i].y, pt[i].x+l, pt[i].y);
g.drawLine(pt[i].x, pt[i].y-l, pt[i].x, pt[i].y+l);
}
// draw dash lines
protected void drawDashLine(Graphics g, int x1, int y1, int x2, int y2)
{
final float seg = 8;
double x, y;
if (x1 == x2)
{
if (y1 > y2)
{
int tmp = y1;
y1 = y2;
y2 = tmp;
}
y = (double)y1;
while (y < y2)
{
double y0 = Math.min(y+seg, (double)y2);
g.drawLine(x1, (int)y, x2, (int)y0);
y = y0 + seg;
}
return;
}
else if (x1 > x2)
{
int tmp = x1;
x1 = x2;
x2 = tmp;
tmp = y1;
y1 = y2;
y2 = tmp;
}
double ratio = 1.0*(y2-y1)/(x2-x1);
double ang = Math.atan(ratio);
double xinc = seg * Math.cos(ang);
double yinc = seg * Math.sin(ang);
x = (double)x1;
y = (double)y1;
while ( x <= x2 )
{
double x0 = x + xinc;
double y0 = y + yinc;
if (x0 > x2) {
x0 = x2;
y0 = y + ratio*(x2-x);
}
g.drawLine((int)x, (int)y, (int)x0, (int)y0);
x = x0 + xinc;
y = y0 + yinc;
}
}
}
class bezierLine extends PointList {
Point bpt[];
int bnum;
boolean ready;
final int MAXPOINT = 1800;
final int ENOUGH = 2;
final int RECURSION = 900;
int nPointAlloc;
int enough; // control how well we draw the curve.
int nRecur; // counter of number of recursion
Point buffer[][];
int nBuf, nBufAlloc;
bezierLine() {
bpt = new Point[MAXPOINT];
nPointAlloc = MAXPOINT;
bnum = 0;
enough = ENOUGH;
showLine = true;
ready = false;
buffer = null;
}
protected int distance(Point p0,Point p1,Point p2)
{
int a,b,y1,x1,d1,d2;
if(p1.x==p2.x && p1.y==p2.y) return Math.min(Math.abs(p0.x-p1.x),Math.abs(p0.y-p1.y));
a=p2.x-p1.x; b=p2.y-p1.y;
y1=b*(p0.x-p1.x)+a*p1.y;
x1=a*(p0.y-p1.y)+b*p1.x;
d1=Math.abs(y1-a*p0.y);
d2=Math.abs(x1-b*p0.x);
if (a==0) return Math.abs(d2/b);
if (b==0) return Math.abs(d1/a);
return Math.min(Math.abs(d1/a),Math.abs(d2/b));
}
protected void curve_split(Point p[],Point q[],Point r[],int num)
{
int i,j;
// for (i=0;i<num;i++) q[i] = new Point(p[i]);
for (i=0;i<num;i++) q[i].copy(p[i]);
for (i=1;i<=num-1;i++) {
// r[num-i] = new Point(q[num-1]);
r[num-i].copy(q[num-1]);
for (j=num-1;j>=i;j--) {
// q[j] = new Point((q[j-1].x+q[j].x)/2, (q[j-1].y+q[j].y)/2);
q[j].x = (q[j-1].x+q[j].x)/2;
q[j].y = (q[j-1].y+q[j].y)/2;
}
}
// r[0] = new Point(q[num-1]);
r[0].copy(q[num-1]);
}
// reuse buffer
private Point get_buf(int num)[]
{
Point b[];
if (buffer == null)
{
buffer = new Point[500][num];
nBufAlloc = 500;
nBuf = 0;
}
if (nBuf == 0)
{
b = new Point[num];
for (int i=0; i< num; i++) b[i] = new Point();
return b;
}
else {
nBuf --;
b = buffer[nBuf];
return b;
}
}
private void put_buf(Point b[])
{
if (nBuf >= nBufAlloc)
{
Point newBuf[][] = new Point[nBufAlloc + 500][num];
for (int i=0; i<nBuf; i++) newBuf[i] = buffer[i];
nBufAlloc += 500;
buffer = newBuf;
}
buffer[nBuf] = b;
nBuf++;
}
protected boolean bezier_generation(Point pt[], int num, Point result[], int n[])
{
Point qt[],rt[]; // for split
int d[],i,max;
nRecur++;
if (nRecur > RECURSION) return false;
d = new int[MAXCNTL];
for (i=1;i<num-1;i++) d[i]=distance(pt[i],pt[0],pt[num-1]);
max=d[1];
for (i=2;i<num-1;i++) if (d[i]>max) max=d[i];
if (max <= enough || nRecur > RECURSION) {
if (n[0]==0) {
if (bnum > 0)
result[0].copy(pt[0]);
else
result[0] = new Point(pt[0]);
n[0]=1;
}
//reuse
if (bnum > n[0])
result[n[0]].copy(pt[num-1]);
else
result[n[0]] = new Point(pt[num-1]);
n[0]++;
if (n[0] == MAXPOINT-1) return false;
}
else {
// qt = new Point[num];
// rt = new Point[num];
qt = get_buf(num);
rt = get_buf(num);
curve_split(pt,qt,rt,num);
if (!bezier_generation(qt,num,result,n)) return false;
put_buf(qt);
if (!bezier_generation(rt,num,result,n)) return false;
put_buf(rt);
}
return true;
}
public boolean try_bezier_generation(Point pt[], int num, Point result[], int n[])
{
int oldN = n[0];
if (enough == ENOUGH && num > 6) enough += 3;
// if (enough > ENOUGH) enough -= 5;
nRecur = 0;
// in case of recursion stack overflow, relax "enough" and keep trying
while (!bezier_generation(pt, num, bpt, n))
{
n[0] = oldN;
enough += 5;
nRecur = 0;
}
return true;
}
boolean createFinal()
{
int n[];
n = new int[1];
if (!try_bezier_generation(pt, num, bpt, n))
{
bnum = 0;
return false;
}
else {
bnum = n[0];
return true;
}
}
boolean done()
{
num --;
showLine = false;
ready = true;
return createFinal();
}
void draw(Graphics g)
{
g.setColor(new Color(x,y,z));
if (showLine)
{
super.draw(g);
if (curPt != -1)
g.drawRect(pt[curPt].x-range, pt[curPt].y-range, 2*range+1,2*range+1);
}
if (ready)
for (int i=0; i< bnum-1; i++)
{
g.drawLine(bpt[i].x,bpt[i].y,bpt[i+1].x,bpt[i+1].y);
}
}
}
class bspline extends bezierLine
{
protected void bspline_to_Bezier(int j, Point p[], Point v[])
{
int h,i;
double tmp,x1,x2;
for (h=0;h<=1;h++) {
for (i=0;i<=1;i++) {
tmp=1.0*((j+h)-(j-2+h+i))*1.0/((j+1+i+h)-(j-2+h+i)); // (2-i)/3
x1=p[j-2+i+h].x;
x2=p[j-2+i+h-1].x;
v[2*h+i].x=(int)(tmp*x1+(1.0-tmp)*x2);
x1=p[j-2+i+h].y;
x2=p[j-2+i+h-1].y;
v[2*h+i].y=(int)(tmp*x1+(1.0-tmp)*x2);
}
tmp=1.0*((j+h)-(j-1+h))/((j+1+h)-(j-1+h)); // 1/2
x1=v[1+2*h].x;
x2=v[2*h].x;
v[3*h].x=(int)(tmp*x1+(1.0-tmp)*x2);
x1=v[1+2*h].y;
x2=v[2*h].y;
v[3*h].y=(int)(tmp*x1+(1-tmp)*x2);
}
}
protected boolean bspline_generation(Point pt[],int n,Point result[],int num[])
{
Point v[];
int i,j;
v = new Point[4];
for (i=0; i<4; i++) v[i] = new Point();
for (j=3;j<n;j++) {
bspline_to_Bezier(j,pt,v);
if (num[0] > 0) num[0]=num[0]-1;
if (!try_bezier_generation(v,4,result,num)) return false;
}
return true;
}
boolean createFinal()
{
int n[];
n = new int[1];
n[0] = 0;
if (bspline_generation(pt, num, bpt, n))
{
bnum = n[0];
return true;
}
else {
bnum = 0;
return false;
}
}
}
class Point
{
int x,y;
Point(Point p)
{
x = p.x;
y = p.y;
}
Point(int _x, int _y)
{
x = _x;
y = _y;
}
Point()
{
x = 0;
y = 0;
}
void copy(Point p)
{
x = p.x;
y = p.y;
}
}