package org.newdawn.slick.geom; import java.util.ArrayList; /** * A set of utilities to play with geometry * * @author kevin */ public class GeomUtil { /** The tolerance for determining changes and steps */ public float EPSILON = 0.0001f; /** The tolerance for determining direction change */ public float EDGE_SCALE = 1f; /** The maximum number of points returned by an operation - prevents full lockups */ public int MAX_POINTS = 10000; /** The listener to notify of operations */ public GeomUtilListener listener; /** * Subtract one shape from another - note this is experimental and doesn't * currently handle islands * * @param target The target to be subtracted from * @param missing The shape to subtract * @return The newly created shapes */ public Shape[] subtract(Shape target, Shape missing) { target = target.transform(new Transform()); missing = missing.transform(new Transform()); int count = 0; for (int i=0;i MAX_POINTS) { break; } // add the current point to the result shape poly.addPoint(px,py); if (listener != null) { listener.pointUsed(px,py); } // if the line between the current point and the next one intersect the // other shape work out where on the other shape and start traversing it's // path instead Line line = getLine(current, px, py, rationalPoint(current, point+dir)); HitResult hit = intersect(other, line); if (hit != null) { Line hitLine = hit.line; Vector2f pt = hit.pt; px = pt.x; py = pt.y; if (listener != null) { listener.pointIntersected(px,py); } if (other.hasVertex(px, py)) { point = other.indexOf(pt.x,pt.y); dir = 1; px = pt.x; py = pt.y; Shape temp = current; current = other; other = temp; continue; } float dx = hitLine.getDX() / hitLine.length(); float dy = hitLine.getDY() / hitLine.length(); dx *= EDGE_SCALE; dy *= EDGE_SCALE; if (current.contains(pt.x + dx, pt.y + dy)) { // the point is the next one, we need to take the first and traverse // the path backwards if (subtract) { if (current == missing) { point = hit.p2; dir = -1; } else { point = hit.p1; dir = 1; } } else { if (current == target) { point = hit.p2; dir = -1; } else { point = hit.p2; dir = -1; } } // swap the shapes over, we'll traverse the other one Shape temp = current; current = other; other = temp; } else if (current.contains(pt.x - dx, pt.y - dy)) { if (subtract) { if (current == target) { point = hit.p2; dir = -1; } else { point = hit.p1; dir = 1; } } else { if (current == missing) { point = hit.p1; dir = 1; } else { point = hit.p1; dir = 1; } } // swap the shapes over, we'll traverse the other one Shape temp = current; current = other; other = temp; } else { // give up if (subtract) { break; } else { point = hit.p1; dir = 1; Shape temp = current; current = other; other = temp; point = rationalPoint(current, point+dir); px = current.getPoint(point)[0]; py = current.getPoint(point)[1]; } } } else { // otherwise just move to the next point in the current shape point = rationalPoint(current, point+dir); px = current.getPoint(point)[0]; py = current.getPoint(point)[1]; } } poly.addPoint(px,py); if (listener != null) { listener.pointUsed(px,py); } return poly; } /** * Intersect a line with a shape * * @param shape The shape to compare * @param line The line to intersect against the shape * @return The result describing the intersection or null if none */ public HitResult intersect(Shape shape, Line line) { float distance = Float.MAX_VALUE; HitResult hit = null; for (int i=0;i EPSILON)) { hit = new HitResult(); hit.pt = pt; hit.line = local; hit.p1 = i; hit.p2 = next; distance = newDis; } } } return hit; } /** * Rationalise a point in terms of a given shape * * @param shape The shape * @param p The index of the point * @return The index that is rational for the shape */ public static int rationalPoint(Shape shape, int p) { while (p < 0) { p += shape.getPointCount(); } while (p >= shape.getPointCount()) { p -= shape.getPointCount(); } return p; } /** * Get a line between two points in a shape * * @param shape The shape * @param s The index of the start point * @param e The index of the end point * @return The line between the two points */ public Line getLine(Shape shape, int s, int e) { float[] start = shape.getPoint(s); float[] end = shape.getPoint(e); Line line = new Line(start[0],start[1],end[0],end[1]); return line; } /** * Get a line between two points in a shape * * @param shape The shape * @param sx The x coordinate of the start point * @param sy The y coordinate of the start point * @param e The index of the end point * @return The line between the two points */ public Line getLine(Shape shape, float sx, float sy, int e) { float[] end = shape.getPoint(e); Line line = new Line(sx,sy,end[0],end[1]); return line; } /** * A lightweigtht description of a intersection between a shape and * line. */ public class HitResult { /** The line on the target shape that intersected */ public Line line; /** The index of the first point on the target shape that forms the line */ public int p1; /** The index of the second point on the target shape that forms the line */ public int p2; /** The position of the intersection */ public Vector2f pt; } }