mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-13 23:26:07 +09:00
added sources for Slick
Former-commit-id: 1647fa32ef6894bd7db44f741f07c2f4dcdf9054 Former-commit-id: 0e5810dcfbe1fd59b13e7cabe9f1e93c5542da2d
This commit is contained in:
449
lib/slick-source/org/newdawn/slick/geom/GeomUtil.java
Normal file
449
lib/slick-source/org/newdawn/slick/geom/GeomUtil.java
Normal file
@@ -0,0 +1,449 @@
|
||||
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<target.getPointCount();i++) {
|
||||
if (missing.contains(target.getPoint(i)[0], target.getPoint(i)[1])) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == target.getPointCount()) {
|
||||
return new Shape[0];
|
||||
}
|
||||
|
||||
if (!target.intersects(missing)) {
|
||||
return new Shape[] {target};
|
||||
}
|
||||
|
||||
int found = 0;
|
||||
for (int i=0;i<missing.getPointCount();i++) {
|
||||
if (target.contains(missing.getPoint(i)[0], missing.getPoint(i)[1])) {
|
||||
if (!onPath(target, missing.getPoint(i)[0], missing.getPoint(i)[1])) {
|
||||
found++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i=0;i<target.getPointCount();i++) {
|
||||
if (missing.contains(target.getPoint(i)[0], target.getPoint(i)[1])) {
|
||||
if (!onPath(missing, target.getPoint(i)[0], target.getPoint(i)[1]))
|
||||
{
|
||||
found++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found < 1) {
|
||||
return new Shape[] {target};
|
||||
}
|
||||
|
||||
return combine(target, missing, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given point is on the path
|
||||
*
|
||||
* @param path The path to check
|
||||
* @param x The x coordinate of the point to check
|
||||
* @param y The y coordiante of teh point to check
|
||||
* @return True if the point is on the path
|
||||
*/
|
||||
private boolean onPath(Shape path, float x, float y) {
|
||||
for (int i=0;i<path.getPointCount()+1;i++) {
|
||||
int n = rationalPoint(path, i+1);
|
||||
Line line = getLine(path, rationalPoint(path, i), n);
|
||||
if (line.distance(new Vector2f(x,y)) < EPSILON*100) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener to be notified of geometry based operations
|
||||
*
|
||||
* @param listener The listener to be notified of geometry based operations
|
||||
*/
|
||||
public void setListener(GeomUtilListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join to shapes together. Note that the shapes must be touching
|
||||
* for this method to work.
|
||||
*
|
||||
* @param target The target shape to union with
|
||||
* @param other The additional shape to union
|
||||
* @return The newly created shapes
|
||||
*/
|
||||
public Shape[] union(Shape target, Shape other) {
|
||||
target = target.transform(new Transform());
|
||||
other = other.transform(new Transform());
|
||||
|
||||
if (!target.intersects(other)) {
|
||||
return new Shape[] {target, other};
|
||||
}
|
||||
|
||||
// handle the case where intersects is true but really we're talking
|
||||
// about edge points
|
||||
boolean touches = false;
|
||||
int buttCount = 0;
|
||||
for (int i=0;i<target.getPointCount();i++) {
|
||||
if (other.contains(target.getPoint(i)[0], target.getPoint(i)[1])) {
|
||||
if (!other.hasVertex(target.getPoint(i)[0], target.getPoint(i)[1])) {
|
||||
touches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (other.hasVertex(target.getPoint(i)[0], target.getPoint(i)[1])) {
|
||||
buttCount++;
|
||||
}
|
||||
}
|
||||
for (int i=0;i<other.getPointCount();i++) {
|
||||
if (target.contains(other.getPoint(i)[0], other.getPoint(i)[1])) {
|
||||
if (!target.hasVertex(other.getPoint(i)[0], other.getPoint(i)[1])) {
|
||||
touches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((!touches) && (buttCount < 2)) {
|
||||
return new Shape[] {target, other};
|
||||
}
|
||||
|
||||
// so they are definitely touching, consider the union
|
||||
return combine(target, other, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the combination
|
||||
*
|
||||
* @param target The target shape we're updating
|
||||
* @param other The other shape in the operation
|
||||
* @param subtract True if it's a subtract operation, otherwise it's union
|
||||
* @return The set of shapes produced
|
||||
*/
|
||||
private Shape[] combine(Shape target, Shape other, boolean subtract) {
|
||||
if (subtract) {
|
||||
ArrayList shapes = new ArrayList();
|
||||
ArrayList used = new ArrayList();
|
||||
|
||||
// remove any points that are contianed in the shape we're removing, these
|
||||
// are implicitly used
|
||||
for (int i=0;i<target.getPointCount();i++) {
|
||||
float[] point = target.getPoint(i);
|
||||
if (other.contains(point[0], point[1])) {
|
||||
used.add(new Vector2f(point[0], point[1]));
|
||||
if (listener != null) {
|
||||
listener.pointExcluded(point[0], point[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0;i<target.getPointCount();i++) {
|
||||
float[] point = target.getPoint(i);
|
||||
Vector2f pt = new Vector2f(point[0], point[1]);
|
||||
|
||||
if (!used.contains(pt)) {
|
||||
Shape result = combineSingle(target, other, true, i);
|
||||
shapes.add(result);
|
||||
for (int j=0;j<result.getPointCount();j++) {
|
||||
float[] kpoint = result.getPoint(j);
|
||||
Vector2f kpt = new Vector2f(kpoint[0], kpoint[1]);
|
||||
used.add(kpt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (Shape[]) shapes.toArray(new Shape[0]);
|
||||
} else {
|
||||
for (int i=0;i<target.getPointCount();i++) {
|
||||
if (!other.contains(target.getPoint(i)[0], target.getPoint(i)[1])) {
|
||||
if (!other.hasVertex(target.getPoint(i)[0], target.getPoint(i)[1])) {
|
||||
Shape shape = combineSingle(target, other, false, i);
|
||||
return new Shape[] {shape};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Shape[] {other};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine two shapes
|
||||
*
|
||||
* @param target The target shape
|
||||
* @param missing The second shape to apply
|
||||
* @param subtract True if we should subtract missing from target, otherwise union
|
||||
* @param start The point to start at
|
||||
* @return The newly created shape
|
||||
*/
|
||||
private Shape combineSingle(Shape target, Shape missing, boolean subtract, int start) {
|
||||
Shape current = target;
|
||||
Shape other = missing;
|
||||
int point = start;
|
||||
int dir = 1;
|
||||
|
||||
Polygon poly = new Polygon();
|
||||
boolean first = true;
|
||||
|
||||
int loop = 0;
|
||||
|
||||
// while we've not reached the same point
|
||||
float px = current.getPoint(point)[0];
|
||||
float py = current.getPoint(point)[1];
|
||||
|
||||
while (!poly.hasVertex(px, py) || (first) || (current != target)) {
|
||||
first = false;
|
||||
loop++;
|
||||
if (loop > 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<shape.getPointCount();i++) {
|
||||
int next = rationalPoint(shape, i+1);
|
||||
Line local = getLine(shape, i, next);
|
||||
|
||||
Vector2f pt = line.intersect(local, true);
|
||||
if (pt != null) {
|
||||
float newDis = pt.distance(line.getStart());
|
||||
if ((newDis < distance) && (newDis > 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user