package org.newdawn.slick.geom;
import java.util.ArrayList;
/**
* A shape built from lines and curves. Hole support is present but
* restricted.
*
* @author kevin
*/
public class Path extends Shape {
/** The local list of points */
private ArrayList localPoints = new ArrayList();
/** The current x coordinate */
private float cx;
/** The current y coordiante */
private float cy;
/** True if the path has been closed */
private boolean closed;
/** The list of holes placed */
private ArrayList holes = new ArrayList();
/** The current hole being built */
private ArrayList hole;
/**
* Create a new path
*
* @param sx The start x coordinate of the path
* @param sy The start y coordiante of the path
*/
public Path(float sx, float sy) {
localPoints.add(new float[] {sx,sy});
cx = sx;
cy = sy;
pointsDirty = true;
}
/**
* Start building a hole in the previously defined contour
*
* @param sx The start point of the hole
* @param sy The start point of the hole
*/
public void startHole(float sx, float sy) {
hole = new ArrayList();
holes.add(hole);
}
/**
* Add a line to the contour or hole which ends at the specified
* location.
*
* @param x The x coordinate to draw the line to
* @param y The y coordiante to draw the line to
*/
public void lineTo(float x, float y) {
if (hole != null) {
hole.add(new float[] {x,y});
} else {
localPoints.add(new float[] {x,y});
}
cx = x;
cy = y;
pointsDirty = true;
}
/**
* Close the path to form a polygon
*/
public void close() {
closed = true;
}
/**
* Add a curve to the specified location (using the default segments 10)
*
* @param x The destination x coordinate
* @param y The destination y coordiante
* @param cx1 The x coordiante of the first control point
* @param cy1 The y coordiante of the first control point
* @param cx2 The x coordinate of the second control point
* @param cy2 The y coordinate of the second control point
*/
public void curveTo(float x, float y, float cx1, float cy1, float cx2, float cy2) {
curveTo(x,y,cx1,cy1,cx2,cy2,10);
}
/**
* Add a curve to the specified location (specifing the number of segments)
*
* @param x The destination x coordinate
* @param y The destination y coordiante
* @param cx1 The x coordiante of the first control point
* @param cy1 The y coordiante of the first control point
* @param cx2 The x coordinate of the second control point
* @param cy2 The y coordinate of the second control point
* @param segments The number of segments to use for the new curve
*/
public void curveTo(float x, float y, float cx1, float cy1, float cx2, float cy2, int segments) {
// special case for zero movement
if ((cx == x) && (cy == y)) {
return;
}
Curve curve = new Curve(new Vector2f(cx,cy),new Vector2f(cx1,cy1),new Vector2f(cx2,cy2),new Vector2f(x,y));
float step = 1.0f / segments;
for (int i=1;i<segments+1;i++) {
float t = i * step;
Vector2f p = curve.pointAt(t);
if (hole != null) {
hole.add(new float[] {p.x,p.y});
} else {
localPoints.add(new float[] {p.x,p.y});
}
cx = p.x;
cy = p.y;
}
pointsDirty = true;
}
/**
* @see org.newdawn.slick.geom.Shape#createPoints()
*/
protected void createPoints() {
points = new float[localPoints.size() * 2];
for (int i=0;i<localPoints.size();i++) {
float[] p = (float[]) localPoints.get(i);
points[(i*2)] = p[0];
points[(i*2)+1] = p[1];
}
}
/**
* @see org.newdawn.slick.geom.Shape#transform(org.newdawn.slick.geom.Transform)
*/
public Shape transform(Transform transform) {
Path p = new Path(cx,cy);
p.localPoints = transform(localPoints, transform);
for (int i=0;i<holes.size();i++) {
p.holes.add(transform((ArrayList) holes.get(i), transform));
}
p.closed = this.closed;
return p;
}
/**
* Transform a list of points
*
* @param pts The pts to transform
* @param t The transform to apply
* @return The transformed points
*/
private ArrayList transform(ArrayList pts, Transform t) {
float[] in = new float[pts.size()*2];
float[] out = new float[pts.size()*2];
for (int i=0;i<pts.size();i++) {
in[i*2] = ((float[]) pts.get(i))[0];
in[(i*2)+1] = ((float[]) pts.get(i))[1];
}
t.transform(in, 0, out, 0, pts.size());
ArrayList outList = new ArrayList();
for (int i=0;i<pts.size();i++) {
outList.add(new float[] {out[(i*2)],out[(i*2)+1]});
}
return outList;
}
// /**
// * Calculate the triangles that can fill this shape
// */
// protected void calculateTriangles() {
// if (!trianglesDirty) {
// return;
// }
// if (points.length >= 6) {
// boolean clockwise = true;
// float area = 0;
// for (int i=0;i<(points.length/2)-1;i++) {
// float x1 = points[(i*2)];
// float y1 = points[(i*2)+1];
// float x2 = points[(i*2)+2];
// float y2 = points[(i*2)+3];
//
// area += (x1 * y2) - (y1 * x2);
// }
// area /= 2;
// clockwise = area > 0;
//
// if (clockwise) {
// tris = new MannTriangulator();
// for (int i=0;i<points.length;i+=2) {
// tris.addPolyPoint(points[i], points[i+1]);
// }
//
// for (int h=0;h<holes.size();h++) {
// ArrayList hole = (ArrayList) holes.get(h);
// tris.startHole();
// for (int i=0;i<hole.size();i++) {
// float[] pt = (float[]) hole.get(i);
// tris.addPolyPoint(pt[0],pt[1]);
// }
// }
// tris.triangulate();
// } else {
// tris = new MannTriangulator();
// for (int i=points.length-2;i>=0;i-=2) {
// tris.addPolyPoint(points[i], points[i+1]);
// }
//
// for (int h=0;h<holes.size();h++) {
// ArrayList hole = (ArrayList) holes.get(h);
// tris.startHole();
// for (int i=hole.size()-1;i>=0;i--) {
// float[] pt = (float[]) hole.get(i);
// tris.addPolyPoint(pt[0],pt[1]);
// }
// }
// tris.triangulate();
// }
//
// } else {
// tris.triangulate();
// }
//
// trianglesDirty = false;
// }
/**
* True if this is a closed shape
*
* @return True if this is a closed shape
*/
public boolean closed() {
return closed;
}
}