package com.vitco.low.triangulate;
import com.vitco.util.misc.IntegerTools;
import gnu.trove.set.hash.TIntHashSet;
import org.poly2tri.Poly2Tri;
import org.poly2tri.geometry.polygon.PolygonPoint;
import org.poly2tri.triangulation.TriangulationAlgorithm;
import org.poly2tri.triangulation.TriangulationContext;
import org.poly2tri.triangulation.delaunay.DelaunayTriangle;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Takes a 2D bit array and converts it into polygons with holes.
*
* Also implements access to the Poly2Tri algorithm to convert the created polygons into triangles.
*
* Note: The main difference between this algorithm and the JAI algorithm of "voxel -> polygon" is
* that this one already combines holes that touch inside a polygon and furthermore already integrates
* holes that touch the outline of the polygon. This is helpful as it is required for processing
* with Poly2Tri, but could probably be changes by changing
* (edgeR[4] == 1 ? 1 : -1)
* to
* (edgeR[4] == 1 ? -1 : 1)
* two times (!). Verification still required.
*/
public class Grid2TriPolyFast {
// interpolation value
private static final double INTERP = 0.000001;
// ==============
// helper - we need only one context for all conversion (faster)
private final static TriangulationContext tcx = Poly2Tri.createContext(TriangulationAlgorithm.DTSweep);
// triangulate a polygon, the input data is interpolated to allow Poly2Tri to process it.
// Hence the output data is slightly "off". This can be fixed by rounding the output data, don't use (int)
// casting though as this might round down instead of up.
public static ArrayList<DelaunayTriangle> triangulate(short[][][] polys) {
ArrayList<DelaunayTriangle> result = new ArrayList<DelaunayTriangle>();
// stores and manages all seen points
TIntHashSet indexer = new TIntHashSet();
// loop over all polygon (a polygon consists of exterior and interior ring)
for (short[][] poly : polys) {
// stores an interpolated polygon ("0" entry is outline, others are holes)
HashMap<Integer, ArrayList<PolygonPoint>> polygon = new HashMap<Integer, ArrayList<PolygonPoint>>();
// stored the current point list (temp variable)
ArrayList<PolygonPoint> resultOutline = new ArrayList<PolygonPoint>();
polygon.put(0,resultOutline);
// loop over polygon outline
short[] outline = poly[0];
for (int i = 0, len = outline.length - 2; i < len; i+=2) {
// check if we need to interpolate
if (!indexer.add(IntegerTools.makeInt(outline[i], outline[i + 1]))) {
resultOutline.add(new PolygonPoint(outline[i] - Math.signum(outline[i] - outline[i+2]) * INTERP, outline[i+1] - Math.signum(outline[i+1] - outline[i+3]) * INTERP));
} else {
resultOutline.add(new PolygonPoint(outline[i], outline[i+1]));
}
}
indexer.clear();
// loop over polygon holes
for (int j = 1; j < poly.length; j++) {
// create new hole outline entry
resultOutline = new ArrayList<PolygonPoint>();
polygon.put(j,resultOutline);
outline = poly[j];
for (int i = 0, len = outline.length - 2; i < len; i+=2) {
// check if we need to interpolate
if (!indexer.add(IntegerTools.makeInt(outline[i], outline[i + 1]))) {
resultOutline.add(new PolygonPoint(outline[i] - Math.signum(outline[i] - outline[i+2]) * INTERP, outline[i+1] - Math.signum(outline[i+1] - outline[i+3]) * INTERP));
} else {
resultOutline.add(new PolygonPoint(outline[i], outline[i+1]));
}
}
indexer.clear();
}
// convert to polygon from raw data (zero is always the id that contains the exterior of the polygon)
org.poly2tri.geometry.polygon.Polygon polyR = new org.poly2tri.geometry.polygon.Polygon(polygon.remove(0));
for (ArrayList<PolygonPoint> hole : polygon.values()) {
polyR.addHole(new org.poly2tri.geometry.polygon.Polygon(hole));
}
// do the triangulation and add the triangles for this polygon
// Note: This needs to be synchronized to prevent multiple instances
// from accessing the tcx context at once
synchronized (tcx) {
tcx.prepareTriangulation(polyR);
Poly2Tri.triangulate(tcx);
tcx.clear();
}
result.addAll(polyR.getTriangles());
}
// return all triangles
return result;
}
}