mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-08 12:51:51 +09:00
Former-commit-id: 1647fa32ef6894bd7db44f741f07c2f4dcdf9054 Former-commit-id: 0e5810dcfbe1fd59b13e7cabe9f1e93c5542da2d
427 lines
11 KiB
Java
427 lines
11 KiB
Java
package org.newdawn.slick.tests;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashSet;
|
|
|
|
import org.newdawn.slick.AppGameContainer;
|
|
import org.newdawn.slick.BasicGame;
|
|
import org.newdawn.slick.Color;
|
|
import org.newdawn.slick.GameContainer;
|
|
import org.newdawn.slick.Graphics;
|
|
import org.newdawn.slick.SlickException;
|
|
import org.newdawn.slick.geom.Circle;
|
|
import org.newdawn.slick.geom.GeomUtil;
|
|
import org.newdawn.slick.geom.GeomUtilListener;
|
|
import org.newdawn.slick.geom.Polygon;
|
|
import org.newdawn.slick.geom.Shape;
|
|
import org.newdawn.slick.geom.Vector2f;
|
|
|
|
/**
|
|
* A test to try shape building from multiple tiles
|
|
*
|
|
* @author Kevin Glass
|
|
*/
|
|
public class GeomUtilTileTest extends BasicGame implements GeomUtilListener {
|
|
/** The shape we're cutting out of */
|
|
private Shape source;
|
|
/** The shape we're cutting */
|
|
private Shape cut;
|
|
/** The resulting shape */
|
|
private Shape[] result;
|
|
|
|
/** The util under test */
|
|
private GeomUtil util = new GeomUtil();
|
|
|
|
/** The original list of shapes */
|
|
private ArrayList original = new ArrayList();
|
|
/** The original list of shapes */
|
|
private ArrayList combined = new ArrayList();
|
|
|
|
/** The list of intersection points */
|
|
private ArrayList intersections = new ArrayList();
|
|
/** The list of used points */
|
|
private ArrayList used = new ArrayList();
|
|
|
|
/** The quad space of shapes that need to be checked against each other */
|
|
private ArrayList[][] quadSpace;
|
|
/** The shapes present in each quad space - used to optimize generation */
|
|
private Shape[][] quadSpaceShapes;
|
|
|
|
/**
|
|
* Create a simple test
|
|
*/
|
|
public GeomUtilTileTest() {
|
|
super("GeomUtilTileTest");
|
|
}
|
|
|
|
/**
|
|
* So this is going to generate a quad space that holds that segments the
|
|
* shapes into quads across the map. This makes it tunable and limits the number
|
|
* of comparisons that need to be done for each shape
|
|
*
|
|
* @param shapes The shapes to be segments
|
|
* @param minx The minimum x value of the map
|
|
* @param miny The mimimum y value of the map
|
|
* @param maxx The maximum x value of the map
|
|
* @param maxy The maximum y value of the map
|
|
* @param segments The number of segments to split the map into
|
|
*/
|
|
private void generateSpace(ArrayList shapes, float minx, float miny, float maxx, float maxy, int segments) {
|
|
quadSpace = new ArrayList[segments][segments];
|
|
quadSpaceShapes = new Shape[segments][segments];
|
|
|
|
float dx = (maxx - minx) / segments;
|
|
float dy = (maxy - miny) / segments;
|
|
|
|
for (int x=0;x<segments;x++) {
|
|
for (int y=0;y<segments;y++) {
|
|
quadSpace[x][y] = new ArrayList();
|
|
|
|
// quad for this segment
|
|
Polygon segmentPolygon = new Polygon();
|
|
segmentPolygon.addPoint(minx+(dx*x), miny+(dy*y));
|
|
segmentPolygon.addPoint(minx+(dx*x)+dx, miny+(dy*y));
|
|
segmentPolygon.addPoint(minx+(dx*x)+dx, miny+(dy*y)+dy);
|
|
segmentPolygon.addPoint(minx+(dx*x), miny+(dy*y)+dy);
|
|
|
|
for (int i=0;i<shapes.size();i++) {
|
|
Shape shape = (Shape) shapes.get(i);
|
|
|
|
if (collides(shape, segmentPolygon)) {
|
|
quadSpace[x][y].add(shape);
|
|
}
|
|
}
|
|
|
|
quadSpaceShapes[x][y] = segmentPolygon;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove the given shape from the quad space
|
|
*
|
|
* @param shape The shape to remove
|
|
*/
|
|
private void removeFromQuadSpace(Shape shape) {
|
|
int segments = quadSpace.length;
|
|
|
|
for (int x=0;x<segments;x++) {
|
|
for (int y=0;y<segments;y++) {
|
|
quadSpace[x][y].remove(shape);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a particular shape to quad space
|
|
*
|
|
* @param shape The shape to be added
|
|
*/
|
|
private void addToQuadSpace(Shape shape) {
|
|
int segments = quadSpace.length;
|
|
|
|
for (int x=0;x<segments;x++) {
|
|
for (int y=0;y<segments;y++) {
|
|
if (collides(shape, quadSpaceShapes[x][y])) {
|
|
quadSpace[x][y].add(shape);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform the cut
|
|
*/
|
|
public void init() {
|
|
int size = 10;
|
|
int[][] map = new int[][] {
|
|
{ 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 },
|
|
{ 0, 1, 1, 1, 0, 0, 1, 1, 1, 0 },
|
|
{ 0, 1, 1, 0, 0, 0, 5, 1, 6, 0 },
|
|
{ 0, 1, 2, 0, 0, 0, 4, 1, 1, 0 },
|
|
{ 0, 1, 1, 0, 0, 0, 1, 1, 0, 0 },
|
|
{ 0, 0, 0, 0, 3, 0, 1, 1, 0, 0 },
|
|
{ 0, 0, 0, 1, 1, 0, 0, 0, 1, 0 },
|
|
{ 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 },
|
|
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
|
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
|
};
|
|
|
|
// size = 100;
|
|
// map = new int[size][size];
|
|
// for (int x=0;x<size;x++) {
|
|
// for (int y=0;y<size;y++) {
|
|
// if ((x+y) % 2 == 0) {
|
|
// map[y][x] = 1;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
for (int x = 0; x < map[0].length; x++) {
|
|
for (int y = 0; y < map.length; y++) {
|
|
if (map[y][x] != 0) {
|
|
switch (map[y][x]) {
|
|
case 1:
|
|
Polygon p2 = new Polygon();
|
|
p2.addPoint(x * 32, y * 32);
|
|
p2.addPoint((x * 32) + 32, y * 32);
|
|
p2.addPoint((x * 32) + 32, (y * 32) + 32);
|
|
p2.addPoint(x * 32, (y * 32) + 32);
|
|
original.add(p2);
|
|
break;
|
|
case 2:
|
|
Polygon poly = new Polygon();
|
|
poly.addPoint(x * 32, y * 32);
|
|
poly.addPoint((x * 32) + 32, y * 32);
|
|
poly.addPoint(x * 32, (y * 32) + 32);
|
|
original.add(poly);
|
|
break;
|
|
case 3:
|
|
Circle ellipse = new Circle((x*32)+16,(y*32)+32,16,16);
|
|
original.add(ellipse);
|
|
break;
|
|
case 4:
|
|
Polygon p = new Polygon();
|
|
p.addPoint((x * 32) + 32, (y * 32));
|
|
p.addPoint((x * 32) + 32, (y * 32)+32);
|
|
p.addPoint(x * 32, (y * 32) + 32);
|
|
original.add(p);
|
|
break;
|
|
case 5:
|
|
Polygon p3 = new Polygon();
|
|
p3.addPoint((x * 32), (y * 32));
|
|
p3.addPoint((x * 32) + 32, (y * 32));
|
|
p3.addPoint((x * 32) + 32, (y * 32)+32);
|
|
original.add(p3);
|
|
break;
|
|
case 6:
|
|
Polygon p4 = new Polygon();
|
|
p4.addPoint((x * 32), (y * 32));
|
|
p4.addPoint((x * 32) + 32, (y * 32));
|
|
p4.addPoint((x * 32), (y * 32)+32);
|
|
original.add(p4);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
long before = System.currentTimeMillis();
|
|
|
|
// the quad spaced method
|
|
generateSpace(original, 0, 0, (size+1)*32,(size+1)*32,8);
|
|
combined = combineQuadSpace();
|
|
|
|
// the brute force method
|
|
//combined = combine(original);
|
|
|
|
long after = System.currentTimeMillis();
|
|
System.out.println("Combine took: "+(after-before));
|
|
System.out.println("Combine result: "+combined.size());
|
|
}
|
|
|
|
/**
|
|
* Combine the shapes in the quad space
|
|
*
|
|
* @return The newly combined list of shapes
|
|
*/
|
|
private ArrayList combineQuadSpace() {
|
|
boolean updated = true;
|
|
while (updated) {
|
|
updated = false;
|
|
|
|
for (int x=0;x<quadSpace.length;x++) {
|
|
for (int y=0;y<quadSpace.length;y++) {
|
|
ArrayList shapes = quadSpace[x][y];
|
|
int before = shapes.size();
|
|
combine(shapes);
|
|
int after = shapes.size();
|
|
|
|
updated |= before != after;
|
|
}
|
|
}
|
|
}
|
|
|
|
// at this stage all the shapes that can be combined within their quads
|
|
// will have gone on - we may need to combine stuff on the boundary tho
|
|
HashSet result = new HashSet();
|
|
|
|
for (int x=0;x<quadSpace.length;x++) {
|
|
for (int y=0;y<quadSpace.length;y++) {
|
|
result.addAll(quadSpace[x][y]);
|
|
}
|
|
}
|
|
|
|
return new ArrayList(result);
|
|
}
|
|
|
|
/**
|
|
* Combine a set of shapes together
|
|
*
|
|
* @param shapes
|
|
* The shapes to be combined
|
|
* @return The list of combined shapes
|
|
*/
|
|
private ArrayList combine(ArrayList shapes) {
|
|
ArrayList last = shapes;
|
|
ArrayList current = shapes;
|
|
boolean first = true;
|
|
|
|
while ((current.size() != last.size()) || (first)) {
|
|
first = false;
|
|
last = current;
|
|
current = combineImpl(current);
|
|
}
|
|
|
|
ArrayList pruned = new ArrayList();
|
|
for (int i = 0; i < current.size(); i++) {
|
|
pruned.add(((Shape) current.get(i)).prune());
|
|
}
|
|
return pruned;
|
|
}
|
|
|
|
/**
|
|
* Attempt to find a simple combination that can be performed
|
|
*
|
|
* @param shapes
|
|
* The shapes to be combined
|
|
* @return The new list of shapes - this will be the same length as the
|
|
* input if there are no new combinations
|
|
*/
|
|
private ArrayList combineImpl(ArrayList shapes) {
|
|
ArrayList result = new ArrayList(shapes);
|
|
if (quadSpace != null) {
|
|
result = shapes;
|
|
}
|
|
|
|
for (int i = 0; i < shapes.size(); i++) {
|
|
Shape first = (Shape) shapes.get(i);
|
|
for (int j = i + 1; j < shapes.size(); j++) {
|
|
Shape second = (Shape) shapes.get(j);
|
|
|
|
if (!first.intersects(second)) {
|
|
continue;
|
|
}
|
|
|
|
Shape[] joined = util.union(first, second);
|
|
if (joined.length == 1) {
|
|
if (quadSpace != null) {
|
|
removeFromQuadSpace(first);
|
|
removeFromQuadSpace(second);
|
|
addToQuadSpace(joined[0]);
|
|
} else {
|
|
result.remove(first);
|
|
result.remove(second);
|
|
result.add(joined[0]);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check if two shapes collide
|
|
*
|
|
* @param shape1 The first shape
|
|
* @param shape2 The second shape
|
|
* @return True if the shapes collide (i.e. intersection or overlap)
|
|
*/
|
|
public boolean collides(Shape shape1, Shape shape2) {
|
|
if (shape1.intersects(shape2)) {
|
|
return true;
|
|
}
|
|
for (int i=0;i<shape1.getPointCount();i++) {
|
|
float[] pt = shape1.getPoint(i);
|
|
if (shape2.contains(pt[0], pt[1])) {
|
|
return true;
|
|
}
|
|
}
|
|
for (int i=0;i<shape2.getPointCount();i++) {
|
|
float[] pt = shape2.getPoint(i);
|
|
if (shape1.contains(pt[0], pt[1])) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @see BasicGame#init(GameContainer)
|
|
*/
|
|
public void init(GameContainer container) throws SlickException {
|
|
util.setListener(this);
|
|
init();
|
|
//container.setVSync(true);
|
|
}
|
|
|
|
/**
|
|
* @see BasicGame#update(GameContainer, int)
|
|
*/
|
|
public void update(GameContainer container, int delta)
|
|
throws SlickException {
|
|
}
|
|
|
|
/**
|
|
* @see org.newdawn.slick.Game#render(GameContainer, Graphics)
|
|
*/
|
|
public void render(GameContainer container, Graphics g)
|
|
throws SlickException {
|
|
g.setColor(Color.green);
|
|
for (int i = 0; i < original.size(); i++) {
|
|
Shape shape = (Shape) original.get(i);
|
|
g.draw(shape);
|
|
}
|
|
|
|
g.setColor(Color.white);
|
|
if (quadSpaceShapes != null) {
|
|
g.draw(quadSpaceShapes[0][0]);
|
|
}
|
|
|
|
g.translate(0, 320);
|
|
|
|
for (int i = 0; i < combined.size(); i++) {
|
|
g.setColor(Color.white);
|
|
Shape shape = (Shape) combined.get(i);
|
|
g.draw(shape);
|
|
for (int j = 0; j < shape.getPointCount(); j++) {
|
|
g.setColor(Color.yellow);
|
|
float[] pt = shape.getPoint(j);
|
|
g.fillOval(pt[0] - 1, pt[1] - 1, 3, 3);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Entry point to our test
|
|
*
|
|
* @param argv
|
|
* The arguments passed to the test
|
|
*/
|
|
public static void main(String[] argv) {
|
|
try {
|
|
AppGameContainer container = new AppGameContainer(
|
|
new GeomUtilTileTest());
|
|
container.setDisplayMode(800, 600, false);
|
|
container.start();
|
|
} catch (SlickException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void pointExcluded(float x, float y) {
|
|
}
|
|
|
|
public void pointIntersected(float x, float y) {
|
|
intersections.add(new Vector2f(x, y));
|
|
}
|
|
|
|
public void pointUsed(float x, float y) {
|
|
used.add(new Vector2f(x, y));
|
|
}
|
|
}
|