package org.poly2tri.geometry.polygon; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.poly2tri.triangulation.Triangulatable; import org.poly2tri.triangulation.TriangulationContext; import org.poly2tri.triangulation.TriangulationMode; import org.poly2tri.triangulation.TriangulationPoint; import org.poly2tri.triangulation.delaunay.DelaunayTriangle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Polygon implements Triangulatable { private static Logger logger = LoggerFactory.getLogger(Polygon.class); protected ArrayList<TriangulationPoint> _points = new ArrayList<TriangulationPoint>(); protected ArrayList<TriangulationPoint> _steinerPoints; protected ArrayList<Polygon> _holes; protected List<DelaunayTriangle> m_triangles; protected PolygonPoint _last; /** * To create a polygon we need atleast 3 separate points * * @param p1 * @param p2 * @param p3 */ public Polygon(PolygonPoint p1, PolygonPoint p2, PolygonPoint p3) { p1._next = p2; p2._next = p3; p3._next = p1; p1._previous = p3; p2._previous = p1; p3._previous = p2; _points.add(p1); _points.add(p2); _points.add(p3); } /** * Requires atleast 3 points * @param points - ordered list of points forming the polygon. * No duplicates are allowed */ public Polygon(List<PolygonPoint> points) { // Lets do one sanity check that first and last point hasn't got same position // Its something that often happen when importing polygon data from other formats if (points.get(0).equals(points.get(points.size() - 1))) { logger.warn("Removed duplicate point"); points.remove(points.size() - 1); } _points.addAll(points); } /** * Requires atleast 3 points * * @param points */ public Polygon(PolygonPoint[] points) { this(Arrays.asList(points)); } public TriangulationMode getTriangulationMode() { return TriangulationMode.POLYGON; } public int pointCount() { int count = _points.size(); if (_steinerPoints != null) { count += _steinerPoints.size(); } return count; } public void addSteinerPoint(TriangulationPoint point) { if (_steinerPoints == null) { _steinerPoints = new ArrayList<TriangulationPoint>(); } _steinerPoints.add(point); } public void addSteinerPoints(List<TriangulationPoint> points) { if (_steinerPoints == null) { _steinerPoints = new ArrayList<TriangulationPoint>(); } _steinerPoints.addAll(points); } public void clearSteinerPoints() { if (_steinerPoints != null) { _steinerPoints.clear(); } } /** * Assumes: that given polygon is fully inside the current polygon * @param poly - a subtraction polygon */ public void addHole(Polygon poly) { if (_holes == null) { _holes = new ArrayList<Polygon>(); } _holes.add(poly); // XXX: tests could be made here to be sure it is fully inside // addSubtraction( poly.getPoints() ); } /** * Will insert a point in the polygon after given point * * @param a * @param b * @param p */ public void insertPointAfter(PolygonPoint a, PolygonPoint newPoint) { // Validate that int index = _points.indexOf(a); if (index != -1) { newPoint.setNext(a.getNext()); newPoint.setPrevious(a); a.getNext().setPrevious(newPoint); a.setNext(newPoint); _points.add(index + 1, newPoint); } else { throw new RuntimeException( "Tried to insert a point into a Polygon after a point not belonging to the Polygon"); } } public void addPoints(List<PolygonPoint> list) { PolygonPoint first; for (PolygonPoint p : list) { p.setPrevious(_last); if (_last != null) { p.setNext(_last.getNext()); _last.setNext(p); } _last = p; _points.add(p); } first = (PolygonPoint) _points.get(0); _last.setNext(first); first.setPrevious(_last); } /** * Will add a point after the last point added * * @param p */ public void addPoint(PolygonPoint p) { p.setPrevious(_last); p.setNext(_last.getNext()); _last.setNext(p); _points.add(p); } public void removePoint(PolygonPoint p) { PolygonPoint next, prev; next = p.getNext(); prev = p.getPrevious(); prev.setNext(next); next.setPrevious(prev); _points.remove(p); } public PolygonPoint getPoint() { return _last; } public List<TriangulationPoint> getPoints() { return _points; } public List<DelaunayTriangle> getTriangles() { return m_triangles; } public void addTriangle(DelaunayTriangle t) { m_triangles.add(t); } public void addTriangles(List<DelaunayTriangle> list) { m_triangles.addAll(list); } public void clearTriangulation() { if (m_triangles != null) { m_triangles.clear(); } } /** * Creates constraints and populates the context with points */ public void prepareTriangulation(TriangulationContext<?> tcx) { if (m_triangles == null) { m_triangles = new ArrayList<DelaunayTriangle>(_points.size()); } else { m_triangles.clear(); } // Outer constraints for (int i = 0; i < _points.size() - 1; i++) { tcx.newConstraint(_points.get(i), _points.get(i + 1)); } tcx.newConstraint(_points.get(0), _points.get(_points.size() - 1)); tcx.addPoints(_points); // Hole constraints if (_holes != null) { for (Polygon p : _holes) { for (int i = 0; i < p._points.size() - 1; i++) { tcx.newConstraint(p._points.get(i), p._points.get(i + 1)); } tcx.newConstraint(p._points.get(0), p._points.get(p._points .size() - 1)); tcx.addPoints(p._points); } } if (_steinerPoints != null) { tcx.addPoints(_steinerPoints); } } }