/* Poly2Tri * Copyright (c) 2009-2010, Poly2Tri Contributors * http://code.google.com/p/poly2tri/ * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Poly2Tri nor the names of its contributors may be * used to endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // Changes from the Java version // Polygon constructors sprused up, checks for 3+ polys // Naming of everything // getTriangulationMode() -> TriangulationMode { get; } // Exceptions replaced // Future possibilities // We have a lot of Add/Clear methods -- we may prefer to just expose the container // Some self-explanitory methods may deserve commenting anyways using System; using System.Collections.Generic; using System.Linq; using Poly2Tri.Triangulation.Delaunay; namespace Poly2Tri.Triangulation.Polygon { public class Polygon : Triangulatable { protected List _holes; protected PolygonPoint _last; protected List _points = new List(); protected List _steinerPoints; protected List _triangles; /// /// Create a polygon from a list of at least 3 points with no duplicates. /// /// A list of unique points public Polygon(IList points) { if (points.Count < 3) throw new ArgumentException("List has fewer than 3 points", "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[0].Equals(points[points.Count - 1])) points.RemoveAt(points.Count - 1); _points.AddRange(points.Cast()); } /// /// Create a polygon from a list of at least 3 points with no duplicates. /// /// A list of unique points. public Polygon(IEnumerable points) : this((points as IList) ?? points.ToArray()) { } public Polygon() { } public IList Holes { get { return _holes; } } #region Triangulatable Members public TriangulationMode TriangulationMode { get { return TriangulationMode.Polygon; } } public IList Points { get { return _points; } } public IList Triangles { get { return _triangles; } } public void AddTriangle(DelaunayTriangle t) { _triangles.Add(t); } public void AddTriangles(IEnumerable list) { _triangles.AddRange(list); } public void ClearTriangles() { if (_triangles != null) _triangles.Clear(); } /// /// Creates constraints and populates the context with points /// /// The context public void PrepareTriangulation(TriangulationContext tcx) { if (_triangles == null) { _triangles = new List(_points.Count); } else { _triangles.Clear(); } // Outer constraints for (int i = 0; i < _points.Count - 1; i++) { tcx.NewConstraint(_points[i], _points[i + 1]); } tcx.NewConstraint(_points[0], _points[_points.Count - 1]); tcx.Points.AddRange(_points); // Hole constraints if (_holes != null) { foreach (Polygon p in _holes) { for (int i = 0; i < p._points.Count - 1; i++) { tcx.NewConstraint(p._points[i], p._points[i + 1]); } tcx.NewConstraint(p._points[0], p._points[p._points.Count - 1]); tcx.Points.AddRange(p._points); } } if (_steinerPoints != null) { tcx.Points.AddRange(_steinerPoints); } } #endregion public void AddSteinerPoint(TriangulationPoint point) { if (_steinerPoints == null) { _steinerPoints = new List(); } _steinerPoints.Add(point); } public void AddSteinerPoints(List points) { if (_steinerPoints == null) { _steinerPoints = new List(); } _steinerPoints.AddRange(points); } public void ClearSteinerPoints() { if (_steinerPoints != null) { _steinerPoints.Clear(); } } /// /// Add a hole to the polygon. /// /// A subtraction polygon fully contained inside this polygon. public void AddHole(Polygon poly) { if (_holes == null) _holes = new List(); _holes.Add(poly); // XXX: tests could be made here to be sure it is fully inside // addSubtraction( poly.getPoints() ); } /// /// Inserts newPoint after point. /// /// The point to insert after in the polygon /// The point to insert into the polygon public void InsertPointAfter(PolygonPoint point, PolygonPoint newPoint) { // Validate that int index = _points.IndexOf(point); if (index == -1) throw new ArgumentException( "Tried to insert a point into a Polygon after a point not belonging to the Polygon", "point"); newPoint.Next = point.Next; newPoint.Previous = point; point.Next.Previous = newPoint; point.Next = newPoint; _points.Insert(index + 1, newPoint); } /// /// Inserts list (after last point in polygon?) /// /// public void AddPoints(IEnumerable list) { PolygonPoint first; foreach (PolygonPoint p in list) { p.Previous = _last; if (_last != null) { p.Next = _last.Next; _last.Next = p; } _last = p; _points.Add(p); } first = (PolygonPoint) _points[0]; _last.Next = first; first.Previous = _last; } /// /// Adds a point after the last in the polygon. /// /// The point to add public void AddPoint(PolygonPoint p) { p.Previous = _last; p.Next = _last.Next; _last.Next = p; _points.Add(p); } /// /// Removes a point from the polygon. /// /// public void RemovePoint(PolygonPoint p) { PolygonPoint next, prev; next = p.Next; prev = p.Previous; prev.Next = next; next.Previous = prev; _points.Remove(p); } } }