173 lines
6.8 KiB
C#
173 lines
6.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using FarseerPhysics.Common.ConvexHull;
|
|
|
|
namespace FarseerPhysics.Common.Decomposition
|
|
{
|
|
public enum TriangulationAlgorithm
|
|
{
|
|
/// <summary>
|
|
/// Convex decomposition algorithm using ear clipping
|
|
///
|
|
/// Properties:
|
|
/// - Only works on simple polygons.
|
|
/// - Does not support holes.
|
|
/// - Running time is O(n^2), n = number of vertices.
|
|
/// </summary>
|
|
Earclip,
|
|
|
|
/// <summary>
|
|
/// Convex decomposition algorithm created by Mark Bayazit (http://mnbayazit.com/)
|
|
///
|
|
/// Properties:
|
|
/// - Tries to decompose using polygons instead of triangles.
|
|
/// - Tends to produce optimal results with low processing time.
|
|
/// - Running time is O(nr), n = number of vertices, r = reflex vertices.
|
|
/// - Does not support holes.
|
|
/// </summary>
|
|
Bayazit,
|
|
|
|
/// <summary>
|
|
/// Convex decomposition algorithm created by unknown
|
|
///
|
|
/// Properties:
|
|
/// - No support for holes
|
|
/// - Very fast
|
|
/// - Only works on simple polygons
|
|
/// - Only works on counter clockwise polygons
|
|
/// </summary>
|
|
Flipcode,
|
|
|
|
/// <summary>
|
|
/// Convex decomposition algorithm created by Raimund Seidel
|
|
///
|
|
/// Properties:
|
|
/// - Decompose the polygon into trapezoids, then triangulate.
|
|
/// - To use the trapezoid data, use ConvexPartitionTrapezoid()
|
|
/// - Generate a lot of garbage due to incapsulation of the Poly2Tri library.
|
|
/// - Running time is O(n log n), n = number of vertices.
|
|
/// - Running time is almost linear for most simple polygons.
|
|
/// - Does not care about winding order.
|
|
/// </summary>
|
|
Seidel,
|
|
SeidelTrapezoids,
|
|
|
|
/// <summary>
|
|
/// 2D constrained Delaunay triangulation algorithm.
|
|
/// Based on the paper "Sweep-line algorithm for constrained Delaunay triangulation" by V. Domiter and and B. Zalik
|
|
///
|
|
/// Properties:
|
|
/// - Creates triangles with a large interior angle.
|
|
/// - Supports holes
|
|
/// - Generate a lot of garbage due to incapsulation of the Poly2Tri library.
|
|
/// - Running time is O(n^2), n = number of vertices.
|
|
/// - Does not care about winding order.
|
|
/// </summary>
|
|
Delauny
|
|
}
|
|
|
|
public static class Triangulate
|
|
{
|
|
public static List<Vertices> ConvexPartition(Vertices vertices, TriangulationAlgorithm algorithm, bool discardAndFixInvalid = true, float tolerance = 0.001f)
|
|
{
|
|
if (vertices.Count <= 3)
|
|
return new List<Vertices> { vertices };
|
|
|
|
List<Vertices> results;
|
|
|
|
switch (algorithm)
|
|
{
|
|
case TriangulationAlgorithm.Earclip:
|
|
if (Settings.SkipSanityChecks)
|
|
Debug.Assert(!vertices.IsCounterClockWise(), "The Earclip algorithm expects the polygon to be clockwise.");
|
|
else
|
|
{
|
|
if (vertices.IsCounterClockWise())
|
|
{
|
|
Vertices temp = new Vertices(vertices);
|
|
temp.Reverse();
|
|
results = EarclipDecomposer.ConvexPartition(temp, tolerance);
|
|
}
|
|
else
|
|
results = EarclipDecomposer.ConvexPartition(vertices, tolerance);
|
|
}
|
|
break;
|
|
case TriangulationAlgorithm.Bayazit:
|
|
if (Settings.SkipSanityChecks)
|
|
Debug.Assert(vertices.IsCounterClockWise(), "The polygon is not counter clockwise. This is needed for Bayazit to work correctly.");
|
|
else
|
|
{
|
|
if (!vertices.IsCounterClockWise())
|
|
{
|
|
Vertices temp = new Vertices(vertices);
|
|
temp.Reverse();
|
|
results = BayazitDecomposer.ConvexPartition(temp);
|
|
}
|
|
else
|
|
results = BayazitDecomposer.ConvexPartition(vertices);
|
|
}
|
|
break;
|
|
case TriangulationAlgorithm.Flipcode:
|
|
if (Settings.SkipSanityChecks)
|
|
Debug.Assert(vertices.IsCounterClockWise(), "The polygon is not counter clockwise. This is needed for Bayazit to work correctly.");
|
|
else
|
|
{
|
|
if (!vertices.IsCounterClockWise())
|
|
{
|
|
Vertices temp = new Vertices(vertices);
|
|
temp.Reverse();
|
|
results = FlipcodeDecomposer.ConvexPartition(temp);
|
|
}
|
|
else
|
|
results = FlipcodeDecomposer.ConvexPartition(vertices);
|
|
}
|
|
break;
|
|
case TriangulationAlgorithm.Seidel:
|
|
results = SeidelDecomposer.ConvexPartition(vertices, tolerance);
|
|
break;
|
|
case TriangulationAlgorithm.SeidelTrapezoids:
|
|
results = SeidelDecomposer.ConvexPartitionTrapezoid(vertices, tolerance);
|
|
break;
|
|
case TriangulationAlgorithm.Delauny:
|
|
results = CDTDecomposer.ConvexPartition(vertices);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException("algorithm");
|
|
}
|
|
|
|
if (discardAndFixInvalid)
|
|
{
|
|
for (int i = results.Count - 1; i >= 0; i--)
|
|
{
|
|
Vertices polygon = results[i];
|
|
|
|
if (!ValidatePolygon(polygon))
|
|
results.RemoveAt(i);
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
private static bool ValidatePolygon(Vertices polygon)
|
|
{
|
|
PolygonError errorCode = polygon.CheckPolygon();
|
|
|
|
if (errorCode == PolygonError.InvalidAmountOfVertices || errorCode == PolygonError.AreaTooSmall || errorCode == PolygonError.SideTooSmall || errorCode == PolygonError.NotSimple)
|
|
return false;
|
|
|
|
if (errorCode == PolygonError.NotCounterClockWise) //NotCounterCloseWise is the last check in CheckPolygon(), thus we don't need to call ValidatePolygon again.
|
|
polygon.Reverse();
|
|
|
|
if (errorCode == PolygonError.NotConvex)
|
|
{
|
|
polygon = GiftWrap.GetConvexHull(polygon);
|
|
return ValidatePolygon(polygon);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|