package org.osm2world.core.math;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import org.osm2world.core.util.exception.TriangulationException;
import org.poly2tri.Poly2Tri;
import org.poly2tri.triangulation.Triangulatable;
import org.poly2tri.triangulation.TriangulationAlgorithm;
import org.poly2tri.triangulation.TriangulationContext;
import org.poly2tri.triangulation.TriangulationMode;
import org.poly2tri.triangulation.TriangulationPoint;
import org.poly2tri.triangulation.delaunay.DelaunayTriangle;
import org.poly2tri.triangulation.point.TPoint;
/**
* @author Hannes Janetzek
* */
public class Poly2TriUtil {
static class CDTSet implements Triangulatable {
List<TriangulationPoint> points = new ArrayList<TriangulationPoint>(20);
List<DelaunayTriangle> triangles = new ArrayList<DelaunayTriangle>(20);
ArrayList<LineSegmentXZ> segmentSet = new ArrayList<LineSegmentXZ>();
// it seems poly2tri requires points to be unique objects
HashMap<VectorXZ, TriangulationPoint> pointSet
= new HashMap<VectorXZ, TriangulationPoint>();
public CDTSet(SimplePolygonXZ polygon,
Collection<SimplePolygonXZ> holes,
Collection<LineSegmentXZ> cSegments,
Collection<VectorXZ> cPoints) {
List<VectorXZ> vertices = polygon.getVertexLoop();
segmentSet.addAll(cSegments);
for (VectorXZ p : cPoints) {
if (!pointSet.containsKey(p)) {
TPoint tp = new TPoint(p.x, p.z);
pointSet.put(p, tp);
points.add(tp);
}
}
for (int i = 0, n = vertices.size() - 1; i < n; i++)
segmentSet.add(new LineSegmentXZ(vertices.get(i),
vertices.get(i + 1)));
for (SimplePolygonXZ hole : holes) {
vertices = hole.getVertexLoop();
for (int i = 0, n = vertices.size() - 1; i < n; i++)
segmentSet.add(new LineSegmentXZ(vertices.get(i),
vertices.get(i + 1)));
}
removeDuplicateSegments();
boolean foundIntersections = false;
// split at intersections
for (int i = 0, size = segmentSet.size(); i < size - 1; i++) {
LineSegmentXZ l1 = segmentSet.get(i);
for (int j = i + 1; j < size; j++) {
LineSegmentXZ l2 = segmentSet.get(j);
VectorXZ crossing;
if ((crossing = l1.getIntersection(l2.p1, l2.p2)) != null) {
System.out.println("split " + l1 + " " + l2 + " at "
+ crossing);
foundIntersections = true;
segmentSet.remove(l1);
segmentSet.remove(l2);
segmentSet.add(new LineSegmentXZ(crossing, l1.p1));
segmentSet.add(new LineSegmentXZ(crossing, l1.p2));
segmentSet.add(new LineSegmentXZ(crossing, l2.p1));
segmentSet.add(new LineSegmentXZ(crossing, l2.p2));
size += 2;
// first segment was removed
i--;
break;
}
}
}
if (foundIntersections)
removeDuplicateSegments();
}
private void removeDuplicateSegments() {
for (int i = 0, size = segmentSet.size(); i < size - 1; i++) {
LineSegmentXZ l1 = segmentSet.get(i);
for (int j = i + 1; j < size; j++) {
LineSegmentXZ l2 = segmentSet.get(j);
if ((l1.p1.equals(l2.p1) && l1.p2.equals(l2.p2))
|| (l1.p1.equals(l2.p2) && l1.p2.equals(l2.p1))) {
//System.out.println("remove dup " + l1 + " " + l2);
segmentSet.remove(j);
size--;
}
}
}
}
public TriangulationMode getTriangulationMode() {
return TriangulationMode.CONSTRAINED;
}
public List<TriangulationPoint> getPoints() {
return points;
}
public List<DelaunayTriangle> getTriangles() {
return triangles;
}
public void addTriangle(DelaunayTriangle t) {
triangles.add(t);
}
public void addTriangles(List<DelaunayTriangle> list) {
triangles.addAll(list);
}
public void clearTriangulation() {
triangles.clear();
}
public void prepareTriangulation(TriangulationContext<?> tcx) {
triangles.clear();
for (LineSegmentXZ l : segmentSet) {
TriangulationPoint tp1, tp2;
if (!pointSet.containsKey(l.p1)){
tp1 = new TPoint(l.p1.x, l.p1.z);
pointSet.put(l.p1, tp1);
points.add(tp1);
} else {
tp1 = pointSet.get(l.p1);
}
if (!pointSet.containsKey(l.p2)){
tp2 = new TPoint(l.p2.x, l.p2.z);
pointSet.put(l.p2, tp2);
points.add(tp2);
} else {
tp2 = pointSet.get(l.p2);
}
tcx.newConstraint(tp1, tp2);
}
segmentSet.clear();
pointSet.clear();
tcx.addPoints(points);
}
}
public static final List<TriangleXZ> triangulate(SimplePolygonXZ polygon,
Collection<SimplePolygonXZ> holes,
Collection<LineSegmentXZ> segments, Collection<VectorXZ> points) throws TriangulationException {
CDTSet cdt = new CDTSet(polygon, holes, segments, points);
TriangulationContext<?> tcx = Poly2Tri
.createContext(TriangulationAlgorithm.DTSweep);
tcx.prepareTriangulation(cdt);
try {
Poly2Tri.triangulate(tcx);
} catch (Exception e) {
throw new TriangulationException(e);
} catch (StackOverflowError e) {
throw new TriangulationException(e);
}
List<TriangleXZ> triangles = new ArrayList<TriangleXZ>();
List<DelaunayTriangle> result = cdt.getTriangles();
if (result == null)
return triangles;
for (DelaunayTriangle t : result) {
TriangulationPoint tCenter = t.centroid();
VectorXZ center = new VectorXZ(tCenter.getX(), tCenter.getY());
boolean triangleInHole = false;
for (SimplePolygonXZ hole : holes) {
if (hole.contains(center)) {
triangleInHole = true;
break;
}
}
if (triangleInHole || !polygon.contains(center))
continue;
triangles.add(new TriangleXZ(new VectorXZ(t.points[0].getX(),
t.points[0].getY()), new VectorXZ(t.points[1].getX(),
t.points[1].getY()), new VectorXZ(t.points[2].getX(),
t.points[2].getY())));
}
return triangles;
}
}