package org.osm2world.core.math.algorithms; import static java.util.Collections.emptyList; import static org.osm2world.core.math.JTSConversionUtil.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.osm2world.core.math.LineSegmentXZ; import org.osm2world.core.math.PolygonWithHolesXZ; import org.osm2world.core.math.SimplePolygonXZ; import org.osm2world.core.math.TriangleXZ; import org.osm2world.core.math.VectorXZ; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.impl.CoordinateArraySequence; import com.vividsolutions.jts.triangulate.ConformingDelaunayTriangulationBuilder; /** * uses the JTS library for triangulation. * Creates a Conforming Delaunay Triangulation with Steiner points! */ public final class JTSTriangulationUtil { private static final Geometry[] EMPTY_GEOM_ARRAY = new Geometry[0]; private JTSTriangulationUtil() { } /** * triangulation of a polygon with holes, based on a * conforming delaunay triangulation */ public static final List<TriangleXZ> triangulate( SimplePolygonXZ polygon, Collection<SimplePolygonXZ> holes) { List<VectorXZ> points = emptyList(); List<LineSegmentXZ> segments = emptyList(); return triangulate(polygon, holes, segments, points); } /** * variant of {@link #triangulate(SimplePolygonXZ, Collection)} * that accepts some unconnected points within the polygon area * and will try to create triangle vertices at these points. * It will also accept line segment as edges that must be integrated * into the resulting triangulation. */ public static final List<TriangleXZ> triangulate( SimplePolygonXZ polygon, Collection<SimplePolygonXZ> holes, Collection<LineSegmentXZ> segments, Collection<VectorXZ> points) { ConformingDelaunayTriangulationBuilder triangulationBuilder = new ConformingDelaunayTriangulationBuilder(); List<Geometry> constraints = new ArrayList<Geometry>(1 + holes.size() + segments.size()); constraints.add(polygonXZToJTSPolygon(polygon)); for (SimplePolygonXZ hole : holes) { constraints.add(polygonXZToJTSPolygon(hole)); } for (LineSegmentXZ segment : segments) { constraints.add(lineSegmentXZToJTSLineString(segment)); } ArrayList<Point> jtsPoints = new ArrayList<Point>(); for (VectorXZ p : points) { CoordinateSequence coordinateSequence = new CoordinateArraySequence(new Coordinate[] { vectorXZToJTSCoordinate(p)}); jtsPoints.add(new Point(coordinateSequence, GF)); } triangulationBuilder.setSites( new GeometryCollection(jtsPoints.toArray(EMPTY_GEOM_ARRAY), GF)); triangulationBuilder.setConstraints( new GeometryCollection(constraints.toArray(EMPTY_GEOM_ARRAY), GF)); triangulationBuilder.setTolerance(0.01); /* run triangulation */ Geometry triangulationResult = triangulationBuilder.getTriangles(GF); /* interpret the resulting polygons as triangles, * filter out those which are outside the polygon or in a hole */ Collection<PolygonWithHolesXZ> trianglesAsPolygons = polygonsXZFromJTSGeometry(triangulationResult); List<TriangleXZ> triangles = new ArrayList<TriangleXZ>(); for (PolygonWithHolesXZ triangleAsPolygon : trianglesAsPolygons) { boolean triangleInHole = false; for (SimplePolygonXZ hole : holes) { if (hole.contains(triangleAsPolygon.getOuter().getCenter())) { triangleInHole = true; break; } } if (!triangleInHole && polygon.contains( triangleAsPolygon.getOuter().getCenter())) { //TODO: create single method for this query within PolygonWithHoles triangles.add(triangleAsPolygon.asTriangleXZ()); } } return triangles; } }