import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import java.util.List;
public class BezierSample extends JFrame
{
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new BezierSample().setVisible(true);
}
});
}
public BezierSample()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500,500);
getContentPane().setLayout(new BorderLayout());
BezierCanvas bezierCanvas = new BezierCanvas();
getContentPane().add(bezierCanvas, BorderLayout.CENTER);
BezierCurve bezierCurve0 = new BezierCurve();
bezierCurve0.addControlPoint(new Point3f(100,100,0));
bezierCurve0.addControlPoint(new Point3f(100,200,0));
bezierCurve0.addControlPoint(new Point3f(200,200,0));
bezierCurve0.addControlPoint(new Point3f(200,100,0));
bezierCanvas.addBezierCurve(bezierCurve0);
}
}
class BezierCurve
{
private int numControlPoints;
private Point3f controlPoints[];
private List<Point3f> points;
private List<Vector3f> tangents;
private int numPoints = 20;
private Point3f pointBuffer[] = null;
public BezierCurve()
{
points = new ArrayList<Point3f>();
tangents = new ArrayList<Vector3f>();
}
public int getNumPoints()
{
return points.size();
}
public Point3f getPointRef(int index)
{
return points.get(index);
}
public Vector3f getTangentRef(int index)
{
return tangents.get(index);
}
public void addControlPoint(Point3f controlPoint)
{
if (controlPoints == null || controlPoints.length == numControlPoints)
{
Point3f newControlPoints[] = new Point3f[numControlPoints+1];
if (controlPoints != null)
{
System.arraycopy(controlPoints, 0, newControlPoints, 0, numControlPoints);
}
controlPoints = newControlPoints;
}
controlPoints[numControlPoints] = controlPoint;
numControlPoints++;
update();
}
public int getNumControlPoints()
{
return numControlPoints;
}
public Point3f getControlPointRef(int index)
{
return controlPoints[index];
}
private void casteljau(Point3f result, Vector3f tangent, Point3f b[], int n, float t)
{
casteljauR(result, tangent, b, n, t);
}
private void casteljauR(Point3f result, Vector3f tangent, Point3f b[], int n, float t)
{
if (pointBuffer == null || pointBuffer.length < 2 * (n+1))
{
pointBuffer = new Point3f[2*(n+1)];
for (int i=0; i<pointBuffer.length; i++)
{
pointBuffer[i] = new Point3f();
}
}
casteljauRec(result, tangent, b, 0, n, t);
}
void casteljauRec(Point3f result, Vector3f tangent, Point3f b[], int i, int n, float t)
{
if (n==0)
{
result.set(b[i]);
return;
}
casteljauRec(pointBuffer[2*n+0], null, b, i+1, n-1, t);
casteljauRec(pointBuffer[2*n+1], null, b, i , n-1, t);
if (result != null)
{
result.interpolate(pointBuffer[2*n+1],pointBuffer[2*n+0],t);
}
if (tangent != null)
{
tangent.sub(pointBuffer[2*n+1],pointBuffer[2*n+0]);
tangent.scale(n);
}
}
public void update()
{
points.clear();
tangents.clear();
for (int i=0; i<numPoints; i++)
{
points.add(new Point3f());
tangents.add(new Vector3f());
}
for (int i=0; i<numPoints; i++)
{
casteljau(points.get(i), tangents.get(i), controlPoints, numControlPoints-1, i/(numPoints-1.0f));
}
}
}
class BezierCanvas extends JComponent implements MouseListener, MouseMotionListener
{
private ArrayList<BezierCurve> bezierCurves = new ArrayList<BezierCurve>();
private Point3f draggedPoint = null;
public BezierCanvas()
{
addMouseListener(this);
addMouseMotionListener(this);
}
public void addBezierCurve(BezierCurve bezierCurve)
{
bezierCurves.add(bezierCurve);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for (int i=0; i<bezierCurves.size(); i++)
{
paint(g, bezierCurves.get(i));
}
}
private void paint(Graphics g, BezierCurve bezierCurve)
{
g.setColor(Color.RED);
for (int i=0; i<bezierCurve.getNumControlPoints(); i++)
{
Point3f p0 = bezierCurve.getControlPointRef(i);
int x0 = (int)p0.x;
int y0 = (int)p0.y;
g.drawOval(x0-4,y0-4,8,8);
}
g.setColor(Color.BLACK);
for (int i=0; i<bezierCurve.getNumPoints()-1; i++)
{
Point3f p0 = bezierCurve.getPointRef(i);
Point3f p1 = bezierCurve.getPointRef(i+1);
//System.out.println(p0);
int x0 = (int)p0.x;
int y0 = (int)p0.y;
int x1 = (int)p1.x;
int y1 = (int)p1.y;
g.drawLine(x0,y0,x1,y1);
g.drawLine(x0-2,y0-2,x0+2,y0+2);
g.drawLine(x0-2,y0+2,x0+2,y0-2);
}
g.setColor(Color.GRAY);
for (int i=0; i<bezierCurve.getNumPoints(); i++)
{
Point3f p0 = bezierCurve.getPointRef(i);
Vector3f t0 = bezierCurve.getTangentRef(i);
int x0 = (int)p0.x;
int y0 = (int)p0.y;
int x1 = (int)(p0.x + t0.x);
int y1 = (int)(p0.y + t0.y);
g.drawLine(x0,y0,x1,y1);
}
}
public void mousePressed(MouseEvent e)
{
for (int i=0; i<bezierCurves.size(); i++)
{
BezierCurve bezierCurve = bezierCurves.get(i);
for (int j=0; j<bezierCurve.getNumControlPoints(); j++)
{
Point3f p0 = bezierCurve.getControlPointRef(j);
int x0 = (int)p0.x;
int y0 = (int)p0.y;
Point p = new Point(x0,y0);
float dist = (float)e.getPoint().distance(p);
if (dist < 4)
{
draggedPoint = p0;
return;
}
}
}
}
public void mouseReleased(MouseEvent e)
{
draggedPoint = null;
}
public void mouseClicked(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mouseMoved(MouseEvent e)
{
}
public void mouseDragged(MouseEvent e)
{
if (draggedPoint == null)
{
return;
}
draggedPoint.x = e.getX();
draggedPoint.y = e.getY();
for (int i=0; i<bezierCurves.size(); i++)
{
BezierCurve bezierCurve = bezierCurves.get(i);
bezierCurve.update();
}
repaint();
}
}
class Tuple3f
{
float x;
float y;
float z;
public void interpolate(Tuple3f t0, Tuple3f t1, float alpha)
{
float dx = t1.x-t0.x;
float dy = t1.y-t0.y;
float dz = t1.z-t0.z;
x = t0.x + dx * alpha;
y = t0.y + dy * alpha;
z = t0.z + dz * alpha;
}
public void set(Tuple3f that)
{
this.x = that.x;
this.y = that.y;
this.z = that.z;
}
public void sub(Tuple3f t0, Tuple3f t1)
{
this.x = t0.x-t1.x;
this.y = t0.y-t1.y;
this.z = t0.z-t1.z;
}
public void scale(float factor)
{
this.x *= factor;
this.y *= factor;
this.z *= factor;
}
public void subX(Tuple3f that)
{
this.x -= that.x;
this.y -= that.y;
this.z -= that.z;
}
}
class Point3f extends Tuple3f
{
public Point3f()
{
}
public Point3f(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
class Vector3f extends Tuple3f
{
}