mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-14 04:24:05 +09:00
added sources for Slick
Former-commit-id: 1647fa32ef6894bd7db44f741f07c2f4dcdf9054 Former-commit-id: 0e5810dcfbe1fd59b13e7cabe9f1e93c5542da2d
This commit is contained in:
437
lib/slick-source/org/newdawn/slick/geom/BasicTriangulator.java
Normal file
437
lib/slick-source/org/newdawn/slick/geom/BasicTriangulator.java
Normal file
@@ -0,0 +1,437 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Triangulates a polygon into triangles - duh. Doesn't handle
|
||||
* holes in polys
|
||||
*
|
||||
* @author Based on Public Source from FlipCode
|
||||
*/
|
||||
public class BasicTriangulator implements Triangulator {
|
||||
/** The accepted error value */
|
||||
private static final float EPSILON = 0.0000000001f;
|
||||
/** The list of points to be triangulated */
|
||||
private PointList poly = new PointList();
|
||||
/** The list of points describing the triangles */
|
||||
private PointList tris = new PointList();
|
||||
/** True if we've tried to triangulate */
|
||||
private boolean tried;
|
||||
|
||||
/**
|
||||
* Create a new triangulator
|
||||
*/
|
||||
public BasicTriangulator() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a point describing the polygon to be triangulated
|
||||
*
|
||||
* @param x The x coordinate of the point
|
||||
* @param y the y coordinate of the point
|
||||
*/
|
||||
public void addPolyPoint(float x, float y) {
|
||||
Point p = new Point(x,y);
|
||||
if (!poly.contains(p)) {
|
||||
poly.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of points in the polygon
|
||||
*
|
||||
* @return The number of points in the polygon
|
||||
*/
|
||||
public int getPolyPointCount() {
|
||||
return poly.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the coordinates of the point at the specified index
|
||||
*
|
||||
* @param index The index of the point to retrieve
|
||||
* @return The oordinates of the point at the specified index
|
||||
*/
|
||||
public float[] getPolyPoint(int index) {
|
||||
return new float[] {poly.get(index).x,poly.get(index).y};
|
||||
}
|
||||
|
||||
/**
|
||||
* Cause the triangulator to split the polygon
|
||||
*
|
||||
* @return True if we managed the task
|
||||
*/
|
||||
public boolean triangulate() {
|
||||
tried = true;
|
||||
|
||||
boolean worked = process(poly,tris);
|
||||
return worked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a count of the number of triangles produced
|
||||
*
|
||||
* @return The number of triangles produced
|
||||
*/
|
||||
public int getTriangleCount() {
|
||||
if (!tried) {
|
||||
throw new RuntimeException("Call triangulate() before accessing triangles");
|
||||
}
|
||||
return tris.size() / 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a point on a specified generated triangle
|
||||
*
|
||||
* @param tri The index of the triangle to interegate
|
||||
* @param i The index of the point within the triangle to retrieve
|
||||
* (0 - 2)
|
||||
* @return The x,y coordinate pair for the point
|
||||
*/
|
||||
public float[] getTrianglePoint(int tri, int i) {
|
||||
if (!tried) {
|
||||
throw new RuntimeException("Call triangulate() before accessing triangles");
|
||||
}
|
||||
|
||||
return tris.get((tri*3)+i).toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the area of a polygon defined by the series of points
|
||||
* in the list
|
||||
*
|
||||
* @param contour The list of points defined the contour of the polygon
|
||||
* (Vector2f)
|
||||
* @return The area of the polygon defined
|
||||
*/
|
||||
private float area(PointList contour) {
|
||||
int n = contour.size();
|
||||
|
||||
float A = 0.0f;
|
||||
|
||||
for (int p = n - 1, q = 0; q < n; p = q++) {
|
||||
Point contourP = contour.get(p);
|
||||
Point contourQ = contour.get(q);
|
||||
|
||||
A += contourP.getX() * contourQ.getY() - contourQ.getX()
|
||||
* contourP.getY();
|
||||
}
|
||||
return A * 0.5f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the point P is inside the triangle defined by
|
||||
* the points A,B,C
|
||||
*
|
||||
* @param Ax Point A x-coordinate
|
||||
* @param Ay Point A y-coordinate
|
||||
* @param Bx Point B x-coordinate
|
||||
* @param By Point B y-coordinate
|
||||
* @param Cx Point C x-coordinate
|
||||
* @param Cy Point C y-coordinate
|
||||
* @param Px Point P x-coordinate
|
||||
* @param Py Point P y-coordinate
|
||||
* @return True if the point specified is within the triangle
|
||||
*/
|
||||
private boolean insideTriangle(float Ax, float Ay, float Bx,
|
||||
float By, float Cx, float Cy, float Px, float Py) {
|
||||
float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
|
||||
float cCROSSap, bCROSScp, aCROSSbp;
|
||||
|
||||
ax = Cx - Bx;
|
||||
ay = Cy - By;
|
||||
bx = Ax - Cx;
|
||||
by = Ay - Cy;
|
||||
cx = Bx - Ax;
|
||||
cy = By - Ay;
|
||||
apx = Px - Ax;
|
||||
apy = Py - Ay;
|
||||
bpx = Px - Bx;
|
||||
bpy = Py - By;
|
||||
cpx = Px - Cx;
|
||||
cpy = Py - Cy;
|
||||
|
||||
aCROSSbp = ax * bpy - ay * bpx;
|
||||
cCROSSap = cx * apy - cy * apx;
|
||||
bCROSScp = bx * cpy - by * cpx;
|
||||
|
||||
return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cut a the contour and add a triangle into V to describe the
|
||||
* location of the cut
|
||||
*
|
||||
* @param contour The list of points defining the polygon
|
||||
* @param u The index of the first point
|
||||
* @param v The index of the second point
|
||||
* @param w The index of the third point
|
||||
* @param n ?
|
||||
* @param V The array to populate with indicies of triangles
|
||||
* @return True if a triangle was found
|
||||
*/
|
||||
private boolean snip(PointList contour, int u, int v, int w, int n,
|
||||
int[] V) {
|
||||
int p;
|
||||
float Ax, Ay, Bx, By, Cx, Cy, Px, Py;
|
||||
|
||||
Ax = contour.get(V[u]).getX();
|
||||
Ay = contour.get(V[u]).getY();
|
||||
|
||||
Bx = contour.get(V[v]).getX();
|
||||
By = contour.get(V[v]).getY();
|
||||
|
||||
Cx = contour.get(V[w]).getX();
|
||||
Cy = contour.get(V[w]).getY();
|
||||
|
||||
if (EPSILON > (((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (p = 0; p < n; p++) {
|
||||
if ((p == u) || (p == v) || (p == w)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Px = contour.get(V[p]).getX();
|
||||
Py = contour.get(V[p]).getY();
|
||||
|
||||
if (insideTriangle(Ax, Ay, Bx, By, Cx, Cy, Px, Py)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a list of points defining a polygon
|
||||
* @param contour The list of points describing the polygon
|
||||
* @param result The list of points describing the triangles. Groups
|
||||
* of 3 describe each triangle
|
||||
*
|
||||
* @return True if we succeeded in completing triangulation
|
||||
*/
|
||||
private boolean process(PointList contour, PointList result) {
|
||||
result.clear();
|
||||
|
||||
/* allocate and initialize list of Vertices in polygon */
|
||||
|
||||
int n = contour.size();
|
||||
if (n < 3)
|
||||
return false;
|
||||
|
||||
int[] V = new int[n];
|
||||
|
||||
/* we want a counter-clockwise polygon in V */
|
||||
|
||||
if (0.0f < area(contour)) {
|
||||
for (int v = 0; v < n; v++)
|
||||
V[v] = v;
|
||||
} else {
|
||||
for (int v = 0; v < n; v++)
|
||||
V[v] = (n - 1) - v;
|
||||
}
|
||||
|
||||
int nv = n;
|
||||
|
||||
/* remove nv-2 Vertices, creating 1 triangle every time */
|
||||
int count = 2 * nv; /* error detection */
|
||||
|
||||
for (int m = 0, v = nv - 1; nv > 2;) {
|
||||
/* if we loop, it is probably a non-simple polygon */
|
||||
if (0 >= (count--)) {
|
||||
//** Triangulator4: ERROR - probable bad polygon!
|
||||
return false;
|
||||
}
|
||||
|
||||
/* three consecutive vertices in current polygon, <u,v,w> */
|
||||
int u = v;
|
||||
if (nv <= u)
|
||||
u = 0; /* previous */
|
||||
v = u + 1;
|
||||
if (nv <= v)
|
||||
v = 0; /* new v */
|
||||
int w = v + 1;
|
||||
if (nv <= w)
|
||||
w = 0; /* next */
|
||||
|
||||
if (snip(contour, u, v, w, nv, V)) {
|
||||
int a, b, c, s, t;
|
||||
|
||||
/* true names of the vertices */
|
||||
a = V[u];
|
||||
b = V[v];
|
||||
c = V[w];
|
||||
|
||||
/* output Triangle */
|
||||
result.add(contour.get(a));
|
||||
result.add(contour.get(b));
|
||||
result.add(contour.get(c));
|
||||
|
||||
m++;
|
||||
|
||||
/* remove v from remaining polygon */
|
||||
for (s = v, t = v + 1; t < nv; s++, t++) {
|
||||
V[s] = V[t];
|
||||
}
|
||||
nv--;
|
||||
|
||||
/* resest error detection counter */
|
||||
count = 2 * nv;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A single point handled by the triangulator
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
private class Point {
|
||||
/** The x coorindate of this point */
|
||||
private float x;
|
||||
/** The y coorindate of this point */
|
||||
private float y;
|
||||
/** The points in an array */
|
||||
private float[] array;
|
||||
|
||||
/**
|
||||
* Create a new point
|
||||
*
|
||||
* @param x The x coordindate of the point
|
||||
* @param y The y coordindate of the point
|
||||
*/
|
||||
public Point(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
array = new float[] {x,y};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the x coordinate of the point
|
||||
*
|
||||
* @return The x coordinate of the point
|
||||
*/
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the y coordinate of the point
|
||||
*
|
||||
* @return The y coordinate of the point
|
||||
*/
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this point into a float array
|
||||
*
|
||||
* @return The contents of this point as a float array
|
||||
*/
|
||||
public float[] toArray() {
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return (int) (x * y * 31);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof Point) {
|
||||
Point p = (Point) other;
|
||||
return (p.x == x) && (p.y == y);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of type <code>Point</code>
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
private class PointList {
|
||||
/** The list of points */
|
||||
private ArrayList points = new ArrayList();
|
||||
|
||||
/**
|
||||
* Create a new empty list
|
||||
*/
|
||||
public PointList() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the list contains a point
|
||||
*
|
||||
* @param p The point to look for
|
||||
* @return True if the point is in the list
|
||||
*/
|
||||
public boolean contains(Point p) {
|
||||
return points.contains(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a point to the list
|
||||
*
|
||||
* @param point The point to add
|
||||
*/
|
||||
public void add(Point point) {
|
||||
points.add(point);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a point from the list
|
||||
*
|
||||
* @param point The point to remove
|
||||
*/
|
||||
public void remove(Point point) {
|
||||
points.remove(point);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the list
|
||||
*
|
||||
* @return The size of the list
|
||||
*/
|
||||
public int size() {
|
||||
return points.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a point a specific index in the list
|
||||
*
|
||||
* @param i The index of the point to retrieve
|
||||
* @return The point
|
||||
*/
|
||||
public Point get(int i) {
|
||||
return (Point) points.get(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the list
|
||||
*/
|
||||
public void clear() {
|
||||
points.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Triangulator#startHole()
|
||||
*/
|
||||
public void startHole() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
}
|
||||
234
lib/slick-source/org/newdawn/slick/geom/Circle.java
Normal file
234
lib/slick-source/org/newdawn/slick/geom/Circle.java
Normal file
@@ -0,0 +1,234 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
/**
|
||||
* A simple Circle geometry
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public strictfp class Circle extends Ellipse {
|
||||
/** The radius of the circle */
|
||||
public float radius;
|
||||
|
||||
/**
|
||||
* Create a new circle based on its radius
|
||||
*
|
||||
* @param centerPointX The x location of the center of the circle
|
||||
* @param centerPointY The y location of the center of the circle
|
||||
* @param radius The radius of the circle
|
||||
*/
|
||||
public Circle(float centerPointX, float centerPointY, float radius) {
|
||||
this(centerPointX, centerPointY, radius, DEFAULT_SEGMENT_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new circle based on its radius
|
||||
*
|
||||
* @param centerPointX The x location of the center of the circle
|
||||
* @param centerPointY The y location of the center of the circle
|
||||
* @param radius The radius of the circle
|
||||
* @param segmentCount The number of segments to build the circle out of
|
||||
*/
|
||||
public Circle(float centerPointX, float centerPointY, float radius, int segmentCount) {
|
||||
super(centerPointX, centerPointY, radius, radius, segmentCount);
|
||||
this.x = centerPointX - radius;
|
||||
this.y = centerPointY - radius;
|
||||
this.radius = radius;
|
||||
boundingCircleRadius = radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the x coordinate of the centre of the circle
|
||||
*
|
||||
* @return The x coordinate of the centre of the circle
|
||||
*/
|
||||
public float getCenterX() {
|
||||
return getX() + radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the y coordinate of the centre of the circle
|
||||
*
|
||||
* @return The y coordinate of the centre of the circle
|
||||
*/
|
||||
public float getCenterY() {
|
||||
return getY() + radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the coordinates of the center of the circle
|
||||
*
|
||||
* @return 2-element array with the center of the circle.
|
||||
*/
|
||||
@Override
|
||||
public float[] getCenter() {
|
||||
return new float[] { getCenterX(), getCenterY() };
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the radius of this circle
|
||||
*
|
||||
* @param radius The radius of this circle
|
||||
*/
|
||||
public void setRadius(float radius) {
|
||||
if (radius != this.radius) {
|
||||
pointsDirty = true;
|
||||
this.radius = radius;
|
||||
setRadii(radius, radius);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the radius of the circle
|
||||
*
|
||||
* @return The radius of the circle
|
||||
*/
|
||||
public float getRadius() {
|
||||
return radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this circle touches another
|
||||
*
|
||||
* @param shape The other circle
|
||||
* @return True if they touch
|
||||
*/
|
||||
public boolean intersects(Shape shape) {
|
||||
if(shape instanceof Circle) {
|
||||
Circle other = (Circle)shape;
|
||||
float totalRad2 = getRadius() + other.getRadius();
|
||||
|
||||
if (Math.abs(other.getCenterX() - getCenterX()) > totalRad2) {
|
||||
return false;
|
||||
}
|
||||
if (Math.abs(other.getCenterY() - getCenterY()) > totalRad2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
totalRad2 *= totalRad2;
|
||||
|
||||
float dx = Math.abs(other.getCenterX() - getCenterX());
|
||||
float dy = Math.abs(other.getCenterY() - getCenterY());
|
||||
|
||||
return totalRad2 >= ((dx*dx) + (dy*dy));
|
||||
}
|
||||
else if(shape instanceof Rectangle) {
|
||||
return intersects((Rectangle)shape);
|
||||
}
|
||||
else {
|
||||
return super.intersects(shape);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a point is contained by this circle
|
||||
*
|
||||
* @param x The x coordinate of the point to check
|
||||
* @param y The y coorindate of the point to check
|
||||
* @return True if the point is contained by this circle
|
||||
*/
|
||||
public boolean contains(float x, float y)
|
||||
{
|
||||
float xDelta = x - getCenterX(), yDelta = y - getCenterY();
|
||||
return xDelta * xDelta + yDelta * yDelta < getRadius() * getRadius();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if circle contains the line
|
||||
* @param line Line to check against
|
||||
* @return True if line inside circle
|
||||
*/
|
||||
private boolean contains(Line line) {
|
||||
return contains(line.getX1(), line.getY1()) && contains(line.getX2(), line.getY2());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Ellipse#findCenter()
|
||||
*/
|
||||
protected void findCenter() {
|
||||
center = new float[2];
|
||||
center[0] = x + radius;
|
||||
center[1] = y + radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Ellipse#calculateRadius()
|
||||
*/
|
||||
protected void calculateRadius() {
|
||||
boundingCircleRadius = radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this circle touches a rectangle
|
||||
*
|
||||
* @param other The rectangle to check against
|
||||
* @return True if they touch
|
||||
*/
|
||||
private boolean intersects(Rectangle other) {
|
||||
Rectangle box = other;
|
||||
Circle circle = this;
|
||||
|
||||
if (box.contains(x+radius,y+radius)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
float x1 = box.getX();
|
||||
float y1 = box.getY();
|
||||
float x2 = box.getX() + box.getWidth();
|
||||
float y2 = box.getY() + box.getHeight();
|
||||
|
||||
Line[] lines = new Line[4];
|
||||
lines[0] = new Line(x1,y1,x2,y1);
|
||||
lines[1] = new Line(x2,y1,x2,y2);
|
||||
lines[2] = new Line(x2,y2,x1,y2);
|
||||
lines[3] = new Line(x1,y2,x1,y1);
|
||||
|
||||
float r2 = circle.getRadius() * circle.getRadius();
|
||||
|
||||
Vector2f pos = new Vector2f(circle.getCenterX(), circle.getCenterY());
|
||||
|
||||
for (int i=0;i<4;i++) {
|
||||
float dis = lines[i].distanceSquared(pos);
|
||||
if (dis < r2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if circle touches a line.
|
||||
* @param other The line to check against
|
||||
* @return True if they touch
|
||||
*/
|
||||
private boolean intersects(Line other) {
|
||||
// put it nicely into vectors
|
||||
Vector2f lineSegmentStart = new Vector2f(other.getX1(), other.getY1());
|
||||
Vector2f lineSegmentEnd = new Vector2f(other.getX2(), other.getY2());
|
||||
Vector2f circleCenter = new Vector2f(getCenterX(), getCenterY());
|
||||
|
||||
// calculate point on line closest to the circle center and then
|
||||
// compare radius to distance to the point for intersection result
|
||||
Vector2f closest;
|
||||
Vector2f segv = lineSegmentEnd.copy().sub(lineSegmentStart);
|
||||
Vector2f ptv = circleCenter.copy().sub(lineSegmentStart);
|
||||
float segvLength = segv.length();
|
||||
float projvl = ptv.dot(segv) / segvLength;
|
||||
if (projvl < 0)
|
||||
{
|
||||
closest = lineSegmentStart;
|
||||
}
|
||||
else if (projvl > segvLength)
|
||||
{
|
||||
closest = lineSegmentEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2f projv = segv.copy().scale(projvl / segvLength);
|
||||
closest = lineSegmentStart.copy().add(projv);
|
||||
}
|
||||
boolean intersects = circleCenter.copy().sub(closest).lengthSquared() <= getRadius()*getRadius();
|
||||
|
||||
return intersects;
|
||||
}
|
||||
}
|
||||
113
lib/slick-source/org/newdawn/slick/geom/Curve.java
Normal file
113
lib/slick-source/org/newdawn/slick/geom/Curve.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
/**
|
||||
* A beizer curve implementation. The curve is defined by a start point, an end point
|
||||
* and two control points that it will tend towards. This is implementation is fixed
|
||||
* segmenting meaning it doesn't scale too well.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class Curve extends Shape {
|
||||
/** The start point of the curve */
|
||||
private Vector2f p1;
|
||||
/** The first control point */
|
||||
private Vector2f c1;
|
||||
/** The second control point */
|
||||
private Vector2f c2;
|
||||
/** The end point of the curve */
|
||||
private Vector2f p2;
|
||||
/** The number of lines segments the curve is built out of */
|
||||
private int segments;
|
||||
|
||||
/**
|
||||
* Create a new curve with the default segments (20)
|
||||
*
|
||||
* @param p1 The start of the curve
|
||||
* @param c1 The first control point
|
||||
* @param c2 The second control point
|
||||
* @param p2 The end of the curve
|
||||
*/
|
||||
public Curve(Vector2f p1, Vector2f c1, Vector2f c2, Vector2f p2) {
|
||||
this(p1,c1,c2,p2,20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new curve
|
||||
*
|
||||
* @param p1 The start of the curve
|
||||
* @param c1 The first control point
|
||||
* @param c2 The second control point
|
||||
* @param p2 The end of the curve
|
||||
* @param segments The number of segments to use
|
||||
*/
|
||||
public Curve(Vector2f p1, Vector2f c1, Vector2f c2, Vector2f p2, int segments) {
|
||||
this.p1 = new Vector2f(p1);
|
||||
this.c1 = new Vector2f(c1);
|
||||
this.c2 = new Vector2f(c2);
|
||||
this.p2 = new Vector2f(p2);
|
||||
|
||||
this.segments = segments;
|
||||
pointsDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the point at a particular location on the curve
|
||||
*
|
||||
* @param t A value between 0 and 1 defining the location of the curve the point is at
|
||||
* @return The point on the curve
|
||||
*/
|
||||
public Vector2f pointAt(float t) {
|
||||
float a = 1 - t;
|
||||
float b = t;
|
||||
|
||||
float f1 = a * a * a;
|
||||
float f2 = 3 * a * a * b;
|
||||
float f3 = 3 * a * b * b;
|
||||
float f4 = b * b * b;
|
||||
|
||||
float nx = (p1.x * f1) + (c1.x * f2) + (c2.x * f3) + (p2.x * f4);
|
||||
float ny = (p1.y * f1) + (c1.y * f2) + (c2.y * f3) + (p2.y * f4);
|
||||
|
||||
return new Vector2f(nx,ny);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#createPoints()
|
||||
*/
|
||||
protected void createPoints() {
|
||||
float step = 1.0f / segments;
|
||||
points = new float[(segments+1) * 2];
|
||||
for (int i=0;i<segments+1;i++) {
|
||||
float t = i * step;
|
||||
|
||||
Vector2f p = pointAt(t);
|
||||
points[i*2] = p.x;
|
||||
points[(i*2)+1] = p.y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#transform(org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public Shape transform(Transform transform) {
|
||||
float[] pts = new float[8];
|
||||
float[] dest = new float[8];
|
||||
pts[0] = p1.x; pts[1] = p1.y;
|
||||
pts[2] = c1.x; pts[3] = c1.y;
|
||||
pts[4] = c2.x; pts[5] = c2.y;
|
||||
pts[6] = p2.x; pts[7] = p2.y;
|
||||
transform.transform(pts, 0, dest, 0, 4);
|
||||
|
||||
return new Curve(new Vector2f(dest[0],dest[1]), new Vector2f(dest[2],dest[3]),
|
||||
new Vector2f(dest[4],dest[5]), new Vector2f(dest[6],dest[7]));
|
||||
}
|
||||
|
||||
/**
|
||||
* True if this is a closed shape
|
||||
*
|
||||
* @return True if this is a closed shape
|
||||
*/
|
||||
public boolean closed() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
196
lib/slick-source/org/newdawn/slick/geom/Ellipse.java
Normal file
196
lib/slick-source/org/newdawn/slick/geom/Ellipse.java
Normal file
@@ -0,0 +1,196 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.newdawn.slick.util.FastTrig;
|
||||
|
||||
/**
|
||||
* An ellipse meeting the <code>Shape</code> contract. The ellipse is actually an approximation using
|
||||
* a series of points generated around the contour of the ellipse.
|
||||
*
|
||||
* @author Mark
|
||||
*/
|
||||
public class Ellipse extends Shape {
|
||||
/**
|
||||
* Default number of segments to draw this ellipse with
|
||||
*/
|
||||
protected static final int DEFAULT_SEGMENT_COUNT = 50;
|
||||
|
||||
/**
|
||||
* The number of segments for graphical representation.
|
||||
*/
|
||||
private int segmentCount;
|
||||
/**
|
||||
* horizontal radius
|
||||
*/
|
||||
private float radius1;
|
||||
/**
|
||||
* vertical radius
|
||||
*/
|
||||
private float radius2;
|
||||
|
||||
/**
|
||||
* Creates a new Ellipse object.
|
||||
*
|
||||
* @param centerPointX x coordinate of the center of the ellipse
|
||||
* @param centerPointY y coordinate of the center of the ellipse
|
||||
* @param radius1 horizontal radius
|
||||
* @param radius2 vertical radius
|
||||
*/
|
||||
public Ellipse(float centerPointX, float centerPointY, float radius1, float radius2) {
|
||||
this(centerPointX, centerPointY, radius1, radius2, DEFAULT_SEGMENT_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Ellipse object.
|
||||
*
|
||||
* @param centerPointX x coordinate of the center of the ellipse
|
||||
* @param centerPointY y coordinate of the center of the ellipse
|
||||
* @param radius1 horizontal radius
|
||||
* @param radius2 vertical radius
|
||||
* @param segmentCount how fine to make the ellipse.
|
||||
*/
|
||||
public Ellipse(float centerPointX, float centerPointY, float radius1, float radius2, int segmentCount) {
|
||||
this.x = centerPointX - radius1;
|
||||
this.y = centerPointY - radius2;
|
||||
this.radius1 = radius1;
|
||||
this.radius2 = radius2;
|
||||
this.segmentCount = segmentCount;
|
||||
checkPoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the shape of this Ellipse
|
||||
*
|
||||
* @param radius1 horizontal radius
|
||||
* @param radius2 vertical radius
|
||||
*/
|
||||
public void setRadii(float radius1, float radius2) {
|
||||
setRadius1(radius1);
|
||||
setRadius2(radius2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the horizontal radius of the ellipse
|
||||
*
|
||||
* @return The horizontal radius of the ellipse
|
||||
*/
|
||||
public float getRadius1() {
|
||||
return radius1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the horizontal radius of the ellipse
|
||||
*
|
||||
* @param radius1 The horizontal radius to set
|
||||
*/
|
||||
public void setRadius1(float radius1) {
|
||||
if (radius1 != this.radius1) {
|
||||
this.radius1 = radius1;
|
||||
pointsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vertical radius of the ellipse
|
||||
*
|
||||
* @return The vertical radius of the ellipse
|
||||
*/
|
||||
public float getRadius2() {
|
||||
return radius2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the vertical radius of the ellipse
|
||||
*
|
||||
* @param radius2 The vertical radius to set
|
||||
*/
|
||||
public void setRadius2(float radius2) {
|
||||
if (radius2 != this.radius2) {
|
||||
this.radius2 = radius2;
|
||||
pointsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the points to outline this ellipse.
|
||||
*
|
||||
*/
|
||||
protected void createPoints() {
|
||||
ArrayList tempPoints = new ArrayList();
|
||||
|
||||
maxX = -Float.MIN_VALUE;
|
||||
maxY = -Float.MIN_VALUE;
|
||||
minX = Float.MAX_VALUE;
|
||||
minY = Float.MAX_VALUE;
|
||||
|
||||
float start = 0;
|
||||
float end = 359;
|
||||
|
||||
float cx = x + radius1;
|
||||
float cy = y + radius2;
|
||||
|
||||
int step = 360 / segmentCount;
|
||||
|
||||
for (float a=start;a<=end+step;a+=step) {
|
||||
float ang = a;
|
||||
if (ang > end) {
|
||||
ang = end;
|
||||
}
|
||||
float newX = (float) (cx + (FastTrig.cos(Math.toRadians(ang)) * radius1));
|
||||
float newY = (float) (cy + (FastTrig.sin(Math.toRadians(ang)) * radius2));
|
||||
|
||||
if(newX > maxX) {
|
||||
maxX = newX;
|
||||
}
|
||||
if(newY > maxY) {
|
||||
maxY = newY;
|
||||
}
|
||||
if(newX < minX) {
|
||||
minX = newX;
|
||||
}
|
||||
if(newY < minY) {
|
||||
minY = newY;
|
||||
}
|
||||
|
||||
tempPoints.add(new Float(newX));
|
||||
tempPoints.add(new Float(newY));
|
||||
}
|
||||
points = new float[tempPoints.size()];
|
||||
for(int i=0;i<points.length;i++) {
|
||||
points[i] = ((Float)tempPoints.get(i)).floatValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#transform(org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public Shape transform(Transform transform) {
|
||||
checkPoints();
|
||||
|
||||
Polygon resultPolygon = new Polygon();
|
||||
|
||||
float result[] = new float[points.length];
|
||||
transform.transform(points, 0, result, 0, points.length / 2);
|
||||
resultPolygon.points = result;
|
||||
resultPolygon.checkPoints();
|
||||
|
||||
return resultPolygon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#findCenter()
|
||||
*/
|
||||
protected void findCenter() {
|
||||
center = new float[2];
|
||||
center[0] = x + radius1;
|
||||
center[1] = y + radius2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#calculateRadius()
|
||||
*/
|
||||
protected void calculateRadius() {
|
||||
boundingCircleRadius = (radius1 > radius2) ? radius1 : radius2;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
/**
|
||||
* Debug listener for notifications assocaited with geometry utilities
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public interface GeomUtilListener {
|
||||
/**
|
||||
* Notification that a point was excluded from geometry
|
||||
*
|
||||
* @param x The x coordinate of the point
|
||||
* @param y The y coordinate of the point
|
||||
*/
|
||||
public void pointExcluded(float x, float y);
|
||||
|
||||
/**
|
||||
* Notification that a point was intersected between two geometries
|
||||
*
|
||||
* @param x The x coordinate of the point
|
||||
* @param y The y coordinate of the point
|
||||
*/
|
||||
public void pointIntersected(float x, float y);
|
||||
|
||||
/**
|
||||
* Notification that a point was used to build a new geometry
|
||||
*
|
||||
* @param x The x coordinate of the point
|
||||
* @param y The y coordinate of the point
|
||||
*/
|
||||
public void pointUsed(float x, float y);
|
||||
}
|
||||
482
lib/slick-source/org/newdawn/slick/geom/Line.java
Normal file
482
lib/slick-source/org/newdawn/slick/geom/Line.java
Normal file
@@ -0,0 +1,482 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
/**
|
||||
* Implemenation of a bunch of maths functions to do with lines. Note that lines
|
||||
* can't be used as dynamic shapes right now - also collision with the end of a
|
||||
* line is undefined.
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public class Line extends Shape {
|
||||
/** The start point of the line */
|
||||
private Vector2f start;
|
||||
/** The end point of the line */
|
||||
private Vector2f end;
|
||||
/** The vector between the two points */
|
||||
private Vector2f vec;
|
||||
/** The length of the line squared */
|
||||
private float lenSquared;
|
||||
|
||||
/** Temporary storage - declared globally to reduce GC */
|
||||
private Vector2f loc = new Vector2f(0, 0);
|
||||
/** Temporary storage - declared globally to reduce GC */
|
||||
private Vector2f v = new Vector2f(0, 0);
|
||||
/** Temporary storage - declared globally to reduce GC */
|
||||
private Vector2f v2 = new Vector2f(0, 0);
|
||||
/** Temporary storage - declared globally to reduce GC */
|
||||
private Vector2f proj = new Vector2f(0, 0);
|
||||
|
||||
/** Temporary storage - declared globally to reduce GC */
|
||||
private Vector2f closest = new Vector2f(0, 0);
|
||||
/** Temporary storage - declared globally to reduce GC */
|
||||
private Vector2f other = new Vector2f(0, 0);
|
||||
|
||||
/** True if this line blocks on the outer edge */
|
||||
private boolean outerEdge = true;
|
||||
/** True if this line blocks on the inner edge */
|
||||
private boolean innerEdge = true;
|
||||
|
||||
/**
|
||||
* Create a new line based on the origin and a single point
|
||||
*
|
||||
* @param x
|
||||
* The end point of the line
|
||||
* @param y
|
||||
* The end point of the line
|
||||
* @param inner
|
||||
* True if this line blocks on it's inner edge
|
||||
* @param outer
|
||||
* True if this line blocks on it's outer edge
|
||||
*/
|
||||
public Line(float x, float y, boolean inner, boolean outer) {
|
||||
this(0, 0, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new line based on the origin and a single point
|
||||
*
|
||||
* @param x
|
||||
* The end point of the line
|
||||
* @param y
|
||||
* The end point of the line
|
||||
*/
|
||||
public Line(float x, float y) {
|
||||
this(x, y, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new line based on two points
|
||||
*
|
||||
* @param x1
|
||||
* The x coordinate of the start point
|
||||
* @param y1
|
||||
* The y coordinate of the start point
|
||||
* @param x2
|
||||
* The x coordinate of the end point
|
||||
* @param y2
|
||||
* The y coordinate of the end point
|
||||
*/
|
||||
public Line(float x1, float y1, float x2, float y2) {
|
||||
this(new Vector2f(x1, y1), new Vector2f(x2, y2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a line with relative second point
|
||||
*
|
||||
* @param x1
|
||||
* The x coordinate of the start point
|
||||
* @param y1
|
||||
* The y coordinate of the start point
|
||||
* @param dx
|
||||
* The x change to get to the second point
|
||||
* @param dy
|
||||
* The y change to get to the second point
|
||||
* @param dummy
|
||||
* A dummy value
|
||||
*/
|
||||
public Line(float x1, float y1, float dx, float dy, boolean dummy) {
|
||||
this(new Vector2f(x1, y1), new Vector2f(x1 + dx, y1 + dy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new line based on two points
|
||||
*
|
||||
* @param start
|
||||
* The start point
|
||||
* @param end
|
||||
* The end point
|
||||
*/
|
||||
public Line(float[] start, float[] end) {
|
||||
super();
|
||||
|
||||
set(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new line based on two points
|
||||
*
|
||||
* @param start
|
||||
* The start point
|
||||
* @param end
|
||||
* The end point
|
||||
*/
|
||||
public Line(Vector2f start, Vector2f end) {
|
||||
super();
|
||||
|
||||
set(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the line
|
||||
*
|
||||
* @param start
|
||||
* The start point of the line
|
||||
* @param end
|
||||
* The end point of the line
|
||||
*/
|
||||
public void set(float[] start, float[] end) {
|
||||
set(start[0], start[1], end[0], end[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the start point of the line
|
||||
*
|
||||
* @return The start point of the line
|
||||
*/
|
||||
public Vector2f getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end point of the line
|
||||
*
|
||||
* @return The end point of the line
|
||||
*/
|
||||
public Vector2f getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the length of the line
|
||||
*
|
||||
* @return The the length of the line
|
||||
*/
|
||||
public float length() {
|
||||
return vec.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the length of the line squared (cheaper and good for comparisons)
|
||||
*
|
||||
* @return The length of the line squared
|
||||
*/
|
||||
public float lengthSquared() {
|
||||
return vec.lengthSquared();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the line
|
||||
*
|
||||
* @param start
|
||||
* The start point of the line
|
||||
* @param end
|
||||
* The end point of the line
|
||||
*/
|
||||
public void set(Vector2f start, Vector2f end) {
|
||||
super.pointsDirty = true;
|
||||
if (this.start == null) {
|
||||
this.start = new Vector2f();
|
||||
}
|
||||
this.start.set(start);
|
||||
|
||||
if (this.end == null) {
|
||||
this.end = new Vector2f();
|
||||
}
|
||||
this.end.set(end);
|
||||
|
||||
vec = new Vector2f(end);
|
||||
vec.sub(start);
|
||||
|
||||
lenSquared = vec.lengthSquared();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the line without garbage
|
||||
*
|
||||
* @param sx
|
||||
* The x coordinate of the start
|
||||
* @param sy
|
||||
* The y coordinate of the start
|
||||
* @param ex
|
||||
* The x coordiante of the end
|
||||
* @param ey
|
||||
* The y coordinate of the end
|
||||
*/
|
||||
public void set(float sx, float sy, float ex, float ey) {
|
||||
super.pointsDirty = true;
|
||||
start.set(sx, sy);
|
||||
end.set(ex, ey);
|
||||
float dx = (ex - sx);
|
||||
float dy = (ey - sy);
|
||||
vec.set(dx,dy);
|
||||
|
||||
lenSquared = (dx * dx) + (dy * dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the x direction of this line
|
||||
*
|
||||
* @return The x direction of this line
|
||||
*/
|
||||
public float getDX() {
|
||||
return end.getX() - start.getX();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the y direction of this line
|
||||
*
|
||||
* @return The y direction of this line
|
||||
*/
|
||||
public float getDY() {
|
||||
return end.getY() - start.getY();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#getX()
|
||||
*/
|
||||
public float getX() {
|
||||
return getX1();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#getY()
|
||||
*/
|
||||
public float getY() {
|
||||
return getY1();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the x coordinate of the start point
|
||||
*
|
||||
* @return The x coordinate of the start point
|
||||
*/
|
||||
public float getX1() {
|
||||
return start.getX();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the y coordinate of the start point
|
||||
*
|
||||
* @return The y coordinate of the start point
|
||||
*/
|
||||
public float getY1() {
|
||||
return start.getY();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the x coordinate of the end point
|
||||
*
|
||||
* @return The x coordinate of the end point
|
||||
*/
|
||||
public float getX2() {
|
||||
return end.getX();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the y coordinate of the end point
|
||||
*
|
||||
* @return The y coordinate of the end point
|
||||
*/
|
||||
public float getY2() {
|
||||
return end.getY();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shortest distance from a point to this line
|
||||
*
|
||||
* @param point
|
||||
* The point from which we want the distance
|
||||
* @return The distance from the line to the point
|
||||
*/
|
||||
public float distance(Vector2f point) {
|
||||
return (float) Math.sqrt(distanceSquared(point));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given point is on the line
|
||||
*
|
||||
* @param point
|
||||
* The point to check
|
||||
* @return True if the point is on this line
|
||||
*/
|
||||
public boolean on(Vector2f point) {
|
||||
getClosestPoint(point, closest);
|
||||
|
||||
return point.equals(closest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shortest distance squared from a point to this line
|
||||
*
|
||||
* @param point
|
||||
* The point from which we want the distance
|
||||
* @return The distance squared from the line to the point
|
||||
*/
|
||||
public float distanceSquared(Vector2f point) {
|
||||
getClosestPoint(point, closest);
|
||||
closest.sub(point);
|
||||
|
||||
float result = closest.lengthSquared();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the closest point on the line to a given point
|
||||
*
|
||||
* @param point
|
||||
* The point which we want to project
|
||||
* @param result
|
||||
* The point on the line closest to the given point
|
||||
*/
|
||||
public void getClosestPoint(Vector2f point, Vector2f result) {
|
||||
loc.set(point);
|
||||
loc.sub(start);
|
||||
|
||||
float projDistance = vec.dot(loc);
|
||||
|
||||
projDistance /= vec.lengthSquared();
|
||||
|
||||
if (projDistance < 0) {
|
||||
result.set(start);
|
||||
return;
|
||||
}
|
||||
if (projDistance > 1) {
|
||||
result.set(end);
|
||||
return;
|
||||
}
|
||||
|
||||
result.x = start.getX() + projDistance * vec.getX();
|
||||
result.y = start.getY() + projDistance * vec.getY();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return "[Line " + start + "," + end + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect this line with another
|
||||
*
|
||||
* @param other
|
||||
* The other line we should intersect with
|
||||
* @return The intersection point or null if the lines are parallel
|
||||
*/
|
||||
public Vector2f intersect(Line other) {
|
||||
return intersect(other, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect this line with another
|
||||
*
|
||||
* @param other
|
||||
* The other line we should intersect with
|
||||
* @param limit
|
||||
* True if the collision is limited to the extent of the lines
|
||||
* @return The intersection point or null if the lines don't intersect
|
||||
*/
|
||||
public Vector2f intersect(Line other, boolean limit) {
|
||||
Vector2f temp = new Vector2f();
|
||||
|
||||
if (!intersect(other, limit, temp)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect this line with another
|
||||
*
|
||||
* @param other
|
||||
* The other line we should intersect with
|
||||
* @param limit
|
||||
* True if the collision is limited to the extent of the lines
|
||||
* @param result
|
||||
* The resulting intersection point if any
|
||||
* @return True if the lines intersect
|
||||
*/
|
||||
public boolean intersect(Line other, boolean limit, Vector2f result) {
|
||||
float dx1 = end.getX() - start.getX();
|
||||
float dx2 = other.end.getX() - other.start.getX();
|
||||
float dy1 = end.getY() - start.getY();
|
||||
float dy2 = other.end.getY() - other.start.getY();
|
||||
float denom = (dy2 * dx1) - (dx2 * dy1);
|
||||
|
||||
if (denom == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float ua = (dx2 * (start.getY() - other.start.getY()))
|
||||
- (dy2 * (start.getX() - other.start.getX()));
|
||||
ua /= denom;
|
||||
float ub = (dx1 * (start.getY() - other.start.getY()))
|
||||
- (dy1 * (start.getX() - other.start.getX()));
|
||||
ub /= denom;
|
||||
|
||||
if ((limit) && ((ua < 0) || (ua > 1) || (ub < 0) || (ub > 1))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float u = ua;
|
||||
|
||||
float ix = start.getX() + (u * (end.getX() - start.getX()));
|
||||
float iy = start.getY() + (u * (end.getY() - start.getY()));
|
||||
|
||||
result.set(ix, iy);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#createPoints()
|
||||
*/
|
||||
protected void createPoints() {
|
||||
points = new float[4];
|
||||
points[0] = getX1();
|
||||
points[1] = getY1();
|
||||
points[2] = getX2();
|
||||
points[3] = getY2();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#transform(org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public Shape transform(Transform transform) {
|
||||
float[] temp = new float[4];
|
||||
createPoints();
|
||||
transform.transform(points, 0, temp, 0, 2);
|
||||
|
||||
return new Line(temp[0], temp[1], temp[2], temp[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#closed()
|
||||
*/
|
||||
public boolean closed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#intersects(org.newdawn.slick.geom.Shape)
|
||||
*/
|
||||
public boolean intersects(Shape shape)
|
||||
{
|
||||
if (shape instanceof Circle)
|
||||
{
|
||||
return shape.intersects(this);
|
||||
}
|
||||
return super.intersects(shape);
|
||||
}
|
||||
}
|
||||
617
lib/slick-source/org/newdawn/slick/geom/MannTriangulator.java
Normal file
617
lib/slick-source/org/newdawn/slick/geom/MannTriangulator.java
Normal file
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
* Triangulator0.java
|
||||
*
|
||||
* (BSD license)
|
||||
*
|
||||
* Copyright (c) 2005, Matthias Mann (www.matthiasmann.de)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the Matthias Mann nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Created on 17. March 2005, 22:19
|
||||
*/
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A 2D Triangulator. Graciously made available by the man(n) himself.
|
||||
*
|
||||
* @author Matthias Mann
|
||||
*/
|
||||
public class MannTriangulator implements Triangulator {
|
||||
/** The allowed error value */
|
||||
private static final double EPSILON = 1e-5;
|
||||
|
||||
/** The outer countour of the shape */
|
||||
protected PointBag contour;
|
||||
/** The holes defined in the polygon */
|
||||
protected PointBag holes;
|
||||
/** The next available point bag */
|
||||
private PointBag nextFreePointBag;
|
||||
/** The next available point */
|
||||
private Point nextFreePoint;
|
||||
/** The list of triangles created (or rather points in triangles, 3xn) */
|
||||
private List triangles = new ArrayList();
|
||||
|
||||
/** Creates a new instance of Triangulator0 */
|
||||
public MannTriangulator() {
|
||||
contour = getPointBag();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Triangulator#addPolyPoint(float, float)
|
||||
*/
|
||||
public void addPolyPoint(float x, float y) {
|
||||
addPoint(new Vector2f(x,y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the internal state of the triangulator
|
||||
*/
|
||||
public void reset() {
|
||||
while (holes != null) {
|
||||
holes = freePointBag(holes);
|
||||
}
|
||||
|
||||
contour.clear();
|
||||
holes = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin adding a hole to the polygon
|
||||
*/
|
||||
public void startHole() {
|
||||
PointBag newHole = getPointBag();
|
||||
newHole.next = holes;
|
||||
holes = newHole;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a defined point to the current contour
|
||||
*
|
||||
* @param pt The point to add
|
||||
*/
|
||||
private void addPoint(Vector2f pt) {
|
||||
if (holes == null) {
|
||||
Point p = getPoint(pt);
|
||||
contour.add(p);
|
||||
} else {
|
||||
Point p = getPoint(pt);
|
||||
holes.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triangulate the points given
|
||||
*
|
||||
* @param result The array to fill with the result or use to determine type
|
||||
* @return The resultng triangles
|
||||
*/
|
||||
private Vector2f[] triangulate(Vector2f[] result) {
|
||||
// Step 1: Compute all angles
|
||||
contour.computeAngles();
|
||||
for (PointBag hole = holes; hole != null; hole = hole.next) {
|
||||
hole.computeAngles();
|
||||
}
|
||||
|
||||
// Step 2: Connect the holes with the contour (build bridges)
|
||||
while (holes != null) {
|
||||
Point pHole = holes.first;
|
||||
outer: do {
|
||||
if (pHole.angle <= 0) {
|
||||
Point pContour = contour.first;
|
||||
do {
|
||||
inner: if (pHole.isInfront(pContour)
|
||||
&& pContour.isInfront(pHole)) {
|
||||
if (!contour.doesIntersectSegment(pHole.pt,
|
||||
pContour.pt)) {
|
||||
PointBag hole = holes;
|
||||
do {
|
||||
if (hole.doesIntersectSegment(pHole.pt,
|
||||
pContour.pt)) {
|
||||
break inner;
|
||||
}
|
||||
} while ((hole = hole.next) != null);
|
||||
|
||||
Point newPtContour = getPoint(pContour.pt);
|
||||
pContour.insertAfter(newPtContour);
|
||||
|
||||
Point newPtHole = getPoint(pHole.pt);
|
||||
pHole.insertBefore(newPtHole);
|
||||
|
||||
pContour.next = pHole;
|
||||
pHole.prev = pContour;
|
||||
|
||||
newPtHole.next = newPtContour;
|
||||
newPtContour.prev = newPtHole;
|
||||
|
||||
pContour.computeAngle();
|
||||
pHole.computeAngle();
|
||||
newPtContour.computeAngle();
|
||||
newPtHole.computeAngle();
|
||||
|
||||
// detach the points from the hole
|
||||
holes.first = null;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
} while ((pContour = pContour.next) != contour.first);
|
||||
}
|
||||
} while ((pHole = pHole.next) != holes.first);
|
||||
|
||||
// free the hole
|
||||
holes = freePointBag(holes);
|
||||
}
|
||||
|
||||
// Step 3: Make sure we have enough space for the result
|
||||
int numTriangles = contour.countPoints() - 2;
|
||||
int neededSpace = numTriangles * 3 + 1; // for the null
|
||||
if (result.length < neededSpace) {
|
||||
result = (Vector2f[]) Array.newInstance(result.getClass()
|
||||
.getComponentType(), neededSpace);
|
||||
}
|
||||
|
||||
// Step 4: Extract the triangles
|
||||
int idx = 0;
|
||||
for (;;) {
|
||||
Point pContour = contour.first;
|
||||
|
||||
if (pContour == null) {
|
||||
break;
|
||||
}
|
||||
// Are there 2 or less points left ?
|
||||
if (pContour.next == pContour.prev) {
|
||||
break;
|
||||
}
|
||||
|
||||
outer: do {
|
||||
if (pContour.angle > 0) {
|
||||
Point prev = pContour.prev;
|
||||
Point next = pContour.next;
|
||||
|
||||
if (next.next == prev || prev.isInfront(next) && next.isInfront(prev)) {
|
||||
if (!contour.doesIntersectSegment(prev.pt, next.pt)) {
|
||||
result[idx++] = pContour.pt;
|
||||
result[idx++] = next.pt;
|
||||
result[idx++] = prev.pt;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ((pContour = pContour.next) != contour.first);
|
||||
|
||||
// remove the point - we do it in every case to prevent endless loop
|
||||
Point prev = pContour.prev;
|
||||
Point next = pContour.next;
|
||||
|
||||
contour.first = prev;
|
||||
pContour.unlink();
|
||||
freePoint(pContour);
|
||||
|
||||
next.computeAngle();
|
||||
prev.computeAngle();
|
||||
}
|
||||
|
||||
// Step 5: Append a null (see Collection.toArray)
|
||||
result[idx] = null;
|
||||
|
||||
// Step 6: Free memory
|
||||
contour.clear();
|
||||
|
||||
// Finished !
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new point bag (or recycle an old one)
|
||||
*
|
||||
* @return The new point bag
|
||||
*/
|
||||
private PointBag getPointBag() {
|
||||
PointBag pb = nextFreePointBag;
|
||||
if (pb != null) {
|
||||
nextFreePointBag = pb.next;
|
||||
pb.next = null;
|
||||
return pb;
|
||||
}
|
||||
return new PointBag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Release a pooled bag
|
||||
*
|
||||
* @param pb The bag to release
|
||||
* @return The next available bag
|
||||
*/
|
||||
private PointBag freePointBag(PointBag pb) {
|
||||
PointBag next = pb.next;
|
||||
pb.clear();
|
||||
pb.next = nextFreePointBag;
|
||||
nextFreePointBag = pb;
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or reuse a point
|
||||
*
|
||||
* @param pt The point data to set
|
||||
* @return The new point
|
||||
*/
|
||||
private Point getPoint(Vector2f pt) {
|
||||
Point p = nextFreePoint;
|
||||
if (p != null) {
|
||||
nextFreePoint = p.next;
|
||||
// initialize new point to safe values
|
||||
p.next = null;
|
||||
p.prev = null;
|
||||
p.pt = pt;
|
||||
return p;
|
||||
}
|
||||
return new Point(pt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release a point into the pool
|
||||
*
|
||||
* @param p The point to release
|
||||
*/
|
||||
private void freePoint(Point p) {
|
||||
p.next = nextFreePoint;
|
||||
nextFreePoint = p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all points
|
||||
*
|
||||
* @param head The head of the points bag
|
||||
*/
|
||||
private void freePoints(Point head) {
|
||||
head.prev.next = nextFreePoint;
|
||||
head.prev = null;
|
||||
nextFreePoint = head;
|
||||
}
|
||||
|
||||
/**
|
||||
* A single point being considered during triangulation
|
||||
*
|
||||
* @author Matthias Mann
|
||||
*/
|
||||
private static class Point implements Serializable {
|
||||
/** The location of the point */
|
||||
protected Vector2f pt;
|
||||
/** The previous point in the contour */
|
||||
protected Point prev;
|
||||
/** The next point in the contour */
|
||||
protected Point next;
|
||||
/** The x component of the of the normal */
|
||||
protected double nx;
|
||||
/** The y component of the of the normal */
|
||||
protected double ny;
|
||||
/** The angle at this point in the path */
|
||||
protected double angle;
|
||||
/** The distance of this point from */
|
||||
protected double dist;
|
||||
|
||||
/**
|
||||
* Create a new point
|
||||
*
|
||||
* @param pt The points location
|
||||
*/
|
||||
public Point(Vector2f pt) {
|
||||
this.pt = pt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove this point from it's contour
|
||||
*/
|
||||
public void unlink() {
|
||||
prev.next = next;
|
||||
next.prev = prev;
|
||||
next = null;
|
||||
prev = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a point before this one (see LinkedList)
|
||||
*
|
||||
* @param p The point to insert
|
||||
*/
|
||||
public void insertBefore(Point p) {
|
||||
prev.next = p;
|
||||
p.prev = prev;
|
||||
p.next = this;
|
||||
prev = p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a point after this one (see LinkedList)
|
||||
*
|
||||
* @param p The point to insert
|
||||
*/
|
||||
public void insertAfter(Point p) {
|
||||
next.prev = p;
|
||||
p.prev = this;
|
||||
p.next = next;
|
||||
next = p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Java 5 hypot method
|
||||
*
|
||||
* @param x The x component
|
||||
* @param y The y component
|
||||
* @return The hypotenuse
|
||||
*/
|
||||
private double hypot(double x, double y) {
|
||||
return Math.sqrt(x*x + y*y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the angle at this point
|
||||
*/
|
||||
public void computeAngle() {
|
||||
if (prev.pt.equals(pt)) {
|
||||
pt.x += 0.01f;
|
||||
}
|
||||
double dx1 = pt.x - prev.pt.x;
|
||||
double dy1 = pt.y - prev.pt.y;
|
||||
double len1 = hypot(dx1, dy1);
|
||||
dx1 /= len1;
|
||||
dy1 /= len1;
|
||||
|
||||
if (next.pt.equals(pt)) {
|
||||
pt.y += 0.01f;
|
||||
}
|
||||
double dx2 = next.pt.x - pt.x;
|
||||
double dy2 = next.pt.y - pt.y;
|
||||
double len2 = hypot(dx2, dy2);
|
||||
dx2 /= len2;
|
||||
dy2 /= len2;
|
||||
|
||||
double nx1 = -dy1;
|
||||
double ny1 = dx1;
|
||||
|
||||
nx = (nx1 - dy2) * 0.5;
|
||||
ny = (ny1 + dx2) * 0.5;
|
||||
|
||||
if (nx * nx + ny * ny < EPSILON) {
|
||||
nx = dx1;
|
||||
ny = dy2;
|
||||
angle = 1; // TODO: nx1,ny1 and nx2,ny2 facing ?
|
||||
if (dx1 * dx2 + dy1 * dy2 > 0) {
|
||||
nx = -dx1;
|
||||
ny = -dy1;
|
||||
}
|
||||
} else {
|
||||
angle = nx * dx2 + ny * dy2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the angle of this point to another
|
||||
*
|
||||
* @param p The other point
|
||||
* @return The angle between this point and another
|
||||
*/
|
||||
public double getAngle(Point p) {
|
||||
double dx = p.pt.x - pt.x;
|
||||
double dy = p.pt.y - pt.y;
|
||||
double dlen = hypot(dx, dy);
|
||||
|
||||
return (nx * dx + ny * dy) / dlen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this point is convave
|
||||
*
|
||||
* @return True if this point remains concave
|
||||
*/
|
||||
public boolean isConcave() {
|
||||
return angle < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this point is infront of another
|
||||
*
|
||||
* @param dx The other x
|
||||
* @param dy The other y
|
||||
* @return True if this point is infront (in the contour)
|
||||
*/
|
||||
public boolean isInfront(double dx, double dy) {
|
||||
// no nead to normalize, amplitude does not metter for side
|
||||
// detection
|
||||
boolean sidePrev = ((prev.pt.y - pt.y) * dx + (pt.x - prev.pt.x)
|
||||
* dy) >= 0;
|
||||
boolean sideNext = ((pt.y - next.pt.y) * dx + (next.pt.x - pt.x)
|
||||
* dy) >= 0;
|
||||
|
||||
return (angle < 0) ? (sidePrev | sideNext) : (sidePrev & sideNext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this point is infront of another
|
||||
*
|
||||
* @param p The other point
|
||||
* @return True if this point is infront (in the contour)
|
||||
*/
|
||||
public boolean isInfront(Point p) {
|
||||
return isInfront(p.pt.x - pt.x, p.pt.y - pt.y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A bag/pool of point objects
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
protected class PointBag implements Serializable {
|
||||
/** The first point in the bag - head of the list */
|
||||
protected Point first;
|
||||
/** The next bag in the list of bags */
|
||||
protected PointBag next;
|
||||
|
||||
/**
|
||||
* Clear all the points from this bag
|
||||
*/
|
||||
public void clear() {
|
||||
if (first != null) {
|
||||
freePoints(first);
|
||||
first = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a point to the bag
|
||||
*
|
||||
* @param p The point to add
|
||||
*/
|
||||
public void add(Point p) {
|
||||
if (first != null) {
|
||||
first.insertBefore(p);
|
||||
} else {
|
||||
first = p;
|
||||
p.next = p;
|
||||
p.prev = p;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the angles for the points in this bag
|
||||
*/
|
||||
public void computeAngles() {
|
||||
if (first == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Point p = first;
|
||||
do {
|
||||
p.computeAngle();
|
||||
} while ((p = p.next) != first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the points in this bag form a path intersecting
|
||||
* with the specified path
|
||||
*
|
||||
* @param v1 The start point of the segment
|
||||
* @param v2 The end point of the segment
|
||||
* @return True if points in this contour intersect with the segment
|
||||
*/
|
||||
public boolean doesIntersectSegment(Vector2f v1, Vector2f v2) {
|
||||
double dxA = v2.x - v1.x;
|
||||
double dyA = v2.y - v1.y;
|
||||
|
||||
for (Point p = first;;) {
|
||||
Point n = p.next;
|
||||
if (p.pt != v1 && n.pt != v1 && p.pt != v2 && n.pt != v2) {
|
||||
double dxB = n.pt.x - p.pt.x;
|
||||
double dyB = n.pt.y - p.pt.y;
|
||||
double d = (dxA * dyB) - (dyA * dxB);
|
||||
|
||||
if (Math.abs(d) > EPSILON) {
|
||||
double tmp1 = p.pt.x - v1.x;
|
||||
double tmp2 = p.pt.y - v1.y;
|
||||
double tA = (dyB * tmp1 - dxB * tmp2) / d;
|
||||
double tB = (dyA * tmp1 - dxA * tmp2) / d;
|
||||
|
||||
if (tA >= 0 && tA <= 1 && tB >= 0 && tB <= 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n == first) {
|
||||
return false;
|
||||
}
|
||||
p = n;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of points in the bag
|
||||
*
|
||||
* @return The number of points in the bag
|
||||
*/
|
||||
public int countPoints() {
|
||||
if (first == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
Point p = first;
|
||||
do {
|
||||
++count;
|
||||
} while ((p = p.next) != first);
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the point provided was contained
|
||||
*
|
||||
* @param point The point provided
|
||||
* @return True if it's in the bag
|
||||
*/
|
||||
public boolean contains(Vector2f point) {
|
||||
if (first == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (first.prev.pt.equals(point)) {
|
||||
return true;
|
||||
}
|
||||
if (first.pt.equals(point)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean triangulate() {
|
||||
Vector2f[] temp = triangulate(new Vector2f[0]);
|
||||
|
||||
for (int i = 0; i < temp.length; i++) {
|
||||
if (temp[i] == null) {
|
||||
break;
|
||||
} else {
|
||||
triangles.add(temp[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Triangulator#getTriangleCount()
|
||||
*/
|
||||
public int getTriangleCount() {
|
||||
return triangles.size() / 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Triangulator#getTrianglePoint(int, int)
|
||||
*/
|
||||
public float[] getTrianglePoint(int tri, int i) {
|
||||
Vector2f pt = (Vector2f) triangles.get((tri * 3) + i);
|
||||
|
||||
return new float[] { pt.x, pt.y };
|
||||
}
|
||||
|
||||
}
|
||||
193
lib/slick-source/org/newdawn/slick/geom/MorphShape.java
Normal file
193
lib/slick-source/org/newdawn/slick/geom/MorphShape.java
Normal file
@@ -0,0 +1,193 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A shape that morphs between a set of other shapes
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class MorphShape extends Shape {
|
||||
/** The shapes to morph between */
|
||||
private ArrayList shapes = new ArrayList();
|
||||
/** The offset between the shapes */
|
||||
private float offset;
|
||||
|
||||
/** The current shape */
|
||||
private Shape current;
|
||||
/** The next shape */
|
||||
private Shape next;
|
||||
|
||||
/**
|
||||
* Create a new mighty morphin shape
|
||||
*
|
||||
* @param base The base shape we're starting the morph from
|
||||
*/
|
||||
public MorphShape(Shape base) {
|
||||
shapes.add(base);
|
||||
float[] copy = base.points;
|
||||
this.points = new float[copy.length];
|
||||
|
||||
current = base;
|
||||
next = base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subsequent shape that we should morph too in order
|
||||
*
|
||||
* @param shape The new shape that forms part of the morphing shape
|
||||
*/
|
||||
public void addShape(Shape shape) {
|
||||
if (shape.points.length != points.length) {
|
||||
throw new RuntimeException("Attempt to morph between two shapes with different vertex counts");
|
||||
}
|
||||
|
||||
Shape prev = (Shape) shapes.get(shapes.size()-1);
|
||||
if (equalShapes(prev, shape)) {
|
||||
shapes.add(prev);
|
||||
} else {
|
||||
shapes.add(shape);
|
||||
}
|
||||
|
||||
if (shapes.size() == 2) {
|
||||
next = (Shape) shapes.get(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the shape's points are all equal
|
||||
*
|
||||
* @param a The first shape to compare
|
||||
* @param b The second shape to compare
|
||||
* @return True if the shapes are equal
|
||||
*/
|
||||
private boolean equalShapes(Shape a, Shape b) {
|
||||
a.checkPoints();
|
||||
b.checkPoints();
|
||||
|
||||
for (int i=0;i<a.points.length;i++) {
|
||||
if (a.points[i] != b.points[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "time" index for this morph. This is given in terms of shapes, so
|
||||
* 0.5f would give you the position half way between the first and second shapes.
|
||||
*
|
||||
* @param time The time index to represent on this shape
|
||||
*/
|
||||
public void setMorphTime(float time) {
|
||||
int p = (int) time;
|
||||
int n = p + 1;
|
||||
float offset = time - p;
|
||||
|
||||
p = rational(p);
|
||||
n = rational(n);
|
||||
|
||||
setFrame(p, n, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the morph time and hence the curent frame
|
||||
*
|
||||
* @param delta The amount to change the morph time by
|
||||
*/
|
||||
public void updateMorphTime(float delta) {
|
||||
offset += delta;
|
||||
if (offset < 0) {
|
||||
int index = shapes.indexOf(current);
|
||||
if (index < 0) {
|
||||
index = shapes.size() - 1;
|
||||
}
|
||||
|
||||
int nframe = rational(index+1);
|
||||
setFrame(index, nframe, offset);
|
||||
offset += 1;
|
||||
} else if (offset > 1) {
|
||||
int index = shapes.indexOf(next);
|
||||
if (index < 1) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
int nframe = rational(index+1);
|
||||
setFrame(index, nframe, offset);
|
||||
offset -= 1;
|
||||
} else {
|
||||
pointsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current frame
|
||||
*
|
||||
* @param current The current frame
|
||||
*/
|
||||
public void setExternalFrame(Shape current) {
|
||||
this.current = current;
|
||||
next = (Shape) shapes.get(0);
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an index that is rational, i.e. fits inside this set of shapes
|
||||
*
|
||||
* @param n The index to rationalize
|
||||
* @return The index rationalized
|
||||
*/
|
||||
private int rational(int n) {
|
||||
while (n >= shapes.size()) {
|
||||
n -= shapes.size();
|
||||
}
|
||||
while (n < 0) {
|
||||
n += shapes.size();
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frame to be represented
|
||||
*
|
||||
* @param a The index of the first shape
|
||||
* @param b The index of the second shape
|
||||
* @param offset The offset between the two shapes to represent
|
||||
*/
|
||||
private void setFrame(int a, int b, float offset) {
|
||||
current = (Shape) shapes.get(a);
|
||||
next = (Shape) shapes.get(b);
|
||||
this.offset = offset;
|
||||
pointsDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see MorphShape#createPoints()
|
||||
*/
|
||||
protected void createPoints() {
|
||||
if (current == next) {
|
||||
System.arraycopy(current.points,0,points,0,points.length);
|
||||
return;
|
||||
}
|
||||
|
||||
float[] apoints = current.points;
|
||||
float[] bpoints = next.points;
|
||||
|
||||
for (int i=0;i<points.length;i++) {
|
||||
points[i] = apoints[i] * (1 - offset);
|
||||
points[i] += bpoints[i] * offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see MorphShape#transform(Transform)
|
||||
*/
|
||||
public Shape transform(Transform transform) {
|
||||
createPoints();
|
||||
Polygon poly = new Polygon(points);
|
||||
|
||||
return poly;
|
||||
}
|
||||
}
|
||||
617
lib/slick-source/org/newdawn/slick/geom/NeatTriangulator.java
Normal file
617
lib/slick-source/org/newdawn/slick/geom/NeatTriangulator.java
Normal file
@@ -0,0 +1,617 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
|
||||
/**
|
||||
* A second triangulator that seems slightly more robust
|
||||
*
|
||||
* @author Online examples
|
||||
*/
|
||||
public class NeatTriangulator implements Triangulator
|
||||
{
|
||||
/** The error factor */
|
||||
static final float EPSILON = 1E-006F;
|
||||
|
||||
/** The x coordinates */
|
||||
private float pointsX[];
|
||||
/** The y coordiantes */
|
||||
private float pointsY[];
|
||||
/** The number of points that have been added */
|
||||
private int numPoints;
|
||||
/** The edges defines by triangulation */
|
||||
private Edge edges[];
|
||||
/** Voroni */
|
||||
private int V[];
|
||||
/** The number of edges found */
|
||||
private int numEdges;
|
||||
/** The triangles that have been found */
|
||||
private Triangle triangles[];
|
||||
/** The number of triangles found */
|
||||
private int numTriangles;
|
||||
/** The current offset */
|
||||
private float offset = EPSILON;
|
||||
|
||||
/**
|
||||
* Create a new triangulator
|
||||
*/
|
||||
public NeatTriangulator()
|
||||
{
|
||||
pointsX = new float[100];
|
||||
pointsY = new float[100];
|
||||
numPoints = 0;
|
||||
edges = new Edge[100];
|
||||
numEdges = 0;
|
||||
triangles = new Triangle[100];
|
||||
numTriangles = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the triangulator status
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
numPoints = 0;
|
||||
numEdges = 0;
|
||||
numTriangles = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an edge between two verts
|
||||
*
|
||||
* @param i The index of the first vert
|
||||
* @param j The index of the second vert
|
||||
* @return The index of the dge
|
||||
*/
|
||||
private int findEdge(int i, int j)
|
||||
{
|
||||
int k;
|
||||
int l;
|
||||
if(i < j)
|
||||
{
|
||||
k = i;
|
||||
l = j;
|
||||
} else
|
||||
{
|
||||
k = j;
|
||||
l = i;
|
||||
}
|
||||
for(int i1 = 0; i1 < numEdges; i1++)
|
||||
if(edges[i1].v0 == k && edges[i1].v1 == l)
|
||||
return i1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a discovered edge
|
||||
*
|
||||
* @param i The index of the first vert
|
||||
* @param j The index of the second vert
|
||||
* @param k The index of the spread vert
|
||||
*/
|
||||
private void addEdge(int i, int j, int k)
|
||||
{
|
||||
int l1 = findEdge(i, j);
|
||||
int j1;
|
||||
int k1;
|
||||
Edge edge;
|
||||
if(l1 < 0)
|
||||
{
|
||||
if(numEdges == edges.length)
|
||||
{
|
||||
Edge aedge[] = new Edge[edges.length * 2];
|
||||
System.arraycopy(edges, 0, aedge, 0, numEdges);
|
||||
edges = aedge;
|
||||
}
|
||||
j1 = -1;
|
||||
k1 = -1;
|
||||
l1 = numEdges++;
|
||||
edge = edges[l1] = new Edge();
|
||||
} else
|
||||
{
|
||||
edge = edges[l1];
|
||||
j1 = edge.t0;
|
||||
k1 = edge.t1;
|
||||
}
|
||||
int l;
|
||||
int i1;
|
||||
if(i < j)
|
||||
{
|
||||
l = i;
|
||||
i1 = j;
|
||||
j1 = k;
|
||||
} else
|
||||
{
|
||||
l = j;
|
||||
i1 = i;
|
||||
k1 = k;
|
||||
}
|
||||
edge.v0 = l;
|
||||
edge.v1 = i1;
|
||||
edge.t0 = j1;
|
||||
edge.t1 = k1;
|
||||
edge.suspect = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and edge identified by it's verts
|
||||
*
|
||||
* @param i The index of the first vert
|
||||
* @param j The index of the second vert
|
||||
* @throws InternalException Indicates the edge didn't exist
|
||||
*/
|
||||
private void deleteEdge(int i, int j) throws InternalException
|
||||
{
|
||||
int k;
|
||||
if(0 > (k = findEdge(i, j)))
|
||||
{
|
||||
throw new InternalException("Attempt to delete unknown edge");
|
||||
}
|
||||
else
|
||||
{
|
||||
edges[k] = edges[--numEdges];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark an edge as either a suspect or not
|
||||
*
|
||||
* @param i The index of the first vert
|
||||
* @param j The index of the second vert
|
||||
* @param flag True if the edge is a suspect
|
||||
* @throws InternalException Indicates the edge didn't exist
|
||||
*/
|
||||
void markSuspect(int i, int j, boolean flag) throws InternalException
|
||||
{
|
||||
int k;
|
||||
if(0 > (k = findEdge(i, j)))
|
||||
{
|
||||
throw new InternalException("Attempt to mark unknown edge");
|
||||
} else
|
||||
{
|
||||
edges[k].suspect = flag;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the suspect to become part of the triangle
|
||||
*
|
||||
* @return The edge selected
|
||||
*/
|
||||
private Edge chooseSuspect()
|
||||
{
|
||||
for(int i = 0; i < numEdges; i++)
|
||||
{
|
||||
Edge edge = edges[i];
|
||||
if(edge.suspect)
|
||||
{
|
||||
edge.suspect = false;
|
||||
if(edge.t0 >= 0 && edge.t1 >= 0)
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factor rho.
|
||||
*
|
||||
* @param f Factor 1
|
||||
* @param f1 Factor 2
|
||||
* @param f2 Factor 3
|
||||
* @param f3 Factor 4
|
||||
* @param f4 Factor 5
|
||||
* @param f5 Factor 6
|
||||
* @return The computation of rho
|
||||
*/
|
||||
private static float rho(float f, float f1, float f2, float f3, float f4, float f5)
|
||||
{
|
||||
float f6 = f4 - f2;
|
||||
float f7 = f5 - f3;
|
||||
float f8 = f - f4;
|
||||
float f9 = f1 - f5;
|
||||
float f18 = f6 * f9 - f7 * f8;
|
||||
if(f18 > 0.0F)
|
||||
{
|
||||
if(f18 < 1E-006F)
|
||||
f18 = 1E-006F;
|
||||
float f12 = f6 * f6;
|
||||
float f13 = f7 * f7;
|
||||
float f14 = f8 * f8;
|
||||
float f15 = f9 * f9;
|
||||
float f10 = f2 - f;
|
||||
float f11 = f3 - f1;
|
||||
float f16 = f10 * f10;
|
||||
float f17 = f11 * f11;
|
||||
return ((f12 + f13) * (f14 + f15) * (f16 + f17)) / (f18 * f18);
|
||||
} else
|
||||
{
|
||||
return -1F;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the point P is inside the triangle defined by
|
||||
* the points A,B,C
|
||||
*
|
||||
* @param f Point A x-coordinate
|
||||
* @param f1 Point A y-coordinate
|
||||
* @param f2 Point B x-coordinate
|
||||
* @param f3 Point B y-coordinate
|
||||
* @param f4 Point C x-coordinate
|
||||
* @param f5 Point C y-coordinate
|
||||
* @param f6 Point P x-coordinate
|
||||
* @param f7 Point P y-coordinate
|
||||
* @return True if the point specified is within the triangle
|
||||
*/
|
||||
private static boolean insideTriangle(float f, float f1, float f2, float f3, float f4, float f5, float f6, float f7)
|
||||
{
|
||||
float f8 = f4 - f2;
|
||||
float f9 = f5 - f3;
|
||||
float f10 = f - f4;
|
||||
float f11 = f1 - f5;
|
||||
float f12 = f2 - f;
|
||||
float f13 = f3 - f1;
|
||||
float f14 = f6 - f;
|
||||
float f15 = f7 - f1;
|
||||
float f16 = f6 - f2;
|
||||
float f17 = f7 - f3;
|
||||
float f18 = f6 - f4;
|
||||
float f19 = f7 - f5;
|
||||
float f22 = f8 * f17 - f9 * f16;
|
||||
float f20 = f12 * f15 - f13 * f14;
|
||||
float f21 = f10 * f19 - f11 * f18;
|
||||
return f22 >= 0.0D && f21 >= 0.0D && f20 >= 0.0D;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cut a the contour and add a triangle into V to describe the
|
||||
* location of the cut
|
||||
*
|
||||
* @param i The index of the first point
|
||||
* @param j The index of the second point
|
||||
* @param k The index of the third point
|
||||
* @param l ?
|
||||
* @return True if a triangle was found
|
||||
*/
|
||||
private boolean snip(int i, int j, int k, int l)
|
||||
{
|
||||
float f = pointsX[V[i]];
|
||||
float f1 = pointsY[V[i]];
|
||||
float f2 = pointsX[V[j]];
|
||||
float f3 = pointsY[V[j]];
|
||||
float f4 = pointsX[V[k]];
|
||||
float f5 = pointsY[V[k]];
|
||||
if(1E-006F > (f2 - f) * (f5 - f1) - (f3 - f1) * (f4 - f))
|
||||
return false;
|
||||
for(int i1 = 0; i1 < l; i1++)
|
||||
if(i1 != i && i1 != j && i1 != k)
|
||||
{
|
||||
float f6 = pointsX[V[i1]];
|
||||
float f7 = pointsY[V[i1]];
|
||||
if(insideTriangle(f, f1, f2, f3, f4, f5, f6, f7))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the area defined by the points
|
||||
*
|
||||
* @return The area defined by the points
|
||||
*/
|
||||
private float area()
|
||||
{
|
||||
float f = 0.0F;
|
||||
int i = numPoints - 1;
|
||||
for(int j = 0; j < numPoints;)
|
||||
{
|
||||
f += pointsX[i] * pointsY[j] - pointsY[i] * pointsX[j];
|
||||
i = j++;
|
||||
}
|
||||
|
||||
return f * 0.5F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform simple triangulation
|
||||
*
|
||||
* @throws InternalException Indicates a polygon that can't be triangulated
|
||||
*/
|
||||
public void basicTriangulation() throws InternalException
|
||||
{
|
||||
int i = numPoints;
|
||||
if(i < 3)
|
||||
return;
|
||||
numEdges = 0;
|
||||
numTriangles = 0;
|
||||
V = new int[i];
|
||||
|
||||
if(0.0D < area())
|
||||
{
|
||||
for(int k = 0; k < i; k++)
|
||||
V[k] = k;
|
||||
|
||||
} else
|
||||
{
|
||||
for(int l = 0; l < i; l++)
|
||||
V[l] = numPoints - 1 - l;
|
||||
|
||||
}
|
||||
int k1 = 2 * i;
|
||||
int i1 = i - 1;
|
||||
while(i > 2)
|
||||
{
|
||||
if(0 >= k1--) {
|
||||
throw new InternalException("Bad polygon");
|
||||
}
|
||||
|
||||
int j = i1;
|
||||
if(i <= j)
|
||||
j = 0;
|
||||
i1 = j + 1;
|
||||
if(i <= i1)
|
||||
i1 = 0;
|
||||
int j1 = i1 + 1;
|
||||
if(i <= j1)
|
||||
j1 = 0;
|
||||
if(snip(j, i1, j1, i))
|
||||
{
|
||||
int l1 = V[j];
|
||||
int i2 = V[i1];
|
||||
int j2 = V[j1];
|
||||
if(numTriangles == triangles.length)
|
||||
{
|
||||
Triangle atriangle[] = new Triangle[triangles.length * 2];
|
||||
System.arraycopy(triangles, 0, atriangle, 0, numTriangles);
|
||||
triangles = atriangle;
|
||||
}
|
||||
triangles[numTriangles] = new Triangle(l1, i2, j2);
|
||||
addEdge(l1, i2, numTriangles);
|
||||
addEdge(i2, j2, numTriangles);
|
||||
addEdge(j2, l1, numTriangles);
|
||||
numTriangles++;
|
||||
int k2 = i1;
|
||||
for(int l2 = i1 + 1; l2 < i; l2++)
|
||||
{
|
||||
V[k2] = V[l2];
|
||||
k2++;
|
||||
}
|
||||
|
||||
i--;
|
||||
k1 = 2 * i;
|
||||
}
|
||||
}
|
||||
V = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize the triangulation by applying delauney rules
|
||||
*
|
||||
* @throws InternalException Indicates an invalid polygon
|
||||
*/
|
||||
private void optimize() throws InternalException
|
||||
{
|
||||
do
|
||||
{
|
||||
Edge edge;
|
||||
if ((edge = chooseSuspect()) == null) {
|
||||
break;
|
||||
}
|
||||
int i1 = edge.v0;
|
||||
int k1 = edge.v1;
|
||||
int i = edge.t0;
|
||||
int j = edge.t1;
|
||||
int j1 = -1;
|
||||
int l1 = -1;
|
||||
for (int k = 0; k < 3; k++)
|
||||
{
|
||||
int i2 = triangles[i].v[k];
|
||||
if(i1 == i2 || k1 == i2) {
|
||||
continue;
|
||||
}
|
||||
l1 = i2;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int l = 0; l < 3; l++)
|
||||
{
|
||||
int j2 = triangles[j].v[l];
|
||||
if(i1 == j2 || k1 == j2) {
|
||||
continue;
|
||||
}
|
||||
j1 = j2;
|
||||
break;
|
||||
}
|
||||
|
||||
if(-1 == j1 || -1 == l1) {
|
||||
throw new InternalException("can't find quad");
|
||||
}
|
||||
|
||||
float f = pointsX[i1];
|
||||
float f1 = pointsY[i1];
|
||||
float f2 = pointsX[j1];
|
||||
float f3 = pointsY[j1];
|
||||
float f4 = pointsX[k1];
|
||||
float f5 = pointsY[k1];
|
||||
float f6 = pointsX[l1];
|
||||
float f7 = pointsY[l1];
|
||||
float f8 = rho(f, f1, f2, f3, f4, f5);
|
||||
float f9 = rho(f, f1, f4, f5, f6, f7);
|
||||
float f10 = rho(f2, f3, f4, f5, f6, f7);
|
||||
float f11 = rho(f2, f3, f6, f7, f, f1);
|
||||
if(0.0F > f8 || 0.0F > f9) {
|
||||
throw new InternalException("original triangles backwards");
|
||||
}
|
||||
if(0.0F <= f10 && 0.0F <= f11)
|
||||
{
|
||||
if(f8 > f9) {
|
||||
f8 = f9;
|
||||
}
|
||||
if(f10 > f11) {
|
||||
f10 = f11;
|
||||
}
|
||||
if(f8 > f10) {
|
||||
deleteEdge(i1, k1);
|
||||
triangles[i].v[0] = j1;
|
||||
triangles[i].v[1] = k1;
|
||||
triangles[i].v[2] = l1;
|
||||
triangles[j].v[0] = j1;
|
||||
triangles[j].v[1] = l1;
|
||||
triangles[j].v[2] = i1;
|
||||
addEdge(j1, k1, i);
|
||||
addEdge(k1, l1, i);
|
||||
addEdge(l1, j1, i);
|
||||
addEdge(l1, i1, j);
|
||||
addEdge(i1, j1, j);
|
||||
addEdge(j1, l1, j);
|
||||
markSuspect(j1, l1, false);
|
||||
}
|
||||
}
|
||||
} while(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upate the triangles
|
||||
*/
|
||||
public boolean triangulate()
|
||||
{
|
||||
try
|
||||
{
|
||||
basicTriangulation();
|
||||
//optimize();
|
||||
return true;
|
||||
}
|
||||
catch (InternalException e)
|
||||
{
|
||||
numEdges = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a point to the polygon
|
||||
*/
|
||||
public void addPolyPoint(float x, float y)
|
||||
{
|
||||
for (int i=0;i<numPoints;i++) {
|
||||
if ((pointsX[i] == x) && (pointsY[i] == y)) {
|
||||
//return;
|
||||
y += offset;
|
||||
offset += EPSILON;
|
||||
}
|
||||
}
|
||||
|
||||
if(numPoints == pointsX.length)
|
||||
{
|
||||
float af[] = new float[numPoints * 2];
|
||||
System.arraycopy(pointsX, 0, af, 0, numPoints);
|
||||
pointsX = af;
|
||||
af = new float[numPoints * 2];
|
||||
System.arraycopy(pointsY, 0, af, 0, numPoints);
|
||||
pointsY = af;
|
||||
}
|
||||
|
||||
pointsX[numPoints] = x;
|
||||
pointsY[numPoints] = y;
|
||||
numPoints++;
|
||||
}
|
||||
|
||||
/**
|
||||
* A single triangle
|
||||
*
|
||||
* @author Online Source
|
||||
*/
|
||||
class Triangle
|
||||
{
|
||||
/** The verticies index */
|
||||
int v[];
|
||||
|
||||
/**
|
||||
* Create a new triangle
|
||||
*
|
||||
* @param i The index of vert 1
|
||||
* @param j The index of vert 2
|
||||
* @param k The index of vert 3
|
||||
*/
|
||||
Triangle(int i, int j, int k)
|
||||
{
|
||||
v = new int[3];
|
||||
v[0] = i;
|
||||
v[1] = j;
|
||||
v[2] = k;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A single edge between two points
|
||||
*
|
||||
* @author Online Source
|
||||
*/
|
||||
class Edge
|
||||
{
|
||||
/** The start vert */
|
||||
int v0;
|
||||
/** The end vert */
|
||||
int v1;
|
||||
/** The start tangent vert */
|
||||
int t0;
|
||||
/** The end tangent vert */
|
||||
int t1;
|
||||
/** True if the edge is marked as a suspect */
|
||||
boolean suspect;
|
||||
|
||||
/**
|
||||
* Create a new empty edge
|
||||
*/
|
||||
Edge()
|
||||
{
|
||||
v0 = -1;
|
||||
v1 = -1;
|
||||
t0 = -1;
|
||||
t1 = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A failure to triangulate, hidden from outside and handled
|
||||
*
|
||||
* @author Online Source
|
||||
*/
|
||||
class InternalException extends Exception {
|
||||
/**
|
||||
* Create an internal exception
|
||||
*
|
||||
* @param msg The message describing the exception
|
||||
*/
|
||||
public InternalException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Triangulator#getTriangleCount()
|
||||
*/
|
||||
public int getTriangleCount() {
|
||||
return numTriangles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Triangulator#getTrianglePoint(int, int)
|
||||
*/
|
||||
public float[] getTrianglePoint(int tri, int i) {
|
||||
float xp = pointsX[triangles[tri].v[i]];
|
||||
float yp = pointsY[triangles[tri].v[i]];
|
||||
|
||||
return new float[] {xp,yp};
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Triangulator#startHole()
|
||||
*/
|
||||
public void startHole() {
|
||||
}
|
||||
}
|
||||
113
lib/slick-source/org/newdawn/slick/geom/OverTriangulator.java
Normal file
113
lib/slick-source/org/newdawn/slick/geom/OverTriangulator.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
/**
|
||||
* A triangulator implementation that splits the triangules of another, subdividing
|
||||
* to give a higher tesselation - and hence smoother transitions.
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class OverTriangulator implements Triangulator {
|
||||
/** The triangles data */
|
||||
private float[][] triangles;
|
||||
|
||||
/**
|
||||
* Create a new triangulator
|
||||
*
|
||||
* @param tris The original set of triangles to be sub-dividied
|
||||
*/
|
||||
public OverTriangulator(Triangulator tris) {
|
||||
triangles = new float[tris.getTriangleCount()*6*3][2];
|
||||
|
||||
int tcount = 0;
|
||||
for (int i=0;i<tris.getTriangleCount();i++) {
|
||||
float cx = 0;
|
||||
float cy = 0;
|
||||
for (int p = 0;p < 3;p++) {
|
||||
float[] pt = tris.getTrianglePoint(i, p);
|
||||
cx += pt[0];
|
||||
cy += pt[1];
|
||||
}
|
||||
|
||||
cx /= 3;
|
||||
cy /= 3;
|
||||
|
||||
for (int p = 0;p < 3;p++) {
|
||||
int n = p +1;
|
||||
if (n > 2) {
|
||||
n = 0;
|
||||
}
|
||||
|
||||
float[] pt1 = tris.getTrianglePoint(i, p);
|
||||
float[] pt2 = tris.getTrianglePoint(i, n);
|
||||
|
||||
pt1[0] = (pt1[0] + pt2[0]) / 2;
|
||||
pt1[1] = (pt1[1] + pt2[1]) / 2;
|
||||
|
||||
triangles[(tcount *3) + 0][0] = cx;
|
||||
triangles[(tcount *3) + 0][1] = cy;
|
||||
triangles[(tcount *3) + 1][0] = pt1[0];
|
||||
triangles[(tcount *3) + 1][1] = pt1[1];
|
||||
triangles[(tcount *3) + 2][0] = pt2[0];
|
||||
triangles[(tcount *3) + 2][1] = pt2[1];
|
||||
tcount++;
|
||||
}
|
||||
|
||||
for (int p = 0;p < 3;p++) {
|
||||
int n = p +1;
|
||||
if (n > 2) {
|
||||
n = 0;
|
||||
}
|
||||
|
||||
float[] pt1 = tris.getTrianglePoint(i, p);
|
||||
float[] pt2 = tris.getTrianglePoint(i, n);
|
||||
|
||||
pt2[0] = (pt1[0] + pt2[0]) / 2;
|
||||
pt2[1] = (pt1[1] + pt2[1]) / 2;
|
||||
|
||||
triangles[(tcount *3) + 0][0] = cx;
|
||||
triangles[(tcount *3) + 0][1] = cy;
|
||||
triangles[(tcount *3) + 1][0] = pt1[0];
|
||||
triangles[(tcount *3) + 1][1] = pt1[1];
|
||||
triangles[(tcount *3) + 2][0] = pt2[0];
|
||||
triangles[(tcount *3) + 2][1] = pt2[1];
|
||||
tcount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Triangulator#addPolyPoint(float, float)
|
||||
*/
|
||||
public void addPolyPoint(float x, float y) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Triangulator#getTriangleCount()
|
||||
*/
|
||||
public int getTriangleCount() {
|
||||
return triangles.length / 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Triangulator#getTrianglePoint(int, int)
|
||||
*/
|
||||
public float[] getTrianglePoint(int tri, int i) {
|
||||
float[] pt = triangles[(tri * 3)+i];
|
||||
|
||||
return new float[] {pt[0],pt[1]};
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Triangulator#startHole()
|
||||
*/
|
||||
public void startHole() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Triangulator#triangulate()
|
||||
*/
|
||||
public boolean triangulate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
241
lib/slick-source/org/newdawn/slick/geom/Path.java
Normal file
241
lib/slick-source/org/newdawn/slick/geom/Path.java
Normal file
@@ -0,0 +1,241 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
72
lib/slick-source/org/newdawn/slick/geom/Point.java
Normal file
72
lib/slick-source/org/newdawn/slick/geom/Point.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
import org.newdawn.slick.geom.Shape;
|
||||
import org.newdawn.slick.geom.Transform;
|
||||
|
||||
/**
|
||||
* A single point shape
|
||||
*
|
||||
* @author Kova
|
||||
*/
|
||||
public class Point extends Shape
|
||||
{
|
||||
/**
|
||||
* Create a new point
|
||||
*
|
||||
* @param x The x coordinate of the point
|
||||
* @param y The y coordinate of the point
|
||||
*/
|
||||
public Point(float x, float y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
checkPoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#transform(org.newdawn.slick.geom.Transform)
|
||||
*/
|
||||
public Shape transform(Transform transform)
|
||||
{
|
||||
float result[] = new float[points.length];
|
||||
transform.transform(points, 0, result, 0, points.length / 2);
|
||||
|
||||
return new Point(points[0], points[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#createPoints()
|
||||
*/
|
||||
protected void createPoints()
|
||||
{
|
||||
points = new float[2];
|
||||
points[0] = getX();
|
||||
points[1] = getY();
|
||||
|
||||
maxX = x;
|
||||
maxY = y;
|
||||
minX = x;
|
||||
minY = y;
|
||||
|
||||
findCenter();
|
||||
calculateRadius();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#findCenter()
|
||||
*/
|
||||
protected void findCenter()
|
||||
{
|
||||
center = new float[2];
|
||||
center[0] = points[0];
|
||||
center[1] = points[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#calculateRadius()
|
||||
*/
|
||||
protected void calculateRadius()
|
||||
{
|
||||
boundingCircleRadius = 0;
|
||||
}
|
||||
}
|
||||
200
lib/slick-source/org/newdawn/slick/geom/Polygon.java
Normal file
200
lib/slick-source/org/newdawn/slick/geom/Polygon.java
Normal file
@@ -0,0 +1,200 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A polygon implementation meeting the <code>Shape</code> contract.
|
||||
*
|
||||
* @author Mark
|
||||
*/
|
||||
public class Polygon extends Shape {
|
||||
/** Allow duplicated points */
|
||||
private boolean allowDups = false;
|
||||
/** True if the polygon is closed */
|
||||
private boolean closed = true;
|
||||
|
||||
/**
|
||||
* Construct a new polygon with 3 or more points.
|
||||
* This constructor will take the first set of points and copy them after
|
||||
* the last set of points to create a closed shape.
|
||||
*
|
||||
* @param points An array of points in x, y order.
|
||||
*/
|
||||
public Polygon(float points[]) {
|
||||
int length = points.length;
|
||||
|
||||
this.points = new float[length];
|
||||
maxX = -Float.MIN_VALUE;
|
||||
maxY = -Float.MIN_VALUE;
|
||||
minX = Float.MAX_VALUE;
|
||||
minY = Float.MAX_VALUE;
|
||||
x = Float.MAX_VALUE;
|
||||
y = Float.MAX_VALUE;
|
||||
|
||||
for(int i=0;i<length;i++) {
|
||||
this.points[i] = points[i];
|
||||
if(i % 2 == 0) {
|
||||
if(points[i] > maxX) {
|
||||
maxX = points[i];
|
||||
}
|
||||
if(points[i] < minX) {
|
||||
minX = points[i];
|
||||
}
|
||||
if(points[i] < x) {
|
||||
x = points[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(points[i] > maxY) {
|
||||
maxY = points[i];
|
||||
}
|
||||
if(points[i] < minY) {
|
||||
minY = points[i];
|
||||
}
|
||||
if(points[i] < y) {
|
||||
y = points[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findCenter();
|
||||
calculateRadius();
|
||||
pointsDirty = true;
|
||||
}
|
||||
/**
|
||||
* Create an empty polygon
|
||||
*
|
||||
*/
|
||||
public Polygon(){
|
||||
points = new float[0];
|
||||
maxX = -Float.MIN_VALUE;
|
||||
maxY = -Float.MIN_VALUE;
|
||||
minX = Float.MAX_VALUE;
|
||||
minY = Float.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if duplicate points are allow
|
||||
*
|
||||
* @param allowDups True if duplicate points are allowed
|
||||
*/
|
||||
public void setAllowDuplicatePoints(boolean allowDups) {
|
||||
this.allowDups = allowDups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a point to the polygon
|
||||
*
|
||||
* @param x The x coordinate of the point
|
||||
* @param y The y coordinate of the point
|
||||
*/
|
||||
public void addPoint(float x, float y) {
|
||||
if (hasVertex(x,y) && (!allowDups)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList tempPoints = new ArrayList();
|
||||
for(int i=0;i<points.length;i++) {
|
||||
tempPoints.add(new Float(points[i]));
|
||||
}
|
||||
tempPoints.add(new Float(x));
|
||||
tempPoints.add(new Float(y));
|
||||
int length = tempPoints.size();
|
||||
points = new float[length];
|
||||
for(int i=0;i<length;i++) {
|
||||
points[i] = ((Float)tempPoints.get(i)).floatValue();
|
||||
}
|
||||
if(x > maxX) {
|
||||
maxX = x;
|
||||
}
|
||||
if(y > maxY) {
|
||||
maxY = y;
|
||||
}
|
||||
if(x < minX) {
|
||||
minX = x;
|
||||
}
|
||||
if(y < minY) {
|
||||
minY = y;
|
||||
}
|
||||
findCenter();
|
||||
calculateRadius();
|
||||
|
||||
pointsDirty = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply a transformation and return a new shape. This will not alter the current shape but will
|
||||
* return the transformed shape.
|
||||
*
|
||||
* @param transform The transform to be applied
|
||||
* @return The transformed shape.
|
||||
*/
|
||||
public Shape transform(Transform transform) {
|
||||
checkPoints();
|
||||
|
||||
Polygon resultPolygon = new Polygon();
|
||||
|
||||
float result[] = new float[points.length];
|
||||
transform.transform(points, 0, result, 0, points.length / 2);
|
||||
resultPolygon.points = result;
|
||||
resultPolygon.findCenter();
|
||||
resultPolygon.closed = closed;
|
||||
|
||||
return resultPolygon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#setX(float)
|
||||
*/
|
||||
public void setX(float x) {
|
||||
super.setX(x);
|
||||
|
||||
pointsDirty = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#setY(float)
|
||||
*/
|
||||
public void setY(float y) {
|
||||
super.setY(y);
|
||||
|
||||
pointsDirty = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#createPoints()
|
||||
*/
|
||||
protected void createPoints() {
|
||||
// This is empty since a polygon must have it's points all the time.
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.newdawn.slick.geom.Shape#closed()
|
||||
*/
|
||||
public boolean closed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if the polygon should be closed
|
||||
*
|
||||
* @param closed True if the polygon should be closed
|
||||
*/
|
||||
public void setClosed(boolean closed) {
|
||||
this.closed = closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a copy of this polygon
|
||||
*
|
||||
* @return A copy of this polygon
|
||||
*/
|
||||
public Polygon copy() {
|
||||
float[] copyPoints = new float[points.length];
|
||||
System.arraycopy(points, 0, copyPoints, 0, copyPoints.length);
|
||||
|
||||
return new Polygon(copyPoints);
|
||||
}
|
||||
}
|
||||
269
lib/slick-source/org/newdawn/slick/geom/Rectangle.java
Normal file
269
lib/slick-source/org/newdawn/slick/geom/Rectangle.java
Normal file
@@ -0,0 +1,269 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
/**
|
||||
* An axis oriented used for shape bounds
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public class Rectangle extends Shape {
|
||||
/** The width of the box */
|
||||
protected float width;
|
||||
/** The height of the box */
|
||||
protected float height;
|
||||
|
||||
/**
|
||||
* Create a new bounding box
|
||||
*
|
||||
* @param x The x position of the box
|
||||
* @param y The y position of the box
|
||||
* @param width The width of the box
|
||||
* @param height The hieght of the box
|
||||
*/
|
||||
public Rectangle(float x, float y, float width, float height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
maxX = x+width;
|
||||
maxY = y+height;
|
||||
checkPoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this rectangle contains a point
|
||||
*
|
||||
* @param xp The x coordinate of the point to check
|
||||
* @param yp The y coordinate of the point to check
|
||||
* @return True if the point is within the rectangle
|
||||
*/
|
||||
public boolean contains(float xp, float yp) {
|
||||
if (xp <= getX()) {
|
||||
return false;
|
||||
}
|
||||
if (yp <= getY()) {
|
||||
return false;
|
||||
}
|
||||
if (xp >= maxX) {
|
||||
return false;
|
||||
}
|
||||
if (yp >= maxY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bounds of this rectangle based on the given rectangle
|
||||
*
|
||||
* @param other The other rectangle whose bounds should be applied
|
||||
*/
|
||||
public void setBounds(Rectangle other) {
|
||||
setBounds(other.getX(), other.getY(), other.getWidth(), other.getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bounds of this rectangle
|
||||
*
|
||||
* @param x The x coordinate of this rectangle
|
||||
* @param y The y coordinate of this rectangle
|
||||
* @param width The width to set in this rectangle
|
||||
* @param height The height to set in this rectangle
|
||||
*/
|
||||
public void setBounds(float x, float y, float width, float height) {
|
||||
setX(x);
|
||||
setY(y);
|
||||
setSize(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the size (widtha and height) of this rectangle
|
||||
*
|
||||
* @param width The width to set in this rectangle
|
||||
* @param height The height to set in this rectangle
|
||||
*/
|
||||
public void setSize(float width, float height) {
|
||||
setWidth(width);
|
||||
setHeight(height);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the width of the box
|
||||
*
|
||||
* @return The width of the box
|
||||
*/
|
||||
public float getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the box
|
||||
*
|
||||
* @return The height of the box
|
||||
*/
|
||||
public float getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow the rectangle at all edges by the given amounts. This will result in the
|
||||
* rectangle getting larger around it's centre.
|
||||
*
|
||||
* @param h The amount to adjust horizontally
|
||||
* @param v The amount to ajust vertically
|
||||
*/
|
||||
public void grow(float h, float v) {
|
||||
setX(getX() - h);
|
||||
setY(getY() - v);
|
||||
setWidth(getWidth() + (h*2));
|
||||
setHeight(getHeight() + (v*2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow the rectangle based on scaling it's size
|
||||
*
|
||||
* @param h The scale to apply to the horizontal
|
||||
* @param v The scale to appy to the vertical
|
||||
*/
|
||||
public void scaleGrow(float h, float v) {
|
||||
grow(getWidth() * (h-1), getHeight() * (v-1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the width of this box
|
||||
*
|
||||
* @param width The new width of this box
|
||||
*/
|
||||
public void setWidth(float width) {
|
||||
if (width != this.width) {
|
||||
pointsDirty = true;
|
||||
this.width = width;
|
||||
maxX = x+width;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the heightof this box
|
||||
*
|
||||
* @param height The height of this box
|
||||
*/
|
||||
public void setHeight(float height) {
|
||||
if (height != this.height) {
|
||||
pointsDirty = true;
|
||||
this.height = height;
|
||||
maxY = y+height;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this box touches another
|
||||
*
|
||||
* @param shape The other shape to check against
|
||||
* @return True if the rectangles touch
|
||||
*/
|
||||
public boolean intersects(Shape shape) {
|
||||
if(shape instanceof Rectangle) {
|
||||
Rectangle other = (Rectangle)shape;
|
||||
if ((x > (other.x + other.width)) || ((x + width) < other.x)) {
|
||||
return false;
|
||||
}
|
||||
if ((y > (other.y + other.height)) || ((y + height) < other.y)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if(shape instanceof Circle) {
|
||||
return intersects((Circle)shape);
|
||||
}
|
||||
else {
|
||||
return super.intersects(shape);
|
||||
}
|
||||
}
|
||||
|
||||
protected void createPoints() {
|
||||
float useWidth = width ;
|
||||
float useHeight = height;
|
||||
points = new float[8];
|
||||
|
||||
points[0] = x;
|
||||
points[1] = y;
|
||||
|
||||
points[2] = x + useWidth;
|
||||
points[3] = y;
|
||||
|
||||
points[4] = x + useWidth;
|
||||
points[5] = y + useHeight;
|
||||
|
||||
points[6] = x;
|
||||
points[7] = y + useHeight;
|
||||
|
||||
maxX = points[2];
|
||||
maxY = points[5];
|
||||
minX = points[0];
|
||||
minY = points[1];
|
||||
|
||||
findCenter();
|
||||
calculateRadius();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a circle touches this rectangle
|
||||
*
|
||||
* @param other The circle to check against
|
||||
* @return True if they touch
|
||||
*/
|
||||
private boolean intersects(Circle other) {
|
||||
return other.intersects(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return "[Rectangle "+width+"x"+height+"]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a rectangle contains a point (static to use it everywhere)
|
||||
*
|
||||
* @param xp
|
||||
* The x coordinate of the point to check
|
||||
* @param yp
|
||||
* The y coordinate of the point to check
|
||||
* @param xr
|
||||
* The x coordinate of the rectangle
|
||||
* @param yr
|
||||
* The y coordinate of the rectangle
|
||||
* @param widthr
|
||||
* The width of the rectangle
|
||||
* @param heightr The height of the rectangle
|
||||
* @return True if the point is within the rectangle
|
||||
*/
|
||||
public static boolean contains(float xp, float yp, float xr, float yr,
|
||||
float widthr, float heightr) {
|
||||
return (xp >= xr) && (yp >= yr) && (xp <= xr + widthr)
|
||||
&& (yp <= yr + heightr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a transformation and return a new shape. This will not alter the current shape but will
|
||||
* return the transformed shape.
|
||||
*
|
||||
* @param transform The transform to be applied
|
||||
* @return The transformed shape.
|
||||
*/
|
||||
public Shape transform(Transform transform) {
|
||||
checkPoints();
|
||||
|
||||
Polygon resultPolygon = new Polygon();
|
||||
|
||||
float result[] = new float[points.length];
|
||||
transform.transform(points, 0, result, 0, points.length / 2);
|
||||
resultPolygon.points = result;
|
||||
resultPolygon.findCenter();
|
||||
resultPolygon.checkPoints();
|
||||
|
||||
return resultPolygon;
|
||||
}
|
||||
}
|
||||
284
lib/slick-source/org/newdawn/slick/geom/RoundedRectangle.java
Normal file
284
lib/slick-source/org/newdawn/slick/geom/RoundedRectangle.java
Normal file
@@ -0,0 +1,284 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.newdawn.slick.util.FastTrig;
|
||||
|
||||
/**
|
||||
* Class to create rounded rectangles with.
|
||||
*
|
||||
* @author Mark Bernard
|
||||
*/
|
||||
public class RoundedRectangle extends Rectangle {
|
||||
/** Indicates the top left corner should be rounded */
|
||||
public static final int TOP_LEFT = 1;
|
||||
/** Indicates the top right corner should be rounded */
|
||||
public static final int TOP_RIGHT = 2;
|
||||
/** Indicates the bottom right corner should be rounded */
|
||||
public static final int BOTTOM_RIGHT = 4;
|
||||
/** Indicates the bottom left corner should be rounded */
|
||||
public static final int BOTTOM_LEFT = 8;
|
||||
/** Indicates the all cornders should be rounded */
|
||||
public static final int ALL = TOP_LEFT | TOP_RIGHT | BOTTOM_RIGHT | BOTTOM_LEFT;
|
||||
|
||||
/** Default number of segments to draw the rounded corners with */
|
||||
private static final int DEFAULT_SEGMENT_COUNT = 25;
|
||||
|
||||
/** radius of each corner */
|
||||
private float cornerRadius;
|
||||
/** number of segments for each corner */
|
||||
private int segmentCount;
|
||||
/** The flags indicating which corners should be rounded */
|
||||
private int cornerFlags;
|
||||
|
||||
/**
|
||||
* Construct a rectangle with rounded corners.
|
||||
*
|
||||
* @param x The x position of the rectangle.
|
||||
* @param y The y position of the rectangle.
|
||||
* @param width The width of the rectangle.
|
||||
* @param height The hieght of the rectangle.
|
||||
* @param cornerRadius The radius to use for the arc in each corner.
|
||||
*/
|
||||
public RoundedRectangle(float x, float y, float width, float height, float cornerRadius) {
|
||||
this(x, y, width, height, cornerRadius, DEFAULT_SEGMENT_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a rectangle with rounded corners.
|
||||
*
|
||||
* @param x The x position of the rectangle.
|
||||
* @param y The y position of the rectangle.
|
||||
* @param width The width of the rectangle.
|
||||
* @param height The hieght of the rectangle.
|
||||
* @param cornerRadius The radius to use for the arc in each corner.
|
||||
* @param segmentCount The number of segments to use to draw each corner arc.
|
||||
*/
|
||||
public RoundedRectangle(float x, float y, float width, float height, float cornerRadius, int segmentCount) {
|
||||
this(x,y,width,height,cornerRadius,segmentCount,ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a rectangle with rounded corners.
|
||||
*
|
||||
* @param x The x position of the rectangle.
|
||||
* @param y The y position of the rectangle.
|
||||
* @param width The width of the rectangle.
|
||||
* @param height The hieght of the rectangle.
|
||||
* @param cornerRadius The radius to use for the arc in each corner.
|
||||
* @param segmentCount The number of segments to use to draw each corner arc.
|
||||
* @param cornerFlags Indicates which corners should be rounded
|
||||
*/
|
||||
public RoundedRectangle(float x, float y, float width, float height,
|
||||
float cornerRadius, int segmentCount, int cornerFlags) {
|
||||
super(x,y,width,height);
|
||||
|
||||
if(cornerRadius < 0) {
|
||||
throw new IllegalArgumentException("corner radius must be >= 0");
|
||||
}
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.cornerRadius = cornerRadius;
|
||||
this.segmentCount = segmentCount;
|
||||
this.pointsDirty = true;
|
||||
this.cornerFlags = cornerFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the radius for each corner.
|
||||
*
|
||||
* @return The radius for each corner.
|
||||
*/
|
||||
public float getCornerRadius() {
|
||||
return cornerRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the radius for each corner.
|
||||
*
|
||||
* @param cornerRadius The radius for each corner to set.
|
||||
*/
|
||||
public void setCornerRadius(float cornerRadius) {
|
||||
if (cornerRadius >= 0) {
|
||||
if (cornerRadius != this.cornerRadius) {
|
||||
this.cornerRadius = cornerRadius;
|
||||
pointsDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of this rectangle.
|
||||
*
|
||||
* @return The height of this rectangle.
|
||||
*/
|
||||
public float getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the height of this rectangle.
|
||||
*
|
||||
* @param height The height to set.
|
||||
*/
|
||||
public void setHeight(float height) {
|
||||
if (this.height != height) {
|
||||
this.height = height;
|
||||
pointsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of this rectangle.
|
||||
*
|
||||
* @return The width of this rectangle.
|
||||
*/
|
||||
public float getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the width of this rectangle.
|
||||
*
|
||||
* @param width The width to set.
|
||||
*/
|
||||
public void setWidth(float width) {
|
||||
if (width != this.width) {
|
||||
this.width = width;
|
||||
pointsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void createPoints() {
|
||||
maxX = x + width;
|
||||
maxY = y + height;
|
||||
minX = x;
|
||||
minY = y;
|
||||
float useWidth = width - 1;
|
||||
float useHeight = height - 1;
|
||||
if(cornerRadius == 0) {
|
||||
points = new float[8];
|
||||
|
||||
points[0] = x;
|
||||
points[1] = y;
|
||||
|
||||
points[2] = x + useWidth;
|
||||
points[3] = y;
|
||||
|
||||
points[4] = x + useWidth;
|
||||
points[5] = y + useHeight;
|
||||
|
||||
points[6] = x;
|
||||
points[7] = y + useHeight;
|
||||
}
|
||||
else {
|
||||
float doubleRadius = cornerRadius * 2;
|
||||
if(doubleRadius > useWidth) {
|
||||
doubleRadius = useWidth;
|
||||
cornerRadius = doubleRadius / 2;
|
||||
}
|
||||
if(doubleRadius > useHeight) {
|
||||
doubleRadius = useHeight;
|
||||
cornerRadius = doubleRadius / 2;
|
||||
}
|
||||
|
||||
ArrayList tempPoints = new ArrayList();
|
||||
//the outer most set of points for each arc will also ac as the points that start the
|
||||
//straight sides, so the straight sides do not have to be added.
|
||||
|
||||
//top left corner arc
|
||||
if ((cornerFlags & TOP_LEFT) != 0) {
|
||||
tempPoints.addAll(createPoints(segmentCount, cornerRadius, x + cornerRadius, y + cornerRadius, 180, 270));
|
||||
} else {
|
||||
tempPoints.add(new Float(x));
|
||||
tempPoints.add(new Float(y));
|
||||
}
|
||||
|
||||
//top right corner arc
|
||||
if ((cornerFlags & TOP_RIGHT) != 0) {
|
||||
tempPoints.addAll(createPoints(segmentCount, cornerRadius, x + useWidth - cornerRadius, y + cornerRadius, 270, 360));
|
||||
} else {
|
||||
tempPoints.add(new Float(x+useWidth));
|
||||
tempPoints.add(new Float(y));
|
||||
}
|
||||
|
||||
//bottom right corner arc
|
||||
if ((cornerFlags & BOTTOM_RIGHT) != 0) {
|
||||
tempPoints.addAll(createPoints(segmentCount, cornerRadius, x + useWidth - cornerRadius, y + useHeight - cornerRadius, 0, 90));
|
||||
} else {
|
||||
tempPoints.add(new Float(x+useWidth));
|
||||
tempPoints.add(new Float(y+useHeight));
|
||||
}
|
||||
|
||||
//bottom left corner arc
|
||||
if ((cornerFlags & BOTTOM_LEFT) != 0) {
|
||||
tempPoints.addAll(createPoints(segmentCount, cornerRadius, x + cornerRadius, y + useHeight - cornerRadius, 90, 180));
|
||||
} else {
|
||||
tempPoints.add(new Float(x));
|
||||
tempPoints.add(new Float(y+useHeight));
|
||||
}
|
||||
|
||||
points = new float[tempPoints.size()];
|
||||
for(int i=0;i<tempPoints.size();i++) {
|
||||
points[i] = ((Float)tempPoints.get(i)).floatValue();
|
||||
}
|
||||
}
|
||||
|
||||
findCenter();
|
||||
calculateRadius();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the points to fill a corner arc.
|
||||
*
|
||||
* @param numberOfSegments How fine to make the ellipse.
|
||||
* @param radius The radius of the arc.
|
||||
* @param cx The x center of the arc.
|
||||
* @param cy The y center of the arc.
|
||||
* @param start The start angle of the arc.
|
||||
* @param end The end angle of the arc.
|
||||
* @return The points created.
|
||||
*/
|
||||
private List createPoints(int numberOfSegments, float radius, float cx, float cy, float start, float end) {
|
||||
ArrayList tempPoints = new ArrayList();
|
||||
|
||||
int step = 360 / numberOfSegments;
|
||||
|
||||
for (float a=start;a<=end+step;a+=step) {
|
||||
float ang = a;
|
||||
if (ang > end) {
|
||||
ang = end;
|
||||
}
|
||||
float x = (float) (cx + (FastTrig.cos(Math.toRadians(ang)) * radius));
|
||||
float y = (float) (cy + (FastTrig.sin(Math.toRadians(ang)) * radius));
|
||||
|
||||
tempPoints.add(new Float(x));
|
||||
tempPoints.add(new Float(y));
|
||||
}
|
||||
|
||||
return tempPoints;
|
||||
}
|
||||
/**
|
||||
* Apply a transformation and return a new shape. This will not alter the current shape but will
|
||||
* return the transformed shape.
|
||||
*
|
||||
* @param transform The transform to be applied
|
||||
* @return The transformed shape.
|
||||
*/
|
||||
public Shape transform(Transform transform) {
|
||||
checkPoints();
|
||||
|
||||
Polygon resultPolygon = new Polygon();
|
||||
|
||||
float result[] = new float[points.length];
|
||||
transform.transform(points, 0, result, 0, points.length / 2);
|
||||
resultPolygon.points = result;
|
||||
resultPolygon.findCenter();
|
||||
|
||||
return resultPolygon;
|
||||
}
|
||||
|
||||
}
|
||||
767
lib/slick-source/org/newdawn/slick/geom/Shape.java
Normal file
767
lib/slick-source/org/newdawn/slick/geom/Shape.java
Normal file
@@ -0,0 +1,767 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* The description of any 2D shape that can be transformed. The points provided approximate the intent
|
||||
* of the shape.
|
||||
*
|
||||
* @author Mark
|
||||
*/
|
||||
public abstract class Shape implements Serializable {
|
||||
/** The points representing this polygon. */
|
||||
protected float points[];
|
||||
/** Center point of the polygon. */
|
||||
protected float center[];
|
||||
/** The left most point of this shape. */
|
||||
protected float x;
|
||||
/** The top most point of this shape. */
|
||||
protected float y;
|
||||
/** The right most point of this shape */
|
||||
protected float maxX;
|
||||
/** The bottom most point of this shape */
|
||||
protected float maxY;
|
||||
/** The left most point of this shape. */
|
||||
protected float minX;
|
||||
/** The top most point of this shape. */
|
||||
protected float minY;
|
||||
/** Radius of a circle that can completely enclose this shape. */
|
||||
protected float boundingCircleRadius;
|
||||
/** Flag to tell whether points need to be generated */
|
||||
protected boolean pointsDirty;
|
||||
/** The triangles that define the shape */
|
||||
protected transient Triangulator tris;
|
||||
/** True if the triangles need updating */
|
||||
protected boolean trianglesDirty;
|
||||
|
||||
/**
|
||||
* Shape constructor.
|
||||
*
|
||||
*/
|
||||
public Shape() {
|
||||
pointsDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the top-left location of this shape
|
||||
*
|
||||
* @param x The x coordinate of the new location of the shape
|
||||
* @param y The y coordinate of the new location of the shape
|
||||
*/
|
||||
public void setLocation(float x, float y) {
|
||||
setX(x);
|
||||
setY(y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a transformation and return a new shape. This will not alter the current shape but will
|
||||
* return the transformed shape.
|
||||
*
|
||||
* @param transform The transform to be applied
|
||||
* @return The transformed shape.
|
||||
*/
|
||||
public abstract Shape transform(Transform transform);
|
||||
|
||||
/**
|
||||
* Subclasses implement this to create the points of the shape.
|
||||
*
|
||||
*/
|
||||
protected abstract void createPoints();
|
||||
|
||||
/**
|
||||
* Get the x location of the left side of this shape.
|
||||
*
|
||||
* @return The x location of the left side of this shape.
|
||||
*/
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the x position of the left side this shape.
|
||||
*
|
||||
* @param x The new x position of the left side this shape.
|
||||
*/
|
||||
public void setX(float x) {
|
||||
if (x != this.x) {
|
||||
float dx = x - this.x;
|
||||
this.x = x;
|
||||
|
||||
if ((points == null) || (center == null)) {
|
||||
checkPoints();
|
||||
}
|
||||
// update the points in the special case
|
||||
for (int i=0;i<points.length/2;i++) {
|
||||
points[i*2] += dx;
|
||||
}
|
||||
center[0] += dx;
|
||||
x += dx;
|
||||
maxX += dx;
|
||||
minX += dx;
|
||||
trianglesDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the y position of the top of this shape.
|
||||
*
|
||||
* @param y The new y position of the top of this shape.
|
||||
*/
|
||||
public void setY(float y) {
|
||||
if (y != this.y) {
|
||||
float dy = y - this.y;
|
||||
this.y = y;
|
||||
|
||||
if ((points == null) || (center == null)) {
|
||||
checkPoints();
|
||||
}
|
||||
// update the points in the special case
|
||||
for (int i=0;i<points.length/2;i++) {
|
||||
points[(i*2)+1] += dy;
|
||||
}
|
||||
center[1] += dy;
|
||||
y += dy;
|
||||
maxY += dy;
|
||||
minY += dy;
|
||||
trianglesDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the y position of the top of this shape.
|
||||
*
|
||||
* @return The y position of the top of this shape.
|
||||
*/
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the top-left location of this shape.
|
||||
*
|
||||
* @return The coordinate of the top-left of this shape
|
||||
*/
|
||||
public Vector2f getLocation() {
|
||||
return new Vector2f(getX(), getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the top-left location of this shape
|
||||
*
|
||||
* @param loc The new coordinate of the top-left of this shape
|
||||
*/
|
||||
public void setLocation(Vector2f loc) {
|
||||
setX(loc.x);
|
||||
setY(loc.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the x center of this shape.
|
||||
*
|
||||
* @return The x center of this shape.
|
||||
*/
|
||||
public float getCenterX() {
|
||||
checkPoints();
|
||||
|
||||
return center[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the x center of this shape.
|
||||
*
|
||||
* @param centerX The center point to set.
|
||||
*/
|
||||
public void setCenterX(float centerX) {
|
||||
if ((points == null) || (center == null)) {
|
||||
checkPoints();
|
||||
}
|
||||
|
||||
float xDiff = centerX - getCenterX();
|
||||
setX(x + xDiff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the y center of this shape.
|
||||
*
|
||||
* @return The y center of this shape.
|
||||
*/
|
||||
public float getCenterY() {
|
||||
checkPoints();
|
||||
|
||||
return center[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the y center of this shape.
|
||||
*
|
||||
* @param centerY The center point to set.
|
||||
*/
|
||||
public void setCenterY(float centerY) {
|
||||
if ((points == null) || (center == null)) {
|
||||
checkPoints();
|
||||
}
|
||||
|
||||
float yDiff = centerY - getCenterY();
|
||||
setY(y + yDiff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the right most point of this shape.
|
||||
*
|
||||
* @return The right most point of this shape.
|
||||
*/
|
||||
public float getMaxX() {
|
||||
checkPoints();
|
||||
return maxX;
|
||||
}
|
||||
/**
|
||||
* Get the bottom most point of this shape.
|
||||
*
|
||||
* @return The bottom most point of this shape.
|
||||
*/
|
||||
public float getMaxY() {
|
||||
checkPoints();
|
||||
return maxY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the left most point of this shape.
|
||||
*
|
||||
* @return The left most point of this shape.
|
||||
*/
|
||||
public float getMinX() {
|
||||
checkPoints();
|
||||
return minX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the top most point of this shape.
|
||||
*
|
||||
* @return The top most point of this shape.
|
||||
*/
|
||||
public float getMinY() {
|
||||
checkPoints();
|
||||
return minY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the radius of a circle that can completely enclose this shape.
|
||||
*
|
||||
* @return The radius of the circle.
|
||||
*/
|
||||
public float getBoundingCircleRadius() {
|
||||
checkPoints();
|
||||
return boundingCircleRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the point closet to the center of all the points in this Shape
|
||||
*
|
||||
* @return The x,y coordinates of the center.
|
||||
*/
|
||||
public float[] getCenter() {
|
||||
checkPoints();
|
||||
return center;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the points that outline this shape. Use CW winding rule
|
||||
*
|
||||
* @return an array of x,y points
|
||||
*/
|
||||
public float[] getPoints() {
|
||||
checkPoints();
|
||||
return points;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of points in this polygon
|
||||
*
|
||||
* @return The number of points in this polygon
|
||||
*/
|
||||
public int getPointCount() {
|
||||
checkPoints();
|
||||
return points.length / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single point in this polygon
|
||||
*
|
||||
* @param index The index of the point to retrieve
|
||||
* @return The point's coordinates
|
||||
*/
|
||||
public float[] getPoint(int index) {
|
||||
checkPoints();
|
||||
|
||||
float result[] = new float[2];
|
||||
|
||||
result[0] = points[index * 2];
|
||||
result[1] = points[index * 2 + 1];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the combine normal of a given point
|
||||
*
|
||||
* @param index The index of the point whose normal should be retrieved
|
||||
* @return The combined normal of a given point
|
||||
*/
|
||||
public float[] getNormal(int index) {
|
||||
float[] current = getPoint(index);
|
||||
float[] prev = getPoint(index - 1 < 0 ? getPointCount() - 1 : index - 1);
|
||||
float[] next = getPoint(index + 1 >= getPointCount() ? 0 : index + 1);
|
||||
|
||||
float[] t1 = getNormal(prev, current);
|
||||
float[] t2 = getNormal(current, next);
|
||||
|
||||
if ((index == 0) && (!closed())) {
|
||||
return t2;
|
||||
}
|
||||
if ((index == getPointCount()-1) && (!closed())) {
|
||||
return t1;
|
||||
}
|
||||
|
||||
float tx = (t1[0]+t2[0])/2;
|
||||
float ty = (t1[1]+t2[1])/2;
|
||||
float len = (float) Math.sqrt((tx*tx)+(ty*ty));
|
||||
return new float[] {tx/len,ty/len};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the shape passed is entirely contained within
|
||||
* this shape.
|
||||
*
|
||||
* @param other The other shape to test against this one
|
||||
* @return True if the other shape supplied is entirely contained
|
||||
* within this one.
|
||||
*/
|
||||
public boolean contains(Shape other) {
|
||||
if (other.intersects(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i=0;i<other.getPointCount();i++) {
|
||||
float[] pt = other.getPoint(i);
|
||||
if (!contains(pt[0], pt[1])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Get the normal of the line between two points
|
||||
*
|
||||
* @param start The start point
|
||||
* @param end The end point
|
||||
* @return The normal of the line between the two points
|
||||
*/
|
||||
private float[] getNormal(float[] start, float[] end) {
|
||||
float dx = start[0] - end[0];
|
||||
float dy = start[1] - end[1];
|
||||
float len = (float) Math.sqrt((dx*dx)+(dy*dy));
|
||||
dx /= len;
|
||||
dy /= len;
|
||||
return new float[] {-dy,dx};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given point is part of the path that
|
||||
* forms this shape
|
||||
*
|
||||
* @param x The x position of the point to check
|
||||
* @param y The y position of the point to check
|
||||
* @return True if the point is includes in the path of the polygon
|
||||
*/
|
||||
public boolean includes(float x, float y) {
|
||||
if (points.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
checkPoints();
|
||||
|
||||
Line testLine = new Line(0,0,0,0);
|
||||
Vector2f pt = new Vector2f(x,y);
|
||||
|
||||
for (int i=0;i<points.length;i+=2) {
|
||||
int n = i+2;
|
||||
if (n >= points.length) {
|
||||
n = 0;
|
||||
}
|
||||
testLine.set(points[i], points[i+1], points[n], points[n+1]);
|
||||
|
||||
if (testLine.on(pt)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of a given point
|
||||
*
|
||||
* @param x The x coordinate of the point
|
||||
* @param y The y coordinate of the point
|
||||
* @return The index of the point or -1 if the point is not part of this shape path
|
||||
*/
|
||||
public int indexOf(float x, float y) {
|
||||
for (int i=0;i<points.length;i+=2) {
|
||||
if ((points[i] == x) && (points[i+1] == y)) {
|
||||
return i / 2;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this polygon contains the given point
|
||||
*
|
||||
* @param x The x position of the point to check
|
||||
* @param y The y position of the point to check
|
||||
* @return True if the point is contained in the polygon
|
||||
*/
|
||||
public boolean contains(float x, float y) {
|
||||
checkPoints();
|
||||
if (points.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = false;
|
||||
float xnew,ynew;
|
||||
float xold,yold;
|
||||
float x1,y1;
|
||||
float x2,y2;
|
||||
int npoints = points.length;
|
||||
|
||||
xold=points[npoints - 2];
|
||||
yold=points[npoints - 1];
|
||||
for (int i=0;i < npoints;i+=2) {
|
||||
xnew = points[i];
|
||||
ynew = points[i + 1];
|
||||
if (xnew > xold) {
|
||||
x1 = xold;
|
||||
x2 = xnew;
|
||||
y1 = yold;
|
||||
y2 = ynew;
|
||||
}
|
||||
else {
|
||||
x1 = xnew;
|
||||
x2 = xold;
|
||||
y1 = ynew;
|
||||
y2 = yold;
|
||||
}
|
||||
if ((xnew < x) == (x <= xold) /* edge "open" at one end */
|
||||
&& ((double)y - (double)y1) * (x2 - x1)
|
||||
< ((double)y2 - (double)y1) * (x - x1)) {
|
||||
result = !result;
|
||||
}
|
||||
xold = xnew;
|
||||
yold = ynew;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this shape intersects with the shape provided.
|
||||
*
|
||||
* @param shape The shape to check if it intersects with this one.
|
||||
* @return True if the shapes do intersect, false otherwise.
|
||||
*/
|
||||
public boolean intersects(Shape shape) {
|
||||
/*
|
||||
* Intersection formula used:
|
||||
* (x4 - x3)(y1 - y3) - (y4 - y3)(x1 - x3)
|
||||
* UA = ---------------------------------------
|
||||
* (y4 - y3)(x2 - x1) - (x4 - x3)(y2 - y1)
|
||||
*
|
||||
* (x2 - x1)(y1 - y3) - (y2 - y1)(x1 - x3)
|
||||
* UB = ---------------------------------------
|
||||
* (y4 - y3)(x2 - x1) - (x4 - x3)(y2 - y1)
|
||||
*
|
||||
* if UA and UB are both between 0 and 1 then the lines intersect.
|
||||
*
|
||||
* Source: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
|
||||
*/
|
||||
checkPoints();
|
||||
|
||||
boolean result = false;
|
||||
float points[] = getPoints(); // (x3, y3) and (x4, y4)
|
||||
float thatPoints[] = shape.getPoints(); // (x1, y1) and (x2, y2)
|
||||
int length = points.length;
|
||||
int thatLength = thatPoints.length;
|
||||
double unknownA;
|
||||
double unknownB;
|
||||
|
||||
if (!closed()) {
|
||||
length -= 2;
|
||||
}
|
||||
if (!shape.closed()) {
|
||||
thatLength -= 2;
|
||||
}
|
||||
|
||||
// x1 = thatPoints[j]
|
||||
// x2 = thatPoints[j + 2]
|
||||
// y1 = thatPoints[j + 1]
|
||||
// y2 = thatPoints[j + 3]
|
||||
// x3 = points[i]
|
||||
// x4 = points[i + 2]
|
||||
// y3 = points[i + 1]
|
||||
// y4 = points[i + 3]
|
||||
for(int i=0;i<length;i+=2) {
|
||||
int iNext = i+2;
|
||||
if (iNext >= points.length) {
|
||||
iNext = 0;
|
||||
}
|
||||
|
||||
for(int j=0;j<thatLength;j+=2) {
|
||||
int jNext = j+2;
|
||||
if (jNext >= thatPoints.length) {
|
||||
jNext = 0;
|
||||
}
|
||||
|
||||
unknownA = (((points[iNext] - points[i]) * (double) (thatPoints[j + 1] - points[i + 1])) -
|
||||
((points[iNext+1] - points[i + 1]) * (thatPoints[j] - points[i]))) /
|
||||
(((points[iNext+1] - points[i + 1]) * (thatPoints[jNext] - thatPoints[j])) -
|
||||
((points[iNext] - points[i]) * (thatPoints[jNext+1] - thatPoints[j + 1])));
|
||||
unknownB = (((thatPoints[jNext] - thatPoints[j]) * (double) (thatPoints[j + 1] - points[i + 1])) -
|
||||
((thatPoints[jNext+1] - thatPoints[j + 1]) * (thatPoints[j] - points[i]))) /
|
||||
(((points[iNext+1] - points[i + 1]) * (thatPoints[jNext] - thatPoints[j])) -
|
||||
((points[iNext] - points[i]) * (thatPoints[jNext+1] - thatPoints[j + 1])));
|
||||
|
||||
if(unknownA >= 0 && unknownA <= 1 && unknownB >= 0 && unknownB <= 1) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a particular location is a vertex of this polygon
|
||||
*
|
||||
* @param x The x coordinate to check
|
||||
* @param y The y coordinate to check
|
||||
* @return True if the cordinates supplied are a vertex of this polygon
|
||||
*/
|
||||
public boolean hasVertex(float x, float y) {
|
||||
if (points.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
checkPoints();
|
||||
|
||||
for (int i=0;i<points.length;i+=2) {
|
||||
if ((points[i] == x) && (points[i+1] == y)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the center of this polygon.
|
||||
*
|
||||
*/
|
||||
protected void findCenter() {
|
||||
center = new float[]{0, 0};
|
||||
int length = points.length;
|
||||
for(int i=0;i<length;i+=2) {
|
||||
center[0] += points[i];
|
||||
center[1] += points[i + 1];
|
||||
}
|
||||
center[0] /= (length / 2);
|
||||
center[1] /= (length / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the radius of a circle that can completely enclose this shape.
|
||||
*
|
||||
*/
|
||||
protected void calculateRadius() {
|
||||
boundingCircleRadius = 0;
|
||||
|
||||
for(int i=0;i<points.length;i+=2) {
|
||||
float temp = ((points[i] - center[0]) * (points[i] - center[0])) +
|
||||
((points[i + 1] - center[1]) * (points[i + 1] - center[1]));
|
||||
boundingCircleRadius = (boundingCircleRadius > temp) ? boundingCircleRadius : temp;
|
||||
}
|
||||
boundingCircleRadius = (float)Math.sqrt(boundingCircleRadius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the triangles that can fill this shape
|
||||
*/
|
||||
protected void calculateTriangles() {
|
||||
if ((!trianglesDirty) && (tris != null)) {
|
||||
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;
|
||||
|
||||
tris = new NeatTriangulator();
|
||||
for (int i=0;i<points.length;i+=2) {
|
||||
tris.addPolyPoint(points[i], points[i+1]);
|
||||
}
|
||||
tris.triangulate();
|
||||
}
|
||||
|
||||
trianglesDirty = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase triangulation
|
||||
*/
|
||||
public void increaseTriangulation() {
|
||||
checkPoints();
|
||||
calculateTriangles();
|
||||
|
||||
tris = new OverTriangulator(tris);
|
||||
}
|
||||
|
||||
/**
|
||||
* The triangles that define the filled version of this shape
|
||||
*
|
||||
* @return The triangles that define the
|
||||
*/
|
||||
public Triangulator getTriangles() {
|
||||
checkPoints();
|
||||
calculateTriangles();
|
||||
return tris;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the dirty flag and create points as necessary.
|
||||
*/
|
||||
protected final void checkPoints() {
|
||||
if (pointsDirty) {
|
||||
createPoints();
|
||||
findCenter();
|
||||
calculateRadius();
|
||||
|
||||
if (points.length > 0) {
|
||||
maxX = points[0];
|
||||
maxY = points[1];
|
||||
minX = points[0];
|
||||
minY = points[1];
|
||||
for (int i=0;i<points.length/2;i++) {
|
||||
maxX = Math.max(points[i*2],maxX);
|
||||
maxY = Math.max(points[(i*2)+1],maxY);
|
||||
minX = Math.min(points[i*2],minX);
|
||||
minY = Math.min(points[(i*2)+1],minY);
|
||||
}
|
||||
}
|
||||
pointsDirty = false;
|
||||
trianglesDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cause all internal state to be generated and cached
|
||||
*/
|
||||
public void preCache() {
|
||||
checkPoints();
|
||||
getTriangles();
|
||||
}
|
||||
|
||||
/**
|
||||
* True if this is a closed shape
|
||||
*
|
||||
* @return True if this is a closed shape
|
||||
*/
|
||||
public boolean closed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prune any required points in this shape
|
||||
*
|
||||
* @return The new shape with points pruned
|
||||
*/
|
||||
public Shape prune() {
|
||||
Polygon result = new Polygon();
|
||||
|
||||
for (int i=0;i<getPointCount();i++) {
|
||||
int next = i+1 >= getPointCount() ? 0 : i+1;
|
||||
int prev = i-1 < 0 ? getPointCount() - 1 : i-1;
|
||||
|
||||
float dx1 = getPoint(i)[0] - getPoint(prev)[0];
|
||||
float dy1 = getPoint(i)[1] - getPoint(prev)[1];
|
||||
float dx2 = getPoint(next)[0] - getPoint(i)[0];
|
||||
float dy2 = getPoint(next)[1] - getPoint(i)[1];
|
||||
|
||||
float len1 = (float) Math.sqrt((dx1*dx1) + (dy1*dy1));
|
||||
float len2 = (float) Math.sqrt((dx2*dx2) + (dy2*dy2));
|
||||
dx1 /= len1;
|
||||
dy1 /= len1;
|
||||
dx2 /= len2;
|
||||
dy2 /= len2;
|
||||
|
||||
if ((dx1 != dx2) || (dy1 != dy2)) {
|
||||
result.addPoint(getPoint(i)[0],getPoint(i)[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract the given shape from this one. Note that this method only deals
|
||||
* with edges, it will not create holes in polygons.
|
||||
*
|
||||
* @param other The other shape to subtract from this one
|
||||
* @return The newly created set of shapes resulting from the operation
|
||||
*/
|
||||
public Shape[] subtract(Shape other) {
|
||||
return new GeomUtil().subtract(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join this shape with another.
|
||||
*
|
||||
* @param other The other shape to join with this one
|
||||
* @return The newly created set of shapes resulting from the operation
|
||||
*/
|
||||
public Shape[] union(Shape other) {
|
||||
return new GeomUtil().union(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the shape
|
||||
*
|
||||
* @return The width of the shape
|
||||
*/
|
||||
public float getWidth() {
|
||||
return maxX - minX;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the height of the shape
|
||||
*
|
||||
* @return The height of the shape
|
||||
*/
|
||||
public float getHeight() {
|
||||
return maxY - minY;
|
||||
}
|
||||
}
|
||||
391
lib/slick-source/org/newdawn/slick/geom/ShapeRenderer.java
Normal file
391
lib/slick-source/org/newdawn/slick/geom/ShapeRenderer.java
Normal file
@@ -0,0 +1,391 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
import org.newdawn.slick.Image;
|
||||
import org.newdawn.slick.ShapeFill;
|
||||
import org.newdawn.slick.opengl.Texture;
|
||||
import org.newdawn.slick.opengl.TextureImpl;
|
||||
import org.newdawn.slick.opengl.renderer.LineStripRenderer;
|
||||
import org.newdawn.slick.opengl.renderer.Renderer;
|
||||
import org.newdawn.slick.opengl.renderer.SGL;
|
||||
|
||||
/**
|
||||
* @author Mark Bernard
|
||||
*
|
||||
* Use this class to render shpaes directly to OpenGL. Allows you to bypass the Graphics class.
|
||||
*/
|
||||
public final class ShapeRenderer {
|
||||
/** The renderer to use for all GL operations */
|
||||
private static SGL GL = Renderer.get();
|
||||
/** The renderer to use line strips */
|
||||
private static LineStripRenderer LSR = Renderer.getLineStripRenderer();
|
||||
|
||||
/**
|
||||
* Draw the outline of the given shape. Only the vertices are set.
|
||||
* The colour has to be set independently of this method.
|
||||
*
|
||||
* @param shape The shape to draw.
|
||||
*/
|
||||
public static final void draw(Shape shape) {
|
||||
Texture t = TextureImpl.getLastBind();
|
||||
TextureImpl.bindNone();
|
||||
|
||||
float points[] = shape.getPoints();
|
||||
|
||||
LSR.start();
|
||||
for(int i=0;i<points.length;i+=2) {
|
||||
LSR.vertex(points[i], points[i + 1]);
|
||||
}
|
||||
|
||||
if (shape.closed()) {
|
||||
LSR.vertex(points[0], points[1]);
|
||||
}
|
||||
|
||||
LSR.end();
|
||||
|
||||
if (t == null) {
|
||||
TextureImpl.bindNone();
|
||||
} else {
|
||||
t.bind();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the outline of the given shape. Only the vertices are set.
|
||||
* The colour has to be set independently of this method.
|
||||
*
|
||||
* @param shape The shape to draw.
|
||||
* @param fill The fill to apply
|
||||
*/
|
||||
public static final void draw(Shape shape, ShapeFill fill) {
|
||||
float points[] = shape.getPoints();
|
||||
|
||||
Texture t = TextureImpl.getLastBind();
|
||||
TextureImpl.bindNone();
|
||||
|
||||
float center[] = shape.getCenter();
|
||||
GL.glBegin(SGL.GL_LINE_STRIP);
|
||||
for(int i=0;i<points.length;i+=2) {
|
||||
fill.colorAt(shape, points[i], points[i + 1]).bind();
|
||||
Vector2f offset = fill.getOffsetAt(shape, points[i], points[i + 1]);
|
||||
GL.glVertex2f(points[i] + offset.x, points[i + 1] + offset.y);
|
||||
}
|
||||
|
||||
if (shape.closed()) {
|
||||
fill.colorAt(shape, points[0], points[1]).bind();
|
||||
Vector2f offset = fill.getOffsetAt(shape, points[0], points[1]);
|
||||
GL.glVertex2f(points[0] + offset.x, points[1] + offset.y);
|
||||
}
|
||||
GL.glEnd();
|
||||
|
||||
if (t == null) {
|
||||
TextureImpl.bindNone();
|
||||
} else {
|
||||
t.bind();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check there are enough points to fill
|
||||
*
|
||||
* @param shape THe shape we're drawing
|
||||
* @return True if the fill is valid
|
||||
*/
|
||||
public static boolean validFill(Shape shape) {
|
||||
if (shape.getTriangles() == null) {
|
||||
return false;
|
||||
}
|
||||
return shape.getTriangles().getTriangleCount() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the the given shape filled in. Only the vertices are set.
|
||||
* The colour has to be set independently of this method.
|
||||
*
|
||||
* @param shape The shape to fill.
|
||||
*/
|
||||
public static final void fill(Shape shape) {
|
||||
if (!validFill(shape)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Texture t = TextureImpl.getLastBind();
|
||||
TextureImpl.bindNone();
|
||||
|
||||
fill(shape, new PointCallback() {
|
||||
public float[] preRenderPoint(Shape shape, float x, float y) {
|
||||
// do nothing, we're just filling the shape this time
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
if (t == null) {
|
||||
TextureImpl.bindNone();
|
||||
} else {
|
||||
t.bind();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the the given shape filled in. Only the vertices are set.
|
||||
* The colour has to be set independently of this method.
|
||||
*
|
||||
* @param shape The shape to fill.
|
||||
* @param callback The callback that will be invoked for each shape point
|
||||
*/
|
||||
private static final void fill(Shape shape, PointCallback callback) {
|
||||
Triangulator tris = shape.getTriangles();
|
||||
|
||||
GL.glBegin(SGL.GL_TRIANGLES);
|
||||
for (int i=0;i<tris.getTriangleCount();i++) {
|
||||
for (int p=0;p<3;p++) {
|
||||
float[] pt = tris.getTrianglePoint(i, p);
|
||||
float[] np = callback.preRenderPoint(shape, pt[0],pt[1]);
|
||||
|
||||
if (np == null) {
|
||||
GL.glVertex2f(pt[0],pt[1]);
|
||||
} else {
|
||||
GL.glVertex2f(np[0],np[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
GL.glEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the the given shape filled in with a texture. Only the vertices are set.
|
||||
* The colour has to be set independently of this method.
|
||||
*
|
||||
* @param shape The shape to texture.
|
||||
* @param image The image to tile across the shape
|
||||
*/
|
||||
public static final void texture(Shape shape, Image image) {
|
||||
texture(shape, image, 0.01f, 0.01f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the the given shape filled in with a texture. Only the vertices are set.
|
||||
* The colour has to be set independently of this method. This method is required to
|
||||
* fit the texture once across the shape.
|
||||
*
|
||||
* @param shape The shape to texture.
|
||||
* @param image The image to tile across the shape
|
||||
*/
|
||||
public static final void textureFit(Shape shape, Image image) {
|
||||
textureFit(shape, image,1f,1f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the the given shape filled in with a texture. Only the vertices are set.
|
||||
* The colour has to be set independently of this method.
|
||||
*
|
||||
* @param shape The shape to texture.
|
||||
* @param image The image to tile across the shape
|
||||
* @param scaleX The scale to apply on the x axis for texturing
|
||||
* @param scaleY The scale to apply on the y axis for texturing
|
||||
*/
|
||||
public static final void texture(Shape shape, final Image image, final float scaleX, final float scaleY) {
|
||||
if (!validFill(shape)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Texture t = TextureImpl.getLastBind();
|
||||
image.getTexture().bind();
|
||||
|
||||
fill(shape, new PointCallback() {
|
||||
public float[] preRenderPoint(Shape shape, float x, float y) {
|
||||
float tx = x * scaleX;
|
||||
float ty = y * scaleY;
|
||||
|
||||
tx = image.getTextureOffsetX() + (image.getTextureWidth() * tx);
|
||||
ty = image.getTextureOffsetY() + (image.getTextureHeight() * ty);
|
||||
|
||||
GL.glTexCoord2f(tx, ty);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
float points[] = shape.getPoints();
|
||||
|
||||
if (t == null) {
|
||||
TextureImpl.bindNone();
|
||||
} else {
|
||||
t.bind();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the the given shape filled in with a texture. Only the vertices are set.
|
||||
* The colour has to be set independently of this method. This method is required to
|
||||
* fit the texture scaleX times across the shape and scaleY times down the shape.
|
||||
*
|
||||
* @param shape The shape to texture.
|
||||
* @param image The image to tile across the shape
|
||||
* @param scaleX The scale to apply on the x axis for texturing
|
||||
* @param scaleY The scale to apply on the y axis for texturing
|
||||
*/
|
||||
public static final void textureFit(Shape shape, final Image image, final float scaleX, final float scaleY) {
|
||||
if (!validFill(shape)) {
|
||||
return;
|
||||
}
|
||||
|
||||
float points[] = shape.getPoints();
|
||||
|
||||
Texture t = TextureImpl.getLastBind();
|
||||
image.getTexture().bind();
|
||||
|
||||
final float minX = shape.getX();
|
||||
final float minY = shape.getY();
|
||||
final float maxX = shape.getMaxX() - minX;
|
||||
final float maxY = shape.getMaxY() - minY;
|
||||
|
||||
fill(shape, new PointCallback() {
|
||||
public float[] preRenderPoint(Shape shape, float x, float y) {
|
||||
x -= shape.getMinX();
|
||||
y -= shape.getMinY();
|
||||
|
||||
x /= (shape.getMaxX() - shape.getMinX());
|
||||
y /= (shape.getMaxY() - shape.getMinY());
|
||||
|
||||
float tx = x * scaleX;
|
||||
float ty = y * scaleY;
|
||||
|
||||
tx = image.getTextureOffsetX() + (image.getTextureWidth() * tx);
|
||||
ty = image.getTextureOffsetY() + (image.getTextureHeight() * ty);
|
||||
|
||||
GL.glTexCoord2f(tx, ty);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
if (t == null) {
|
||||
TextureImpl.bindNone();
|
||||
} else {
|
||||
t.bind();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the the given shape filled in. Only the vertices are set.
|
||||
* The colour has to be set independently of this method.
|
||||
*
|
||||
* @param shape The shape to fill.
|
||||
* @param fill The fill to apply
|
||||
*/
|
||||
public static final void fill(final Shape shape, final ShapeFill fill) {
|
||||
if (!validFill(shape)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Texture t = TextureImpl.getLastBind();
|
||||
TextureImpl.bindNone();
|
||||
|
||||
final float center[] = shape.getCenter();
|
||||
fill(shape, new PointCallback() {
|
||||
public float[] preRenderPoint(Shape shape, float x, float y) {
|
||||
fill.colorAt(shape, x, y).bind();
|
||||
Vector2f offset = fill.getOffsetAt(shape, x, y);
|
||||
|
||||
return new float[] {offset.x + x,offset.y + y};
|
||||
}
|
||||
});
|
||||
|
||||
if (t == null) {
|
||||
TextureImpl.bindNone();
|
||||
} else {
|
||||
t.bind();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw the the given shape filled in with a texture. Only the vertices are set.
|
||||
* The colour has to be set independently of this method.
|
||||
*
|
||||
* @param shape The shape to texture.
|
||||
* @param image The image to tile across the shape
|
||||
* @param scaleX The scale to apply on the x axis for texturing
|
||||
* @param scaleY The scale to apply on the y axis for texturing
|
||||
* @param fill The fill to apply
|
||||
*/
|
||||
public static final void texture(final Shape shape, final Image image, final float scaleX, final float scaleY, final ShapeFill fill) {
|
||||
if (!validFill(shape)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Texture t = TextureImpl.getLastBind();
|
||||
image.getTexture().bind();
|
||||
|
||||
final float center[] = shape.getCenter();
|
||||
fill(shape, new PointCallback() {
|
||||
public float[] preRenderPoint(Shape shape, float x, float y) {
|
||||
fill.colorAt(shape, x - center[0], y - center[1]).bind();
|
||||
Vector2f offset = fill.getOffsetAt(shape, x, y);
|
||||
|
||||
x += offset.x;
|
||||
y += offset.y;
|
||||
|
||||
float tx = x * scaleX;
|
||||
float ty = y * scaleY;
|
||||
|
||||
tx = image.getTextureOffsetX() + (image.getTextureWidth() * tx);
|
||||
ty = image.getTextureOffsetY() + (image.getTextureHeight() * ty);
|
||||
|
||||
GL.glTexCoord2f(tx, ty);
|
||||
|
||||
return new float[] {offset.x + x,offset.y + y};
|
||||
}
|
||||
});
|
||||
|
||||
if (t == null) {
|
||||
TextureImpl.bindNone();
|
||||
} else {
|
||||
t.bind();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Draw the the given shape filled in with a texture. Only the vertices are set.
|
||||
* The colour has to be set independently of this method.
|
||||
*
|
||||
* @param shape The shape to texture.
|
||||
* @param image The image to tile across the shape
|
||||
* @param gen The texture coordinate generator to create coordiantes for the shape
|
||||
*/
|
||||
public static final void texture(final Shape shape, Image image, final TexCoordGenerator gen) {
|
||||
Texture t = TextureImpl.getLastBind();
|
||||
|
||||
image.getTexture().bind();
|
||||
|
||||
final float center[] = shape.getCenter();
|
||||
fill(shape, new PointCallback() {
|
||||
public float[] preRenderPoint(Shape shape, float x, float y) {
|
||||
Vector2f tex = gen.getCoordFor(x, y);
|
||||
GL.glTexCoord2f(tex.x, tex.y);
|
||||
|
||||
return new float[] {x,y};
|
||||
}
|
||||
});
|
||||
|
||||
if (t == null) {
|
||||
TextureImpl.bindNone();
|
||||
} else {
|
||||
t.bind();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of some feature that will be applied to each point render
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
private static interface PointCallback {
|
||||
/**
|
||||
* Apply feature before the call to glVertex
|
||||
*
|
||||
* @param shape The shape the point belongs to
|
||||
* @param x The x poisiton the vertex will be at
|
||||
* @param y The y position the vertex will be at
|
||||
* @return The new coordinates of null
|
||||
*/
|
||||
float[] preRenderPoint(Shape shape, float x, float y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
/**
|
||||
* A class capable of generating texture coordiantes based on
|
||||
* rendering positions of verticies. This allows custom texturing
|
||||
* of geometric shapes
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public interface TexCoordGenerator {
|
||||
/**
|
||||
* Get the texture coordinate for a given render position
|
||||
*
|
||||
* @param x The x coordinate of the vertex being rendered
|
||||
* @param y The y coordinate of the vertex being rendered
|
||||
* @return The texture coordinate to apply
|
||||
*/
|
||||
public Vector2f getCoordFor(float x, float y);
|
||||
}
|
||||
230
lib/slick-source/org/newdawn/slick/geom/Transform.java
Normal file
230
lib/slick-source/org/newdawn/slick/geom/Transform.java
Normal file
@@ -0,0 +1,230 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
import org.newdawn.slick.util.FastTrig;
|
||||
|
||||
/**
|
||||
* A 2 dimensional transformation that can be applied to <code>Shape</code> implemenations.
|
||||
*
|
||||
* @author Mark
|
||||
*/
|
||||
public class Transform {
|
||||
/**
|
||||
* Value for each position in the matrix
|
||||
*
|
||||
* |0 1 2|
|
||||
* |3 4 5|
|
||||
* |6 7 8|
|
||||
*/
|
||||
private float matrixPosition[];
|
||||
|
||||
/**
|
||||
* Create and identity transform
|
||||
*
|
||||
*/
|
||||
public Transform() {
|
||||
matrixPosition = new float[]{1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a transform
|
||||
*
|
||||
* @param other The other transform to copy
|
||||
*/
|
||||
public Transform(Transform other) {
|
||||
matrixPosition = new float[9];
|
||||
for (int i=0;i<9;i++) {
|
||||
matrixPosition[i] = other.matrixPosition[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatanate to transform into one
|
||||
*
|
||||
* @param t1 The first transform to join
|
||||
* @param t2 The second transform to join
|
||||
*/
|
||||
public Transform(Transform t1, Transform t2) {
|
||||
this(t1);
|
||||
concatenate(t2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a transform for the given positions
|
||||
*
|
||||
* @param matrixPosition An array of float[6] to set up a transform
|
||||
* @throws RuntimeException if the array is not of length 6
|
||||
*/
|
||||
public Transform(float matrixPosition[]) {
|
||||
if(matrixPosition.length != 6) {
|
||||
throw new RuntimeException("The parameter must be a float array of length 6.");
|
||||
}
|
||||
this.matrixPosition = new float[]{matrixPosition[0], matrixPosition[1], matrixPosition[2],
|
||||
matrixPosition[3], matrixPosition[4], matrixPosition[5],
|
||||
0, 0, 1};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a transform for the given positions
|
||||
*
|
||||
* @param point00 float for the first position
|
||||
* @param point01 float for the second position
|
||||
* @param point02 float for the third position
|
||||
* @param point10 float for the fourth position
|
||||
* @param point11 float for the fifth position
|
||||
* @param point12 float for the sixth position
|
||||
*/
|
||||
public Transform(float point00, float point01, float point02, float point10, float point11, float point12) {
|
||||
matrixPosition = new float[]{point00, point01, point02, point10, point11, point12, 0, 0, 1};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the point pairs in the source array and store them in the destination array.
|
||||
* All operations will be done before storing the results in the destination. This way the source
|
||||
* and destination array can be the same without worry of overwriting information before it is transformed.
|
||||
*
|
||||
* @param source Array of floats containing the points to be transformed
|
||||
* @param sourceOffset Where in the array to start processing
|
||||
* @param destination Array of floats to store the results.
|
||||
* @param destOffset Where in the array to start storing
|
||||
* @param numberOfPoints Number of points to be transformed
|
||||
* @throws ArrayIndexOutOfBoundsException if sourceOffset + numberOfPoints * 2 > source.length or the same operation on the destination array
|
||||
*/
|
||||
public void transform(float source[], int sourceOffset, float destination[], int destOffset, int numberOfPoints) {
|
||||
//TODO performance can be improved by removing the safety to the destination array
|
||||
float result[] = source == destination ? new float[numberOfPoints * 2] : destination;
|
||||
|
||||
for(int i=0;i<numberOfPoints * 2;i+=2) {
|
||||
for(int j=0;j<6;j+=3) {
|
||||
result[i + (j / 3)] = source[i + sourceOffset] * matrixPosition[j] + source[i + sourceOffset + 1] * matrixPosition[j + 1] + 1 * matrixPosition[j + 2];
|
||||
}
|
||||
}
|
||||
|
||||
if (source == destination) {
|
||||
//for safety of the destination, the results are copied after the entire operation.
|
||||
for(int i=0;i<numberOfPoints * 2;i+=2) {
|
||||
destination[i + destOffset] = result[i];
|
||||
destination[i + destOffset + 1] = result[i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this Transform by concatenating the given Transform to this one.
|
||||
*
|
||||
* @param tx The Transfrom to concatenate to this one.
|
||||
* @return The resulting Transform
|
||||
*/
|
||||
public Transform concatenate(Transform tx) {
|
||||
float[] mp = new float[9];
|
||||
float n00 = matrixPosition[0] * tx.matrixPosition[0] + matrixPosition[1] * tx.matrixPosition[3];
|
||||
float n01 = matrixPosition[0] * tx.matrixPosition[1] + matrixPosition[1] * tx.matrixPosition[4];
|
||||
float n02 = matrixPosition[0] * tx.matrixPosition[2] + matrixPosition[1] * tx.matrixPosition[5] + matrixPosition[2];
|
||||
float n10 = matrixPosition[3] * tx.matrixPosition[0] + matrixPosition[4] * tx.matrixPosition[3];
|
||||
float n11 = matrixPosition[3] * tx.matrixPosition[1] + matrixPosition[4] * tx.matrixPosition[4];
|
||||
float n12 = matrixPosition[3] * tx.matrixPosition[2] + matrixPosition[4] * tx.matrixPosition[5] + matrixPosition[5];
|
||||
mp[0] = n00;
|
||||
mp[1] = n01;
|
||||
mp[2] = n02;
|
||||
mp[3] = n10;
|
||||
mp[4] = n11;
|
||||
mp[5] = n12;
|
||||
//
|
||||
// mp[0] = matrixPosition[0] * transform.matrixPosition[0] + matrixPosition[0] * transform.matrixPosition[3] + matrixPosition[0] * transform.matrixPosition[6];
|
||||
// mp[1] = matrixPosition[1] * transform.matrixPosition[1] + matrixPosition[1] * transform.matrixPosition[4] + matrixPosition[1] * transform.matrixPosition[7];
|
||||
// mp[2] = matrixPosition[2] * transform.matrixPosition[2] + matrixPosition[2] * transform.matrixPosition[5] + matrixPosition[2] * transform.matrixPosition[8];
|
||||
// mp[3] = matrixPosition[3] * transform.matrixPosition[0] + matrixPosition[3] * transform.matrixPosition[3] + matrixPosition[3] * transform.matrixPosition[6];
|
||||
// mp[4] = matrixPosition[4] * transform.matrixPosition[1] + matrixPosition[4] * transform.matrixPosition[4] + matrixPosition[4] * transform.matrixPosition[7];
|
||||
// mp[5] = matrixPosition[5] * transform.matrixPosition[2] + matrixPosition[5] * transform.matrixPosition[5] + matrixPosition[5] * transform.matrixPosition[8];
|
||||
//
|
||||
matrixPosition = mp;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert this Transform to a String.
|
||||
*
|
||||
* @return This Transform in human readable format.
|
||||
*/
|
||||
public String toString() {
|
||||
String result = "Transform[[" + matrixPosition[0] + "," + matrixPosition[1] + "," + matrixPosition[2] +
|
||||
"][" + matrixPosition[3] + "," + matrixPosition[4] + "," + matrixPosition[5] +
|
||||
"][" + matrixPosition[6] + "," + matrixPosition[7] + "," + matrixPosition[8] + "]]";
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array representing this Transform.
|
||||
*
|
||||
* @return an array representing this Transform.
|
||||
*/
|
||||
public float[] getMatrixPosition() {
|
||||
return matrixPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new rotation Transform
|
||||
*
|
||||
* @param angle The angle in radians to set the transform.
|
||||
* @return The resulting Transform
|
||||
*/
|
||||
public static Transform createRotateTransform(float angle) {
|
||||
return new Transform((float)FastTrig.cos(angle), -(float)FastTrig.sin(angle), 0, (float)FastTrig.sin(angle), (float)FastTrig.cos(angle), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new rotation Transform around the specified point
|
||||
*
|
||||
* @param angle The angle in radians to set the transform.
|
||||
* @param x The x coordinate around which to rotate.
|
||||
* @param y The y coordinate around which to rotate.
|
||||
* @return The resulting Transform
|
||||
*/
|
||||
public static Transform createRotateTransform(float angle, float x, float y) {
|
||||
Transform temp = Transform.createRotateTransform(angle);
|
||||
float sinAngle = temp.matrixPosition[3];
|
||||
float oneMinusCosAngle = 1.0f - temp.matrixPosition[4];
|
||||
temp.matrixPosition[2] = x * oneMinusCosAngle + y * sinAngle;
|
||||
temp.matrixPosition[5] = y * oneMinusCosAngle - x * sinAngle;
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new translation Transform
|
||||
*
|
||||
* @param xOffset The amount to move in the x direction
|
||||
* @param yOffset The amount to move in the y direction
|
||||
* @return The resulting Transform
|
||||
*/
|
||||
public static Transform createTranslateTransform(float xOffset, float yOffset) {
|
||||
return new Transform(1, 0, xOffset, 0, 1, yOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an new scaling Transform
|
||||
*
|
||||
* @param xScale The amount to scale in the x coordinate
|
||||
* @param yScale The amount to scale in the x coordinate
|
||||
* @return The resulting Transform
|
||||
*/
|
||||
public static Transform createScaleTransform(float xScale, float yScale) {
|
||||
return new Transform(xScale, 0, 0, 0, yScale, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the vector2f based on the matrix defined in this transform
|
||||
*
|
||||
* @param pt The point to be transformed
|
||||
* @return The resulting point transformed by this matrix
|
||||
*/
|
||||
public Vector2f transform(Vector2f pt) {
|
||||
float[] in = new float[] {pt.x, pt.y};
|
||||
float[] out = new float[2];
|
||||
|
||||
transform(in, 0, out, 0, 1);
|
||||
|
||||
return new Vector2f(out[0], out[1]);
|
||||
}
|
||||
}
|
||||
48
lib/slick-source/org/newdawn/slick/geom/Triangulator.java
Normal file
48
lib/slick-source/org/newdawn/slick/geom/Triangulator.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A collection of triangles
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public interface Triangulator extends Serializable {
|
||||
|
||||
/**
|
||||
* Get a count of the number of triangles produced
|
||||
*
|
||||
* @return The number of triangles produced
|
||||
*/
|
||||
public int getTriangleCount();
|
||||
|
||||
/**
|
||||
* Get a point on a specified generated triangle
|
||||
*
|
||||
* @param tri The index of the triangle to interegate
|
||||
* @param i The index of the point within the triangle to retrieve
|
||||
* (0 - 2)
|
||||
* @return The x,y coordinate pair for the point
|
||||
*/
|
||||
public float[] getTrianglePoint(int tri, int i);
|
||||
|
||||
/**
|
||||
* Add a point that forms part of the outer polygon
|
||||
*
|
||||
* @param x The x coordinate of the point
|
||||
* @param y The y coordiante of the point
|
||||
*/
|
||||
public void addPolyPoint(float x, float y);
|
||||
|
||||
/**
|
||||
* Start a hole in the polygon
|
||||
*/
|
||||
public void startHole();
|
||||
|
||||
/**
|
||||
* Run the triangulation
|
||||
*
|
||||
* @return True if successful
|
||||
*/
|
||||
public boolean triangulate();
|
||||
}
|
||||
396
lib/slick-source/org/newdawn/slick/geom/Vector2f.java
Normal file
396
lib/slick-source/org/newdawn/slick/geom/Vector2f.java
Normal file
@@ -0,0 +1,396 @@
|
||||
package org.newdawn.slick.geom;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.newdawn.slick.util.FastTrig;
|
||||
|
||||
/**
|
||||
* A two dimensional vector
|
||||
*
|
||||
* @author Kevin Glass
|
||||
*/
|
||||
public strictfp class Vector2f implements Serializable {
|
||||
/** The version ID for this class */
|
||||
private static final long serialVersionUID = 1339934L;
|
||||
|
||||
/** The x component of this vector */
|
||||
public float x;
|
||||
/** The y component of this vector */
|
||||
public float y;
|
||||
|
||||
/**
|
||||
* Create an empty vector
|
||||
*/
|
||||
public Vector2f() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a vector based on the contents of a coordinate array
|
||||
*
|
||||
* @param coords The coordinates array, index 0 = x, index 1 = y
|
||||
*/
|
||||
public Vector2f(float[] coords) {
|
||||
x = coords[0];
|
||||
y = coords[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new vector based on an angle
|
||||
*
|
||||
* @param theta The angle of the vector in degrees
|
||||
*/
|
||||
public Vector2f(double theta) {
|
||||
x = 1;
|
||||
y = 0;
|
||||
setTheta(theta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the components of the vectors based on a angle
|
||||
*
|
||||
* @param theta The angle to calculate the components from (in degrees)
|
||||
*/
|
||||
public void setTheta(double theta) {
|
||||
// Next lines are to prevent numbers like -1.8369701E-16
|
||||
// when working with negative numbers
|
||||
if ((theta < -360) || (theta > 360)) {
|
||||
theta = theta % 360;
|
||||
}
|
||||
if (theta < 0) {
|
||||
theta = 360 + theta;
|
||||
}
|
||||
double oldTheta = getTheta();
|
||||
if ((theta < -360) || (theta > 360)) {
|
||||
oldTheta = oldTheta % 360;
|
||||
}
|
||||
if (theta < 0) {
|
||||
oldTheta = 360 + oldTheta;
|
||||
}
|
||||
|
||||
float len = length();
|
||||
x = len * (float) FastTrig.cos(StrictMath.toRadians(theta));
|
||||
y = len * (float) FastTrig.sin(StrictMath.toRadians(theta));
|
||||
|
||||
// x = x / (float) FastTrig.cos(StrictMath.toRadians(oldTheta))
|
||||
// * (float) FastTrig.cos(StrictMath.toRadians(theta));
|
||||
// y = x / (float) FastTrig.sin(StrictMath.toRadians(oldTheta))
|
||||
// * (float) FastTrig.sin(StrictMath.toRadians(theta));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust this vector by a given angle
|
||||
*
|
||||
* @param theta
|
||||
* The angle to adjust the angle by (in degrees)
|
||||
* @return This vector - useful for chaining operations
|
||||
*
|
||||
*/
|
||||
public Vector2f add(double theta) {
|
||||
setTheta(getTheta() + theta);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust this vector by a given angle
|
||||
*
|
||||
* @param theta The angle to adjust the angle by (in degrees)
|
||||
* @return This vector - useful for chaining operations
|
||||
*/
|
||||
public Vector2f sub(double theta) {
|
||||
setTheta(getTheta() - theta);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the angle this vector is at
|
||||
*
|
||||
* @return The angle this vector is at (in degrees)
|
||||
*/
|
||||
public double getTheta() {
|
||||
double theta = StrictMath.toDegrees(StrictMath.atan2(y, x));
|
||||
if ((theta < -360) || (theta > 360)) {
|
||||
theta = theta % 360;
|
||||
}
|
||||
if (theta < 0) {
|
||||
theta = 360 + theta;
|
||||
}
|
||||
|
||||
return theta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the x component
|
||||
*
|
||||
* @return The x component
|
||||
*/
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the y component
|
||||
*
|
||||
* @return The y component
|
||||
*/
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new vector based on another
|
||||
*
|
||||
* @param other The other vector to copy into this one
|
||||
*/
|
||||
public Vector2f(Vector2f other) {
|
||||
this(other.getX(),other.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new vector
|
||||
*
|
||||
* @param x The x component to assign
|
||||
* @param y The y component to assign
|
||||
*/
|
||||
public Vector2f(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of this vector
|
||||
*
|
||||
* @param other The values to set into the vector
|
||||
*/
|
||||
public void set(Vector2f other) {
|
||||
set(other.getX(),other.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Dot this vector against another
|
||||
*
|
||||
* @param other The other vector dot agianst
|
||||
* @return The dot product of the two vectors
|
||||
*/
|
||||
public float dot(Vector2f other) {
|
||||
return (x * other.getX()) + (y * other.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the values in this vector
|
||||
*
|
||||
* @param x The x component to set
|
||||
* @param y The y component to set
|
||||
* @return This vector - useful for chaining operations
|
||||
*/
|
||||
public Vector2f set(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A vector perpendicular to this vector.
|
||||
*
|
||||
* @return a vector perpendicular to this vector
|
||||
*/
|
||||
public Vector2f getPerpendicular() {
|
||||
return new Vector2f(-y, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the values in this vector
|
||||
*
|
||||
* @param pt The pair of values to set into the vector
|
||||
* @return This vector - useful for chaining operations
|
||||
*/
|
||||
public Vector2f set(float[] pt) {
|
||||
return set(pt[0], pt[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Negate this vector
|
||||
*
|
||||
* @return A copy of this vector negated
|
||||
*/
|
||||
public Vector2f negate() {
|
||||
return new Vector2f(-x, -y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Negate this vector without creating a new copy
|
||||
*
|
||||
* @return This vector - useful for chaning operations
|
||||
*/
|
||||
public Vector2f negateLocal() {
|
||||
x = -x;
|
||||
y = -y;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a vector to this vector
|
||||
*
|
||||
* @param v The vector to add
|
||||
* @return This vector - useful for chaning operations
|
||||
*/
|
||||
public Vector2f add(Vector2f v)
|
||||
{
|
||||
x += v.getX();
|
||||
y += v.getY();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract a vector from this vector
|
||||
*
|
||||
* @param v The vector subtract
|
||||
* @return This vector - useful for chaining operations
|
||||
*/
|
||||
public Vector2f sub(Vector2f v)
|
||||
{
|
||||
x -= v.getX();
|
||||
y -= v.getY();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale this vector by a value
|
||||
*
|
||||
* @param a The value to scale this vector by
|
||||
* @return This vector - useful for chaining operations
|
||||
*/
|
||||
public Vector2f scale(float a)
|
||||
{
|
||||
x *= a;
|
||||
y *= a;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalise the vector
|
||||
*
|
||||
* @return This vector - useful for chaning operations
|
||||
*/
|
||||
public Vector2f normalise() {
|
||||
float l = length();
|
||||
|
||||
if (l == 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
x /= l;
|
||||
y /= l;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The normal of the vector
|
||||
*
|
||||
* @return A unit vector with the same direction as the vector
|
||||
*/
|
||||
public Vector2f getNormal() {
|
||||
Vector2f cp = copy();
|
||||
cp.normalise();
|
||||
return cp;
|
||||
}
|
||||
|
||||
/**
|
||||
* The length of the vector squared
|
||||
*
|
||||
* @return The length of the vector squared
|
||||
*/
|
||||
public float lengthSquared() {
|
||||
return (x * x) + (y * y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of this vector
|
||||
*
|
||||
* @return The length of this vector
|
||||
*/
|
||||
public float length()
|
||||
{
|
||||
return (float) Math.sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
/**
|
||||
* Project this vector onto another
|
||||
*
|
||||
* @param b The vector to project onto
|
||||
* @param result The projected vector
|
||||
*/
|
||||
public void projectOntoUnit(Vector2f b, Vector2f result) {
|
||||
float dp = b.dot(this);
|
||||
|
||||
result.x = dp * b.getX();
|
||||
result.y = dp * b.getY();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this vector
|
||||
*
|
||||
* @return The new instance that copies this vector
|
||||
*/
|
||||
public Vector2f copy() {
|
||||
return new Vector2f(x,y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return "[Vector2f "+x+","+y+" ("+length()+")]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the distance from this point to another
|
||||
*
|
||||
* @param other The other point we're measuring to
|
||||
* @return The distance to the other point
|
||||
*/
|
||||
public float distance(Vector2f other) {
|
||||
return (float) Math.sqrt(distanceSquared(other));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the distance from this point to another, squared. This
|
||||
* can sometimes be used in place of distance and avoids the
|
||||
* additional sqrt.
|
||||
*
|
||||
* @param other The other point we're measuring to
|
||||
* @return The distance to the other point squared
|
||||
*/
|
||||
public float distanceSquared(Vector2f other) {
|
||||
float dx = other.getX() - getX();
|
||||
float dy = other.getY() - getY();
|
||||
|
||||
return (float) (dx*dx)+(dy*dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return 997 * ((int)x) ^ 991 * ((int)y); //large primes!
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof Vector2f) {
|
||||
Vector2f o = ((Vector2f) other);
|
||||
return (o.x == x) && (o.y == y);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
3
lib/slick-source/org/newdawn/slick/geom/package.html
Normal file
3
lib/slick-source/org/newdawn/slick/geom/package.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<BODY>
|
||||
Simple geometric wrappers that can be used for rendering and collision.
|
||||
</BODY>
|
||||
Reference in New Issue
Block a user