// -----------------------------------------------------------------------
//
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
//
// -----------------------------------------------------------------------
namespace UnityEngine.U2D.Animation.TriangleNet
.Geometry
{
using System;
using System.Linq;
using System.Collections.Generic;
internal class Contour
{
int marker;
bool convex;
///
/// Gets or sets the list of points making up the contour.
///
public List Points { get; set; }
///
/// Initializes a new instance of the class.
///
/// The points that make up the contour.
public Contour(IEnumerable points)
: this(points, 0, false)
{
}
///
/// Initializes a new instance of the class.
///
/// The points that make up the contour.
/// Contour marker.
public Contour(IEnumerable points, int marker)
: this(points, marker, false)
{
}
///
/// Initializes a new instance of the class.
///
/// The points that make up the contour.
/// Contour marker.
/// The hole is convex.
public Contour(IEnumerable points, int marker, bool convex)
{
AddPoints(points);
this.marker = marker;
this.convex = convex;
}
public List GetSegments()
{
var segments = new List();
var p = this.Points;
int count = p.Count - 1;
for (int i = 0; i < count; i++)
{
// Add segments to polygon.
segments.Add(new Segment(p[i], p[i + 1], marker));
}
// Close the contour.
segments.Add(new Segment(p[count], p[0], marker));
return segments;
}
///
/// Try to find a point inside the contour.
///
/// The number of iterations on each segment (default = 5).
/// Threshold for co-linear points (default = 2e-5).
/// Point inside the contour
/// Throws if no point could be found.
///
/// For each corner (index i) of the contour, the 3 points with indices i-1, i and i+1
/// are considered and a search on the line through the corner vertex is started (either
/// on the bisecting line, or, if is less than
/// eps, on the perpendicular line.
/// A given number of points will be tested (limit), while the distance to the contour
/// boundary will be reduced in each iteration (with a factor 1 / 2^i, i = 1 ... limit).
///
public Point FindInteriorPoint(int limit = 5, double eps = 2e-5)
{
if (convex)
{
int count = this.Points.Count;
var point = new Point(0.0, 0.0);
for (int i = 0; i < count; i++)
{
point.x += this.Points[i].x;
point.y += this.Points[i].y;
}
// If the contour is convex, use its centroid.
point.x /= count;
point.y /= count;
return point;
}
return FindPointInPolygon(this.Points, limit, eps);
}
private void AddPoints(IEnumerable points)
{
this.Points = new List(points);
int count = Points.Count - 1;
// Check if first vertex equals last vertex.
if (Points[0] == Points[count])
{
Points.RemoveAt(count);
}
}
#region Helper methods
private static Point FindPointInPolygon(List contour, int limit, double eps)
{
var bounds = new Rectangle();
bounds.Expand(contour.Cast());
int length = contour.Count;
var test = new Point();
Point a, b, c; // Current corner points.
double bx, by;
double dx, dy;
double h;
var predicates = new RobustPredicates();
a = contour[0];
b = contour[1];
for (int i = 0; i < length; i++)
{
c = contour[(i + 2) % length];
// Corner point.
bx = b.x;
by = b.y;
// NOTE: if we knew the contour points were in counterclockwise order, we
// could skip concave corners and search only in one direction.
h = predicates.CounterClockwise(a, b, c);
if (Math.Abs(h) < eps)
{
// Points are nearly co-linear. Use perpendicular direction.
dx = (c.y - a.y) / 2;
dy = (a.x - c.x) / 2;
}
else
{
// Direction [midpoint(a-c) -> corner point]
dx = (a.x + c.x) / 2 - bx;
dy = (a.y + c.y) / 2 - by;
}
// Move around the contour.
a = b;
b = c;
h = 1.0;
for (int j = 0; j < limit; j++)
{
// Search in direction.
test.x = bx + dx * h;
test.y = by + dy * h;
if (bounds.Contains(test) && IsPointInPolygon(test, contour))
{
return test;
}
// Search in opposite direction (see NOTE above).
test.x = bx - dx * h;
test.y = by - dy * h;
if (bounds.Contains(test) && IsPointInPolygon(test, contour))
{
return test;
}
h = h / 2;
}
}
throw new Exception();
}
///
/// Return true if the given point is inside the polygon, or false if it is not.
///
/// The point to check.
/// The polygon (list of contour points).
///
///
/// WARNING: If the point is exactly on the edge of the polygon, then the function
/// may return true or false.
///
/// See http://alienryderflex.com/polygon/
///
private static bool IsPointInPolygon(Point point, List poly)
{
bool inside = false;
double x = point.x;
double y = point.y;
int count = poly.Count;
for (int i = 0, j = count - 1; i < count; i++)
{
if (((poly[i].y < y && poly[j].y >= y) || (poly[j].y < y && poly[i].y >= y))
&& (poly[i].x <= x || poly[j].x <= x))
{
inside ^= (poly[i].x + (y - poly[i].y) / (poly[j].y - poly[i].y) * (poly[j].x - poly[i].x) < x);
}
j = i;
}
return inside;
}
#endregion
}
}