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 final static Logger logger = LoggerFactory.getLogger( Polygon.class ); protected ArrayList _points = new ArrayList(); protected ArrayList _steinerPoints; protected ArrayList _holes; protected List 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 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(); } _steinerPoints.add( point ); } public void addSteinerPoints( List points ) { if( _steinerPoints == null ) { _steinerPoints = new ArrayList(); } _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(); } _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 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 getPoints() { return _points; } public List getTriangles() { return m_triangles; } public void addTriangle( DelaunayTriangle t ) { m_triangles.add( t ); } public void addTriangles( List 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( _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 ); } } }