1 package de.lathanda.eos.game.geom.tesselation;
3 import java.text.MessageFormat;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Iterator;
7 import java.util.LinkedList;
8 import java.util.TreeSet;
10 import de.lathanda.eos.base.math.Point;
11 import de.lathanda.eos.base.math.Vector;
12 import de.lathanda.eos.base.util.IntervalTree;
13 import de.lathanda.eos.game.geom.BoundingBox;
14 import de.lathanda.eos.game.geom.Triangle;
15 import de.lathanda.eos.game.geom.Tesselation;
32 private LinkedList<Vertice> v =
new LinkedList<>() ;
34 private LinkedList<Vertice> p =
new LinkedList<>();
36 private LinkedList<Triangle> t =
new LinkedList<>();
38 private ArrayList<Edge> edges;
40 private boolean triangleReady =
false;
41 private boolean borderReady =
false;
45 if (x.length != y.length)
throw new ArrayIndexOutOfBoundsException();
46 for(
int i = 0; i < x.length; i++) {
47 v.add(
new Vertice(x[i], y[i]));
51 for(
Point p : points) {
52 v.add(
new Vertice(p.getX(), p.getY()));
56 for(
Point p : points) {
57 v.add(
new Vertice(((
int)(raster*p.getX()))/raster, ((
int)(raster*p.getY()))/raster));
61 v.add(
new Vertice(x, y));
62 triangleReady =
false;
76 Edge start = createEdges();
77 convertToSimplePolygon(start);
94 Edge start = createEdges();
95 convertToSimplePolygon(start);
101 if (!triangleReady && ! borderReady ) {
103 }
else if (!triangleReady && borderReady) {
122 IntervalTree<BoundingBox<Edge>.xInterval> xRange =
new IntervalTree<>();
123 IntervalTree<BoundingBox<Edge>.yInterval> yRange =
new IntervalTree<>();
124 for (Edge e : edges) {
125 xRange.insert(e.b.new xInterval());
126 yRange.insert(e.b.new yInterval());
130 int failed = v.size() * 4;
132 if (failed -- == 0) {
136 TreeSet<BoundingBox<Edge>.xInterval> xHits =
new TreeSet<>();
137 TreeSet<BoundingBox<Edge>.yInterval> yHits =
new TreeSet<>();
138 xRange.seek(act.b.new xInterval(), xHits);
139 yRange.seek(act.b.new yInterval(), yHits);
141 TreeSet<Edge> hits =
new TreeSet<>();
142 BoundingBox<Edge>.xInterval xInt = xHits.pollFirst();
143 BoundingBox<Edge>.yInterval yInt = yHits.pollFirst();
144 while (xInt !=
null && yInt !=
null) {
145 if (xInt.getSource() == yInt.getSource()) {
147 hits.add(xInt.getSource());
148 xInt = xHits.pollFirst();
149 yInt = yHits.pollFirst();
150 }
else if ( xInt.getSource().compareTo(yInt.getSource()) < 0) {
151 xInt = xHits.pollFirst();
153 yInt = yHits.pollFirst();
157 CrossingPoint best =
null;
158 for (Edge e : hits) {
159 if (e.id == act.id) {
162 CrossingPoint inter = act.getIntersection(e, edges);
164 if (inter !=
null && (inter.edge.x1 != act.x1 || inter.edge.y1 != act.y1)) {
167 if (inter.lambda < best.lambda && act.v.crossproduct(inter.edge.v) > 0) {
171 if (inter.lambda == best.lambda) {
173 if (best.angle < inter.angle) {
176 }
else if (best.angle == inter.angle) {
178 if (best.edge.v.getSquareLength() < inter.edge.v.getSquareLength()) {
185 if (inter.lambda == 1.0 || act.v.crossproduct(inter.edge.v) > 0) {
193 p.add(
new Vertice(act.x1, act.y1));
194 }
while (act.id != start.id);
197 private void earCut() throws TesselationFailedException {
199 Vertice prev = p.getLast();
200 Vertice[] last =
new Vertice[] {prev};
202 a.linkPredecessor(prev);
205 LinkedList<Vertice> concaves =
new LinkedList<>();
212 while (!concaves.isEmpty() && ok) {
214 for(Iterator<Vertice> i = concaves.iterator(); i.hasNext(); ) {
215 Vertice v = i.next();
216 if (v.suc ==
null || !v.isConcave()) {
220 if (v.pre.cutEmpty(last)) {
222 }
else if (v.suc.cutEmpty(last)) {
224 }
else if (v.pre.isEar(concaves)) {
225 t.add(v.pre.cutEar(last));
227 }
else if (v.suc.isEar(concaves)) {
228 t.add(v.suc.cutEar(last));
235 throw new TesselationFailedException();
237 while (last[0].pre.pre != last[0]) {
238 t.add(last[0].cutEar(last));
246 private Edge createEdges() {
247 edges =
new ArrayList<>();
249 Vertice v1 = v.get(v.size() - 1);
250 double minX = Double.POSITIVE_INFINITY;
251 double minY = Double.POSITIVE_INFINITY;
252 for(Vertice v2 : v) {
253 if (v1.isDifferent(v2)) {
254 edges.add(
new Edge(v1, v2, edges.size()));
256 if (v2.getX() < minX) {
262 Edge start =
new Edge(minX, minY, edges.size());
266 private static class Vertice
extends Point {
270 public Vertice(
double x,
double y) {
273 public boolean isDifferent(Vertice v2) {
274 return x != v2.x ||
y != v2.y;
276 public void linkPredecessor(Vertice predecessor) {
277 this.pre = predecessor;
278 predecessor.suc =
this;
279 v =
new Vector(pre,
this);
281 public Triangle cutEar(Vertice[] last) {
282 if (
this == last[0]) {
285 Triangle t =
new Triangle(pre.x, pre.y, x, y, suc.x, suc.y);
286 suc.linkPredecessor(pre);
291 public boolean cutEmpty(Vertice[] last) {
292 if (v.crossproduct(suc.v) != 0) {
293 if (
this == last[0]) {
296 suc.linkPredecessor(pre);
304 public boolean isEar(Vertice p) {
305 Vector cv =
new Vector(pre.x - suc.x, pre.y - suc.y);
306 Vector a =
new Vector(
this, p);
307 Vector b =
new Vector(suc, p);
308 Vector c =
new Vector(pre, p);
309 return suc.v.crossproduct(a) >= 0 || cv.crossproduct(b) >= 0 || v.crossproduct(c) >= 0;
311 public boolean isEar(Collection<Vertice> c) {
312 if (isConcave())
return false;
320 public boolean isConcave() {
321 return v.crossproduct(suc.v) > 0;
324 public String toString() {
325 return super.toString();
328 private static class Edge
implements Comparable<Edge> {
330 private final double x1, y1;
332 private final double x2, y2;
334 private final int id;
336 private final Vector v;
338 private final BoundingBox<Edge> b;
340 private final double angle;
347 public Edge(
double x,
double y,
int id) {
348 this.x1 = Double.NEGATIVE_INFINITY;
352 this.b =
new BoundingBox<>(x2, y2, x2, y2,
this);
354 this.v =
new Vector(1, 0);
355 this.angle = v.getAngleOrdernumber();
357 public Edge(
double x1,
double y1,
double x2,
double y2,
int id) {
362 this.b =
new BoundingBox<>(this.x1, this.y1, this.x2, this.y2,
this);
364 this.v =
new Vector(this.x2 - this.x1, this.y2 - this.y1);
365 this.angle = v.getAngleOrdernumber();
367 public Edge(Vertice v1, Vertice v2,
int id) {
368 this(v1.getX(), v1.getY(), v2.getX(), v2.getY(), id);
370 private final static double HALF = Double.longBitsToDouble(0x0000000000080000l);
371 private static double round(
double a) {
374 return Double.longBitsToDouble(Double.doubleToRawLongBits(a + HALF) & 0xFFFFFFFFFFF00000l);
381 public Edge(Edge e) {
388 this.v = e.v.negative();
389 this.angle = (e.angle >= 2)?e.angle - 2:e.angle + 2;
392 public CrossingPoint getIntersection(Edge b, ArrayList<Edge> edges) {
405 if (x2 == b.x1 && y2 == b.y1) {
407 return new CrossingPoint(
this, b,
false,
true);
409 if (x2 == b.x2 && y2 == b.y2) {
411 return new CrossingPoint(
this, b,
true,
true);
413 if (x1 == b.x1 && y1 == b.y1) {
415 return new CrossingPoint(
this, b,
false,
false);
417 if (x1 == b.x2 && y1 == b.y2) {
419 return new CrossingPoint(
this, b,
true,
false);
425 bm = edges.get(b.id);
427 am = edges.get(b.id);
431 Vector base =
new Vector(bm.x1 - am.x1, bm.y1 - am.y1);
432 double n = am.v.crossproduct(bm.v);
433 double za = base.crossproduct(bm.v);
437 return CrossingPoint.createCrossingPoint(
this, b, x2, y2);
443 double lambdaM = za / n;
444 double lambdaM2 = 1 - lambdaM;
445 double px = am.x1 * lambdaM2 + lambdaM * am.x2;
446 double py = am.y1 * lambdaM2 + lambdaM * am.y2;
447 return CrossingPoint.createCrossingPoint(
this, b, round(px), round(py));
450 public Edge invert() {
451 return new Edge(
this);
454 public int compareTo(Edge o) {
459 public String toString() {
460 return MessageFormat.format(
"id {4,number,#} ({0,number,#.##}/{1,number,#.##}) -({5,number,#.###})-> ({2,number,#.##}/{3,number,#.##})", x1, y1, x2, y2,
id, angle);
464 private static class CrossingPoint {
465 private final double lambda;
466 private final double angle;
467 private final Edge edge;
468 private CrossingPoint(Edge edge,
double lambda,
double angle) {
471 this.lambda = lambda;
480 public CrossingPoint(Edge a, Edge b,
boolean inverted,
boolean continues) {
491 double diffA = 2 + edge.angle - a.angle;
493 this.angle = diffA + 4;
494 }
else if (diffA >= 4) {
495 this.angle = diffA - 4;
501 private static CrossingPoint createCrossingPoint(Edge a, Edge b,
double px,
double py) {
505 if (Math.abs(a.x2 - a.x1) > Math.abs(a.y2 - a.y1)) {
506 lambda = (px - a.x1) / (a.x2 - a.x1);
508 lambda = (py - a.y1) / (a.y2 - a.y1);
510 if (Math.abs(b.x2 - b.x1) > Math.abs(b.y2 - b.y1)) {
511 my = (px - b.x1) / (b.x2 - b.x1);
513 my = (py - b.y1) / (b.y2 - b.y1);
516 if (lambda < 0.0000001 && lambda > -0.0000001) {
520 }
else if (lambda > 0.9999999 && lambda < 1.0000001) {
525 if (my < 0.0000001 && my > -0.0000001) {
527 }
else if (my > 0.9999999 && my < 1.0000001) {
531 if (lambda >= 0 && my >= 0 && lambda <= 1 && my <= 1) {
537 }
else if (my == 1) {
542 double c = a.v.crossproduct(b.v);
545 edge =
new Edge(px, py, b.x2, b.y2, b.id);
547 edge =
new Edge(px, py, b.x1, b.y1, b.id);
550 if (b.v.dotProduct(b.v) > 0) {
551 edge =
new Edge(px, py, b.x2, b.y2, b.id);
553 edge =
new Edge(px, py, b.x1, b.y1, b.id);
558 double diffA = 2 + edge.angle - a.angle;
562 }
else if (diffA >= 4) {
567 return new CrossingPoint(edge, lambda, angle);
573 public String toString() {
574 return edge +
" " + MessageFormat.format(
"lambda = {0,number,#.##}, angle = {1,number,#.###}", lambda, angle);
LinkedList<? extends Point > getPolygon()
BorderWalkTesselation(Collection<? extends Point > points)
void addVertice(double x, double y)
BorderWalkTesselation(double[] x, double[] y)
BorderWalkTesselation(Collection<? extends Point > points, double raster)
Collection< Triangle > getTriangles()
LinkedList<? extends Point > getOuterBorder()