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()