Adding initial files
This commit is contained in:
246
axios/Common/PolygonManipulation/CuttingTools.cs
Normal file
246
axios/Common/PolygonManipulation/CuttingTools.cs
Normal file
@@ -0,0 +1,246 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using FarseerPhysics.Collision.Shapes;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Factories;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace FarseerPhysics.Common.PolygonManipulation
|
||||
{
|
||||
public static class CuttingTools
|
||||
{
|
||||
//Cutting a shape into two is based on the work of Daid and his prototype BoxCutter: http://www.box2d.org/forum/viewtopic.php?f=3&t=1473
|
||||
|
||||
/// <summary>
|
||||
/// Split a fixture into 2 vertice collections using the given entry and exit-point.
|
||||
/// </summary>
|
||||
/// <param name="fixture">The Fixture to split</param>
|
||||
/// <param name="entryPoint">The entry point - The start point</param>
|
||||
/// <param name="exitPoint">The exit point - The end point</param>
|
||||
/// <param name="splitSize">The size of the split. Think of this as the laser-width</param>
|
||||
/// <param name="first">The first collection of vertexes</param>
|
||||
/// <param name="second">The second collection of vertexes</param>
|
||||
public static void SplitShape(Fixture fixture, Vector2 entryPoint, Vector2 exitPoint, float splitSize,
|
||||
out Vertices first, out Vertices second)
|
||||
{
|
||||
Vector2 localEntryPoint = fixture.Body.GetLocalPoint(ref entryPoint);
|
||||
Vector2 localExitPoint = fixture.Body.GetLocalPoint(ref exitPoint);
|
||||
|
||||
PolygonShape shape = fixture.Shape as PolygonShape;
|
||||
|
||||
if (shape == null)
|
||||
{
|
||||
first = new Vertices();
|
||||
second = new Vertices();
|
||||
return;
|
||||
}
|
||||
|
||||
Vertices vertices = new Vertices(shape.Vertices);
|
||||
Vertices[] newPolygon = new Vertices[2];
|
||||
|
||||
for (int i = 0; i < newPolygon.Length; i++)
|
||||
{
|
||||
newPolygon[i] = new Vertices(vertices.Count);
|
||||
}
|
||||
|
||||
int[] cutAdded = { -1, -1 };
|
||||
int last = -1;
|
||||
for (int i = 0; i < vertices.Count; i++)
|
||||
{
|
||||
int n;
|
||||
//Find out if this vertex is on the old or new shape.
|
||||
if (Vector2.Dot(MathUtils.Cross(localExitPoint - localEntryPoint, 1), vertices[i] - localEntryPoint) > Settings.Epsilon)
|
||||
n = 0;
|
||||
else
|
||||
n = 1;
|
||||
|
||||
if (last != n)
|
||||
{
|
||||
//If we switch from one shape to the other add the cut vertices.
|
||||
if (last == 0)
|
||||
{
|
||||
Debug.Assert(cutAdded[0] == -1);
|
||||
cutAdded[0] = newPolygon[last].Count;
|
||||
newPolygon[last].Add(localExitPoint);
|
||||
newPolygon[last].Add(localEntryPoint);
|
||||
}
|
||||
if (last == 1)
|
||||
{
|
||||
Debug.Assert(cutAdded[last] == -1);
|
||||
cutAdded[last] = newPolygon[last].Count;
|
||||
newPolygon[last].Add(localEntryPoint);
|
||||
newPolygon[last].Add(localExitPoint);
|
||||
}
|
||||
}
|
||||
|
||||
newPolygon[n].Add(vertices[i]);
|
||||
last = n;
|
||||
}
|
||||
|
||||
//Add the cut in case it has not been added yet.
|
||||
if (cutAdded[0] == -1)
|
||||
{
|
||||
cutAdded[0] = newPolygon[0].Count;
|
||||
newPolygon[0].Add(localExitPoint);
|
||||
newPolygon[0].Add(localEntryPoint);
|
||||
}
|
||||
if (cutAdded[1] == -1)
|
||||
{
|
||||
cutAdded[1] = newPolygon[1].Count;
|
||||
newPolygon[1].Add(localEntryPoint);
|
||||
newPolygon[1].Add(localExitPoint);
|
||||
}
|
||||
|
||||
for (int n = 0; n < 2; n++)
|
||||
{
|
||||
Vector2 offset;
|
||||
if (cutAdded[n] > 0)
|
||||
{
|
||||
offset = (newPolygon[n][cutAdded[n] - 1] - newPolygon[n][cutAdded[n]]);
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = (newPolygon[n][newPolygon[n].Count - 1] - newPolygon[n][0]);
|
||||
}
|
||||
offset.Normalize();
|
||||
|
||||
newPolygon[n][cutAdded[n]] += splitSize * offset;
|
||||
|
||||
if (cutAdded[n] < newPolygon[n].Count - 2)
|
||||
{
|
||||
offset = (newPolygon[n][cutAdded[n] + 2] - newPolygon[n][cutAdded[n] + 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = (newPolygon[n][0] - newPolygon[n][newPolygon[n].Count - 1]);
|
||||
}
|
||||
offset.Normalize();
|
||||
|
||||
newPolygon[n][cutAdded[n] + 1] += splitSize * offset;
|
||||
}
|
||||
|
||||
first = newPolygon[0];
|
||||
second = newPolygon[1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a high-level function to cuts fixtures inside the given world, using the start and end points.
|
||||
/// Note: We don't support cutting when the start or end is inside a shape.
|
||||
/// </summary>
|
||||
/// <param name="world">The world.</param>
|
||||
/// <param name="start">The startpoint.</param>
|
||||
/// <param name="end">The endpoint.</param>
|
||||
/// <param name="thickness">The thickness of the cut</param>
|
||||
public static void Cut(World world, Vector2 start, Vector2 end, float thickness)
|
||||
{
|
||||
List<Fixture> fixtures = new List<Fixture>();
|
||||
List<Vector2> entryPoints = new List<Vector2>();
|
||||
List<Vector2> exitPoints = new List<Vector2>();
|
||||
|
||||
//We don't support cutting when the start or end is inside a shape.
|
||||
if (world.TestPoint(start) != null || world.TestPoint(end) != null)
|
||||
return;
|
||||
|
||||
//Get the entry points
|
||||
world.RayCast((f, p, n, fr) =>
|
||||
{
|
||||
fixtures.Add(f);
|
||||
entryPoints.Add(p);
|
||||
return 1;
|
||||
}, start, end);
|
||||
|
||||
//Reverse the ray to get the exitpoints
|
||||
world.RayCast((f, p, n, fr) =>
|
||||
{
|
||||
exitPoints.Add(p);
|
||||
return 1;
|
||||
}, end, start);
|
||||
|
||||
//We only have a single point. We need at least 2
|
||||
if (entryPoints.Count + exitPoints.Count < 2)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < fixtures.Count; i++)
|
||||
{
|
||||
// can't cut circles yet !
|
||||
if (fixtures[i].Shape.ShapeType != ShapeType.Polygon)
|
||||
continue;
|
||||
|
||||
if (fixtures[i].Body.BodyType != BodyType.Static)
|
||||
{
|
||||
//Split the shape up into two shapes
|
||||
Vertices first;
|
||||
Vertices second;
|
||||
SplitShape(fixtures[i], entryPoints[i], exitPoints[i], thickness, out first, out second);
|
||||
|
||||
//Delete the original shape and create two new. Retain the properties of the body.
|
||||
if (SanityCheck(first))
|
||||
{
|
||||
Body firstFixture = BodyFactory.CreatePolygon(world, first, fixtures[i].Shape.Density,
|
||||
fixtures[i].Body.Position);
|
||||
firstFixture.Rotation = fixtures[i].Body.Rotation;
|
||||
firstFixture.LinearVelocity = fixtures[i].Body.LinearVelocity;
|
||||
firstFixture.AngularVelocity = fixtures[i].Body.AngularVelocity;
|
||||
firstFixture.BodyType = BodyType.Dynamic;
|
||||
}
|
||||
|
||||
if (SanityCheck(second))
|
||||
{
|
||||
Body secondFixture = BodyFactory.CreatePolygon(world, second, fixtures[i].Shape.Density,
|
||||
fixtures[i].Body.Position);
|
||||
secondFixture.Rotation = fixtures[i].Body.Rotation;
|
||||
secondFixture.LinearVelocity = fixtures[i].Body.LinearVelocity;
|
||||
secondFixture.AngularVelocity = fixtures[i].Body.AngularVelocity;
|
||||
secondFixture.BodyType = BodyType.Dynamic;
|
||||
}
|
||||
world.RemoveBody(fixtures[i].Body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SanityCheck(Vertices vertices)
|
||||
{
|
||||
if (vertices.Count < 3)
|
||||
return false;
|
||||
|
||||
if (vertices.GetArea() < 0.00001f)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < vertices.Count; ++i)
|
||||
{
|
||||
int i1 = i;
|
||||
int i2 = i + 1 < vertices.Count ? i + 1 : 0;
|
||||
Vector2 edge = vertices[i2] - vertices[i1];
|
||||
if (edge.LengthSquared() < Settings.Epsilon * Settings.Epsilon)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < vertices.Count; ++i)
|
||||
{
|
||||
int i1 = i;
|
||||
int i2 = i + 1 < vertices.Count ? i + 1 : 0;
|
||||
Vector2 edge = vertices[i2] - vertices[i1];
|
||||
|
||||
for (int j = 0; j < vertices.Count; ++j)
|
||||
{
|
||||
// Don't check vertices on the current edge.
|
||||
if (j == i1 || j == i2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 r = vertices[j] - vertices[i1];
|
||||
|
||||
// Your polygon is non-convex (it has an indentation) or
|
||||
// has colinear edges.
|
||||
float s = edge.X * r.Y - edge.Y * r.X;
|
||||
|
||||
if (s < 0.0f)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
359
axios/Common/PolygonManipulation/SimplifyTools.cs
Normal file
359
axios/Common/PolygonManipulation/SimplifyTools.cs
Normal file
@@ -0,0 +1,359 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace FarseerPhysics.Common.PolygonManipulation
|
||||
{
|
||||
public static class SimplifyTools
|
||||
{
|
||||
private static bool[] _usePt;
|
||||
private static double _distanceTolerance;
|
||||
|
||||
/// <summary>
|
||||
/// Removes all collinear points on the polygon.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The polygon that needs simplification.</param>
|
||||
/// <param name="collinearityTolerance">The collinearity tolerance.</param>
|
||||
/// <returns>A simplified polygon.</returns>
|
||||
public static Vertices CollinearSimplify(Vertices vertices, float collinearityTolerance)
|
||||
{
|
||||
//We can't simplify polygons under 3 vertices
|
||||
if (vertices.Count < 3)
|
||||
return vertices;
|
||||
|
||||
Vertices simplified = new Vertices();
|
||||
|
||||
for (int i = 0; i < vertices.Count; i++)
|
||||
{
|
||||
int prevId = vertices.PreviousIndex(i);
|
||||
int nextId = vertices.NextIndex(i);
|
||||
|
||||
Vector2 prev = vertices[prevId];
|
||||
Vector2 current = vertices[i];
|
||||
Vector2 next = vertices[nextId];
|
||||
|
||||
//If they collinear, continue
|
||||
if (MathUtils.Collinear(ref prev, ref current, ref next, collinearityTolerance))
|
||||
continue;
|
||||
|
||||
simplified.Add(current);
|
||||
}
|
||||
|
||||
return simplified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all collinear points on the polygon.
|
||||
/// Has a default bias of 0
|
||||
/// </summary>
|
||||
/// <param name="vertices">The polygon that needs simplification.</param>
|
||||
/// <returns>A simplified polygon.</returns>
|
||||
public static Vertices CollinearSimplify(Vertices vertices)
|
||||
{
|
||||
return CollinearSimplify(vertices, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ramer-Douglas-Peucker polygon simplification algorithm. This is the general recursive version that does not use the
|
||||
/// speed-up technique by using the Melkman convex hull.
|
||||
///
|
||||
/// If you pass in 0, it will remove all collinear points
|
||||
/// </summary>
|
||||
/// <returns>The simplified polygon</returns>
|
||||
public static Vertices DouglasPeuckerSimplify(Vertices vertices, float distanceTolerance)
|
||||
{
|
||||
_distanceTolerance = distanceTolerance;
|
||||
|
||||
_usePt = new bool[vertices.Count];
|
||||
for (int i = 0; i < vertices.Count; i++)
|
||||
_usePt[i] = true;
|
||||
|
||||
SimplifySection(vertices, 0, vertices.Count - 1);
|
||||
Vertices result = new Vertices();
|
||||
|
||||
for (int i = 0; i < vertices.Count; i++)
|
||||
if (_usePt[i])
|
||||
result.Add(vertices[i]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void SimplifySection(Vertices vertices, int i, int j)
|
||||
{
|
||||
if ((i + 1) == j)
|
||||
return;
|
||||
|
||||
Vector2 A = vertices[i];
|
||||
Vector2 B = vertices[j];
|
||||
double maxDistance = -1.0;
|
||||
int maxIndex = i;
|
||||
for (int k = i + 1; k < j; k++)
|
||||
{
|
||||
double distance = DistancePointLine(vertices[k], A, B);
|
||||
|
||||
if (distance > maxDistance)
|
||||
{
|
||||
maxDistance = distance;
|
||||
maxIndex = k;
|
||||
}
|
||||
}
|
||||
if (maxDistance <= _distanceTolerance)
|
||||
for (int k = i + 1; k < j; k++)
|
||||
_usePt[k] = false;
|
||||
else
|
||||
{
|
||||
SimplifySection(vertices, i, maxIndex);
|
||||
SimplifySection(vertices, maxIndex, j);
|
||||
}
|
||||
}
|
||||
|
||||
private static double DistancePointPoint(Vector2 p, Vector2 p2)
|
||||
{
|
||||
double dx = p.X - p2.X;
|
||||
double dy = p.Y - p2.X;
|
||||
return Math.Sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
private static double DistancePointLine(Vector2 p, Vector2 A, Vector2 B)
|
||||
{
|
||||
// if start == end, then use point-to-point distance
|
||||
if (A.X == B.X && A.Y == B.Y)
|
||||
return DistancePointPoint(p, A);
|
||||
|
||||
// otherwise use comp.graphics.algorithms Frequently Asked Questions method
|
||||
/*(1) AC dot AB
|
||||
r = ---------
|
||||
||AB||^2
|
||||
|
||||
r has the following meaning:
|
||||
r=0 Point = A
|
||||
r=1 Point = B
|
||||
r<0 Point is on the backward extension of AB
|
||||
r>1 Point is on the forward extension of AB
|
||||
0<r<1 Point is interior to AB
|
||||
*/
|
||||
|
||||
double r = ((p.X - A.X) * (B.X - A.X) + (p.Y - A.Y) * (B.Y - A.Y))
|
||||
/
|
||||
((B.X - A.X) * (B.X - A.X) + (B.Y - A.Y) * (B.Y - A.Y));
|
||||
|
||||
if (r <= 0.0) return DistancePointPoint(p, A);
|
||||
if (r >= 1.0) return DistancePointPoint(p, B);
|
||||
|
||||
|
||||
/*(2)
|
||||
(Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
|
||||
s = -----------------------------
|
||||
Curve^2
|
||||
|
||||
Then the distance from C to Point = |s|*Curve.
|
||||
*/
|
||||
|
||||
double s = ((A.Y - p.Y) * (B.X - A.X) - (A.X - p.X) * (B.Y - A.Y))
|
||||
/
|
||||
((B.X - A.X) * (B.X - A.X) + (B.Y - A.Y) * (B.Y - A.Y));
|
||||
|
||||
return Math.Abs(s) * Math.Sqrt(((B.X - A.X) * (B.X - A.X) + (B.Y - A.Y) * (B.Y - A.Y)));
|
||||
}
|
||||
|
||||
//From physics2d.net
|
||||
public static Vertices ReduceByArea(Vertices vertices, float areaTolerance)
|
||||
{
|
||||
if (vertices.Count <= 3)
|
||||
return vertices;
|
||||
|
||||
if (areaTolerance < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("areaTolerance", "must be equal to or greater then zero.");
|
||||
}
|
||||
|
||||
Vertices result = new Vertices();
|
||||
Vector2 v1, v2, v3;
|
||||
float old1, old2, new1;
|
||||
v1 = vertices[vertices.Count - 2];
|
||||
v2 = vertices[vertices.Count - 1];
|
||||
areaTolerance *= 2;
|
||||
for (int index = 0; index < vertices.Count; ++index, v2 = v3)
|
||||
{
|
||||
if (index == vertices.Count - 1)
|
||||
{
|
||||
if (result.Count == 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("areaTolerance", "The tolerance is too high!");
|
||||
}
|
||||
v3 = result[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
v3 = vertices[index];
|
||||
}
|
||||
MathUtils.Cross(ref v1, ref v2, out old1);
|
||||
MathUtils.Cross(ref v2, ref v3, out old2);
|
||||
MathUtils.Cross(ref v1, ref v3, out new1);
|
||||
if (Math.Abs(new1 - (old1 + old2)) > areaTolerance)
|
||||
{
|
||||
result.Add(v2);
|
||||
v1 = v2;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//From Eric Jordan's convex decomposition library
|
||||
|
||||
/// <summary>
|
||||
/// Merges all parallel edges in the list of vertices
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices.</param>
|
||||
/// <param name="tolerance">The tolerance.</param>
|
||||
public static void MergeParallelEdges(Vertices vertices, float tolerance)
|
||||
{
|
||||
if (vertices.Count <= 3)
|
||||
return; //Can't do anything useful here to a triangle
|
||||
|
||||
bool[] mergeMe = new bool[vertices.Count];
|
||||
int newNVertices = vertices.Count;
|
||||
|
||||
//Gather points to process
|
||||
for (int i = 0; i < vertices.Count; ++i)
|
||||
{
|
||||
int lower = (i == 0) ? (vertices.Count - 1) : (i - 1);
|
||||
int middle = i;
|
||||
int upper = (i == vertices.Count - 1) ? (0) : (i + 1);
|
||||
|
||||
float dx0 = vertices[middle].X - vertices[lower].X;
|
||||
float dy0 = vertices[middle].Y - vertices[lower].Y;
|
||||
float dx1 = vertices[upper].Y - vertices[middle].X;
|
||||
float dy1 = vertices[upper].Y - vertices[middle].Y;
|
||||
float norm0 = (float)Math.Sqrt(dx0 * dx0 + dy0 * dy0);
|
||||
float norm1 = (float)Math.Sqrt(dx1 * dx1 + dy1 * dy1);
|
||||
|
||||
if (!(norm0 > 0.0f && norm1 > 0.0f) && newNVertices > 3)
|
||||
{
|
||||
//Merge identical points
|
||||
mergeMe[i] = true;
|
||||
--newNVertices;
|
||||
}
|
||||
|
||||
dx0 /= norm0;
|
||||
dy0 /= norm0;
|
||||
dx1 /= norm1;
|
||||
dy1 /= norm1;
|
||||
float cross = dx0 * dy1 - dx1 * dy0;
|
||||
float dot = dx0 * dx1 + dy0 * dy1;
|
||||
|
||||
if (Math.Abs(cross) < tolerance && dot > 0 && newNVertices > 3)
|
||||
{
|
||||
mergeMe[i] = true;
|
||||
--newNVertices;
|
||||
}
|
||||
else
|
||||
mergeMe[i] = false;
|
||||
}
|
||||
|
||||
if (newNVertices == vertices.Count || newNVertices == 0)
|
||||
return;
|
||||
|
||||
int currIndex = 0;
|
||||
|
||||
//Copy the vertices to a new list and clear the old
|
||||
Vertices oldVertices = new Vertices(vertices);
|
||||
vertices.Clear();
|
||||
|
||||
for (int i = 0; i < oldVertices.Count; ++i)
|
||||
{
|
||||
if (mergeMe[i] || newNVertices == 0 || currIndex == newNVertices)
|
||||
continue;
|
||||
|
||||
Debug.Assert(currIndex < newNVertices);
|
||||
|
||||
vertices.Add(oldVertices[i]);
|
||||
++currIndex;
|
||||
}
|
||||
}
|
||||
|
||||
//Misc
|
||||
|
||||
/// <summary>
|
||||
/// Merges the identical points in the polygon.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices.</param>
|
||||
/// <returns></returns>
|
||||
public static Vertices MergeIdenticalPoints(Vertices vertices)
|
||||
{
|
||||
//We use a dictonary here because HashSet is not avaliable on all platforms.
|
||||
HashSet<Vector2> results = new HashSet<Vector2>();
|
||||
|
||||
for (int i = 0; i < vertices.Count; i++)
|
||||
{
|
||||
results.Add(vertices[i]);
|
||||
}
|
||||
|
||||
Vertices returnResults = new Vertices();
|
||||
foreach (Vector2 v in results)
|
||||
{
|
||||
returnResults.Add(v);
|
||||
}
|
||||
|
||||
return returnResults;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reduces the polygon by distance.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices.</param>
|
||||
/// <param name="distance">The distance between points. Points closer than this will be 'joined'.</param>
|
||||
/// <returns></returns>
|
||||
public static Vertices ReduceByDistance(Vertices vertices, float distance)
|
||||
{
|
||||
//We can't simplify polygons under 3 vertices
|
||||
if (vertices.Count < 3)
|
||||
return vertices;
|
||||
|
||||
Vertices simplified = new Vertices();
|
||||
|
||||
for (int i = 0; i < vertices.Count; i++)
|
||||
{
|
||||
Vector2 current = vertices[i];
|
||||
Vector2 next = vertices.NextVertex(i);
|
||||
|
||||
//If they are closer than the distance, continue
|
||||
if ((next - current).LengthSquared() <= distance)
|
||||
continue;
|
||||
|
||||
simplified.Add(current);
|
||||
}
|
||||
|
||||
return simplified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reduces the polygon by removing the Nth vertex in the vertices list.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices.</param>
|
||||
/// <param name="nth">The Nth point to remove. Example: 5.</param>
|
||||
/// <returns></returns>
|
||||
public static Vertices ReduceByNth(Vertices vertices, int nth)
|
||||
{
|
||||
//We can't simplify polygons under 3 vertices
|
||||
if (vertices.Count < 3)
|
||||
return vertices;
|
||||
|
||||
if (nth == 0)
|
||||
return vertices;
|
||||
|
||||
Vertices result = new Vertices(vertices.Count);
|
||||
|
||||
for (int i = 0; i < vertices.Count; i++)
|
||||
{
|
||||
if (i % nth == 0)
|
||||
continue;
|
||||
|
||||
result.Add(vertices[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
513
axios/Common/PolygonManipulation/YuPengClipper.cs
Normal file
513
axios/Common/PolygonManipulation/YuPengClipper.cs
Normal file
@@ -0,0 +1,513 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using FarseerPhysics.Collision.Shapes;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace FarseerPhysics.Common.PolygonManipulation
|
||||
{
|
||||
internal enum PolyClipType
|
||||
{
|
||||
Intersect,
|
||||
Union,
|
||||
Difference
|
||||
}
|
||||
|
||||
public enum PolyClipError
|
||||
{
|
||||
None,
|
||||
DegeneratedOutput,
|
||||
NonSimpleInput,
|
||||
BrokenResult
|
||||
}
|
||||
|
||||
//Clipper contributed by Helge Backhaus
|
||||
|
||||
public static class YuPengClipper
|
||||
{
|
||||
private const float ClipperEpsilonSquared = 1.192092896e-07f;
|
||||
|
||||
public static List<Vertices> Union(Vertices polygon1, Vertices polygon2, out PolyClipError error)
|
||||
{
|
||||
return Execute(polygon1, polygon2, PolyClipType.Union, out error);
|
||||
}
|
||||
|
||||
public static List<Vertices> Difference(Vertices polygon1, Vertices polygon2, out PolyClipError error)
|
||||
{
|
||||
return Execute(polygon1, polygon2, PolyClipType.Difference, out error);
|
||||
}
|
||||
|
||||
public static List<Vertices> Intersect(Vertices polygon1, Vertices polygon2, out PolyClipError error)
|
||||
{
|
||||
return Execute(polygon1, polygon2, PolyClipType.Intersect, out error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements "A new algorithm for Boolean operations on general polygons"
|
||||
/// available here: http://liama.ia.ac.cn/wiki/_media/user:dong:dong_cg_05.pdf
|
||||
/// Merges two polygons, a subject and a clip with the specified operation. Polygons may not be
|
||||
/// self-intersecting.
|
||||
///
|
||||
/// Warning: May yield incorrect results or even crash if polygons contain collinear points.
|
||||
/// </summary>
|
||||
/// <param name="subject">The subject polygon.</param>
|
||||
/// <param name="clip">The clip polygon, which is added,
|
||||
/// substracted or intersected with the subject</param>
|
||||
/// <param name="clipType">The operation to be performed. Either
|
||||
/// Union, Difference or Intersection.</param>
|
||||
/// <param name="error">The error generated (if any)</param>
|
||||
/// <returns>A list of closed polygons, which make up the result of the clipping operation.
|
||||
/// Outer contours are ordered counter clockwise, holes are ordered clockwise.</returns>
|
||||
private static List<Vertices> Execute(Vertices subject, Vertices clip,
|
||||
PolyClipType clipType, out PolyClipError error)
|
||||
{
|
||||
Debug.Assert(subject.IsSimple() && clip.IsSimple(), "Non simple input!", "Input polygons must be simple (cannot intersect themselves).");
|
||||
|
||||
// Copy polygons
|
||||
Vertices slicedSubject;
|
||||
Vertices slicedClip;
|
||||
// Calculate the intersection and touch points between
|
||||
// subject and clip and add them to both
|
||||
CalculateIntersections(subject, clip, out slicedSubject, out slicedClip);
|
||||
|
||||
// Translate polygons into upper right quadrant
|
||||
// as the algorithm depends on it
|
||||
Vector2 lbSubject = subject.GetCollisionBox().LowerBound;
|
||||
Vector2 lbClip = clip.GetCollisionBox().LowerBound;
|
||||
Vector2 translate;
|
||||
Vector2.Min(ref lbSubject, ref lbClip, out translate);
|
||||
translate = Vector2.One - translate;
|
||||
if (translate != Vector2.Zero)
|
||||
{
|
||||
slicedSubject.Translate(ref translate);
|
||||
slicedClip.Translate(ref translate);
|
||||
}
|
||||
|
||||
// Enforce counterclockwise contours
|
||||
slicedSubject.ForceCounterClockWise();
|
||||
slicedClip.ForceCounterClockWise();
|
||||
|
||||
List<Edge> subjectSimplices;
|
||||
List<float> subjectCoeff;
|
||||
List<Edge> clipSimplices;
|
||||
List<float> clipCoeff;
|
||||
// Build simplical chains from the polygons and calculate the
|
||||
// the corresponding coefficients
|
||||
CalculateSimplicalChain(slicedSubject, out subjectCoeff, out subjectSimplices);
|
||||
CalculateSimplicalChain(slicedClip, out clipCoeff, out clipSimplices);
|
||||
|
||||
List<Edge> resultSimplices;
|
||||
|
||||
// Determine the characteristics function for all non-original edges
|
||||
// in subject and clip simplical chain and combine the edges contributing
|
||||
// to the result, depending on the clipType
|
||||
CalculateResultChain(subjectCoeff, subjectSimplices, clipCoeff, clipSimplices, clipType,
|
||||
out resultSimplices);
|
||||
|
||||
List<Vertices> result;
|
||||
// Convert result chain back to polygon(s)
|
||||
error = BuildPolygonsFromChain(resultSimplices, out result);
|
||||
|
||||
// Reverse the polygon translation from the beginning
|
||||
// and remove collinear points from output
|
||||
translate *= -1f;
|
||||
for (int i = 0; i < result.Count; ++i)
|
||||
{
|
||||
result[i].Translate(ref translate);
|
||||
SimplifyTools.CollinearSimplify(result[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates all intersections between two polygons.
|
||||
/// </summary>
|
||||
/// <param name="polygon1">The first polygon.</param>
|
||||
/// <param name="polygon2">The second polygon.</param>
|
||||
/// <param name="slicedPoly1">Returns the first polygon with added intersection points.</param>
|
||||
/// <param name="slicedPoly2">Returns the second polygon with added intersection points.</param>
|
||||
private static void CalculateIntersections(Vertices polygon1, Vertices polygon2,
|
||||
out Vertices slicedPoly1, out Vertices slicedPoly2)
|
||||
{
|
||||
slicedPoly1 = new Vertices(polygon1);
|
||||
slicedPoly2 = new Vertices(polygon2);
|
||||
|
||||
// Iterate through polygon1's edges
|
||||
for (int i = 0; i < polygon1.Count; i++)
|
||||
{
|
||||
// Get edge vertices
|
||||
Vector2 a = polygon1[i];
|
||||
Vector2 b = polygon1[polygon1.NextIndex(i)];
|
||||
|
||||
// Get intersections between this edge and polygon2
|
||||
for (int j = 0; j < polygon2.Count; j++)
|
||||
{
|
||||
Vector2 c = polygon2[j];
|
||||
Vector2 d = polygon2[polygon2.NextIndex(j)];
|
||||
|
||||
Vector2 intersectionPoint;
|
||||
// Check if the edges intersect
|
||||
if (LineTools.LineIntersect(a, b, c, d, out intersectionPoint))
|
||||
{
|
||||
// calculate alpha values for sorting multiple intersections points on a edge
|
||||
float alpha;
|
||||
// Insert intersection point into first polygon
|
||||
alpha = GetAlpha(a, b, intersectionPoint);
|
||||
if (alpha > 0f && alpha < 1f)
|
||||
{
|
||||
int index = slicedPoly1.IndexOf(a) + 1;
|
||||
while (index < slicedPoly1.Count &&
|
||||
GetAlpha(a, b, slicedPoly1[index]) <= alpha)
|
||||
{
|
||||
++index;
|
||||
}
|
||||
slicedPoly1.Insert(index, intersectionPoint);
|
||||
}
|
||||
// Insert intersection point into second polygon
|
||||
alpha = GetAlpha(c, d, intersectionPoint);
|
||||
if (alpha > 0f && alpha < 1f)
|
||||
{
|
||||
int index = slicedPoly2.IndexOf(c) + 1;
|
||||
while (index < slicedPoly2.Count &&
|
||||
GetAlpha(c, d, slicedPoly2[index]) <= alpha)
|
||||
{
|
||||
++index;
|
||||
}
|
||||
slicedPoly2.Insert(index, intersectionPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for very small edges
|
||||
for (int i = 0; i < slicedPoly1.Count; ++i)
|
||||
{
|
||||
int iNext = slicedPoly1.NextIndex(i);
|
||||
//If they are closer than the distance remove vertex
|
||||
if ((slicedPoly1[iNext] - slicedPoly1[i]).LengthSquared() <= ClipperEpsilonSquared)
|
||||
{
|
||||
slicedPoly1.RemoveAt(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < slicedPoly2.Count; ++i)
|
||||
{
|
||||
int iNext = slicedPoly2.NextIndex(i);
|
||||
//If they are closer than the distance remove vertex
|
||||
if ((slicedPoly2[iNext] - slicedPoly2[i]).LengthSquared() <= ClipperEpsilonSquared)
|
||||
{
|
||||
slicedPoly2.RemoveAt(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the simplical chain corresponding to the input polygon.
|
||||
/// </summary>
|
||||
/// <remarks>Used by method <c>Execute()</c>.</remarks>
|
||||
private static void CalculateSimplicalChain(Vertices poly, out List<float> coeff,
|
||||
out List<Edge> simplicies)
|
||||
{
|
||||
simplicies = new List<Edge>();
|
||||
coeff = new List<float>();
|
||||
for (int i = 0; i < poly.Count; ++i)
|
||||
{
|
||||
simplicies.Add(new Edge(poly[i], poly[poly.NextIndex(i)]));
|
||||
coeff.Add(CalculateSimplexCoefficient(Vector2.Zero, poly[i], poly[poly.NextIndex(i)]));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the characteristics function for all edges of
|
||||
/// the given simplical chains and builds the result chain.
|
||||
/// </summary>
|
||||
/// <remarks>Used by method <c>Execute()</c>.</remarks>
|
||||
private static void CalculateResultChain(List<float> poly1Coeff, List<Edge> poly1Simplicies,
|
||||
List<float> poly2Coeff, List<Edge> poly2Simplicies,
|
||||
PolyClipType clipType, out List<Edge> resultSimplices)
|
||||
{
|
||||
resultSimplices = new List<Edge>();
|
||||
|
||||
for (int i = 0; i < poly1Simplicies.Count; ++i)
|
||||
{
|
||||
float edgeCharacter = 0f;
|
||||
if (poly2Simplicies.Contains(poly1Simplicies[i]) ||
|
||||
(poly2Simplicies.Contains(-poly1Simplicies[i]) && clipType == PolyClipType.Union))
|
||||
{
|
||||
edgeCharacter = 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < poly2Simplicies.Count; ++j)
|
||||
{
|
||||
if (!poly2Simplicies.Contains(-poly1Simplicies[i]))
|
||||
{
|
||||
edgeCharacter += CalculateBeta(poly1Simplicies[i].GetCenter(),
|
||||
poly2Simplicies[j], poly2Coeff[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clipType == PolyClipType.Intersect)
|
||||
{
|
||||
if (edgeCharacter == 1f)
|
||||
{
|
||||
resultSimplices.Add(poly1Simplicies[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (edgeCharacter == 0f)
|
||||
{
|
||||
resultSimplices.Add(poly1Simplicies[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < poly2Simplicies.Count; ++i)
|
||||
{
|
||||
if (!resultSimplices.Contains(poly2Simplicies[i]) &&
|
||||
!resultSimplices.Contains(-poly2Simplicies[i]))
|
||||
{
|
||||
float edgeCharacter = 0f;
|
||||
if (poly1Simplicies.Contains(poly2Simplicies[i]) ||
|
||||
(poly1Simplicies.Contains(-poly2Simplicies[i]) && clipType == PolyClipType.Union))
|
||||
{
|
||||
edgeCharacter = 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < poly1Simplicies.Count; ++j)
|
||||
{
|
||||
if (!poly1Simplicies.Contains(-poly2Simplicies[i]))
|
||||
{
|
||||
edgeCharacter += CalculateBeta(poly2Simplicies[i].GetCenter(),
|
||||
poly1Simplicies[j], poly1Coeff[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clipType == PolyClipType.Intersect || clipType == PolyClipType.Difference)
|
||||
{
|
||||
if (edgeCharacter == 1f)
|
||||
{
|
||||
resultSimplices.Add(-poly2Simplicies[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (edgeCharacter == 0f)
|
||||
{
|
||||
resultSimplices.Add(poly2Simplicies[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the polygon(s) from the result simplical chain.
|
||||
/// </summary>
|
||||
/// <remarks>Used by method <c>Execute()</c>.</remarks>
|
||||
private static PolyClipError BuildPolygonsFromChain(List<Edge> simplicies, out List<Vertices> result)
|
||||
{
|
||||
result = new List<Vertices>();
|
||||
PolyClipError errVal = PolyClipError.None;
|
||||
|
||||
while (simplicies.Count > 0)
|
||||
{
|
||||
Vertices output = new Vertices();
|
||||
output.Add(simplicies[0].EdgeStart);
|
||||
output.Add(simplicies[0].EdgeEnd);
|
||||
simplicies.RemoveAt(0);
|
||||
bool closed = false;
|
||||
int index = 0;
|
||||
int count = simplicies.Count; // Needed to catch infinite loops
|
||||
while (!closed && simplicies.Count > 0)
|
||||
{
|
||||
if (VectorEqual(output[output.Count - 1], simplicies[index].EdgeStart))
|
||||
{
|
||||
if (VectorEqual(simplicies[index].EdgeEnd, output[0]))
|
||||
{
|
||||
closed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Add(simplicies[index].EdgeEnd);
|
||||
}
|
||||
simplicies.RemoveAt(index);
|
||||
--index;
|
||||
}
|
||||
else if (VectorEqual(output[output.Count - 1], simplicies[index].EdgeEnd))
|
||||
{
|
||||
if (VectorEqual(simplicies[index].EdgeStart, output[0]))
|
||||
{
|
||||
closed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Add(simplicies[index].EdgeStart);
|
||||
}
|
||||
simplicies.RemoveAt(index);
|
||||
--index;
|
||||
}
|
||||
if (!closed)
|
||||
{
|
||||
if (++index == simplicies.Count)
|
||||
{
|
||||
if (count == simplicies.Count)
|
||||
{
|
||||
result = new List<Vertices>();
|
||||
Debug.WriteLine("Undefined error while building result polygon(s).");
|
||||
return PolyClipError.BrokenResult;
|
||||
}
|
||||
index = 0;
|
||||
count = simplicies.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (output.Count < 3)
|
||||
{
|
||||
errVal = PolyClipError.DegeneratedOutput;
|
||||
Debug.WriteLine("Degenerated output polygon produced (vertices < 3).");
|
||||
}
|
||||
result.Add(output);
|
||||
}
|
||||
return errVal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Needed to calculate the characteristics function of a simplex.
|
||||
/// </summary>
|
||||
/// <remarks>Used by method <c>CalculateEdgeCharacter()</c>.</remarks>
|
||||
private static float CalculateBeta(Vector2 point, Edge e, float coefficient)
|
||||
{
|
||||
float result = 0f;
|
||||
if (PointInSimplex(point, e))
|
||||
{
|
||||
result = coefficient;
|
||||
}
|
||||
if (PointOnLineSegment(Vector2.Zero, e.EdgeStart, point) ||
|
||||
PointOnLineSegment(Vector2.Zero, e.EdgeEnd, point))
|
||||
{
|
||||
result = .5f * coefficient;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Needed for sorting multiple intersections points on the same edge.
|
||||
/// </summary>
|
||||
/// <remarks>Used by method <c>CalculateIntersections()</c>.</remarks>
|
||||
private static float GetAlpha(Vector2 start, Vector2 end, Vector2 point)
|
||||
{
|
||||
return (point - start).LengthSquared() / (end - start).LengthSquared();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the coefficient of a simplex.
|
||||
/// </summary>
|
||||
/// <remarks>Used by method <c>CalculateSimplicalChain()</c>.</remarks>
|
||||
private static float CalculateSimplexCoefficient(Vector2 a, Vector2 b, Vector2 c)
|
||||
{
|
||||
float isLeft = MathUtils.Area(ref a, ref b, ref c);
|
||||
if (isLeft < 0f)
|
||||
{
|
||||
return -1f;
|
||||
}
|
||||
|
||||
if (isLeft > 0f)
|
||||
{
|
||||
return 1f;
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Winding number test for a point in a simplex.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to be tested.</param>
|
||||
/// <param name="edge">The edge that the point is tested against.</param>
|
||||
/// <returns>False if the winding number is even and the point is outside
|
||||
/// the simplex and True otherwise.</returns>
|
||||
private static bool PointInSimplex(Vector2 point, Edge edge)
|
||||
{
|
||||
Vertices polygon = new Vertices();
|
||||
polygon.Add(Vector2.Zero);
|
||||
polygon.Add(edge.EdgeStart);
|
||||
polygon.Add(edge.EdgeEnd);
|
||||
return (polygon.PointInPolygon(ref point) == 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if a point lies on a line segment.
|
||||
/// </summary>
|
||||
/// <remarks>Used by method <c>CalculateBeta()</c>.</remarks>
|
||||
private static bool PointOnLineSegment(Vector2 start, Vector2 end, Vector2 point)
|
||||
{
|
||||
Vector2 segment = end - start;
|
||||
return MathUtils.Area(ref start, ref end, ref point) == 0f &&
|
||||
Vector2.Dot(point - start, segment) >= 0f &&
|
||||
Vector2.Dot(point - end, segment) <= 0f;
|
||||
}
|
||||
|
||||
private static bool VectorEqual(Vector2 vec1, Vector2 vec2)
|
||||
{
|
||||
return (vec2 - vec1).LengthSquared() <= ClipperEpsilonSquared;
|
||||
}
|
||||
|
||||
#region Nested type: Edge
|
||||
|
||||
/// <summary>Specifies an Edge. Edges are used to represent simplicies in simplical chains</summary>
|
||||
private sealed class Edge
|
||||
{
|
||||
public Edge(Vector2 edgeStart, Vector2 edgeEnd)
|
||||
{
|
||||
EdgeStart = edgeStart;
|
||||
EdgeEnd = edgeEnd;
|
||||
}
|
||||
|
||||
public Vector2 EdgeStart { get; private set; }
|
||||
public Vector2 EdgeEnd { get; private set; }
|
||||
|
||||
public Vector2 GetCenter()
|
||||
{
|
||||
return (EdgeStart + EdgeEnd) / 2f;
|
||||
}
|
||||
|
||||
public static Edge operator -(Edge e)
|
||||
{
|
||||
return new Edge(e.EdgeEnd, e.EdgeStart);
|
||||
}
|
||||
|
||||
public override bool Equals(Object obj)
|
||||
{
|
||||
// If parameter is null return false.
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If parameter cannot be cast to Point return false.
|
||||
return Equals(obj as Edge);
|
||||
}
|
||||
|
||||
public bool Equals(Edge e)
|
||||
{
|
||||
// If parameter is null return false:
|
||||
if (e == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true if the fields match
|
||||
return VectorEqual(EdgeStart, e.EdgeStart) && VectorEqual(EdgeEnd, e.EdgeEnd);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return EdgeStart.GetHashCode() ^ EdgeEnd.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user