Adding initial files

This commit is contained in:
nathan@daedalus
2012-03-19 18:57:59 -05:00
commit 5bdc5db408
162 changed files with 43840 additions and 0 deletions

View File

@@ -0,0 +1,126 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.ConvexHull
{
public static class ChainHull
{
//Andrew's monotone chain 2D convex hull algorithm.
//Copyright 2001, softSurfer (www.softsurfer.com)
/// <summary>
/// Gets the convex hull.
/// </summary>
/// <remarks>
/// http://www.softsurfer.com/Archive/algorithm_0109/algorithm_0109.htm
/// </remarks>
/// <returns></returns>
public static Vertices GetConvexHull(Vertices P)
{
P.Sort(new PointComparer());
Vector2[] H = new Vector2[P.Count];
Vertices res = new Vertices();
int n = P.Count;
int bot, top = -1; // indices for bottom and top of the stack
int i; // array scan index
// Get the indices of points with min x-coord and min|max y-coord
int minmin = 0, minmax;
float xmin = P[0].X;
for (i = 1; i < n; i++)
if (P[i].X != xmin) break;
minmax = i - 1;
if (minmax == n - 1)
{
// degenerate case: all x-coords == xmin
H[++top] = P[minmin];
if (P[minmax].Y != P[minmin].Y) // a nontrivial segment
H[++top] = P[minmax];
H[++top] = P[minmin]; // add polygon endpoint
for (int j = 0; j < top + 1; j++)
{
res.Add(H[j]);
}
return res;
}
top = res.Count - 1;
// Get the indices of points with max x-coord and min|max y-coord
int maxmin, maxmax = n - 1;
float xmax = P[n - 1].X;
for (i = n - 2; i >= 0; i--)
if (P[i].X != xmax) break;
maxmin = i + 1;
// Compute the lower hull on the stack H
H[++top] = P[minmin]; // push minmin point onto stack
i = minmax;
while (++i <= maxmin)
{
// the lower line joins P[minmin] with P[maxmin]
if (MathUtils.Area(P[minmin], P[maxmin], P[i]) >= 0 && i < maxmin)
continue; // ignore P[i] above or on the lower line
while (top > 0) // there are at least 2 points on the stack
{
// test if P[i] is left of the line at the stack top
if (MathUtils.Area(H[top - 1], H[top], P[i]) > 0)
break; // P[i] is a new hull vertex
else
top--; // pop top point off stack
}
H[++top] = P[i]; // push P[i] onto stack
}
// Next, compute the upper hull on the stack H above the bottom hull
if (maxmax != maxmin) // if distinct xmax points
H[++top] = P[maxmax]; // push maxmax point onto stack
bot = top; // the bottom point of the upper hull stack
i = maxmin;
while (--i >= minmax)
{
// the upper line joins P[maxmax] with P[minmax]
if (MathUtils.Area(P[maxmax], P[minmax], P[i]) >= 0 && i > minmax)
continue; // ignore P[i] below or on the upper line
while (top > bot) // at least 2 points on the upper stack
{
// test if P[i] is left of the line at the stack top
if (MathUtils.Area(H[top - 1], H[top], P[i]) > 0)
break; // P[i] is a new hull vertex
else
top--; // pop top point off stack
}
H[++top] = P[i]; // push P[i] onto stack
}
if (minmax != minmin)
H[++top] = P[minmin]; // push joining endpoint onto stack
for (int j = 0; j < top + 1; j++)
{
res.Add(H[j]);
}
return res;
}
#region Nested type: PointComparer
public class PointComparer : Comparer<Vector2>
{
public override int Compare(Vector2 a, Vector2 b)
{
int f = a.X.CompareTo(b.X);
return f != 0 ? f : a.Y.CompareTo(b.Y);
}
}
#endregion
}
}

View File

@@ -0,0 +1,99 @@
using System;
namespace FarseerPhysics.Common.ConvexHull
{
public static class GiftWrap
{
// From Eric Jordan's convex decomposition library (box2D rev 32)
/// <summary>
/// Find the convex hull of a point cloud using "Gift-wrap" algorithm - start
/// with an extremal point, and walk around the outside edge by testing
/// angles.
///
/// Runs in O(N*S) time where S is number of sides of resulting polygon.
/// Worst case: point cloud is all vertices of convex polygon: O(N^2).
/// There may be faster algorithms to do this, should you need one -
/// this is just the simplest. You can get O(N log N) expected time if you
/// try, I think, and O(N) if you restrict inputs to simple polygons.
/// Returns null if number of vertices passed is less than 3.
/// Results should be passed through convex decomposition afterwards
/// to ensure that each shape has few enough points to be used in Box2d.
///
/// Warning: May be buggy with colinear points on hull.
/// </summary>
/// <param name="vertices">The vertices.</param>
/// <returns></returns>
public static Vertices GetConvexHull(Vertices vertices)
{
if (vertices.Count < 3)
return vertices;
int[] edgeList = new int[vertices.Count];
int numEdges = 0;
float minY = float.MaxValue;
int minYIndex = vertices.Count;
for (int i = 0; i < vertices.Count; ++i)
{
if (vertices[i].Y < minY)
{
minY = vertices[i].Y;
minYIndex = i;
}
}
int startIndex = minYIndex;
int winIndex = -1;
float dx = -1.0f;
float dy = 0.0f;
while (winIndex != minYIndex)
{
float maxDot = -2.0f;
float nrm;
for (int i = 0; i < vertices.Count; ++i)
{
if (i == startIndex)
continue;
float newdx = vertices[i].X - vertices[startIndex].X;
float newdy = vertices[i].Y - vertices[startIndex].Y;
nrm = (float)Math.Sqrt(newdx * newdx + newdy * newdy);
nrm = (nrm == 0.0f) ? 1.0f : nrm;
newdx /= nrm;
newdy /= nrm;
//Dot products act as proxy for angle
//without requiring inverse trig.
float newDot = newdx * dx + newdy * dy;
if (newDot > maxDot)
{
maxDot = newDot;
winIndex = i;
}
}
edgeList[numEdges++] = winIndex;
dx = vertices[winIndex].X - vertices[startIndex].X;
dy = vertices[winIndex].Y - vertices[startIndex].Y;
nrm = (float)Math.Sqrt(dx * dx + dy * dy);
nrm = (nrm == 0.0f) ? 1.0f : nrm;
dx /= nrm;
dy /= nrm;
startIndex = winIndex;
}
Vertices returnVal = new Vertices(numEdges);
for (int i = 0; i < numEdges; i++)
{
returnVal.Add(vertices[edgeList[i]]);
//Debug.WriteLine(string.Format("{0}, {1}", vertices[edgeList[i]].X, vertices[edgeList[i]].Y));
}
//Not sure if we need this
//returnVal.MergeParallelEdges(Settings.b2_angularSlop);
return returnVal;
}
}
}

View File

@@ -0,0 +1,122 @@
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.ConvexHull
{
public static class Melkman
{
//Melkman based convex hull algorithm contributed by Cowdozer
/// <summary>
/// Creates a convex hull.
/// Note:
/// 1. Vertices must be of a simple polygon, i.e. edges do not overlap.
/// 2. Melkman does not work on point clouds
/// </summary>
/// <remarks>
/// Implemented using Melkman's Convex Hull Algorithm - O(n) time complexity.
/// Reference: http://www.ams.sunysb.edu/~jsbm/courses/345/melkman.pdf
/// </remarks>
/// <returns>A convex hull in counterclockwise winding order.</returns>
public static Vertices GetConvexHull(Vertices vertices)
{
//With less than 3 vertices, this is about the best we can do for a convex hull
if (vertices.Count < 3)
return vertices;
//We'll never need a queue larger than the current number of Vertices +1
//Create double-ended queue
Vector2[] deque = new Vector2[vertices.Count + 1];
int qf = 3, qb = 0; //Queue front index, queue back index
int qfm1, qbm1; //qfm1 = second element, qbm1 = second last element
//Start by placing first 3 vertices in convex CCW order
int startIndex = 3;
float k = MathUtils.Area(vertices[0], vertices[1], vertices[2]);
if (k == 0)
{
//Vertices are collinear.
deque[0] = vertices[0];
deque[1] = vertices[2]; //We can skip vertex 1 because it should be between 0 and 2
deque[2] = vertices[0];
qf = 2;
//Go until the end of the collinear sequence of vertices
for (startIndex = 3; startIndex < vertices.Count; startIndex++)
{
Vector2 tmp = vertices[startIndex];
if (MathUtils.Area(ref deque[0], ref deque[1], ref tmp) == 0) //This point is also collinear
deque[1] = vertices[startIndex];
else break;
}
}
else
{
deque[0] = deque[3] = vertices[2];
if (k > 0)
{
//Is Left. Set deque = {2, 0, 1, 2}
deque[1] = vertices[0];
deque[2] = vertices[1];
}
else
{
//Is Right. Set deque = {2, 1, 0, 2}
deque[1] = vertices[1];
deque[2] = vertices[0];
}
}
qfm1 = qf == 0 ? deque.Length - 1 : qf - 1; //qfm1 = qf - 1;
qbm1 = qb == deque.Length - 1 ? 0 : qb + 1; //qbm1 = qb + 1;
//Add vertices one at a time and adjust convex hull as needed
for (int i = startIndex; i < vertices.Count; i++)
{
Vector2 nextPt = vertices[i];
//Ignore if it is already within the convex hull we have constructed
if (MathUtils.Area(ref deque[qfm1], ref deque[qf], ref nextPt) > 0 &&
MathUtils.Area(ref deque[qb], ref deque[qbm1], ref nextPt) > 0)
continue;
//Pop front until convex
while (!(MathUtils.Area(ref deque[qfm1], ref deque[qf], ref nextPt) > 0))
{
//Pop the front element from the queue
qf = qfm1; //qf--;
qfm1 = qf == 0 ? deque.Length - 1 : qf - 1; //qfm1 = qf - 1;
}
//Add vertex to the front of the queue
qf = qf == deque.Length - 1 ? 0 : qf + 1; //qf++;
qfm1 = qf == 0 ? deque.Length - 1 : qf - 1; //qfm1 = qf - 1;
deque[qf] = nextPt;
//Pop back until convex
while (!(MathUtils.Area(ref deque[qb], ref deque[qbm1], ref nextPt) > 0))
{
//Pop the back element from the queue
qb = qbm1; //qb++;
qbm1 = qb == deque.Length - 1 ? 0 : qb + 1; //qbm1 = qb + 1;
}
//Add vertex to the back of the queue
qb = qb == 0 ? deque.Length - 1 : qb - 1; //qb--;
qbm1 = qb == deque.Length - 1 ? 0 : qb + 1; //qbm1 = qb + 1;
deque[qb] = nextPt;
}
//Create the convex hull from what is left in the deque
Vertices convexHull = new Vertices(vertices.Count + 1);
if (qb < qf)
for (int i = qb; i < qf; i++)
convexHull.Add(deque[i]);
else
{
for (int i = 0; i < qf; i++)
convexHull.Add(deque[i]);
for (int i = qb; i < deque.Length; i++)
convexHull.Add(deque[i]);
}
return convexHull;
}
}
}

View File

@@ -0,0 +1,253 @@
using System.Collections.Generic;
using FarseerPhysics.Common.PolygonManipulation;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.Decomposition
{
//From phed rev 36
/// <summary>
/// Convex decomposition algorithm created by Mark Bayazit (http://mnbayazit.com/)
/// For more information about this algorithm, see http://mnbayazit.com/406/bayazit
/// </summary>
public static class BayazitDecomposer
{
private static Vector2 At(int i, Vertices vertices)
{
int s = vertices.Count;
return vertices[i < 0 ? s - (-i % s) : i % s];
}
private static Vertices Copy(int i, int j, Vertices vertices)
{
Vertices p = new Vertices();
while (j < i) j += vertices.Count;
//p.reserve(j - i + 1);
for (; i <= j; ++i)
{
p.Add(At(i, vertices));
}
return p;
}
/// <summary>
/// Decompose the polygon into several smaller non-concave polygon.
/// If the polygon is already convex, it will return the original polygon, unless it is over Settings.MaxPolygonVertices.
/// Precondition: Counter Clockwise polygon
/// </summary>
/// <param name="vertices"></param>
/// <returns></returns>
public static List<Vertices> ConvexPartition(Vertices vertices)
{
//We force it to CCW as it is a precondition in this algorithm.
vertices.ForceCounterClockWise();
List<Vertices> list = new List<Vertices>();
float d, lowerDist, upperDist;
Vector2 p;
Vector2 lowerInt = new Vector2();
Vector2 upperInt = new Vector2(); // intersection points
int lowerIndex = 0, upperIndex = 0;
Vertices lowerPoly, upperPoly;
for (int i = 0; i < vertices.Count; ++i)
{
if (Reflex(i, vertices))
{
lowerDist = upperDist = float.MaxValue; // std::numeric_limits<qreal>::max();
for (int j = 0; j < vertices.Count; ++j)
{
// if line intersects with an edge
if (Left(At(i - 1, vertices), At(i, vertices), At(j, vertices)) &&
RightOn(At(i - 1, vertices), At(i, vertices), At(j - 1, vertices)))
{
// find the point of intersection
p = LineTools.LineIntersect(At(i - 1, vertices), At(i, vertices), At(j, vertices),
At(j - 1, vertices));
if (Right(At(i + 1, vertices), At(i, vertices), p))
{
// make sure it's inside the poly
d = SquareDist(At(i, vertices), p);
if (d < lowerDist)
{
// keep only the closest intersection
lowerDist = d;
lowerInt = p;
lowerIndex = j;
}
}
}
if (Left(At(i + 1, vertices), At(i, vertices), At(j + 1, vertices)) &&
RightOn(At(i + 1, vertices), At(i, vertices), At(j, vertices)))
{
p = LineTools.LineIntersect(At(i + 1, vertices), At(i, vertices), At(j, vertices),
At(j + 1, vertices));
if (Left(At(i - 1, vertices), At(i, vertices), p))
{
d = SquareDist(At(i, vertices), p);
if (d < upperDist)
{
upperDist = d;
upperIndex = j;
upperInt = p;
}
}
}
}
// if there are no vertices to connect to, choose a point in the middle
if (lowerIndex == (upperIndex + 1) % vertices.Count)
{
Vector2 sp = ((lowerInt + upperInt) / 2);
lowerPoly = Copy(i, upperIndex, vertices);
lowerPoly.Add(sp);
upperPoly = Copy(lowerIndex, i, vertices);
upperPoly.Add(sp);
}
else
{
double highestScore = 0, bestIndex = lowerIndex;
while (upperIndex < lowerIndex) upperIndex += vertices.Count;
for (int j = lowerIndex; j <= upperIndex; ++j)
{
if (CanSee(i, j, vertices))
{
double score = 1 / (SquareDist(At(i, vertices), At(j, vertices)) + 1);
if (Reflex(j, vertices))
{
if (RightOn(At(j - 1, vertices), At(j, vertices), At(i, vertices)) &&
LeftOn(At(j + 1, vertices), At(j, vertices), At(i, vertices)))
{
score += 3;
}
else
{
score += 2;
}
}
else
{
score += 1;
}
if (score > highestScore)
{
bestIndex = j;
highestScore = score;
}
}
}
lowerPoly = Copy(i, (int)bestIndex, vertices);
upperPoly = Copy((int)bestIndex, i, vertices);
}
list.AddRange(ConvexPartition(lowerPoly));
list.AddRange(ConvexPartition(upperPoly));
return list;
}
}
// polygon is already convex
if (vertices.Count > Settings.MaxPolygonVertices)
{
lowerPoly = Copy(0, vertices.Count / 2, vertices);
upperPoly = Copy(vertices.Count / 2, 0, vertices);
list.AddRange(ConvexPartition(lowerPoly));
list.AddRange(ConvexPartition(upperPoly));
}
else
list.Add(vertices);
//The polygons are not guaranteed to be without collinear points. We remove
//them to be sure.
for (int i = 0; i < list.Count; i++)
{
list[i] = SimplifyTools.CollinearSimplify(list[i], 0);
}
//Remove empty vertice collections
for (int i = list.Count - 1; i >= 0; i--)
{
if (list[i].Count == 0)
list.RemoveAt(i);
}
return list;
}
private static bool CanSee(int i, int j, Vertices vertices)
{
if (Reflex(i, vertices))
{
if (LeftOn(At(i, vertices), At(i - 1, vertices), At(j, vertices)) &&
RightOn(At(i, vertices), At(i + 1, vertices), At(j, vertices))) return false;
}
else
{
if (RightOn(At(i, vertices), At(i + 1, vertices), At(j, vertices)) ||
LeftOn(At(i, vertices), At(i - 1, vertices), At(j, vertices))) return false;
}
if (Reflex(j, vertices))
{
if (LeftOn(At(j, vertices), At(j - 1, vertices), At(i, vertices)) &&
RightOn(At(j, vertices), At(j + 1, vertices), At(i, vertices))) return false;
}
else
{
if (RightOn(At(j, vertices), At(j + 1, vertices), At(i, vertices)) ||
LeftOn(At(j, vertices), At(j - 1, vertices), At(i, vertices))) return false;
}
for (int k = 0; k < vertices.Count; ++k)
{
if ((k + 1) % vertices.Count == i || k == i || (k + 1) % vertices.Count == j || k == j)
{
continue; // ignore incident edges
}
Vector2 intersectionPoint;
if (LineTools.LineIntersect(At(i, vertices), At(j, vertices), At(k, vertices), At(k + 1, vertices), out intersectionPoint))
{
return false;
}
}
return true;
}
// precondition: ccw
private static bool Reflex(int i, Vertices vertices)
{
return Right(i, vertices);
}
private static bool Right(int i, Vertices vertices)
{
return Right(At(i - 1, vertices), At(i, vertices), At(i + 1, vertices));
}
private static bool Left(Vector2 a, Vector2 b, Vector2 c)
{
return MathUtils.Area(ref a, ref b, ref c) > 0;
}
private static bool LeftOn(Vector2 a, Vector2 b, Vector2 c)
{
return MathUtils.Area(ref a, ref b, ref c) >= 0;
}
private static bool Right(Vector2 a, Vector2 b, Vector2 c)
{
return MathUtils.Area(ref a, ref b, ref c) < 0;
}
private static bool RightOn(Vector2 a, Vector2 b, Vector2 c)
{
return MathUtils.Area(ref a, ref b, ref c) <= 0;
}
private static float SquareDist(Vector2 a, Vector2 b)
{
float dx = b.X - a.X;
float dy = b.Y - a.Y;
return dx * dx + dy * dy;
}
}
}

View File

@@ -0,0 +1,420 @@
/* 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
// attributification
// Future possibilities
// Flattening out the number of indirections
// Replacing arrays of 3 with fixed-length arrays?
// Replacing bool[3] with a bit array of some sort?
// Bundling everything into an AoS mess?
// Hardcode them all as ABC ?
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Poly2Tri.Triangulation.Delaunay.Sweep;
using Poly2Tri.Triangulation.Util;
namespace Poly2Tri.Triangulation.Delaunay
{
public class DelaunayTriangle
{
/** Neighbor pointers */
/** Flags to determine if an edge is a Delauney edge */
public FixedBitArray3 EdgeIsConstrained;
/** Flags to determine if an edge is a Constrained edge */
public FixedBitArray3 EdgeIsDelaunay;
public FixedArray3<DelaunayTriangle> Neighbors;
/** Has this triangle been marked as an interior triangle? */
public FixedArray3<TriangulationPoint> Points;
public DelaunayTriangle(TriangulationPoint p1, TriangulationPoint p2, TriangulationPoint p3)
{
Points[0] = p1;
Points[1] = p2;
Points[2] = p3;
}
public bool IsInterior { get; set; }
public int IndexOf(TriangulationPoint p)
{
int i = Points.IndexOf(p);
if (i == -1) throw new Exception("Calling index with a point that doesn't exist in triangle");
return i;
}
//TODO: Port note - different implementation
public int IndexCW(TriangulationPoint p)
{
int index = IndexOf(p);
switch (index)
{
case 0:
return 2;
case 1:
return 0;
default:
return 1;
}
}
//TODO: Port note - different implementation
public int IndexCCW(TriangulationPoint p)
{
int index = IndexOf(p);
switch (index)
{
case 0:
return 1;
case 1:
return 2;
default:
return 0;
}
}
public bool Contains(TriangulationPoint p)
{
return (p == Points[0] || p == Points[1] || p == Points[2]);
}
public bool Contains(DTSweepConstraint e)
{
return (Contains(e.P) && Contains(e.Q));
}
public bool Contains(TriangulationPoint p, TriangulationPoint q)
{
return (Contains(p) && Contains(q));
}
/// <summary>
/// Update neighbor pointers
/// </summary>
/// <param name="p1">Point 1 of the shared edge</param>
/// <param name="p2">Point 2 of the shared edge</param>
/// <param name="t">This triangle's new neighbor</param>
private void MarkNeighbor(TriangulationPoint p1, TriangulationPoint p2, DelaunayTriangle t)
{
if ((p1 == Points[2] && p2 == Points[1]) || (p1 == Points[1] && p2 == Points[2]))
{
Neighbors[0] = t;
}
else if ((p1 == Points[0] && p2 == Points[2]) || (p1 == Points[2] && p2 == Points[0]))
{
Neighbors[1] = t;
}
else if ((p1 == Points[0] && p2 == Points[1]) || (p1 == Points[1] && p2 == Points[0]))
{
Neighbors[2] = t;
}
else
{
Debug.WriteLine("Neighbor error, please report!");
// throw new Exception("Neighbor error, please report!");
}
}
/// <summary>
/// Exhaustive search to update neighbor pointers
/// </summary>
public void MarkNeighbor(DelaunayTriangle t)
{
if (t.Contains(Points[1], Points[2]))
{
Neighbors[0] = t;
t.MarkNeighbor(Points[1], Points[2], this);
}
else if (t.Contains(Points[0], Points[2]))
{
Neighbors[1] = t;
t.MarkNeighbor(Points[0], Points[2], this);
}
else if (t.Contains(Points[0], Points[1]))
{
Neighbors[2] = t;
t.MarkNeighbor(Points[0], Points[1], this);
}
else
{
Debug.WriteLine("markNeighbor failed");
}
}
public void ClearNeighbors()
{
Neighbors[0] = Neighbors[1] = Neighbors[2] = null;
}
public void ClearNeighbor(DelaunayTriangle triangle)
{
if (Neighbors[0] == triangle)
{
Neighbors[0] = null;
}
else if (Neighbors[1] == triangle)
{
Neighbors[1] = null;
}
else
{
Neighbors[2] = null;
}
}
/**
* Clears all references to all other triangles and points
*/
public void Clear()
{
DelaunayTriangle t;
for (int i = 0; i < 3; i++)
{
t = Neighbors[i];
if (t != null)
{
t.ClearNeighbor(this);
}
}
ClearNeighbors();
Points[0] = Points[1] = Points[2] = null;
}
/// <param name="t">Opposite triangle</param>
/// <param name="p">The point in t that isn't shared between the triangles</param>
public TriangulationPoint OppositePoint(DelaunayTriangle t, TriangulationPoint p)
{
Debug.Assert(t != this, "self-pointer error");
return PointCW(t.PointCW(p));
}
public DelaunayTriangle NeighborCW(TriangulationPoint point)
{
return Neighbors[(Points.IndexOf(point) + 1)%3];
}
public DelaunayTriangle NeighborCCW(TriangulationPoint point)
{
return Neighbors[(Points.IndexOf(point) + 2)%3];
}
public DelaunayTriangle NeighborAcross(TriangulationPoint point)
{
return Neighbors[Points.IndexOf(point)];
}
public TriangulationPoint PointCCW(TriangulationPoint point)
{
return Points[(IndexOf(point) + 1)%3];
}
public TriangulationPoint PointCW(TriangulationPoint point)
{
return Points[(IndexOf(point) + 2)%3];
}
private void RotateCW()
{
var t = Points[2];
Points[2] = Points[1];
Points[1] = Points[0];
Points[0] = t;
}
/// <summary>
/// Legalize triangle by rotating clockwise around oPoint
/// </summary>
/// <param name="oPoint">The origin point to rotate around</param>
/// <param name="nPoint">???</param>
public void Legalize(TriangulationPoint oPoint, TriangulationPoint nPoint)
{
RotateCW();
Points[IndexCCW(oPoint)] = nPoint;
}
public override string ToString()
{
return Points[0] + "," + Points[1] + "," + Points[2];
}
/// <summary>
/// Finalize edge marking
/// </summary>
public void MarkNeighborEdges()
{
for (int i = 0; i < 3; i++)
if (EdgeIsConstrained[i] && Neighbors[i] != null)
{
Neighbors[i].MarkConstrainedEdge(Points[(i + 1)%3], Points[(i + 2)%3]);
}
}
public void MarkEdge(DelaunayTriangle triangle)
{
for (int i = 0; i < 3; i++)
if (EdgeIsConstrained[i])
{
triangle.MarkConstrainedEdge(Points[(i + 1)%3], Points[(i + 2)%3]);
}
}
public void MarkEdge(List<DelaunayTriangle> tList)
{
foreach (DelaunayTriangle t in tList)
for (int i = 0; i < 3; i++)
if (t.EdgeIsConstrained[i])
{
MarkConstrainedEdge(t.Points[(i + 1)%3], t.Points[(i + 2)%3]);
}
}
public void MarkConstrainedEdge(int index)
{
EdgeIsConstrained[index] = true;
}
public void MarkConstrainedEdge(DTSweepConstraint edge)
{
MarkConstrainedEdge(edge.P, edge.Q);
}
/// <summary>
/// Mark edge as constrained
/// </summary>
public void MarkConstrainedEdge(TriangulationPoint p, TriangulationPoint q)
{
int i = EdgeIndex(p, q);
if (i != -1) EdgeIsConstrained[i] = true;
}
public double Area()
{
double b = Points[0].X - Points[1].X;
double h = Points[2].Y - Points[1].Y;
return Math.Abs((b*h*0.5f));
}
public TriangulationPoint Centroid()
{
double cx = (Points[0].X + Points[1].X + Points[2].X)/3f;
double cy = (Points[0].Y + Points[1].Y + Points[2].Y)/3f;
return new TriangulationPoint(cx, cy);
}
/// <summary>
/// Get the index of the neighbor that shares this edge (or -1 if it isn't shared)
/// </summary>
/// <returns>index of the shared edge or -1 if edge isn't shared</returns>
public int EdgeIndex(TriangulationPoint p1, TriangulationPoint p2)
{
int i1 = Points.IndexOf(p1);
int i2 = Points.IndexOf(p2);
// Points of this triangle in the edge p1-p2
bool a = (i1 == 0 || i2 == 0);
bool b = (i1 == 1 || i2 == 1);
bool c = (i1 == 2 || i2 == 2);
if (b && c) return 0;
if (a && c) return 1;
if (a && b) return 2;
return -1;
}
public bool GetConstrainedEdgeCCW(TriangulationPoint p)
{
return EdgeIsConstrained[(IndexOf(p) + 2)%3];
}
public bool GetConstrainedEdgeCW(TriangulationPoint p)
{
return EdgeIsConstrained[(IndexOf(p) + 1)%3];
}
public bool GetConstrainedEdgeAcross(TriangulationPoint p)
{
return EdgeIsConstrained[IndexOf(p)];
}
public void SetConstrainedEdgeCCW(TriangulationPoint p, bool ce)
{
EdgeIsConstrained[(IndexOf(p) + 2)%3] = ce;
}
public void SetConstrainedEdgeCW(TriangulationPoint p, bool ce)
{
EdgeIsConstrained[(IndexOf(p) + 1)%3] = ce;
}
public void SetConstrainedEdgeAcross(TriangulationPoint p, bool ce)
{
EdgeIsConstrained[IndexOf(p)] = ce;
}
public bool GetDelaunayEdgeCCW(TriangulationPoint p)
{
return EdgeIsDelaunay[(IndexOf(p) + 2)%3];
}
public bool GetDelaunayEdgeCW(TriangulationPoint p)
{
return EdgeIsDelaunay[(IndexOf(p) + 1)%3];
}
public bool GetDelaunayEdgeAcross(TriangulationPoint p)
{
return EdgeIsDelaunay[IndexOf(p)];
}
public void SetDelaunayEdgeCCW(TriangulationPoint p, bool ce)
{
EdgeIsDelaunay[(IndexOf(p) + 2)%3] = ce;
}
public void SetDelaunayEdgeCW(TriangulationPoint p, bool ce)
{
EdgeIsDelaunay[(IndexOf(p) + 1)%3] = ce;
}
public void SetDelaunayEdgeAcross(TriangulationPoint p, bool ce)
{
EdgeIsDelaunay[IndexOf(p)] = ce;
}
}
}

View File

@@ -0,0 +1,180 @@
/* 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
// Removed BST code, but not all artifacts of it
// Future possibilities
// Eliminate Add/RemoveNode ?
// Comments comments and more comments!
using System;
using System.Text;
namespace Poly2Tri.Triangulation.Delaunay.Sweep
{
/**
* @author Thomas Åhlen (thahlen@gmail.com)
*/
public class AdvancingFront
{
public AdvancingFrontNode Head;
protected AdvancingFrontNode Search;
public AdvancingFrontNode Tail;
public AdvancingFront(AdvancingFrontNode head, AdvancingFrontNode tail)
{
Head = head;
Tail = tail;
Search = head;
AddNode(head);
AddNode(tail);
}
public void AddNode(AdvancingFrontNode node)
{
//_searchTree.put(node.key, node);
}
public void RemoveNode(AdvancingFrontNode node)
{
//_searchTree.delete( node.key );
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
AdvancingFrontNode node = Head;
while (node != Tail)
{
sb.Append(node.Point.X).Append("->");
node = node.Next;
}
sb.Append(Tail.Point.X);
return sb.ToString();
}
/// <summary>
/// MM: This seems to be used by LocateNode to guess a position in the implicit linked list of AdvancingFrontNodes near x
/// Removed an overload that depended on this being exact
/// </summary>
private AdvancingFrontNode FindSearchNode(double x)
{
// TODO: implement BST index
return Search;
}
/// <summary>
/// We use a balancing tree to locate a node smaller or equal to given key value
/// </summary>
public AdvancingFrontNode LocateNode(TriangulationPoint point)
{
return LocateNode(point.X);
}
private AdvancingFrontNode LocateNode(double x)
{
AdvancingFrontNode node = FindSearchNode(x);
if (x < node.Value)
{
while ((node = node.Prev) != null)
if (x >= node.Value)
{
Search = node;
return node;
}
}
else
{
while ((node = node.Next) != null)
if (x < node.Value)
{
Search = node.Prev;
return node.Prev;
}
}
return null;
}
/// <summary>
/// This implementation will use simple node traversal algorithm to find a point on the front
/// </summary>
public AdvancingFrontNode LocatePoint(TriangulationPoint point)
{
double px = point.X;
AdvancingFrontNode node = FindSearchNode(px);
double nx = node.Point.X;
if (px == nx)
{
if (point != node.Point)
{
// We might have two nodes with same x value for a short time
if (point == node.Prev.Point)
{
node = node.Prev;
}
else if (point == node.Next.Point)
{
node = node.Next;
}
else
{
throw new Exception("Failed to find Node for given afront point");
//node = null;
}
}
}
else if (px < nx)
{
while ((node = node.Prev) != null)
{
if (point == node.Point)
{
break;
}
}
}
else
{
while ((node = node.Next) != null)
{
if (point == node.Point)
{
break;
}
}
}
Search = node;
return node;
}
}
}

View File

@@ -0,0 +1,64 @@
/* 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
// Removed getters
// Has* turned into attributes
// Future possibilities
// Comments!
namespace Poly2Tri.Triangulation.Delaunay.Sweep
{
public class AdvancingFrontNode
{
public AdvancingFrontNode Next;
public TriangulationPoint Point;
public AdvancingFrontNode Prev;
public DelaunayTriangle Triangle;
public double Value;
public AdvancingFrontNode(TriangulationPoint point)
{
Point = point;
Value = point.X;
}
public bool HasNext
{
get { return Next != null; }
}
public bool HasPrev
{
get { return Prev != null; }
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
/* 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.
*/
namespace Poly2Tri.Triangulation.Delaunay.Sweep
{
public class DTSweepConstraint : TriangulationConstraint
{
/// <summary>
/// Give two points in any order. Will always be ordered so
/// that q.y > p.y and q.x > p.x if same y value
/// </summary>
public DTSweepConstraint(TriangulationPoint p1, TriangulationPoint p2)
{
P = p1;
Q = p2;
if (p1.Y > p2.Y)
{
Q = p1;
P = p2;
}
else if (p1.Y == p2.Y)
{
if (p1.X > p2.X)
{
Q = p1;
P = p2;
}
else if (p1.X == p2.X)
{
// logger.info( "Failed to create constraint {}={}", p1, p2 );
// throw new DuplicatePointException( p1 + "=" + p2 );
// return;
}
}
Q.AddEdge(this);
}
}
}

View File

@@ -0,0 +1,236 @@
/* 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.
*/
namespace Poly2Tri.Triangulation.Delaunay.Sweep
{
/**
*
* @author Thomas Åhlén, thahlen@gmail.com
*
*/
public class DTSweepContext : TriangulationContext
{
// Inital triangle factor, seed triangle will extend 30% of
// PointSet width to both left and right.
private const float ALPHA = 0.3f;
public DTSweepBasin Basin = new DTSweepBasin();
public DTSweepEdgeEvent EdgeEvent = new DTSweepEdgeEvent();
private DTSweepPointComparator _comparator = new DTSweepPointComparator();
public AdvancingFront aFront;
public DTSweepContext()
{
Clear();
}
public TriangulationPoint Head { get; set; }
public TriangulationPoint Tail { get; set; }
public void RemoveFromList(DelaunayTriangle triangle)
{
Triangles.Remove(triangle);
// TODO: remove all neighbor pointers to this triangle
// for( int i=0; i<3; i++ )
// {
// if( triangle.neighbors[i] != null )
// {
// triangle.neighbors[i].clearNeighbor( triangle );
// }
// }
// triangle.clearNeighbors();
}
public void MeshClean(DelaunayTriangle triangle)
{
MeshCleanReq(triangle);
}
private void MeshCleanReq(DelaunayTriangle triangle)
{
if (triangle != null && !triangle.IsInterior)
{
triangle.IsInterior = true;
Triangulatable.AddTriangle(triangle);
for (int i = 0; i < 3; i++)
{
if (!triangle.EdgeIsConstrained[i])
{
MeshCleanReq(triangle.Neighbors[i]);
}
}
}
}
public override void Clear()
{
base.Clear();
Triangles.Clear();
}
public void AddNode(AdvancingFrontNode node)
{
// Console.WriteLine( "add:" + node.key + ":" + System.identityHashCode(node.key));
// m_nodeTree.put( node.getKey(), node );
aFront.AddNode(node);
}
public void RemoveNode(AdvancingFrontNode node)
{
// Console.WriteLine( "remove:" + node.key + ":" + System.identityHashCode(node.key));
// m_nodeTree.delete( node.getKey() );
aFront.RemoveNode(node);
}
public AdvancingFrontNode LocateNode(TriangulationPoint point)
{
return aFront.LocateNode(point);
}
public void CreateAdvancingFront()
{
AdvancingFrontNode head, tail, middle;
// Initial triangle
DelaunayTriangle iTriangle = new DelaunayTriangle(Points[0], Tail, Head);
Triangles.Add(iTriangle);
head = new AdvancingFrontNode(iTriangle.Points[1]);
head.Triangle = iTriangle;
middle = new AdvancingFrontNode(iTriangle.Points[0]);
middle.Triangle = iTriangle;
tail = new AdvancingFrontNode(iTriangle.Points[2]);
aFront = new AdvancingFront(head, tail);
aFront.AddNode(middle);
// TODO: I think it would be more intuitive if head is middles next and not previous
// so swap head and tail
aFront.Head.Next = middle;
middle.Next = aFront.Tail;
middle.Prev = aFront.Head;
aFront.Tail.Prev = middle;
}
/// <summary>
/// Try to map a node to all sides of this triangle that don't have
/// a neighbor.
/// </summary>
public void MapTriangleToNodes(DelaunayTriangle t)
{
AdvancingFrontNode n;
for (int i = 0; i < 3; i++)
{
if (t.Neighbors[i] == null)
{
n = aFront.LocatePoint(t.PointCW(t.Points[i]));
if (n != null)
{
n.Triangle = t;
}
}
}
}
public override void PrepareTriangulation(Triangulatable t)
{
base.PrepareTriangulation(t);
double xmax, xmin;
double ymax, ymin;
xmax = xmin = Points[0].X;
ymax = ymin = Points[0].Y;
// Calculate bounds. Should be combined with the sorting
foreach (TriangulationPoint p in Points)
{
if (p.X > xmax)
xmax = p.X;
if (p.X < xmin)
xmin = p.X;
if (p.Y > ymax)
ymax = p.Y;
if (p.Y < ymin)
ymin = p.Y;
}
double deltaX = ALPHA*(xmax - xmin);
double deltaY = ALPHA*(ymax - ymin);
TriangulationPoint p1 = new TriangulationPoint(xmax + deltaX, ymin - deltaY);
TriangulationPoint p2 = new TriangulationPoint(xmin - deltaX, ymin - deltaY);
Head = p1;
Tail = p2;
// long time = System.nanoTime();
// Sort the points along y-axis
Points.Sort(_comparator);
// logger.info( "Triangulation setup [{}ms]", ( System.nanoTime() - time ) / 1e6 );
}
public void FinalizeTriangulation()
{
Triangulatable.AddTriangles(Triangles);
Triangles.Clear();
}
public override TriangulationConstraint NewConstraint(TriangulationPoint a, TriangulationPoint b)
{
return new DTSweepConstraint(a, b);
}
#region Nested type: DTSweepBasin
public class DTSweepBasin
{
public AdvancingFrontNode bottomNode;
public bool leftHighest;
public AdvancingFrontNode leftNode;
public AdvancingFrontNode rightNode;
public double width;
}
#endregion
#region Nested type: DTSweepEdgeEvent
public class DTSweepEdgeEvent
{
public DTSweepConstraint ConstrainedEdge;
public bool Right;
}
#endregion
}
}

View File

@@ -0,0 +1,69 @@
/* 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.
*/
using System.Collections.Generic;
namespace Poly2Tri.Triangulation.Delaunay.Sweep
{
public class DTSweepPointComparator : IComparer<TriangulationPoint>
{
#region IComparer<TriangulationPoint> Members
public int Compare(TriangulationPoint p1, TriangulationPoint p2)
{
if (p1.Y < p2.Y)
{
return -1;
}
else if (p1.Y > p2.Y)
{
return 1;
}
else
{
if (p1.X < p2.X)
{
return -1;
}
else if (p1.X > p2.X)
{
return 1;
}
else
{
return 0;
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,43 @@
/* 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.
*/
using System;
namespace Poly2Tri.Triangulation.Delaunay.Sweep
{
public class PointOnEdgeException : NotImplementedException
{
public PointOnEdgeException(string message)
: base(message)
{
}
}
}

View File

@@ -0,0 +1,48 @@
/* 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.
*/
using System.Collections.Generic;
using Poly2Tri.Triangulation.Delaunay;
namespace Poly2Tri.Triangulation
{
public interface Triangulatable
{
IList<TriangulationPoint> Points { get; } // MM: Neither of these are used via interface (yet?)
IList<DelaunayTriangle> Triangles { get; }
TriangulationMode TriangulationMode { get; }
void PrepareTriangulation(TriangulationContext tcx);
void AddTriangle(DelaunayTriangle t);
void AddTriangles(IEnumerable<DelaunayTriangle> list);
void ClearTriangles();
}
}

View File

@@ -0,0 +1,40 @@
/* 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.
*/
namespace FarseerPhysics.Common.Decomposition.CDT
{
public enum Orientation
{
CW,
CCW,
Collinear
}
}

View File

@@ -0,0 +1,272 @@
/* 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<Polygon> _holes;
protected PolygonPoint _last;
protected List<TriangulationPoint> _points = new List<TriangulationPoint>();
protected List<TriangulationPoint> _steinerPoints;
protected List<DelaunayTriangle> _triangles;
/// <summary>
/// Create a polygon from a list of at least 3 points with no duplicates.
/// </summary>
/// <param name="points">A list of unique points</param>
public Polygon(IList<PolygonPoint> 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<TriangulationPoint>());
}
/// <summary>
/// Create a polygon from a list of at least 3 points with no duplicates.
/// </summary>
/// <param name="points">A list of unique points.</param>
public Polygon(IEnumerable<PolygonPoint> points) : this((points as IList<PolygonPoint>) ?? points.ToArray())
{
}
public Polygon()
{
}
public IList<Polygon> Holes
{
get { return _holes; }
}
#region Triangulatable Members
public TriangulationMode TriangulationMode
{
get { return TriangulationMode.Polygon; }
}
public IList<TriangulationPoint> Points
{
get { return _points; }
}
public IList<DelaunayTriangle> Triangles
{
get { return _triangles; }
}
public void AddTriangle(DelaunayTriangle t)
{
_triangles.Add(t);
}
public void AddTriangles(IEnumerable<DelaunayTriangle> list)
{
_triangles.AddRange(list);
}
public void ClearTriangles()
{
if (_triangles != null) _triangles.Clear();
}
/// <summary>
/// Creates constraints and populates the context with points
/// </summary>
/// <param name="tcx">The context</param>
public void PrepareTriangulation(TriangulationContext tcx)
{
if (_triangles == null)
{
_triangles = new List<DelaunayTriangle>(_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<TriangulationPoint>();
}
_steinerPoints.Add(point);
}
public void AddSteinerPoints(List<TriangulationPoint> points)
{
if (_steinerPoints == null)
{
_steinerPoints = new List<TriangulationPoint>();
}
_steinerPoints.AddRange(points);
}
public void ClearSteinerPoints()
{
if (_steinerPoints != null)
{
_steinerPoints.Clear();
}
}
/// <summary>
/// Add a hole to the polygon.
/// </summary>
/// <param name="poly">A subtraction polygon fully contained inside this polygon.</param>
public void AddHole(Polygon poly)
{
if (_holes == null) _holes = new List<Polygon>();
_holes.Add(poly);
// XXX: tests could be made here to be sure it is fully inside
// addSubtraction( poly.getPoints() );
}
/// <summary>
/// Inserts newPoint after point.
/// </summary>
/// <param name="point">The point to insert after in the polygon</param>
/// <param name="newPoint">The point to insert into the polygon</param>
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);
}
/// <summary>
/// Inserts list (after last point in polygon?)
/// </summary>
/// <param name="list"></param>
public void AddPoints(IEnumerable<PolygonPoint> 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;
}
/// <summary>
/// Adds a point after the last in the polygon.
/// </summary>
/// <param name="p">The point to add</param>
public void AddPoint(PolygonPoint p)
{
p.Previous = _last;
p.Next = _last.Next;
_last.Next = p;
_points.Add(p);
}
/// <summary>
/// Removes a point from the polygon.
/// </summary>
/// <param name="p"></param>
public void RemovePoint(PolygonPoint p)
{
PolygonPoint next, prev;
next = p.Next;
prev = p.Previous;
prev.Next = next;
next.Previous = prev;
_points.Remove(p);
}
}
}

View File

@@ -0,0 +1,48 @@
/* 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
// Replaced get/set Next/Previous with attributes
// Future possibilities
// Documentation!
namespace Poly2Tri.Triangulation.Polygon
{
public class PolygonPoint : TriangulationPoint
{
public PolygonPoint(double x, double y) : base(x, y)
{
}
public PolygonPoint Next { get; set; }
public PolygonPoint Previous { get; set; }
}
}

View File

@@ -0,0 +1,65 @@
/* 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
// Replaced getPolygons with attribute
// Future possibilities
// Replace Add(Polygon) with exposed container?
// Replace entire class with HashSet<Polygon> ?
using System.Collections.Generic;
namespace Poly2Tri.Triangulation.Polygon
{
public class PolygonSet
{
protected List<Polygon> _polygons = new List<Polygon>();
public PolygonSet()
{
}
public PolygonSet(Polygon poly)
{
_polygons.Add(poly);
}
public IEnumerable<Polygon> Polygons
{
get { return _polygons; }
}
public void Add(Polygon p)
{
_polygons.Add(p);
}
}
}

View File

@@ -0,0 +1,114 @@
/* 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.
*/
using System.Collections.Generic;
namespace Poly2Tri.Triangulation.Sets
{
/*
* Extends the PointSet by adding some Constraints on how it will be triangulated<br>
* A constraint defines an edge between two points in the set, these edges can not
* be crossed. They will be enforced triangle edges after a triangulation.
* <p>
*
*
* @author Thomas Åhlén, thahlen@gmail.com
*/
public class ConstrainedPointSet : PointSet
{
private List<TriangulationPoint> _constrainedPointList = null;
public ConstrainedPointSet(List<TriangulationPoint> points, int[] index)
: base(points)
{
EdgeIndex = index;
}
/**
*
* @param points - A list of all points in PointSet
* @param constraints - Pairs of two points defining a constraint, all points <b>must</b> be part of given PointSet!
*/
public ConstrainedPointSet(List<TriangulationPoint> points, IEnumerable<TriangulationPoint> constraints)
: base(points)
{
_constrainedPointList = new List<TriangulationPoint>();
_constrainedPointList.AddRange(constraints);
}
public int[] EdgeIndex { get; private set; }
public override TriangulationMode TriangulationMode
{
get { return TriangulationMode.Constrained; }
}
public override void PrepareTriangulation(TriangulationContext tcx)
{
base.PrepareTriangulation(tcx);
if (_constrainedPointList != null)
{
TriangulationPoint p1, p2;
List<TriangulationPoint>.Enumerator iterator = _constrainedPointList.GetEnumerator();
while (iterator.MoveNext())
{
p1 = iterator.Current;
iterator.MoveNext();
p2 = iterator.Current;
tcx.NewConstraint(p1, p2);
}
}
else
{
for (int i = 0; i < EdgeIndex.Length; i += 2)
{
// XXX: must change!!
tcx.NewConstraint(Points[EdgeIndex[i]], Points[EdgeIndex[i + 1]]);
}
}
}
/**
* TODO: TO BE IMPLEMENTED!
* Peforms a validation on given input<br>
* 1. Check's if there any constraint edges are crossing or collinear<br>
* 2.
* @return
*/
public bool isValid()
{
return true;
}
}
}

View File

@@ -0,0 +1,84 @@
/* 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.
*/
using System.Collections.Generic;
using Poly2Tri.Triangulation.Delaunay;
namespace Poly2Tri.Triangulation.Sets
{
public class PointSet : Triangulatable
{
public PointSet(List<TriangulationPoint> points)
{
Points = new List<TriangulationPoint>(points);
}
#region Triangulatable Members
public IList<TriangulationPoint> Points { get; private set; }
public IList<DelaunayTriangle> Triangles { get; private set; }
public virtual TriangulationMode TriangulationMode
{
get { return TriangulationMode.Unconstrained; }
}
public void AddTriangle(DelaunayTriangle t)
{
Triangles.Add(t);
}
public void AddTriangles(IEnumerable<DelaunayTriangle> list)
{
foreach (DelaunayTriangle tri in list) Triangles.Add(tri);
}
public void ClearTriangles()
{
Triangles.Clear();
}
public virtual void PrepareTriangulation(TriangulationContext tcx)
{
if (Triangles == null)
{
Triangles = new List<DelaunayTriangle>(Points.Count);
}
else
{
Triangles.Clear();
}
tcx.Points.AddRange(Points);
}
#endregion
}
}

View File

@@ -0,0 +1,46 @@
/* 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.
*/
/**
* Forces a triangle edge between two points p and q
* when triangulating. For example used to enforce
* Polygon Edges during a polygon triangulation.
*
* @author Thomas Åhlén, thahlen@gmail.com
*/
namespace Poly2Tri.Triangulation
{
public class TriangulationConstraint
{
public TriangulationPoint P;
public TriangulationPoint Q;
}
}

View File

@@ -0,0 +1,87 @@
/* 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.
*/
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Poly2Tri.Triangulation.Delaunay;
namespace Poly2Tri.Triangulation
{
public abstract class TriangulationContext
{
public readonly List<TriangulationPoint> Points = new List<TriangulationPoint>(200);
public readonly List<DelaunayTriangle> Triangles = new List<DelaunayTriangle>();
#pragma warning disable 414
private int _stepTime = -1;
#pragma warning restore 414
public TriangulationContext()
{
Terminated = false;
}
public TriangulationMode TriangulationMode { get; protected set; }
public Triangulatable Triangulatable { get; private set; }
public bool WaitUntilNotified { get; private set; }
public bool Terminated { get; set; }
public int StepCount { get; private set; }
public virtual bool IsDebugEnabled { get; protected set; }
public void Done()
{
StepCount++;
}
public virtual void PrepareTriangulation(Triangulatable t)
{
Triangulatable = t;
TriangulationMode = t.TriangulationMode;
t.PrepareTriangulation(this);
}
public abstract TriangulationConstraint NewConstraint(TriangulationPoint a, TriangulationPoint b);
[MethodImpl(MethodImplOptions.Synchronized)]
public void Update(string message)
{
}
public virtual void Clear()
{
Points.Clear();
Terminated = false;
StepCount = 0;
}
}
}

View File

@@ -0,0 +1,40 @@
/* 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.
*/
namespace Poly2Tri.Triangulation
{
public enum TriangulationMode
{
Unconstrained,
Constrained,
Polygon
}
}

View File

@@ -0,0 +1,82 @@
/* 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.
*/
using System.Collections.Generic;
using Poly2Tri.Triangulation.Delaunay.Sweep;
namespace Poly2Tri.Triangulation
{
public class TriangulationPoint
{
// List of edges this point constitutes an upper ending point (CDT)
public double X, Y;
public TriangulationPoint(double x, double y)
{
X = x;
Y = y;
}
public List<DTSweepConstraint> Edges { get; private set; }
public float Xf
{
get { return (float) X; }
set { X = value; }
}
public float Yf
{
get { return (float) Y; }
set { Y = value; }
}
public bool HasEdges
{
get { return Edges != null; }
}
public override string ToString()
{
return "[" + X + "," + Y + "]";
}
public void AddEdge(DTSweepConstraint e)
{
if (Edges == null)
{
Edges = new List<DTSweepConstraint>();
}
Edges.Add(e);
}
}
}

View File

@@ -0,0 +1,160 @@
/* 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.
*/
using FarseerPhysics.Common.Decomposition.CDT;
namespace Poly2Tri.Triangulation
{
/**
* @author Thomas Åhlén, thahlen@gmail.com
*/
public class TriangulationUtil
{
public static double EPSILON = 1e-12;
/// <summary>
/// Requirements:
/// 1. a,b and c form a triangle.
/// 2. a and d is know to be on opposite side of bc
/// <code>
/// a
/// +
/// / \
/// / \
/// b/ \c
/// +-------+
/// / B \
/// / \
/// </code>
/// Facts:
/// d has to be in area B to have a chance to be inside the circle formed by a,b and c
/// d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW
/// This preknowledge gives us a way to optimize the incircle test
/// </summary>
/// <param name="pa">triangle point, opposite d</param>
/// <param name="pb">triangle point</param>
/// <param name="pc">triangle point</param>
/// <param name="pd">point opposite a</param>
/// <returns>true if d is inside circle, false if on circle edge</returns>
public static bool SmartIncircle(TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc,
TriangulationPoint pd)
{
double pdx = pd.X;
double pdy = pd.Y;
double adx = pa.X - pdx;
double ady = pa.Y - pdy;
double bdx = pb.X - pdx;
double bdy = pb.Y - pdy;
double adxbdy = adx*bdy;
double bdxady = bdx*ady;
double oabd = adxbdy - bdxady;
// oabd = orient2d(pa,pb,pd);
if (oabd <= 0) return false;
double cdx = pc.X - pdx;
double cdy = pc.Y - pdy;
double cdxady = cdx*ady;
double adxcdy = adx*cdy;
double ocad = cdxady - adxcdy;
// ocad = orient2d(pc,pa,pd);
if (ocad <= 0) return false;
double bdxcdy = bdx*cdy;
double cdxbdy = cdx*bdy;
double alift = adx*adx + ady*ady;
double blift = bdx*bdx + bdy*bdy;
double clift = cdx*cdx + cdy*cdy;
double det = alift*(bdxcdy - cdxbdy) + blift*ocad + clift*oabd;
return det > 0;
}
public static bool InScanArea(TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc,
TriangulationPoint pd)
{
double pdx = pd.X;
double pdy = pd.Y;
double adx = pa.X - pdx;
double ady = pa.Y - pdy;
double bdx = pb.X - pdx;
double bdy = pb.Y - pdy;
double adxbdy = adx*bdy;
double bdxady = bdx*ady;
double oabd = adxbdy - bdxady;
// oabd = orient2d(pa,pb,pd);
if (oabd <= 0)
{
return false;
}
double cdx = pc.X - pdx;
double cdy = pc.Y - pdy;
double cdxady = cdx*ady;
double adxcdy = adx*cdy;
double ocad = cdxady - adxcdy;
// ocad = orient2d(pc,pa,pd);
if (ocad <= 0)
{
return false;
}
return true;
}
/// Forumla to calculate signed area
/// Positive if CCW
/// Negative if CW
/// 0 if collinear
/// A[P1,P2,P3] = (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
/// = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
public static Orientation Orient2d(TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc)
{
double detleft = (pa.X - pc.X)*(pb.Y - pc.Y);
double detright = (pa.Y - pc.Y)*(pb.X - pc.X);
double val = detleft - detright;
if (val > -EPSILON && val < EPSILON)
{
return Orientation.Collinear;
}
else if (val > 0)
{
return Orientation.CCW;
}
return Orientation.CW;
}
}
}

View File

@@ -0,0 +1,118 @@
/* 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.
*/
using System;
using System.Collections;
using System.Collections.Generic;
namespace Poly2Tri.Triangulation.Util
{
public struct FixedArray3<T> : IEnumerable<T> where T : class
{
public T _0, _1, _2;
public T this[int index]
{
get
{
switch (index)
{
case 0:
return _0;
case 1:
return _1;
case 2:
return _2;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
_0 = value;
break;
case 1:
_1 = value;
break;
case 2:
_2 = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return Enumerate().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
public bool Contains(T value)
{
for (int i = 0; i < 3; ++i) if (this[i] == value) return true;
return false;
}
public int IndexOf(T value)
{
for (int i = 0; i < 3; ++i) if (this[i] == value) return i;
return -1;
}
public void Clear()
{
_0 = _1 = _2 = null;
}
public void Clear(T value)
{
for (int i = 0; i < 3; ++i) if (this[i] == value) this[i] = null;
}
private IEnumerable<T> Enumerate()
{
for (int i = 0; i < 3; ++i) yield return this[i];
}
}
}

View File

@@ -0,0 +1,118 @@
/* 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.
*/
using System;
using System.Collections;
using System.Collections.Generic;
namespace Poly2Tri.Triangulation.Util
{
public struct FixedBitArray3 : IEnumerable<bool>
{
public bool _0, _1, _2;
public bool this[int index]
{
get
{
switch (index)
{
case 0:
return _0;
case 1:
return _1;
case 2:
return _2;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
_0 = value;
break;
case 1:
_1 = value;
break;
case 2:
_2 = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
}
#region IEnumerable<bool> Members
public IEnumerator<bool> GetEnumerator()
{
return Enumerate().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
public bool Contains(bool value)
{
for (int i = 0; i < 3; ++i) if (this[i] == value) return true;
return false;
}
public int IndexOf(bool value)
{
for (int i = 0; i < 3; ++i) if (this[i] == value) return i;
return -1;
}
public void Clear()
{
_0 = _1 = _2 = false;
}
public void Clear(bool value)
{
for (int i = 0; i < 3; ++i) if (this[i] == value) this[i] = false;
}
private IEnumerable<bool> Enumerate()
{
for (int i = 0; i < 3; ++i) yield return this[i];
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
namespace Poly2Tri.Triangulation.Util
{
public class PointGenerator
{
private static readonly Random RNG = new Random();
public static List<TriangulationPoint> UniformDistribution(int n, double scale)
{
List<TriangulationPoint> points = new List<TriangulationPoint>();
for (int i = 0; i < n; i++)
{
points.Add(new TriangulationPoint(scale*(0.5 - RNG.NextDouble()), scale*(0.5 - RNG.NextDouble())));
}
return points;
}
public static List<TriangulationPoint> UniformGrid(int n, double scale)
{
double x = 0;
double size = scale/n;
double halfScale = 0.5*scale;
List<TriangulationPoint> points = new List<TriangulationPoint>();
for (int i = 0; i < n + 1; i++)
{
x = halfScale - i*size;
for (int j = 0; j < n + 1; j++)
{
points.Add(new TriangulationPoint(x, halfScale - j*size));
}
}
return points;
}
}
}

View File

@@ -0,0 +1,98 @@
/* 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.
*/
using System;
using Poly2Tri.Triangulation.Polygon;
namespace Poly2Tri.Triangulation.Util
{
public class PolygonGenerator
{
private static readonly Random RNG = new Random();
private static double PI_2 = 2.0*Math.PI;
public static Polygon.Polygon RandomCircleSweep(double scale, int vertexCount)
{
PolygonPoint point;
PolygonPoint[] points;
double radius = scale/4;
points = new PolygonPoint[vertexCount];
for (int i = 0; i < vertexCount; i++)
{
do
{
if (i%250 == 0)
{
radius += scale/2*(0.5 - RNG.NextDouble());
}
else if (i%50 == 0)
{
radius += scale/5*(0.5 - RNG.NextDouble());
}
else
{
radius += 25*scale/vertexCount*(0.5 - RNG.NextDouble());
}
radius = radius > scale/2 ? scale/2 : radius;
radius = radius < scale/10 ? scale/10 : radius;
} while (radius < scale/10 || radius > scale/2);
point = new PolygonPoint(radius*Math.Cos((PI_2*i)/vertexCount),
radius*Math.Sin((PI_2*i)/vertexCount));
points[i] = point;
}
return new Polygon.Polygon(points);
}
public static Polygon.Polygon RandomCircleSweep2(double scale, int vertexCount)
{
PolygonPoint point;
PolygonPoint[] points;
double radius = scale/4;
points = new PolygonPoint[vertexCount];
for (int i = 0; i < vertexCount; i++)
{
do
{
radius += scale/5*(0.5 - RNG.NextDouble());
radius = radius > scale/2 ? scale/2 : radius;
radius = radius < scale/10 ? scale/10 : radius;
} while (radius < scale/10 || radius > scale/2);
point = new PolygonPoint(radius*Math.Cos((PI_2*i)/vertexCount),
radius*Math.Sin((PI_2*i)/vertexCount));
points[i] = point;
}
return new Polygon.Polygon(points);
}
}
}

View File

@@ -0,0 +1,110 @@
/* 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.
*/
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Poly2Tri.Triangulation;
using Poly2Tri.Triangulation.Delaunay;
using Poly2Tri.Triangulation.Delaunay.Sweep;
using Poly2Tri.Triangulation.Polygon;
using System.Linq;
namespace FarseerPhysics.Common.Decomposition
{
public static class CDTDecomposer
{
public static List<Vertices> ConvexPartition(Vertices vertices)
{
Polygon poly = new Polygon();
foreach (Vector2 vertex in vertices)
{
poly.Points.Add(new TriangulationPoint(vertex.X, vertex.Y));
}
DTSweepContext tcx = new DTSweepContext();
tcx.PrepareTriangulation(poly);
DTSweep.Triangulate(tcx);
List<Vertices> results = new List<Vertices>();
foreach (DelaunayTriangle triangle in poly.Triangles)
{
Vertices v = new Vertices();
foreach (TriangulationPoint p in triangle.Points)
{
v.Add(new Vector2((float)p.X, (float)p.Y));
}
results.Add(v);
}
return results;
}
public static List<Vertices> ConvexPartition(DetectedVertices vertices)
{
Polygon poly = new Polygon();
foreach (var vertex in vertices)
poly.Points.Add(new TriangulationPoint(vertex.X, vertex.Y));
if (vertices.Holes != null)
{
foreach (var holeVertices in vertices.Holes)
{
Polygon hole = new Polygon();
foreach (var vertex in holeVertices)
hole.Points.Add(new TriangulationPoint(vertex.X, vertex.Y));
poly.AddHole(hole);
}
}
DTSweepContext tcx = new DTSweepContext();
tcx.PrepareTriangulation(poly);
DTSweep.Triangulate(tcx);
List<Vertices> results = new List<Vertices>();
foreach (DelaunayTriangle triangle in poly.Triangles)
{
Vertices v = new Vertices();
foreach (TriangulationPoint p in triangle.Points)
{
v.Add(new Vector2((float)p.X, (float)p.Y));
}
results.Add(v);
}
return results;
}
}
}

View File

@@ -0,0 +1,691 @@
/*
* C# Version Ported by Matt Bettcher and Ian Qvist 2009-2010
*
* Original C++ Version Copyright (c) 2007 Eric Jordan
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
using System;
using System.Collections.Generic;
using FarseerPhysics.Common.PolygonManipulation;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.Decomposition
{
/// <summary>
/// Ported from jBox2D. Original author: ewjordan
/// Triangulates a polygon using simple ear-clipping algorithm.
///
/// Only works on simple polygons.
///
/// Triangles may be degenerate, especially if you have identical points
/// in the input to the algorithm. Check this before you use them.
/// </summary>
public static class EarclipDecomposer
{
//box2D rev 32 - for details, see http://www.box2d.org/forum/viewtopic.php?f=4&t=83&start=50
private const float Tol = .001f;
/// <summary>
/// Decomposes a non-convex polygon into a number of convex polygons, up
/// to maxPolys (remaining pieces are thrown out).
///
/// Each resulting polygon will have no more than Settings.MaxPolygonVertices
/// vertices.
///
/// Warning: Only works on simple polygons
/// </summary>
/// <param name="vertices">The vertices.</param>
/// <returns></returns>
public static List<Vertices> ConvexPartition(Vertices vertices)
{
return ConvexPartition(vertices, int.MaxValue, 0);
}
/// <summary>
/// Decomposes a non-convex polygon into a number of convex polygons, up
/// to maxPolys (remaining pieces are thrown out).
/// Each resulting polygon will have no more than Settings.MaxPolygonVertices
/// vertices.
/// Warning: Only works on simple polygons
/// </summary>
/// <param name="vertices">The vertices.</param>
/// <param name="maxPolys">The maximum number of polygons.</param>
/// <param name="tolerance">The tolerance.</param>
/// <returns></returns>
public static List<Vertices> ConvexPartition(Vertices vertices, int maxPolys, float tolerance)
{
if (vertices.Count < 3)
return new List<Vertices> { vertices };
/*
if (vertices.IsConvex() && vertices.Count <= Settings.MaxPolygonVertices)
{
if (vertices.IsCounterClockWise())
{
Vertices tempP = new Vertices(vertices);
tempP.Reverse();
tempP = SimplifyTools.CollinearSimplify(tempP);
tempP.ForceCounterClockWise();
return new List<Vertices> { tempP };
}
vertices = SimplifyTools.CollinearSimplify(vertices);
vertices.ForceCounterClockWise();
return new List<Vertices> { vertices };
}
*/
List<Triangle> triangulated;
if (vertices.IsCounterClockWise())
{
Vertices tempP = new Vertices(vertices);
tempP.Reverse();
triangulated = TriangulatePolygon(tempP);
}
else
{
triangulated = TriangulatePolygon(vertices);
}
if (triangulated.Count < 1)
{
//Still no luck? Oh well...
throw new Exception("Can't triangulate your polygon.");
}
List<Vertices> polygonizedTriangles = PolygonizeTriangles(triangulated, maxPolys, tolerance);
//The polygonized triangles are not guaranteed to be without collinear points. We remove
//them to be sure.
for (int i = 0; i < polygonizedTriangles.Count; i++)
{
polygonizedTriangles[i] = SimplifyTools.CollinearSimplify(polygonizedTriangles[i], 0);
}
//Remove empty vertice collections
for (int i = polygonizedTriangles.Count - 1; i >= 0; i--)
{
if (polygonizedTriangles[i].Count == 0)
polygonizedTriangles.RemoveAt(i);
}
return polygonizedTriangles;
}
/// <summary>
/// Turns a list of triangles into a list of convex polygons. Very simple
/// method - start with a seed triangle, keep adding triangles to it until
/// you can't add any more without making the polygon non-convex.
///
/// Returns an integer telling how many polygons were created. Will fill
/// polys array up to polysLength entries, which may be smaller or larger
/// than the return value.
///
/// Takes O(N///P) where P is the number of resultant polygons, N is triangle
/// count.
///
/// The final polygon list will not necessarily be minimal, though in
/// practice it works fairly well.
/// </summary>
/// <param name="triangulated">The triangulated.</param>
///<param name="maxPolys">The maximun number of polygons</param>
///<param name="tolerance">The tolerance</param>
///<returns></returns>
public static List<Vertices> PolygonizeTriangles(List<Triangle> triangulated, int maxPolys, float tolerance)
{
List<Vertices> polys = new List<Vertices>(50);
int polyIndex = 0;
if (triangulated.Count <= 0)
{
//return empty polygon list
return polys;
}
bool[] covered = new bool[triangulated.Count];
for (int i = 0; i < triangulated.Count; ++i)
{
covered[i] = false;
//Check here for degenerate triangles
if (((triangulated[i].X[0] == triangulated[i].X[1]) && (triangulated[i].Y[0] == triangulated[i].Y[1]))
||
((triangulated[i].X[1] == triangulated[i].X[2]) && (triangulated[i].Y[1] == triangulated[i].Y[2]))
||
((triangulated[i].X[0] == triangulated[i].X[2]) && (triangulated[i].Y[0] == triangulated[i].Y[2])))
{
covered[i] = true;
}
}
bool notDone = true;
while (notDone)
{
int currTri = -1;
for (int i = 0; i < triangulated.Count; ++i)
{
if (covered[i])
continue;
currTri = i;
break;
}
if (currTri == -1)
{
notDone = false;
}
else
{
Vertices poly = new Vertices(3);
for (int i = 0; i < 3; i++)
{
poly.Add(new Vector2(triangulated[currTri].X[i], triangulated[currTri].Y[i]));
}
covered[currTri] = true;
int index = 0;
for (int i = 0; i < 2 * triangulated.Count; ++i, ++index)
{
while (index >= triangulated.Count) index -= triangulated.Count;
if (covered[index])
{
continue;
}
Vertices newP = AddTriangle(triangulated[index], poly);
if (newP == null)
continue; // is this right
if (newP.Count > Settings.MaxPolygonVertices)
continue;
if (newP.IsConvex())
{
//Or should it be IsUsable? Maybe re-write IsConvex to apply the angle threshold from Box2d
poly = new Vertices(newP);
covered[index] = true;
}
}
//We have a maximum of polygons that we need to keep under.
if (polyIndex < maxPolys)
{
//SimplifyTools.MergeParallelEdges(poly, tolerance);
//If identical points are present, a triangle gets
//borked by the MergeParallelEdges function, hence
//the vertex number check
if (poly.Count >= 3)
polys.Add(new Vertices(poly));
//else
// printf("Skipping corrupt poly\n");
}
if (poly.Count >= 3)
polyIndex++; //Must be outside (polyIndex < polysLength) test
}
}
return polys;
}
/// <summary>
/// Triangulates a polygon using simple ear-clipping algorithm. Returns
/// size of Triangle array unless the polygon can't be triangulated.
/// This should only happen if the polygon self-intersects,
/// though it will not _always_ return null for a bad polygon - it is the
/// caller's responsibility to check for self-intersection, and if it
/// doesn't, it should at least check that the return value is non-null
/// before using. You're warned!
///
/// Triangles may be degenerate, especially if you have identical points
/// in the input to the algorithm. Check this before you use them.
///
/// This is totally unoptimized, so for large polygons it should not be part
/// of the simulation loop.
///
/// Warning: Only works on simple polygons.
/// </summary>
/// <returns></returns>
public static List<Triangle> TriangulatePolygon(Vertices vertices)
{
List<Triangle> results = new List<Triangle>();
if (vertices.Count < 3)
return new List<Triangle>();
//Recurse and split on pinch points
Vertices pA, pB;
Vertices pin = new Vertices(vertices);
if (ResolvePinchPoint(pin, out pA, out pB))
{
List<Triangle> mergeA = TriangulatePolygon(pA);
List<Triangle> mergeB = TriangulatePolygon(pB);
if (mergeA.Count == -1 || mergeB.Count == -1)
throw new Exception("Can't triangulate your polygon.");
for (int i = 0; i < mergeA.Count; ++i)
{
results.Add(new Triangle(mergeA[i]));
}
for (int i = 0; i < mergeB.Count; ++i)
{
results.Add(new Triangle(mergeB[i]));
}
return results;
}
Triangle[] buffer = new Triangle[vertices.Count - 2];
int bufferSize = 0;
float[] xrem = new float[vertices.Count];
float[] yrem = new float[vertices.Count];
for (int i = 0; i < vertices.Count; ++i)
{
xrem[i] = vertices[i].X;
yrem[i] = vertices[i].Y;
}
int vNum = vertices.Count;
while (vNum > 3)
{
// Find an ear
int earIndex = -1;
float earMaxMinCross = -10.0f;
for (int i = 0; i < vNum; ++i)
{
if (IsEar(i, xrem, yrem, vNum))
{
int lower = Remainder(i - 1, vNum);
int upper = Remainder(i + 1, vNum);
Vector2 d1 = new Vector2(xrem[upper] - xrem[i], yrem[upper] - yrem[i]);
Vector2 d2 = new Vector2(xrem[i] - xrem[lower], yrem[i] - yrem[lower]);
Vector2 d3 = new Vector2(xrem[lower] - xrem[upper], yrem[lower] - yrem[upper]);
d1.Normalize();
d2.Normalize();
d3.Normalize();
float cross12;
MathUtils.Cross(ref d1, ref d2, out cross12);
cross12 = Math.Abs(cross12);
float cross23;
MathUtils.Cross(ref d2, ref d3, out cross23);
cross23 = Math.Abs(cross23);
float cross31;
MathUtils.Cross(ref d3, ref d1, out cross31);
cross31 = Math.Abs(cross31);
//Find the maximum minimum angle
float minCross = Math.Min(cross12, Math.Min(cross23, cross31));
if (minCross > earMaxMinCross)
{
earIndex = i;
earMaxMinCross = minCross;
}
}
}
// If we still haven't found an ear, we're screwed.
// Note: sometimes this is happening because the
// remaining points are collinear. Really these
// should just be thrown out without halting triangulation.
if (earIndex == -1)
{
for (int i = 0; i < bufferSize; i++)
{
results.Add(new Triangle(buffer[i]));
}
return results;
}
// Clip off the ear:
// - remove the ear tip from the list
--vNum;
float[] newx = new float[vNum];
float[] newy = new float[vNum];
int currDest = 0;
for (int i = 0; i < vNum; ++i)
{
if (currDest == earIndex) ++currDest;
newx[i] = xrem[currDest];
newy[i] = yrem[currDest];
++currDest;
}
// - add the clipped triangle to the triangle list
int under = (earIndex == 0) ? (vNum) : (earIndex - 1);
int over = (earIndex == vNum) ? 0 : (earIndex + 1);
Triangle toAdd = new Triangle(xrem[earIndex], yrem[earIndex], xrem[over], yrem[over], xrem[under],
yrem[under]);
buffer[bufferSize] = toAdd;
++bufferSize;
// - replace the old list with the new one
xrem = newx;
yrem = newy;
}
Triangle tooAdd = new Triangle(xrem[1], yrem[1], xrem[2], yrem[2], xrem[0], yrem[0]);
buffer[bufferSize] = tooAdd;
++bufferSize;
for (int i = 0; i < bufferSize; i++)
{
results.Add(new Triangle(buffer[i]));
}
return results;
}
/// <summary>
/// Finds and fixes "pinch points," points where two polygon
/// vertices are at the same point.
///
/// If a pinch point is found, pin is broken up into poutA and poutB
/// and true is returned; otherwise, returns false.
///
/// Mostly for internal use.
///
/// O(N^2) time, which sucks...
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="poutA">The pout A.</param>
/// <param name="poutB">The pout B.</param>
/// <returns></returns>
private static bool ResolvePinchPoint(Vertices pin, out Vertices poutA, out Vertices poutB)
{
poutA = new Vertices();
poutB = new Vertices();
if (pin.Count < 3)
return false;
bool hasPinchPoint = false;
int pinchIndexA = -1;
int pinchIndexB = -1;
for (int i = 0; i < pin.Count; ++i)
{
for (int j = i + 1; j < pin.Count; ++j)
{
//Don't worry about pinch points where the points
//are actually just dupe neighbors
if (Math.Abs(pin[i].X - pin[j].X) < Tol && Math.Abs(pin[i].Y - pin[j].Y) < Tol && j != i + 1)
{
pinchIndexA = i;
pinchIndexB = j;
hasPinchPoint = true;
break;
}
}
if (hasPinchPoint) break;
}
if (hasPinchPoint)
{
int sizeA = pinchIndexB - pinchIndexA;
if (sizeA == pin.Count) return false; //has dupe points at wraparound, not a problem here
for (int i = 0; i < sizeA; ++i)
{
int ind = Remainder(pinchIndexA + i, pin.Count); // is this right
poutA.Add(pin[ind]);
}
int sizeB = pin.Count - sizeA;
for (int i = 0; i < sizeB; ++i)
{
int ind = Remainder(pinchIndexB + i, pin.Count); // is this right
poutB.Add(pin[ind]);
}
}
return hasPinchPoint;
}
/// <summary>
/// Fix for obnoxious behavior for the % operator for negative numbers...
/// </summary>
/// <param name="x">The x.</param>
/// <param name="modulus">The modulus.</param>
/// <returns></returns>
private static int Remainder(int x, int modulus)
{
int rem = x % modulus;
while (rem < 0)
{
rem += modulus;
}
return rem;
}
private static Vertices AddTriangle(Triangle t, Vertices vertices)
{
// First, find vertices that connect
int firstP = -1;
int firstT = -1;
int secondP = -1;
int secondT = -1;
for (int i = 0; i < vertices.Count; i++)
{
if (t.X[0] == vertices[i].X && t.Y[0] == vertices[i].Y)
{
if (firstP == -1)
{
firstP = i;
firstT = 0;
}
else
{
secondP = i;
secondT = 0;
}
}
else if (t.X[1] == vertices[i].X && t.Y[1] == vertices[i].Y)
{
if (firstP == -1)
{
firstP = i;
firstT = 1;
}
else
{
secondP = i;
secondT = 1;
}
}
else if (t.X[2] == vertices[i].X && t.Y[2] == vertices[i].Y)
{
if (firstP == -1)
{
firstP = i;
firstT = 2;
}
else
{
secondP = i;
secondT = 2;
}
}
}
// Fix ordering if first should be last vertex of poly
if (firstP == 0 && secondP == vertices.Count - 1)
{
firstP = vertices.Count - 1;
secondP = 0;
}
// Didn't find it
if (secondP == -1)
{
return null;
}
// Find tip index on triangle
int tipT = 0;
if (tipT == firstT || tipT == secondT)
tipT = 1;
if (tipT == firstT || tipT == secondT)
tipT = 2;
Vertices result = new Vertices(vertices.Count + 1);
for (int i = 0; i < vertices.Count; i++)
{
result.Add(vertices[i]);
if (i == firstP)
result.Add(new Vector2(t.X[tipT], t.Y[tipT]));
}
return result;
}
/// <summary>
/// Checks if vertex i is the tip of an ear in polygon defined by xv[] and
/// yv[].
///
/// Assumes clockwise orientation of polygon...ick
/// </summary>
/// <param name="i">The i.</param>
/// <param name="xv">The xv.</param>
/// <param name="yv">The yv.</param>
/// <param name="xvLength">Length of the xv.</param>
/// <returns>
/// <c>true</c> if the specified i is ear; otherwise, <c>false</c>.
/// </returns>
private static bool IsEar(int i, float[] xv, float[] yv, int xvLength)
{
float dx0, dy0, dx1, dy1;
if (i >= xvLength || i < 0 || xvLength < 3)
{
return false;
}
int upper = i + 1;
int lower = i - 1;
if (i == 0)
{
dx0 = xv[0] - xv[xvLength - 1];
dy0 = yv[0] - yv[xvLength - 1];
dx1 = xv[1] - xv[0];
dy1 = yv[1] - yv[0];
lower = xvLength - 1;
}
else if (i == xvLength - 1)
{
dx0 = xv[i] - xv[i - 1];
dy0 = yv[i] - yv[i - 1];
dx1 = xv[0] - xv[i];
dy1 = yv[0] - yv[i];
upper = 0;
}
else
{
dx0 = xv[i] - xv[i - 1];
dy0 = yv[i] - yv[i - 1];
dx1 = xv[i + 1] - xv[i];
dy1 = yv[i + 1] - yv[i];
}
float cross = dx0 * dy1 - dx1 * dy0;
if (cross > 0)
return false;
Triangle myTri = new Triangle(xv[i], yv[i], xv[upper], yv[upper],
xv[lower], yv[lower]);
for (int j = 0; j < xvLength; ++j)
{
if (j == i || j == lower || j == upper)
continue;
if (myTri.IsInside(xv[j], yv[j]))
return false;
}
return true;
}
}
public class Triangle
{
public float[] X;
public float[] Y;
//Constructor automatically fixes orientation to ccw
public Triangle(float x1, float y1, float x2, float y2, float x3, float y3)
{
X = new float[3];
Y = new float[3];
float dx1 = x2 - x1;
float dx2 = x3 - x1;
float dy1 = y2 - y1;
float dy2 = y3 - y1;
float cross = dx1 * dy2 - dx2 * dy1;
bool ccw = (cross > 0);
if (ccw)
{
X[0] = x1;
X[1] = x2;
X[2] = x3;
Y[0] = y1;
Y[1] = y2;
Y[2] = y3;
}
else
{
X[0] = x1;
X[1] = x3;
X[2] = x2;
Y[0] = y1;
Y[1] = y3;
Y[2] = y2;
}
}
public Triangle(Triangle t)
{
X = new float[3];
Y = new float[3];
X[0] = t.X[0];
X[1] = t.X[1];
X[2] = t.X[2];
Y[0] = t.Y[0];
Y[1] = t.Y[1];
Y[2] = t.Y[2];
}
public bool IsInside(float x, float y)
{
if (x < X[0] && x < X[1] && x < X[2]) return false;
if (x > X[0] && x > X[1] && x > X[2]) return false;
if (y < Y[0] && y < Y[1] && y < Y[2]) return false;
if (y > Y[0] && y > Y[1] && y > Y[2]) return false;
float vx2 = x - X[0];
float vy2 = y - Y[0];
float vx1 = X[1] - X[0];
float vy1 = Y[1] - Y[0];
float vx0 = X[2] - X[0];
float vy0 = Y[2] - Y[0];
float dot00 = vx0 * vx0 + vy0 * vy0;
float dot01 = vx0 * vx1 + vy0 * vy1;
float dot02 = vx0 * vx2 + vy0 * vy2;
float dot11 = vx1 * vx1 + vy1 * vy1;
float dot12 = vx1 * vx2 + vy1 * vy2;
float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
return ((u > 0) && (v > 0) && (u + v < 1));
}
}
}

View File

@@ -0,0 +1,160 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.Decomposition
{
// Original code can be found here: http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
/// <summary>
/// Triangulates a polygon into triangles.
/// Doesn't handle holes.
/// </summary>
public static class FlipcodeDecomposer
{
private static Vector2 _tmpA;
private static Vector2 _tmpB;
private static Vector2 _tmpC;
/// <summary>
/// Check if the point P is inside the triangle defined by
/// the points A, B, C
/// </summary>
/// <param name="a">The A point.</param>
/// <param name="b">The B point.</param>
/// <param name="c">The C point.</param>
/// <param name="p">The point to be tested.</param>
/// <returns>True if the point is inside the triangle</returns>
private static bool InsideTriangle(ref Vector2 a, ref Vector2 b, ref Vector2 c, ref Vector2 p)
{
//A cross bp
float abp = (c.X - b.X) * (p.Y - b.Y) - (c.Y - b.Y) * (p.X - b.X);
//A cross ap
float aap = (b.X - a.X) * (p.Y - a.Y) - (b.Y - a.Y) * (p.X - a.X);
//b cross cp
float bcp = (a.X - c.X) * (p.Y - c.Y) - (a.Y - c.Y) * (p.X - c.X);
return ((abp >= 0.0f) && (bcp >= 0.0f) && (aap >= 0.0f));
}
/// <summary>
/// Cut a the contour and add a triangle into V to describe the
/// location of the cut
/// </summary>
/// <param name="contour">The list of points defining the polygon</param>
/// <param name="u">The index of the first point</param>
/// <param name="v">The index of the second point</param>
/// <param name="w">The index of the third point</param>
/// <param name="n">The number of elements in the array.</param>
/// <param name="V">The array to populate with indicies of triangles.</param>
/// <returns>True if a triangle was found</returns>
private static bool Snip(Vertices contour, int u, int v, int w, int n,
int[] V)
{
if (Settings.Epsilon > MathUtils.Area(ref _tmpA, ref _tmpB, ref _tmpC))
{
return false;
}
for (int p = 0; p < n; p++)
{
if ((p == u) || (p == v) || (p == w))
{
continue;
}
Vector2 point = contour[V[p]];
if (InsideTriangle(ref _tmpA, ref _tmpB, ref _tmpC, ref point))
{
return false;
}
}
return true;
}
/// <summary>
/// Decompose the polygon into triangles
/// </summary>
/// <param name="contour">The list of points describing the polygon</param>
/// <returns></returns>
public static List<Vertices> ConvexPartition(Vertices contour)
{
int n = contour.Count;
if (n < 3)
return new List<Vertices>();
int[] V = new int[n];
// We want a counter-clockwise polygon in V
if (contour.IsCounterClockWise())
{
for (int v = 0; v < n; v++)
V[v] = v;
}
else
{
for (int v = 0; v < n; v++)
V[v] = (n - 1) - v;
}
int nv = n;
// Remove nv-2 Vertices, creating 1 triangle every time
int count = 2 * nv; /* error detection */
List<Vertices> result = new List<Vertices>();
for (int v = nv - 1; nv > 2; )
{
// If we loop, it is probably a non-simple polygon
if (0 >= (count--))
{
// Triangulate: ERROR - probable bad polygon!
return new List<Vertices>();
}
// Three consecutive vertices in current polygon, <u,v,w>
int u = v;
if (nv <= u)
u = 0; // Previous
v = u + 1;
if (nv <= v)
v = 0; // New v
int w = v + 1;
if (nv <= w)
w = 0; // Next
_tmpA = contour[V[u]];
_tmpB = contour[V[v]];
_tmpC = contour[V[w]];
if (Snip(contour, u, v, w, nv, V))
{
int s, t;
// Output Triangle
Vertices triangle = new Vertices(3);
triangle.Add(_tmpA);
triangle.Add(_tmpB);
triangle.Add(_tmpC);
result.Add(triangle);
// Remove v from remaining polygon
for (s = v, t = v + 1; t < nv; s++, t++)
{
V[s] = V[t];
}
nv--;
// Reset error detection counter
count = 2 * nv;
}
}
return result;
}
}
}

File diff suppressed because it is too large Load Diff

227
axios/Common/FixedArray.cs Normal file
View File

@@ -0,0 +1,227 @@
/*
* Farseer Physics Engine based on Box2D.XNA port:
* Copyright (c) 2010 Ian Qvist
*
* Box2D.XNA port of Box2D:
* Copyright (c) 2009 Brandon Furtwangler, Nathan Furtwangler
*
* Original source Box2D:
* Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
using System;
namespace FarseerPhysics.Common
{
public struct FixedArray2<T>
{
private T _value0;
private T _value1;
public T this[int index]
{
get
{
switch (index)
{
case 0:
return _value0;
case 1:
return _value1;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
_value0 = value;
break;
case 1:
_value1 = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
}
}
public struct FixedArray3<T>
{
private T _value0;
private T _value1;
private T _value2;
public T this[int index]
{
get
{
switch (index)
{
case 0:
return _value0;
case 1:
return _value1;
case 2:
return _value2;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
_value0 = value;
break;
case 1:
_value1 = value;
break;
case 2:
_value2 = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
}
}
public struct FixedArray4<T>
{
private T _value0;
private T _value1;
private T _value2;
private T _value3;
public T this[int index]
{
get
{
switch (index)
{
case 0:
return _value0;
case 1:
return _value1;
case 2:
return _value2;
case 3:
return _value3;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
_value0 = value;
break;
case 1:
_value1 = value;
break;
case 2:
_value2 = value;
break;
case 3:
_value3 = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
}
}
public struct FixedArray8<T>
{
private T _value0;
private T _value1;
private T _value2;
private T _value3;
private T _value4;
private T _value5;
private T _value6;
private T _value7;
public T this[int index]
{
get
{
switch (index)
{
case 0:
return _value0;
case 1:
return _value1;
case 2:
return _value2;
case 3:
return _value3;
case 4:
return _value4;
case 5:
return _value5;
case 6:
return _value6;
case 7:
return _value7;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
_value0 = value;
break;
case 1:
_value1 = value;
break;
case 2:
_value2 = value;
break;
case 3:
_value3 = value;
break;
case 4:
_value4 = value;
break;
case 5:
_value5 = value;
break;
case 6:
_value6 = value;
break;
case 7:
_value7 = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
}
}
}

81
axios/Common/HashSet.cs Normal file
View File

@@ -0,0 +1,81 @@
#if WINDOWS_PHONE || XBOX
//TODO: FIX
using System;
using System.Collections;
using System.Collections.Generic;
namespace FarseerPhysics.Common
{
public class HashSet<T> : ICollection<T>
{
private Dictionary<T, short> _dict;
public HashSet(int capacity)
{
_dict = new Dictionary<T, short>(capacity);
}
public HashSet()
{
_dict = new Dictionary<T, short>();
}
// Methods
#region ICollection<T> Members
public void Add(T item)
{
// We don't care for the value in dictionary, Keys matter.
_dict.Add(item, 0);
}
public void Clear()
{
_dict.Clear();
}
public bool Contains(T item)
{
return _dict.ContainsKey(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public bool Remove(T item)
{
return _dict.Remove(item);
}
public IEnumerator<T> GetEnumerator()
{
return _dict.Keys.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _dict.Keys.GetEnumerator();
}
// Properties
public int Count
{
get { return _dict.Keys.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
#endregion
}
}
#endif

308
axios/Common/LineTools.cs Normal file
View File

@@ -0,0 +1,308 @@
using System;
using System.Collections.Generic;
using FarseerPhysics.Collision;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common
{
/// <summary>
/// Collection of helper methods for misc collisions.
/// Does float tolerance and line collisions with lines and AABBs.
/// </summary>
public static class LineTools
{
public static float DistanceBetweenPointAndPoint(ref Vector2 point1, ref Vector2 point2)
{
Vector2 v;
Vector2.Subtract(ref point1, ref point2, out v);
return v.Length();
}
public static float DistanceBetweenPointAndLineSegment(ref Vector2 point, ref Vector2 lineEndPoint1,
ref Vector2 lineEndPoint2)
{
Vector2 v = Vector2.Subtract(lineEndPoint2, lineEndPoint1);
Vector2 w = Vector2.Subtract(point, lineEndPoint1);
float c1 = Vector2.Dot(w, v);
if (c1 <= 0) return DistanceBetweenPointAndPoint(ref point, ref lineEndPoint1);
float c2 = Vector2.Dot(v, v);
if (c2 <= c1) return DistanceBetweenPointAndPoint(ref point, ref lineEndPoint2);
float b = c1 / c2;
Vector2 pointOnLine = Vector2.Add(lineEndPoint1, Vector2.Multiply(v, b));
return DistanceBetweenPointAndPoint(ref point, ref pointOnLine);
}
// From Eric Jordan's convex decomposition library
/// <summary>
///Check if the lines a0->a1 and b0->b1 cross.
///If they do, intersectionPoint will be filled
///with the point of crossing.
///
///Grazing lines should not return true.
///
/// </summary>
/// <param name="a0"></param>
/// <param name="a1"></param>
/// <param name="b0"></param>
/// <param name="b1"></param>
/// <param name="intersectionPoint"></param>
/// <returns></returns>
public static bool LineIntersect2(Vector2 a0, Vector2 a1, Vector2 b0, Vector2 b1, out Vector2 intersectionPoint)
{
intersectionPoint = Vector2.Zero;
if (a0 == b0 || a0 == b1 || a1 == b0 || a1 == b1)
return false;
float x1 = a0.X;
float y1 = a0.Y;
float x2 = a1.X;
float y2 = a1.Y;
float x3 = b0.X;
float y3 = b0.Y;
float x4 = b1.X;
float y4 = b1.Y;
//AABB early exit
if (Math.Max(x1, x2) < Math.Min(x3, x4) || Math.Max(x3, x4) < Math.Min(x1, x2))
return false;
if (Math.Max(y1, y2) < Math.Min(y3, y4) || Math.Max(y3, y4) < Math.Min(y1, y2))
return false;
float ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3));
float ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3));
float denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
if (Math.Abs(denom) < Settings.Epsilon)
{
//Lines are too close to parallel to call
return false;
}
ua /= denom;
ub /= denom;
if ((0 < ua) && (ua < 1) && (0 < ub) && (ub < 1))
{
intersectionPoint.X = (x1 + ua * (x2 - x1));
intersectionPoint.Y = (y1 + ua * (y2 - y1));
return true;
}
return false;
}
//From Mark Bayazit's convex decomposition algorithm
public static Vector2 LineIntersect(Vector2 p1, Vector2 p2, Vector2 q1, Vector2 q2)
{
Vector2 i = Vector2.Zero;
float a1 = p2.Y - p1.Y;
float b1 = p1.X - p2.X;
float c1 = a1 * p1.X + b1 * p1.Y;
float a2 = q2.Y - q1.Y;
float b2 = q1.X - q2.X;
float c2 = a2 * q1.X + b2 * q1.Y;
float det = a1 * b2 - a2 * b1;
if (!MathUtils.FloatEquals(det, 0))
{
// lines are not parallel
i.X = (b2 * c1 - b1 * c2) / det;
i.Y = (a1 * c2 - a2 * c1) / det;
}
return i;
}
/// <summary>
/// This method detects if two line segments (or lines) intersect,
/// and, if so, the point of intersection. Use the <paramref name="firstIsSegment"/> and
/// <paramref name="secondIsSegment"/> parameters to set whether the intersection point
/// must be on the first and second line segments. Setting these
/// both to true means you are doing a line-segment to line-segment
/// intersection. Setting one of them to true means you are doing a
/// line to line-segment intersection test, and so on.
/// Note: If two line segments are coincident, then
/// no intersection is detected (there are actually
/// infinite intersection points).
/// Author: Jeremy Bell
/// </summary>
/// <param name="point1">The first point of the first line segment.</param>
/// <param name="point2">The second point of the first line segment.</param>
/// <param name="point3">The first point of the second line segment.</param>
/// <param name="point4">The second point of the second line segment.</param>
/// <param name="point">This is set to the intersection
/// point if an intersection is detected.</param>
/// <param name="firstIsSegment">Set this to true to require that the
/// intersection point be on the first line segment.</param>
/// <param name="secondIsSegment">Set this to true to require that the
/// intersection point be on the second line segment.</param>
/// <returns>True if an intersection is detected, false otherwise.</returns>
public static bool LineIntersect(ref Vector2 point1, ref Vector2 point2, ref Vector2 point3, ref Vector2 point4,
bool firstIsSegment, bool secondIsSegment,
out Vector2 point)
{
point = new Vector2();
// these are reused later.
// each lettered sub-calculation is used twice, except
// for b and d, which are used 3 times
float a = point4.Y - point3.Y;
float b = point2.X - point1.X;
float c = point4.X - point3.X;
float d = point2.Y - point1.Y;
// denominator to solution of linear system
float denom = (a * b) - (c * d);
// if denominator is 0, then lines are parallel
if (!(denom >= -Settings.Epsilon && denom <= Settings.Epsilon))
{
float e = point1.Y - point3.Y;
float f = point1.X - point3.X;
float oneOverDenom = 1.0f / denom;
// numerator of first equation
float ua = (c * e) - (a * f);
ua *= oneOverDenom;
// check if intersection point of the two lines is on line segment 1
if (!firstIsSegment || ua >= 0.0f && ua <= 1.0f)
{
// numerator of second equation
float ub = (b * e) - (d * f);
ub *= oneOverDenom;
// check if intersection point of the two lines is on line segment 2
// means the line segments intersect, since we know it is on
// segment 1 as well.
if (!secondIsSegment || ub >= 0.0f && ub <= 1.0f)
{
// check if they are coincident (no collision in this case)
if (ua != 0f || ub != 0f)
{
//There is an intersection
point.X = point1.X + ua * b;
point.Y = point1.Y + ua * d;
return true;
}
}
}
}
return false;
}
/// <summary>
/// This method detects if two line segments (or lines) intersect,
/// and, if so, the point of intersection. Use the <paramref name="firstIsSegment"/> and
/// <paramref name="secondIsSegment"/> parameters to set whether the intersection point
/// must be on the first and second line segments. Setting these
/// both to true means you are doing a line-segment to line-segment
/// intersection. Setting one of them to true means you are doing a
/// line to line-segment intersection test, and so on.
/// Note: If two line segments are coincident, then
/// no intersection is detected (there are actually
/// infinite intersection points).
/// Author: Jeremy Bell
/// </summary>
/// <param name="point1">The first point of the first line segment.</param>
/// <param name="point2">The second point of the first line segment.</param>
/// <param name="point3">The first point of the second line segment.</param>
/// <param name="point4">The second point of the second line segment.</param>
/// <param name="intersectionPoint">This is set to the intersection
/// point if an intersection is detected.</param>
/// <param name="firstIsSegment">Set this to true to require that the
/// intersection point be on the first line segment.</param>
/// <param name="secondIsSegment">Set this to true to require that the
/// intersection point be on the second line segment.</param>
/// <returns>True if an intersection is detected, false otherwise.</returns>
public static bool LineIntersect(Vector2 point1, Vector2 point2, Vector2 point3, Vector2 point4,
bool firstIsSegment,
bool secondIsSegment, out Vector2 intersectionPoint)
{
return LineIntersect(ref point1, ref point2, ref point3, ref point4, firstIsSegment, secondIsSegment,
out intersectionPoint);
}
/// <summary>
/// This method detects if two line segments intersect,
/// and, if so, the point of intersection.
/// Note: If two line segments are coincident, then
/// no intersection is detected (there are actually
/// infinite intersection points).
/// </summary>
/// <param name="point1">The first point of the first line segment.</param>
/// <param name="point2">The second point of the first line segment.</param>
/// <param name="point3">The first point of the second line segment.</param>
/// <param name="point4">The second point of the second line segment.</param>
/// <param name="intersectionPoint">This is set to the intersection
/// point if an intersection is detected.</param>
/// <returns>True if an intersection is detected, false otherwise.</returns>
public static bool LineIntersect(ref Vector2 point1, ref Vector2 point2, ref Vector2 point3, ref Vector2 point4,
out Vector2 intersectionPoint)
{
return LineIntersect(ref point1, ref point2, ref point3, ref point4, true, true, out intersectionPoint);
}
/// <summary>
/// This method detects if two line segments intersect,
/// and, if so, the point of intersection.
/// Note: If two line segments are coincident, then
/// no intersection is detected (there are actually
/// infinite intersection points).
/// </summary>
/// <param name="point1">The first point of the first line segment.</param>
/// <param name="point2">The second point of the first line segment.</param>
/// <param name="point3">The first point of the second line segment.</param>
/// <param name="point4">The second point of the second line segment.</param>
/// <param name="intersectionPoint">This is set to the intersection
/// point if an intersection is detected.</param>
/// <returns>True if an intersection is detected, false otherwise.</returns>
public static bool LineIntersect(Vector2 point1, Vector2 point2, Vector2 point3, Vector2 point4,
out Vector2 intersectionPoint)
{
return LineIntersect(ref point1, ref point2, ref point3, ref point4, true, true, out intersectionPoint);
}
/// <summary>
/// Get all intersections between a line segment and a list of vertices
/// representing a polygon. The vertices reuse adjacent points, so for example
/// edges one and two are between the first and second vertices and between the
/// second and third vertices. The last edge is between vertex vertices.Count - 1
/// and verts0. (ie, vertices from a Geometry or AABB)
/// </summary>
/// <param name="point1">The first point of the line segment to test</param>
/// <param name="point2">The second point of the line segment to test.</param>
/// <param name="vertices">The vertices, as described above</param>
/// <param name="intersectionPoints">An list of intersection points. Any intersection points
/// found will be added to this list.</param>
public static void LineSegmentVerticesIntersect(ref Vector2 point1, ref Vector2 point2, Vertices vertices,
ref List<Vector2> intersectionPoints)
{
for (int i = 0; i < vertices.Count; i++)
{
Vector2 point;
if (LineIntersect(vertices[i], vertices[vertices.NextIndex(i)],
point1, point2, true, true, out point))
{
intersectionPoints.Add(point);
}
}
}
/// <summary>
/// Get all intersections between a line segment and an AABB.
/// </summary>
/// <param name="point1">The first point of the line segment to test</param>
/// <param name="point2">The second point of the line segment to test.</param>
/// <param name="aabb">The AABB that is used for testing intersection.</param>
/// <param name="intersectionPoints">An list of intersection points. Any intersection points found will be added to this list.</param>
public static void LineSegmentAABBIntersect(ref Vector2 point1, ref Vector2 point2, AABB aabb,
ref List<Vector2> intersectionPoints)
{
LineSegmentVerticesIntersect(ref point1, ref point2, aabb.Vertices, ref intersectionPoints);
}
}
}

638
axios/Common/Math.cs Normal file
View File

@@ -0,0 +1,638 @@
/*
* Farseer Physics Engine based on Box2D.XNA port:
* Copyright (c) 2010 Ian Qvist
*
* Box2D.XNA port of Box2D:
* Copyright (c) 2009 Brandon Furtwangler, Nathan Furtwangler
*
* Original source Box2D:
* Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common
{
public static class MathUtils
{
public static float Cross(Vector2 a, Vector2 b)
{
return a.X * b.Y - a.Y * b.X;
}
public static Vector2 Cross(Vector2 a, float s)
{
return new Vector2(s * a.Y, -s * a.X);
}
public static Vector2 Cross(float s, Vector2 a)
{
return new Vector2(-s * a.Y, s * a.X);
}
public static Vector2 Abs(Vector2 v)
{
return new Vector2(Math.Abs(v.X), Math.Abs(v.Y));
}
public static Vector2 Multiply(ref Mat22 A, Vector2 v)
{
return Multiply(ref A, ref v);
}
public static Vector2 Multiply(ref Mat22 A, ref Vector2 v)
{
return new Vector2(A.Col1.X * v.X + A.Col2.X * v.Y, A.Col1.Y * v.X + A.Col2.Y * v.Y);
}
public static Vector2 MultiplyT(ref Mat22 A, Vector2 v)
{
return MultiplyT(ref A, ref v);
}
public static Vector2 MultiplyT(ref Mat22 A, ref Vector2 v)
{
return new Vector2(v.X * A.Col1.X + v.Y * A.Col1.Y, v.X * A.Col2.X + v.Y * A.Col2.Y);
}
public static Vector2 Multiply(ref Transform T, Vector2 v)
{
return Multiply(ref T, ref v);
}
public static Vector2 Multiply(ref Transform T, ref Vector2 v)
{
return new Vector2(T.Position.X + T.R.Col1.X * v.X + T.R.Col2.X * v.Y,
T.Position.Y + T.R.Col1.Y * v.X + T.R.Col2.Y * v.Y);
}
public static Vector2 MultiplyT(ref Transform T, Vector2 v)
{
return MultiplyT(ref T, ref v);
}
public static Vector2 MultiplyT(ref Transform T, ref Vector2 v)
{
Vector2 tmp = Vector2.Zero;
tmp.X = v.X - T.Position.X;
tmp.Y = v.Y - T.Position.Y;
return MultiplyT(ref T.R, ref tmp);
}
// A^T * B
public static void MultiplyT(ref Mat22 A, ref Mat22 B, out Mat22 C)
{
C = new Mat22();
C.Col1.X = A.Col1.X * B.Col1.X + A.Col1.Y * B.Col1.Y;
C.Col1.Y = A.Col2.X * B.Col1.X + A.Col2.Y * B.Col1.Y;
C.Col2.X = A.Col1.X * B.Col2.X + A.Col1.Y * B.Col2.Y;
C.Col2.Y = A.Col2.X * B.Col2.X + A.Col2.Y * B.Col2.Y;
}
// v2 = A.R' * (B.R * v1 + B.p - A.p) = (A.R' * B.R) * v1 + (B.p - A.p)
public static void MultiplyT(ref Transform A, ref Transform B, out Transform C)
{
C = new Transform();
MultiplyT(ref A.R, ref B.R, out C.R);
C.Position.X = B.Position.X - A.Position.X;
C.Position.Y = B.Position.Y - A.Position.Y;
}
public static void Swap<T>(ref T a, ref T b)
{
T tmp = a;
a = b;
b = tmp;
}
/// <summary>
/// This function is used to ensure that a floating point number is
/// not a NaN or infinity.
/// </summary>
/// <param name="x">The x.</param>
/// <returns>
/// <c>true</c> if the specified x is valid; otherwise, <c>false</c>.
/// </returns>
public static bool IsValid(float x)
{
if (float.IsNaN(x))
{
// NaN.
return false;
}
return !float.IsInfinity(x);
}
public static bool IsValid(this Vector2 x)
{
return IsValid(x.X) && IsValid(x.Y);
}
/// <summary>
/// This is a approximate yet fast inverse square-root.
/// </summary>
/// <param name="x">The x.</param>
/// <returns></returns>
public static float InvSqrt(float x)
{
FloatConverter convert = new FloatConverter();
convert.x = x;
float xhalf = 0.5f * x;
convert.i = 0x5f3759df - (convert.i >> 1);
x = convert.x;
x = x * (1.5f - xhalf * x * x);
return x;
}
public static int Clamp(int a, int low, int high)
{
return Math.Max(low, Math.Min(a, high));
}
public static float Clamp(float a, float low, float high)
{
return Math.Max(low, Math.Min(a, high));
}
public static Vector2 Clamp(Vector2 a, Vector2 low, Vector2 high)
{
return Vector2.Max(low, Vector2.Min(a, high));
}
public static void Cross(ref Vector2 a, ref Vector2 b, out float c)
{
c = a.X * b.Y - a.Y * b.X;
}
/// <summary>
/// Return the angle between two vectors on a plane
/// The angle is from vector 1 to vector 2, positive anticlockwise
/// The result is between -pi -> pi
/// </summary>
public static double VectorAngle(ref Vector2 p1, ref Vector2 p2)
{
double theta1 = Math.Atan2(p1.Y, p1.X);
double theta2 = Math.Atan2(p2.Y, p2.X);
double dtheta = theta2 - theta1;
while (dtheta > Math.PI)
dtheta -= (2 * Math.PI);
while (dtheta < -Math.PI)
dtheta += (2 * Math.PI);
return (dtheta);
}
public static double VectorAngle(Vector2 p1, Vector2 p2)
{
return VectorAngle(ref p1, ref p2);
}
/// <summary>
/// Returns a positive number if c is to the left of the line going from a to b.
/// </summary>
/// <returns>Positive number if point is left, negative if point is right,
/// and 0 if points are collinear.</returns>
public static float Area(Vector2 a, Vector2 b, Vector2 c)
{
return Area(ref a, ref b, ref c);
}
/// <summary>
/// Returns a positive number if c is to the left of the line going from a to b.
/// </summary>
/// <returns>Positive number if point is left, negative if point is right,
/// and 0 if points are collinear.</returns>
public static float Area(ref Vector2 a, ref Vector2 b, ref Vector2 c)
{
return a.X * (b.Y - c.Y) + b.X * (c.Y - a.Y) + c.X * (a.Y - b.Y);
}
/// <summary>
/// Determines if three vertices are collinear (ie. on a straight line)
/// </summary>
/// <param name="a">First vertex</param>
/// <param name="b">Second vertex</param>
/// <param name="c">Third vertex</param>
/// <returns></returns>
public static bool Collinear(ref Vector2 a, ref Vector2 b, ref Vector2 c)
{
return Collinear(ref a, ref b, ref c, 0);
}
public static bool Collinear(ref Vector2 a, ref Vector2 b, ref Vector2 c, float tolerance)
{
return FloatInRange(Area(ref a, ref b, ref c), -tolerance, tolerance);
}
public static void Cross(float s, ref Vector2 a, out Vector2 b)
{
b = new Vector2(-s * a.Y, s * a.X);
}
public static bool FloatEquals(float value1, float value2)
{
return Math.Abs(value1 - value2) <= Settings.Epsilon;
}
/// <summary>
/// Checks if a floating point Value is equal to another,
/// within a certain tolerance.
/// </summary>
/// <param name="value1">The first floating point Value.</param>
/// <param name="value2">The second floating point Value.</param>
/// <param name="delta">The floating point tolerance.</param>
/// <returns>True if the values are "equal", false otherwise.</returns>
public static bool FloatEquals(float value1, float value2, float delta)
{
return FloatInRange(value1, value2 - delta, value2 + delta);
}
/// <summary>
/// Checks if a floating point Value is within a specified
/// range of values (inclusive).
/// </summary>
/// <param name="value">The Value to check.</param>
/// <param name="min">The minimum Value.</param>
/// <param name="max">The maximum Value.</param>
/// <returns>True if the Value is within the range specified,
/// false otherwise.</returns>
public static bool FloatInRange(float value, float min, float max)
{
return (value >= min && value <= max);
}
#region Nested type: FloatConverter
[StructLayout(LayoutKind.Explicit)]
private struct FloatConverter
{
[FieldOffset(0)]
public float x;
[FieldOffset(0)]
public int i;
}
#endregion
}
/// <summary>
/// A 2-by-2 matrix. Stored in column-major order.
/// </summary>
public struct Mat22
{
public Vector2 Col1, Col2;
/// <summary>
/// Construct this matrix using columns.
/// </summary>
/// <param name="c1">The c1.</param>
/// <param name="c2">The c2.</param>
public Mat22(Vector2 c1, Vector2 c2)
{
Col1 = c1;
Col2 = c2;
}
/// <summary>
/// Construct this matrix using scalars.
/// </summary>
/// <param name="a11">The a11.</param>
/// <param name="a12">The a12.</param>
/// <param name="a21">The a21.</param>
/// <param name="a22">The a22.</param>
public Mat22(float a11, float a12, float a21, float a22)
{
Col1 = new Vector2(a11, a21);
Col2 = new Vector2(a12, a22);
}
/// <summary>
/// Construct this matrix using an angle. This matrix becomes
/// an orthonormal rotation matrix.
/// </summary>
/// <param name="angle">The angle.</param>
public Mat22(float angle)
{
// TODO_ERIN compute sin+cos together.
float c = (float)Math.Cos(angle), s = (float)Math.Sin(angle);
Col1 = new Vector2(c, s);
Col2 = new Vector2(-s, c);
}
/// <summary>
/// Extract the angle from this matrix (assumed to be
/// a rotation matrix).
/// </summary>
/// <value></value>
public float Angle
{
get { return (float)Math.Atan2(Col1.Y, Col1.X); }
}
public Mat22 Inverse
{
get
{
float a = Col1.X, b = Col2.X, c = Col1.Y, d = Col2.Y;
float det = a * d - b * c;
if (det != 0.0f)
{
det = 1.0f / det;
}
Mat22 result = new Mat22();
result.Col1.X = det * d;
result.Col1.Y = -det * c;
result.Col2.X = -det * b;
result.Col2.Y = det * a;
return result;
}
}
/// <summary>
/// Initialize this matrix using columns.
/// </summary>
/// <param name="c1">The c1.</param>
/// <param name="c2">The c2.</param>
public void Set(Vector2 c1, Vector2 c2)
{
Col1 = c1;
Col2 = c2;
}
/// <summary>
/// Initialize this matrix using an angle. This matrix becomes
/// an orthonormal rotation matrix.
/// </summary>
/// <param name="angle">The angle.</param>
public void Set(float angle)
{
float c = (float)Math.Cos(angle), s = (float)Math.Sin(angle);
Col1.X = c;
Col2.X = -s;
Col1.Y = s;
Col2.Y = c;
}
/// <summary>
/// Set this to the identity matrix.
/// </summary>
public void SetIdentity()
{
Col1.X = 1.0f;
Col2.X = 0.0f;
Col1.Y = 0.0f;
Col2.Y = 1.0f;
}
/// <summary>
/// Set this matrix to all zeros.
/// </summary>
public void SetZero()
{
Col1.X = 0.0f;
Col2.X = 0.0f;
Col1.Y = 0.0f;
Col2.Y = 0.0f;
}
/// <summary>
/// Solve A * x = b, where b is a column vector. This is more efficient
/// than computing the inverse in one-shot cases.
/// </summary>
/// <param name="b">The b.</param>
/// <returns></returns>
public Vector2 Solve(Vector2 b)
{
float a11 = Col1.X, a12 = Col2.X, a21 = Col1.Y, a22 = Col2.Y;
float det = a11 * a22 - a12 * a21;
if (det != 0.0f)
{
det = 1.0f / det;
}
return new Vector2(det * (a22 * b.X - a12 * b.Y), det * (a11 * b.Y - a21 * b.X));
}
public static void Add(ref Mat22 A, ref Mat22 B, out Mat22 R)
{
R.Col1 = A.Col1 + B.Col1;
R.Col2 = A.Col2 + B.Col2;
}
}
/// <summary>
/// A 3-by-3 matrix. Stored in column-major order.
/// </summary>
public struct Mat33
{
public Vector3 Col1, Col2, Col3;
/// <summary>
/// Construct this matrix using columns.
/// </summary>
/// <param name="c1">The c1.</param>
/// <param name="c2">The c2.</param>
/// <param name="c3">The c3.</param>
public Mat33(Vector3 c1, Vector3 c2, Vector3 c3)
{
Col1 = c1;
Col2 = c2;
Col3 = c3;
}
/// <summary>
/// Set this matrix to all zeros.
/// </summary>
public void SetZero()
{
Col1 = Vector3.Zero;
Col2 = Vector3.Zero;
Col3 = Vector3.Zero;
}
/// <summary>
/// Solve A * x = b, where b is a column vector. This is more efficient
/// than computing the inverse in one-shot cases.
/// </summary>
/// <param name="b">The b.</param>
/// <returns></returns>
public Vector3 Solve33(Vector3 b)
{
float det = Vector3.Dot(Col1, Vector3.Cross(Col2, Col3));
if (det != 0.0f)
{
det = 1.0f / det;
}
return new Vector3(det * Vector3.Dot(b, Vector3.Cross(Col2, Col3)),
det * Vector3.Dot(Col1, Vector3.Cross(b, Col3)),
det * Vector3.Dot(Col1, Vector3.Cross(Col2, b)));
}
/// <summary>
/// Solve A * x = b, where b is a column vector. This is more efficient
/// than computing the inverse in one-shot cases. Solve only the upper
/// 2-by-2 matrix equation.
/// </summary>
/// <param name="b">The b.</param>
/// <returns></returns>
public Vector2 Solve22(Vector2 b)
{
float a11 = Col1.X, a12 = Col2.X, a21 = Col1.Y, a22 = Col2.Y;
float det = a11 * a22 - a12 * a21;
if (det != 0.0f)
{
det = 1.0f / det;
}
return new Vector2(det * (a22 * b.X - a12 * b.Y), det * (a11 * b.Y - a21 * b.X));
}
}
/// <summary>
/// A transform contains translation and rotation. It is used to represent
/// the position and orientation of rigid frames.
/// </summary>
public struct Transform
{
public Vector2 Position;
public Mat22 R;
/// <summary>
/// Initialize using a position vector and a rotation matrix.
/// </summary>
/// <param name="position">The position.</param>
/// <param name="r">The r.</param>
public Transform(ref Vector2 position, ref Mat22 r)
{
Position = position;
R = r;
}
/// <summary>
/// Calculate the angle that the rotation matrix represents.
/// </summary>
/// <value></value>
public float Angle
{
get { return (float)Math.Atan2(R.Col1.Y, R.Col1.X); }
}
/// <summary>
/// Set this to the identity transform.
/// </summary>
public void SetIdentity()
{
Position = Vector2.Zero;
R.SetIdentity();
}
/// <summary>
/// Set this based on the position and angle.
/// </summary>
/// <param name="position">The position.</param>
/// <param name="angle">The angle.</param>
public void Set(Vector2 position, float angle)
{
Position = position;
R.Set(angle);
}
}
/// <summary>
/// This describes the motion of a body/shape for TOI computation.
/// Shapes are defined with respect to the body origin, which may
/// no coincide with the center of mass. However, to support dynamics
/// we must interpolate the center of mass position.
/// </summary>
public struct Sweep
{
/// <summary>
/// World angles
/// </summary>
public float A;
public float A0;
/// <summary>
/// Fraction of the current time step in the range [0,1]
/// c0 and a0 are the positions at alpha0.
/// </summary>
public float Alpha0;
/// <summary>
/// Center world positions
/// </summary>
public Vector2 C;
public Vector2 C0;
/// <summary>
/// Local center of mass position
/// </summary>
public Vector2 LocalCenter;
/// <summary>
/// Get the interpolated transform at a specific time.
/// </summary>
/// <param name="xf">The transform.</param>
/// <param name="beta">beta is a factor in [0,1], where 0 indicates alpha0.</param>
public void GetTransform(out Transform xf, float beta)
{
xf = new Transform();
xf.Position.X = (1.0f - beta) * C0.X + beta * C.X;
xf.Position.Y = (1.0f - beta) * C0.Y + beta * C.Y;
float angle = (1.0f - beta) * A0 + beta * A;
xf.R.Set(angle);
// Shift to origin
xf.Position -= MathUtils.Multiply(ref xf.R, ref LocalCenter);
}
/// <summary>
/// Advance the sweep forward, yielding a new initial state.
/// </summary>
/// <param name="alpha">new initial time..</param>
public void Advance(float alpha)
{
Debug.Assert(Alpha0 < 1.0f);
float beta = (alpha - Alpha0) / (1.0f - Alpha0);
C0.X = (1.0f - beta) * C0.X + beta * C.X;
C0.Y = (1.0f - beta) * C0.Y + beta * C.Y;
A0 = (1.0f - beta) * A0 + beta * A;
Alpha0 = alpha;
}
/// <summary>
/// Normalize the angles.
/// </summary>
public void Normalize()
{
float d = MathHelper.TwoPi * (float)Math.Floor(A0 / MathHelper.TwoPi);
A0 -= d;
A -= d;
}
}
}

341
axios/Common/Path.cs Normal file
View File

@@ -0,0 +1,341 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common
{
//Contributed by Matthew Bettcher
/// <summary>
/// Path:
/// Very similar to Vertices, but this
/// class contains vectors describing
/// control points on a Catmull-Rom
/// curve.
/// </summary>
[XmlRoot("Path")]
public class Path
{
/// <summary>
/// All the points that makes up the curve
/// </summary>
[XmlElement("ControlPoints")]
public List<Vector2> ControlPoints;
private float _deltaT;
/// <summary>
/// Initializes a new instance of the <see cref="Path"/> class.
/// </summary>
public Path()
{
ControlPoints = new List<Vector2>();
}
/// <summary>
/// Initializes a new instance of the <see cref="Path"/> class.
/// </summary>
/// <param name="vertices">The vertices to created the path from.</param>
public Path(Vector2[] vertices)
{
ControlPoints = new List<Vector2>(vertices.Length);
for (int i = 0; i < vertices.Length; i++)
{
Add(vertices[i]);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Path"/> class.
/// </summary>
/// <param name="vertices">The vertices to created the path from.</param>
public Path(IList<Vector2> vertices)
{
ControlPoints = new List<Vector2>(vertices.Count);
for (int i = 0; i < vertices.Count; i++)
{
Add(vertices[i]);
}
}
/// <summary>
/// True if the curve is closed.
/// </summary>
/// <value><c>true</c> if closed; otherwise, <c>false</c>.</value>
[XmlElement("Closed")]
public bool Closed { get; set; }
/// <summary>
/// Gets the next index of a controlpoint
/// </summary>
/// <param name="index">The index.</param>
/// <returns></returns>
public int NextIndex(int index)
{
if (index == ControlPoints.Count - 1)
{
return 0;
}
return index + 1;
}
/// <summary>
/// Gets the previous index of a controlpoint
/// </summary>
/// <param name="index">The index.</param>
/// <returns></returns>
public int PreviousIndex(int index)
{
if (index == 0)
{
return ControlPoints.Count - 1;
}
return index - 1;
}
/// <summary>
/// Translates the control points by the specified vector.
/// </summary>
/// <param name="vector">The vector.</param>
public void Translate(ref Vector2 vector)
{
for (int i = 0; i < ControlPoints.Count; i++)
ControlPoints[i] = Vector2.Add(ControlPoints[i], vector);
}
/// <summary>
/// Scales the control points by the specified vector.
/// </summary>
/// <param name="value">The Value.</param>
public void Scale(ref Vector2 value)
{
for (int i = 0; i < ControlPoints.Count; i++)
ControlPoints[i] = Vector2.Multiply(ControlPoints[i], value);
}
/// <summary>
/// Rotate the control points by the defined value in radians.
/// </summary>
/// <param name="value">The amount to rotate by in radians.</param>
public void Rotate(float value)
{
Matrix rotationMatrix;
Matrix.CreateRotationZ(value, out rotationMatrix);
for (int i = 0; i < ControlPoints.Count; i++)
ControlPoints[i] = Vector2.Transform(ControlPoints[i], rotationMatrix);
}
public override string ToString()
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < ControlPoints.Count; i++)
{
builder.Append(ControlPoints[i].ToString());
if (i < ControlPoints.Count - 1)
{
builder.Append(" ");
}
}
return builder.ToString();
}
/// <summary>
/// Returns a set of points defining the
/// curve with the specifed number of divisions
/// between each control point.
/// </summary>
/// <param name="divisions">Number of divisions between each control point.</param>
/// <returns></returns>
public Vertices GetVertices(int divisions)
{
Vertices verts = new Vertices();
float timeStep = 1f / divisions;
for (float i = 0; i < 1f; i += timeStep)
{
verts.Add(GetPosition(i));
}
return verts;
}
public Vector2 GetPosition(float time)
{
Vector2 temp;
if (ControlPoints.Count < 2)
throw new Exception("You need at least 2 control points to calculate a position.");
if (Closed)
{
Add(ControlPoints[0]);
_deltaT = 1f / (ControlPoints.Count - 1);
int p = (int)(time / _deltaT);
// use a circular indexing system
int p0 = p - 1;
if (p0 < 0) p0 = p0 + (ControlPoints.Count - 1);
else if (p0 >= ControlPoints.Count - 1) p0 = p0 - (ControlPoints.Count - 1);
int p1 = p;
if (p1 < 0) p1 = p1 + (ControlPoints.Count - 1);
else if (p1 >= ControlPoints.Count - 1) p1 = p1 - (ControlPoints.Count - 1);
int p2 = p + 1;
if (p2 < 0) p2 = p2 + (ControlPoints.Count - 1);
else if (p2 >= ControlPoints.Count - 1) p2 = p2 - (ControlPoints.Count - 1);
int p3 = p + 2;
if (p3 < 0) p3 = p3 + (ControlPoints.Count - 1);
else if (p3 >= ControlPoints.Count - 1) p3 = p3 - (ControlPoints.Count - 1);
// relative time
float lt = (time - _deltaT * p) / _deltaT;
temp = Vector2.CatmullRom(ControlPoints[p0], ControlPoints[p1], ControlPoints[p2], ControlPoints[p3], lt);
RemoveAt(ControlPoints.Count - 1);
}
else
{
int p = (int)(time / _deltaT);
//
int p0 = p - 1;
if (p0 < 0) p0 = 0;
else if (p0 >= ControlPoints.Count - 1) p0 = ControlPoints.Count - 1;
int p1 = p;
if (p1 < 0) p1 = 0;
else if (p1 >= ControlPoints.Count - 1) p1 = ControlPoints.Count - 1;
int p2 = p + 1;
if (p2 < 0) p2 = 0;
else if (p2 >= ControlPoints.Count - 1) p2 = ControlPoints.Count - 1;
int p3 = p + 2;
if (p3 < 0) p3 = 0;
else if (p3 >= ControlPoints.Count - 1) p3 = ControlPoints.Count - 1;
// relative time
float lt = (time - _deltaT * p) / _deltaT;
temp = Vector2.CatmullRom(ControlPoints[p0], ControlPoints[p1], ControlPoints[p2], ControlPoints[p3], lt);
}
return temp;
}
/// <summary>
/// Gets the normal for the given time.
/// </summary>
/// <param name="time">The time</param>
/// <returns>The normal.</returns>
public Vector2 GetPositionNormal(float time)
{
float offsetTime = time + 0.0001f;
Vector2 a = GetPosition(time);
Vector2 b = GetPosition(offsetTime);
Vector2 output, temp;
Vector2.Subtract(ref a, ref b, out temp);
#if (XBOX360 || WINDOWS_PHONE)
output = new Vector2();
#endif
output.X = -temp.Y;
output.Y = temp.X;
Vector2.Normalize(ref output, out output);
return output;
}
public void Add(Vector2 point)
{
ControlPoints.Add(point);
_deltaT = 1f / (ControlPoints.Count - 1);
}
public void Remove(Vector2 point)
{
ControlPoints.Remove(point);
_deltaT = 1f / (ControlPoints.Count - 1);
}
public void RemoveAt(int index)
{
ControlPoints.RemoveAt(index);
_deltaT = 1f / (ControlPoints.Count - 1);
}
public float GetLength()
{
List<Vector2> verts = GetVertices(ControlPoints.Count * 25);
float length = 0;
for (int i = 1; i < verts.Count; i++)
{
length += Vector2.Distance(verts[i - 1], verts[i]);
}
if (Closed)
length += Vector2.Distance(verts[ControlPoints.Count - 1], verts[0]);
return length;
}
public List<Vector3> SubdivideEvenly(int divisions)
{
List<Vector3> verts = new List<Vector3>();
float length = GetLength();
float deltaLength = length / divisions + 0.001f;
float t = 0.000f;
// we always start at the first control point
Vector2 start = ControlPoints[0];
Vector2 end = GetPosition(t);
// increment t until we are at half the distance
while (deltaLength * 0.5f >= Vector2.Distance(start, end))
{
end = GetPosition(t);
t += 0.0001f;
if (t >= 1f)
break;
}
start = end;
// for each box
for (int i = 1; i < divisions; i++)
{
Vector2 normal = GetPositionNormal(t);
float angle = (float)Math.Atan2(normal.Y, normal.X);
verts.Add(new Vector3(end, angle));
// until we reach the correct distance down the curve
while (deltaLength >= Vector2.Distance(start, end))
{
end = GetPosition(t);
t += 0.00001f;
if (t >= 1f)
break;
}
if (t >= 1f)
break;
start = end;
}
return verts;
}
}
}

240
axios/Common/PathManager.cs Normal file
View File

@@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using FarseerPhysics.Collision.Shapes;
using FarseerPhysics.Common;
using FarseerPhysics.Common.Decomposition;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Factories
{
/// <summary>
/// An easy to use manager for creating paths.
/// </summary>
public static class PathManager
{
#region LinkType enum
public enum LinkType
{
Revolute,
Slider
}
#endregion
//Contributed by Matthew Bettcher
/// <summary>
/// Convert a path into a set of edges and attaches them to the specified body.
/// Note: use only for static edges.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="body">The body.</param>
/// <param name="subdivisions">The subdivisions.</param>
public static void ConvertPathToEdges(Path path, Body body, int subdivisions)
{
Vertices verts = path.GetVertices(subdivisions);
if (path.Closed)
{
LoopShape loop = new LoopShape(verts);
body.CreateFixture(loop);
}
else
{
for (int i = 1; i < verts.Count; i++)
{
body.CreateFixture(new EdgeShape(verts[i], verts[i - 1]));
}
}
}
/// <summary>
/// Convert a closed path into a polygon.
/// Convex decomposition is automatically performed.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="body">The body.</param>
/// <param name="density">The density.</param>
/// <param name="subdivisions">The subdivisions.</param>
public static void ConvertPathToPolygon(Path path, Body body, float density, int subdivisions)
{
if (!path.Closed)
throw new Exception("The path must be closed to convert to a polygon.");
List<Vector2> verts = path.GetVertices(subdivisions);
List<Vertices> decomposedVerts = EarclipDecomposer.ConvexPartition(new Vertices(verts));
//List<Vertices> decomposedVerts = BayazitDecomposer.ConvexPartition(new Vertices(verts));
foreach (Vertices item in decomposedVerts)
{
body.CreateFixture(new PolygonShape(item, density));
}
}
/// <summary>
/// Duplicates the given Body along the given path for approximatly the given copies.
/// </summary>
/// <param name="world">The world.</param>
/// <param name="path">The path.</param>
/// <param name="shapes">The shapes.</param>
/// <param name="type">The type.</param>
/// <param name="copies">The copies.</param>
/// <param name="userData"></param>
/// <returns></returns>
public static List<Body> EvenlyDistributeShapesAlongPath(World world, Path path, IEnumerable<Shape> shapes,
BodyType type, int copies, object userData)
{
List<Vector3> centers = path.SubdivideEvenly(copies);
List<Body> bodyList = new List<Body>();
for (int i = 0; i < centers.Count; i++)
{
Body b = new Body(world);
// copy the type from original body
b.BodyType = type;
b.Position = new Vector2(centers[i].X, centers[i].Y);
b.Rotation = centers[i].Z;
foreach (Shape shape in shapes)
{
b.CreateFixture(shape, userData);
}
bodyList.Add(b);
}
return bodyList;
}
public static List<Body> EvenlyDistributeShapesAlongPath(World world, Path path, IEnumerable<Shape> shapes,
BodyType type, int copies)
{
return EvenlyDistributeShapesAlongPath(world, path, shapes, type, copies, null);
}
/// <summary>
/// Duplicates the given Body along the given path for approximatly the given copies.
/// </summary>
/// <param name="world">The world.</param>
/// <param name="path">The path.</param>
/// <param name="shape">The shape.</param>
/// <param name="type">The type.</param>
/// <param name="copies">The copies.</param>
/// <param name="userData">The user data.</param>
/// <returns></returns>
public static List<Body> EvenlyDistributeShapesAlongPath(World world, Path path, Shape shape, BodyType type,
int copies, object userData)
{
List<Shape> shapes = new List<Shape>(1);
shapes.Add(shape);
return EvenlyDistributeShapesAlongPath(world, path, shapes, type, copies, userData);
}
public static List<Body> EvenlyDistributeShapesAlongPath(World world, Path path, Shape shape, BodyType type,
int copies)
{
return EvenlyDistributeShapesAlongPath(world, path, shape, type, copies, null);
}
//TODO: Comment better
/// <summary>
/// Moves the body on the path.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="body">The body.</param>
/// <param name="time">The time.</param>
/// <param name="strength">The strength.</param>
/// <param name="timeStep">The time step.</param>
public static void MoveBodyOnPath(Path path, Body body, float time, float strength, float timeStep)
{
Vector2 destination = path.GetPosition(time);
Vector2 positionDelta = body.Position - destination;
Vector2 velocity = (positionDelta / timeStep) * strength;
body.LinearVelocity = -velocity;
}
/// <summary>
/// Attaches the bodies with revolute joints.
/// </summary>
/// <param name="world">The world.</param>
/// <param name="bodies">The bodies.</param>
/// <param name="localAnchorA">The local anchor A.</param>
/// <param name="localAnchorB">The local anchor B.</param>
/// <param name="connectFirstAndLast">if set to <c>true</c> [connect first and last].</param>
/// <param name="collideConnected">if set to <c>true</c> [collide connected].</param>
public static List<RevoluteJoint> AttachBodiesWithRevoluteJoint(World world, List<Body> bodies,
Vector2 localAnchorA,
Vector2 localAnchorB, bool connectFirstAndLast,
bool collideConnected)
{
List<RevoluteJoint> joints = new List<RevoluteJoint>(bodies.Count + 1);
for (int i = 1; i < bodies.Count; i++)
{
RevoluteJoint joint = new RevoluteJoint(bodies[i], bodies[i - 1], localAnchorA, localAnchorB);
joint.CollideConnected = collideConnected;
world.AddJoint(joint);
joints.Add(joint);
}
if (connectFirstAndLast)
{
RevoluteJoint lastjoint = new RevoluteJoint(bodies[0], bodies[bodies.Count - 1], localAnchorA,
localAnchorB);
lastjoint.CollideConnected = collideConnected;
world.AddJoint(lastjoint);
joints.Add(lastjoint);
}
return joints;
}
/// <summary>
/// Attaches the bodies with revolute joints.
/// </summary>
/// <param name="world">The world.</param>
/// <param name="bodies">The bodies.</param>
/// <param name="localAnchorA">The local anchor A.</param>
/// <param name="localAnchorB">The local anchor B.</param>
/// <param name="connectFirstAndLast">if set to <c>true</c> [connect first and last].</param>
/// <param name="collideConnected">if set to <c>true</c> [collide connected].</param>
/// <param name="minLength">Minimum length of the slider joint.</param>
/// <param name="maxLength">Maximum length of the slider joint.</param>
/// <returns></returns>
public static List<SliderJoint> AttachBodiesWithSliderJoint(World world, List<Body> bodies, Vector2 localAnchorA,
Vector2 localAnchorB, bool connectFirstAndLast,
bool collideConnected, float minLength,
float maxLength)
{
List<SliderJoint> joints = new List<SliderJoint>(bodies.Count + 1);
for (int i = 1; i < bodies.Count; i++)
{
SliderJoint joint = new SliderJoint(bodies[i], bodies[i - 1], localAnchorA, localAnchorB, minLength,
maxLength);
joint.CollideConnected = collideConnected;
world.AddJoint(joint);
joints.Add(joint);
}
if (connectFirstAndLast)
{
SliderJoint lastjoint = new SliderJoint(bodies[0], bodies[bodies.Count - 1], localAnchorA, localAnchorB,
minLength, maxLength);
lastjoint.CollideConnected = collideConnected;
world.AddJoint(lastjoint);
joints.Add(lastjoint);
}
return joints;
}
}
}

View File

@@ -0,0 +1,464 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FarseerPhysics.Collision;
using FarseerPhysics.Collision.Shapes;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.PhysicsLogic
{
internal struct ShapeData
{
public Body Body;
public float Max;
public float Min; // absolute angles
}
/// <summary>
/// This is a comprarer used for
/// detecting angle difference between rays
/// </summary>
internal class RayDataComparer : IComparer<float>
{
#region IComparer<float> Members
int IComparer<float>.Compare(float a, float b)
{
float diff = (a - b);
if (diff > 0)
return 1;
if (diff < 0)
return -1;
return 0;
}
#endregion
}
/* Methodology:
* Force applied at a ray is inversely proportional to the square of distance from source
* AABB is used to query for shapes that may be affected
* For each RIGID BODY (not shape -- this is an optimization) that is matched, loop through its vertices to determine
* the extreme points -- if there is structure that contains outlining polygon, use that as an additional optimization
* Evenly cast a number of rays against the shape - number roughly proportional to the arc coverage
* -Something like every 3 degrees should do the trick although this can be altered depending on the distance (if really close don't need such a high density of rays)
* -There should be a minimum number of rays (3-5?) applied to each body so that small bodies far away are still accurately modeled
* -Be sure to have the forces of each ray be proportional to the average arc length covered by each.
* For each ray that actually intersects with the shape (non intersections indicate something blocking the path of explosion):
* > apply the appropriate force dotted with the negative of the collision normal at the collision point
* > optionally apply linear interpolation between aforementioned Normal force and the original explosion force in the direction of ray to simulate "surface friction" of sorts
*/
/// <summary>
/// This is an explosive... it explodes.
/// </summary>
/// <remarks>
/// Original Code by Steven Lu - see http://www.box2d.org/forum/viewtopic.php?f=3&t=1688
/// Ported to Farseer 3.0 by Nicol<6F>s Hormaz<61>bal
/// </remarks>
public sealed class Explosion : PhysicsLogic
{
/// <summary>
/// Two degrees: maximum angle from edges to first ray tested
/// </summary>
private const float MaxEdgeOffset = MathHelper.Pi / 90;
/// <summary>
/// Ratio of arc length to angle from edges to first ray tested.
/// Defaults to 1/40.
/// </summary>
public float EdgeRatio = 1.0f / 40.0f;
/// <summary>
/// Ignore Explosion if it happens inside a shape.
/// Default value is false.
/// </summary>
public bool IgnoreWhenInsideShape = false;
/// <summary>
/// Max angle between rays (used when segment is large).
/// Defaults to 15 degrees
/// </summary>
public float MaxAngle = MathHelper.Pi / 15;
/// <summary>
/// Maximum number of shapes involved in the explosion.
/// Defaults to 100
/// </summary>
public int MaxShapes = 100;
/// <summary>
/// How many rays per shape/body/segment.
/// Defaults to 5
/// </summary>
public int MinRays = 5;
private List<ShapeData> _data = new List<ShapeData>();
private Dictionary<Fixture, List<Vector2>> _exploded;
private RayDataComparer _rdc;
public Explosion(World world)
: base(world, PhysicsLogicType.Explosion)
{
_exploded = new Dictionary<Fixture, List<Vector2>>();
_rdc = new RayDataComparer();
_data = new List<ShapeData>();
}
/// <summary>
/// This makes the explosive explode
/// </summary>
/// <param name="pos">
/// The position where the explosion happens
/// </param>
/// <param name="radius">
/// The explosion radius
/// </param>
/// <param name="maxForce">
/// The explosion force at the explosion point
/// (then is inversely proportional to the square of the distance)
/// </param>
/// <returns>
/// A dictionnary containing all the "exploded" fixtures
/// with a list of the applied impulses
/// </returns>
public Dictionary<Fixture, List<Vector2>> Activate(Vector2 pos, float radius, float maxForce)
{
_exploded.Clear();
AABB aabb;
aabb.LowerBound = pos + new Vector2(-radius, -radius);
aabb.UpperBound = pos + new Vector2(radius, radius);
Fixture[] shapes = new Fixture[MaxShapes];
// More than 5 shapes in an explosion could be possible, but still strange.
Fixture[] containedShapes = new Fixture[5];
bool exit = false;
int shapeCount = 0;
int containedShapeCount = 0;
// Query the world for overlapping shapes.
World.QueryAABB(
fixture =>
{
if (fixture.TestPoint(ref pos))
{
if (IgnoreWhenInsideShape)
exit = true;
else
containedShapes[containedShapeCount++] = fixture;
}
else
{
shapes[shapeCount++] = fixture;
}
// Continue the query.
return true;
}, ref aabb);
if (exit)
{
return _exploded;
}
// Per shape max/min angles for now.
float[] vals = new float[shapeCount * 2];
int valIndex = 0;
for (int i = 0; i < shapeCount; ++i)
{
PolygonShape ps;
CircleShape cs = shapes[i].Shape as CircleShape;
if (cs != null)
{
// We create a "diamond" approximation of the circle
Vertices v = new Vertices();
Vector2 vec = Vector2.Zero + new Vector2(cs.Radius, 0);
v.Add(vec);
vec = Vector2.Zero + new Vector2(0, cs.Radius);
v.Add(vec);
vec = Vector2.Zero + new Vector2(-cs.Radius, cs.Radius);
v.Add(vec);
vec = Vector2.Zero + new Vector2(0, -cs.Radius);
v.Add(vec);
ps = new PolygonShape(v, 0);
}
else
ps = shapes[i].Shape as PolygonShape;
if ((shapes[i].Body.BodyType == BodyType.Dynamic) && ps != null)
{
Vector2 toCentroid = shapes[i].Body.GetWorldPoint(ps.MassData.Centroid) - pos;
float angleToCentroid = (float)Math.Atan2(toCentroid.Y, toCentroid.X);
float min = float.MaxValue;
float max = float.MinValue;
float minAbsolute = 0.0f;
float maxAbsolute = 0.0f;
for (int j = 0; j < (ps.Vertices.Count()); ++j)
{
Vector2 toVertex = (shapes[i].Body.GetWorldPoint(ps.Vertices[j]) - pos);
float newAngle = (float)Math.Atan2(toVertex.Y, toVertex.X);
float diff = (newAngle - angleToCentroid);
diff = (diff - MathHelper.Pi) % (2 * MathHelper.Pi);
// the minus pi is important. It means cutoff for going other direction is at 180 deg where it needs to be
if (diff < 0.0f)
diff += 2 * MathHelper.Pi; // correction for not handling negs
diff -= MathHelper.Pi;
if (Math.Abs(diff) > MathHelper.Pi)
throw new ArgumentException("OMG!");
// Something's wrong, point not in shape but exists angle diff > 180
if (diff > max)
{
max = diff;
maxAbsolute = newAngle;
}
if (diff < min)
{
min = diff;
minAbsolute = newAngle;
}
}
vals[valIndex] = minAbsolute;
++valIndex;
vals[valIndex] = maxAbsolute;
++valIndex;
}
}
Array.Sort(vals, 0, valIndex, _rdc);
_data.Clear();
bool rayMissed = true;
for (int i = 0; i < valIndex; ++i)
{
Fixture shape = null;
float midpt;
int iplus = (i == valIndex - 1 ? 0 : i + 1);
if (vals[i] == vals[iplus])
continue;
if (i == valIndex - 1)
{
// the single edgecase
midpt = (vals[0] + MathHelper.Pi * 2 + vals[i]);
}
else
{
midpt = (vals[i + 1] + vals[i]);
}
midpt = midpt / 2;
Vector2 p1 = pos;
Vector2 p2 = radius * new Vector2((float)Math.Cos(midpt),
(float)Math.Sin(midpt)) + pos;
// RaycastOne
bool hitClosest = false;
World.RayCast((f, p, n, fr) =>
{
Body body = f.Body;
if (!IsActiveOn(body))
return 0;
if (body.UserData != null)
{
int index = (int)body.UserData;
if (index == 0)
{
// filter
return -1.0f;
}
}
hitClosest = true;
shape = f;
return fr;
}, p1, p2);
//draws radius points
if ((hitClosest) && (shape.Body.BodyType == BodyType.Dynamic))
{
if ((_data.Count() > 0) && (_data.Last().Body == shape.Body) && (!rayMissed))
{
int laPos = _data.Count - 1;
ShapeData la = _data[laPos];
la.Max = vals[iplus];
_data[laPos] = la;
}
else
{
// make new
ShapeData d;
d.Body = shape.Body;
d.Min = vals[i];
d.Max = vals[iplus];
_data.Add(d);
}
if ((_data.Count() > 1)
&& (i == valIndex - 1)
&& (_data.Last().Body == _data.First().Body)
&& (_data.Last().Max == _data.First().Min))
{
ShapeData fi = _data[0];
fi.Min = _data.Last().Min;
_data.RemoveAt(_data.Count() - 1);
_data[0] = fi;
while (_data.First().Min >= _data.First().Max)
{
fi.Min -= MathHelper.Pi * 2;
_data[0] = fi;
}
}
int lastPos = _data.Count - 1;
ShapeData last = _data[lastPos];
while ((_data.Count() > 0)
&& (_data.Last().Min >= _data.Last().Max)) // just making sure min<max
{
last.Min = _data.Last().Min - 2 * MathHelper.Pi;
_data[lastPos] = last;
}
rayMissed = false;
}
else
{
rayMissed = true; // raycast did not find a shape
}
}
for (int i = 0; i < _data.Count(); ++i)
{
if (!IsActiveOn(_data[i].Body))
continue;
float arclen = _data[i].Max - _data[i].Min;
float first = MathHelper.Min(MaxEdgeOffset, EdgeRatio * arclen);
int insertedRays = (int)Math.Ceiling(((arclen - 2.0f * first) - (MinRays - 1) * MaxAngle) / MaxAngle);
if (insertedRays < 0)
insertedRays = 0;
float offset = (arclen - first * 2.0f) / ((float)MinRays + insertedRays - 1);
//Note: This loop can go into infinite as it operates on floats.
//Added FloatEquals with a large epsilon.
for (float j = _data[i].Min + first;
j < _data[i].Max || MathUtils.FloatEquals(j, _data[i].Max, 0.0001f);
j += offset)
{
Vector2 p1 = pos;
Vector2 p2 = pos + radius * new Vector2((float)Math.Cos(j), (float)Math.Sin(j));
Vector2 hitpoint = Vector2.Zero;
float minlambda = float.MaxValue;
List<Fixture> fl = _data[i].Body.FixtureList;
for (int x = 0; x < fl.Count; x++)
{
Fixture f = fl[x];
RayCastInput ri;
ri.Point1 = p1;
ri.Point2 = p2;
ri.MaxFraction = 50f;
RayCastOutput ro;
if (f.RayCast(out ro, ref ri, 0))
{
if (minlambda > ro.Fraction)
{
minlambda = ro.Fraction;
hitpoint = ro.Fraction * p2 + (1 - ro.Fraction) * p1;
}
}
// the force that is to be applied for this particular ray.
// offset is angular coverage. lambda*length of segment is distance.
float impulse = (arclen / (MinRays + insertedRays)) * maxForce * 180.0f / MathHelper.Pi *
(1.0f - Math.Min(1.0f, minlambda));
// We Apply the impulse!!!
Vector2 vectImp = Vector2.Dot(impulse * new Vector2((float)Math.Cos(j),
(float)Math.Sin(j)), -ro.Normal) *
new Vector2((float)Math.Cos(j),
(float)Math.Sin(j));
_data[i].Body.ApplyLinearImpulse(ref vectImp, ref hitpoint);
// We gather the fixtures for returning them
Vector2 val = Vector2.Zero;
List<Vector2> vectorList;
if (_exploded.TryGetValue(f, out vectorList))
{
val.X += Math.Abs(vectImp.X);
val.Y += Math.Abs(vectImp.Y);
vectorList.Add(val);
}
else
{
vectorList = new List<Vector2>();
val.X = Math.Abs(vectImp.X);
val.Y = Math.Abs(vectImp.Y);
vectorList.Add(val);
_exploded.Add(f, vectorList);
}
if (minlambda > 1.0f)
{
hitpoint = p2;
}
}
}
}
// We check contained shapes
for (int i = 0; i < containedShapeCount; ++i)
{
Fixture fix = containedShapes[i];
if (!IsActiveOn(fix.Body))
continue;
float impulse = MinRays * maxForce * 180.0f / MathHelper.Pi;
Vector2 hitPoint;
CircleShape circShape = fix.Shape as CircleShape;
if (circShape != null)
{
hitPoint = fix.Body.GetWorldPoint(circShape.Position);
}
else
{
PolygonShape shape = fix.Shape as PolygonShape;
hitPoint = fix.Body.GetWorldPoint(shape.MassData.Centroid);
}
Vector2 vectImp = impulse * (hitPoint - pos);
List<Vector2> vectorList = new List<Vector2>();
vectorList.Add(vectImp);
fix.Body.ApplyLinearImpulse(ref vectImp, ref hitPoint);
if (!_exploded.ContainsKey(fix))
_exploded.Add(fix, vectorList);
}
return _exploded;
}
}
}

View File

@@ -0,0 +1,66 @@
using System;
using FarseerPhysics.Dynamics;
namespace FarseerPhysics.Common.PhysicsLogic
{
[Flags]
public enum PhysicsLogicType
{
Explosion = (1 << 0)
}
public struct PhysicsLogicFilter
{
public PhysicsLogicType ControllerIgnores;
/// <summary>
/// Ignores the controller. The controller has no effect on this body.
/// </summary>
/// <param name="type">The logic type.</param>
public void IgnorePhysicsLogic(PhysicsLogicType type)
{
ControllerIgnores |= type;
}
/// <summary>
/// Restore the controller. The controller affects this body.
/// </summary>
/// <param name="type">The logic type.</param>
public void RestorePhysicsLogic(PhysicsLogicType type)
{
ControllerIgnores &= ~type;
}
/// <summary>
/// Determines whether this body ignores the the specified controller.
/// </summary>
/// <param name="type">The logic type.</param>
/// <returns>
/// <c>true</c> if the body has the specified flag; otherwise, <c>false</c>.
/// </returns>
public bool IsPhysicsLogicIgnored(PhysicsLogicType type)
{
return (ControllerIgnores & type) == type;
}
}
public abstract class PhysicsLogic : FilterData
{
private PhysicsLogicType _type;
public World World;
public override bool IsActiveOn(Body body)
{
if (body.PhysicsLogicFilter.IsPhysicsLogicIgnored(_type))
return false;
return base.IsActiveOn(body);
}
public PhysicsLogic(World world, PhysicsLogicType type)
{
_type = type;
World = world;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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
}
}

View File

@@ -0,0 +1,368 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common
{
public static class PolygonTools
{
/// <summary>
/// Build vertices to represent an axis-aligned box.
/// </summary>
/// <param name="hx">the half-width.</param>
/// <param name="hy">the half-height.</param>
public static Vertices CreateRectangle(float hx, float hy)
{
Vertices vertices = new Vertices(4);
vertices.Add(new Vector2(-hx, -hy));
vertices.Add(new Vector2(hx, -hy));
vertices.Add(new Vector2(hx, hy));
vertices.Add(new Vector2(-hx, hy));
return vertices;
}
public static Vertices CreateTriangle(float hx, float hy)
{
Vertices vertices = new Vertices(3);
vertices.Add(new Vector2(hx/2, hy));
vertices.Add(new Vector2(hx, 0));
vertices.Add(new Vector2(0, 0));
return vertices;
}
/// <summary>
/// Build vertices to represent an oriented box.
/// </summary>
/// <param name="hx">the half-width.</param>
/// <param name="hy">the half-height.</param>
/// <param name="center">the center of the box in local coordinates.</param>
/// <param name="angle">the rotation of the box in local coordinates.</param>
public static Vertices CreateRectangle(float hx, float hy, Vector2 center, float angle)
{
Vertices vertices = CreateRectangle(hx, hy);
Transform xf = new Transform();
xf.Position = center;
xf.R.Set(angle);
// Transform vertices
for (int i = 0; i < 4; ++i)
{
vertices[i] = MathUtils.Multiply(ref xf, vertices[i]);
}
return vertices;
}
//Rounded rectangle contributed by Jonathan Smars - jsmars@gmail.com
/// <summary>
/// Creates a rounded rectangle with the specified width and height.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="xRadius">The rounding X radius.</param>
/// <param name="yRadius">The rounding Y radius.</param>
/// <param name="segments">The number of segments to subdivide the edges.</param>
/// <returns></returns>
public static Vertices CreateRoundedRectangle(float width, float height, float xRadius, float yRadius,
int segments)
{
if (yRadius > height / 2 || xRadius > width / 2)
throw new Exception("Rounding amount can't be more than half the height and width respectively.");
if (segments < 0)
throw new Exception("Segments must be zero or more.");
//We need at least 8 vertices to create a rounded rectangle
Debug.Assert(Settings.MaxPolygonVertices >= 8);
Vertices vertices = new Vertices();
if (segments == 0)
{
vertices.Add(new Vector2(width * .5f - xRadius, -height * .5f));
vertices.Add(new Vector2(width * .5f, -height * .5f + yRadius));
vertices.Add(new Vector2(width * .5f, height * .5f - yRadius));
vertices.Add(new Vector2(width * .5f - xRadius, height * .5f));
vertices.Add(new Vector2(-width * .5f + xRadius, height * .5f));
vertices.Add(new Vector2(-width * .5f, height * .5f - yRadius));
vertices.Add(new Vector2(-width * .5f, -height * .5f + yRadius));
vertices.Add(new Vector2(-width * .5f + xRadius, -height * .5f));
}
else
{
int numberOfEdges = (segments * 4 + 8);
float stepSize = MathHelper.TwoPi / (numberOfEdges - 4);
int perPhase = numberOfEdges / 4;
Vector2 posOffset = new Vector2(width / 2 - xRadius, height / 2 - yRadius);
vertices.Add(posOffset + new Vector2(xRadius, -yRadius + yRadius));
short phase = 0;
for (int i = 1; i < numberOfEdges; i++)
{
if (i - perPhase == 0 || i - perPhase * 3 == 0)
{
posOffset.X *= -1;
phase--;
}
else if (i - perPhase * 2 == 0)
{
posOffset.Y *= -1;
phase--;
}
vertices.Add(posOffset + new Vector2(xRadius * (float)Math.Cos(stepSize * -(i + phase)),
-yRadius * (float)Math.Sin(stepSize * -(i + phase))));
}
}
return vertices;
}
/// <summary>
/// Set this as a single edge.
/// </summary>
/// <param name="start">The first point.</param>
/// <param name="end">The second point.</param>
public static Vertices CreateLine(Vector2 start, Vector2 end)
{
Vertices vertices = new Vertices(2);
vertices.Add(start);
vertices.Add(end);
return vertices;
}
/// <summary>
/// Creates a circle with the specified radius and number of edges.
/// </summary>
/// <param name="radius">The radius.</param>
/// <param name="numberOfEdges">The number of edges. The more edges, the more it resembles a circle</param>
/// <returns></returns>
public static Vertices CreateCircle(float radius, int numberOfEdges)
{
return CreateEllipse(radius, radius, numberOfEdges);
}
/// <summary>
/// Creates a ellipse with the specified width, height and number of edges.
/// </summary>
/// <param name="xRadius">Width of the ellipse.</param>
/// <param name="yRadius">Height of the ellipse.</param>
/// <param name="numberOfEdges">The number of edges. The more edges, the more it resembles an ellipse</param>
/// <returns></returns>
public static Vertices CreateEllipse(float xRadius, float yRadius, int numberOfEdges)
{
Vertices vertices = new Vertices();
float stepSize = MathHelper.TwoPi / numberOfEdges;
vertices.Add(new Vector2(xRadius, 0));
for (int i = numberOfEdges - 1; i > 0; --i)
vertices.Add(new Vector2(xRadius * (float)Math.Cos(stepSize * i),
-yRadius * (float)Math.Sin(stepSize * i)));
return vertices;
}
public static Vertices CreateArc(float radians, int sides, float radius)
{
Debug.Assert(radians > 0, "The arc needs to be larger than 0");
Debug.Assert(sides > 1, "The arc needs to have more than 1 sides");
Debug.Assert(radius > 0, "The arc needs to have a radius larger than 0");
Vertices vertices = new Vertices();
float stepSize = radians / sides;
for (int i = sides - 1; i > 0; i--)
{
vertices.Add(new Vector2(radius * (float)Math.Cos(stepSize * i),
radius * (float)Math.Sin(stepSize * i)));
}
return vertices;
}
//Capsule contributed by Yobiv
/// <summary>
/// Creates an capsule with the specified height, radius and number of edges.
/// A capsule has the same form as a pill capsule.
/// </summary>
/// <param name="height">Height (inner height + 2 * radius) of the capsule.</param>
/// <param name="endRadius">Radius of the capsule ends.</param>
/// <param name="edges">The number of edges of the capsule ends. The more edges, the more it resembles an capsule</param>
/// <returns></returns>
public static Vertices CreateCapsule(float height, float endRadius, int edges)
{
if (endRadius >= height / 2)
throw new ArgumentException(
"The radius must be lower than height / 2. Higher values of radius would create a circle, and not a half circle.",
"endRadius");
return CreateCapsule(height, endRadius, edges, endRadius, edges);
}
/// <summary>
/// Creates an capsule with the specified height, radius and number of edges.
/// A capsule has the same form as a pill capsule.
/// </summary>
/// <param name="height">Height (inner height + radii) of the capsule.</param>
/// <param name="topRadius">Radius of the top.</param>
/// <param name="topEdges">The number of edges of the top. The more edges, the more it resembles an capsule</param>
/// <param name="bottomRadius">Radius of bottom.</param>
/// <param name="bottomEdges">The number of edges of the bottom. The more edges, the more it resembles an capsule</param>
/// <returns></returns>
public static Vertices CreateCapsule(float height, float topRadius, int topEdges, float bottomRadius,
int bottomEdges)
{
if (height <= 0)
throw new ArgumentException("Height must be longer than 0", "height");
if (topRadius <= 0)
throw new ArgumentException("The top radius must be more than 0", "topRadius");
if (topEdges <= 0)
throw new ArgumentException("Top edges must be more than 0", "topEdges");
if (bottomRadius <= 0)
throw new ArgumentException("The bottom radius must be more than 0", "bottomRadius");
if (bottomEdges <= 0)
throw new ArgumentException("Bottom edges must be more than 0", "bottomEdges");
if (topRadius >= height / 2)
throw new ArgumentException(
"The top radius must be lower than height / 2. Higher values of top radius would create a circle, and not a half circle.",
"topRadius");
if (bottomRadius >= height / 2)
throw new ArgumentException(
"The bottom radius must be lower than height / 2. Higher values of bottom radius would create a circle, and not a half circle.",
"bottomRadius");
Vertices vertices = new Vertices();
float newHeight = (height - topRadius - bottomRadius) * 0.5f;
// top
vertices.Add(new Vector2(topRadius, newHeight));
float stepSize = MathHelper.Pi / topEdges;
for (int i = 1; i < topEdges; i++)
{
vertices.Add(new Vector2(topRadius * (float)Math.Cos(stepSize * i),
topRadius * (float)Math.Sin(stepSize * i) + newHeight));
}
vertices.Add(new Vector2(-topRadius, newHeight));
// bottom
vertices.Add(new Vector2(-bottomRadius, -newHeight));
stepSize = MathHelper.Pi / bottomEdges;
for (int i = 1; i < bottomEdges; i++)
{
vertices.Add(new Vector2(-bottomRadius * (float)Math.Cos(stepSize * i),
-bottomRadius * (float)Math.Sin(stepSize * i) - newHeight));
}
vertices.Add(new Vector2(bottomRadius, -newHeight));
return vertices;
}
/// <summary>
/// Creates a gear shape with the specified radius and number of teeth.
/// </summary>
/// <param name="radius">The radius.</param>
/// <param name="numberOfTeeth">The number of teeth.</param>
/// <param name="tipPercentage">The tip percentage.</param>
/// <param name="toothHeight">Height of the tooth.</param>
/// <returns></returns>
public static Vertices CreateGear(float radius, int numberOfTeeth, float tipPercentage, float toothHeight)
{
Vertices vertices = new Vertices();
float stepSize = MathHelper.TwoPi / numberOfTeeth;
tipPercentage /= 100f;
MathHelper.Clamp(tipPercentage, 0f, 1f);
float toothTipStepSize = (stepSize / 2f) * tipPercentage;
float toothAngleStepSize = (stepSize - (toothTipStepSize * 2f)) / 2f;
for (int i = numberOfTeeth - 1; i >= 0; --i)
{
if (toothTipStepSize > 0f)
{
vertices.Add(
new Vector2(radius *
(float)Math.Cos(stepSize * i + toothAngleStepSize * 2f + toothTipStepSize),
-radius *
(float)Math.Sin(stepSize * i + toothAngleStepSize * 2f + toothTipStepSize)));
vertices.Add(
new Vector2((radius + toothHeight) *
(float)Math.Cos(stepSize * i + toothAngleStepSize + toothTipStepSize),
-(radius + toothHeight) *
(float)Math.Sin(stepSize * i + toothAngleStepSize + toothTipStepSize)));
}
vertices.Add(new Vector2((radius + toothHeight) *
(float)Math.Cos(stepSize * i + toothAngleStepSize),
-(radius + toothHeight) *
(float)Math.Sin(stepSize * i + toothAngleStepSize)));
vertices.Add(new Vector2(radius * (float)Math.Cos(stepSize * i),
-radius * (float)Math.Sin(stepSize * i)));
}
return vertices;
}
/// <summary>
/// Detects the vertices by analyzing the texture data.
/// </summary>
/// <param name="data">The texture data.</param>
/// <param name="width">The texture width.</param>
/// <returns></returns>
public static Vertices CreatePolygon(uint[] data, int width)
{
return TextureConverter.DetectVertices(data, width);
}
/// <summary>
/// Detects the vertices by analyzing the texture data.
/// </summary>
/// <param name="data">The texture data.</param>
/// <param name="width">The texture width.</param>
/// <param name="holeDetection">if set to <c>true</c> it will perform hole detection.</param>
/// <returns></returns>
public static Vertices CreatePolygon(uint[] data, int width, bool holeDetection)
{
return TextureConverter.DetectVertices(data, width, holeDetection);
}
/// <summary>
/// Detects the vertices by analyzing the texture data.
/// </summary>
/// <param name="data">The texture data.</param>
/// <param name="width">The texture width.</param>
/// <param name="hullTolerance">The hull tolerance.</param>
/// <param name="alphaTolerance">The alpha tolerance.</param>
/// <param name="multiPartDetection">if set to <c>true</c> it will perform multi part detection.</param>
/// <param name="holeDetection">if set to <c>true</c> it will perform hole detection.</param>
/// <returns></returns>
public static List<Vertices> CreatePolygon(uint[] data, int width, float hullTolerance,
byte alphaTolerance, bool multiPartDetection, bool holeDetection)
{
return TextureConverter.DetectVertices(data, width, hullTolerance, alphaTolerance,
multiPartDetection, holeDetection);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,367 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Collision;
using FarseerPhysics.Factories;
namespace FarseerPhysics.Common
{
public enum Decomposer
{
Bayazit,
CDT,
Earclip,
Flipcode,
Seidel,
}
/// <summary>
/// Return true if the specified color is inside the terrain.
/// </summary>
public delegate bool TerrainTester(Color color);
/// <summary>
/// Simple class to maintain a terrain.
/// </summary>
public class MSTerrain
{
/// <summary>
/// World to manage terrain in.
/// </summary>
public World World;
/// <summary>
/// Center of terrain in world units.
/// </summary>
public Vector2 Center;
/// <summary>
/// Width of terrain in world units.
/// </summary>
public float Width;
/// <summary>
/// Height of terrain in world units.
/// </summary>
public float Height;
/// <summary>
/// Points per each world unit used to define the terrain in the point cloud.
/// </summary>
public int PointsPerUnit;
/// <summary>
/// Points per cell.
/// </summary>
public int CellSize;
/// <summary>
/// Points per sub cell.
/// </summary>
public int SubCellSize;
/// <summary>
/// Number of iterations to perform in the Marching Squares algorithm.
/// Note: More then 3 has almost no effect on quality.
/// </summary>
public int Iterations = 2;
/// <summary>
/// Decomposer to use when regenerating terrain. Can be changed on the fly without consequence.
/// Note: Some decomposerers are unstable.
/// </summary>
public Decomposer Decomposer;
/// <summary>
/// Point cloud defining the terrain.
/// </summary>
private sbyte[,] _terrainMap;
/// <summary>
/// Generated bodies.
/// </summary>
private List<Body>[,] _bodyMap;
private float _localWidth;
private float _localHeight;
private int _xnum;
private int _ynum;
private AABB _dirtyArea;
private Vector2 _topLeft;
public MSTerrain(World world, AABB area)
{
World = world;
Width = area.Extents.X * 2;
Height = area.Extents.Y * 2;
Center = area.Center;
}
/// <summary>
/// Initialize the terrain for use.
/// </summary>
public void Initialize()
{
// find top left of terrain in world space
_topLeft = new Vector2(Center.X - (Width * 0.5f), Center.Y - (-Height * 0.5f));
// convert the terrains size to a point cloud size
_localWidth = Width * PointsPerUnit;
_localHeight = Height * PointsPerUnit;
_terrainMap = new sbyte[(int)_localWidth + 1, (int)_localHeight + 1];
for (int x = 0; x < _localWidth; x++)
{
for (int y = 0; y < _localHeight; y++)
{
_terrainMap[x, y] = 1;
}
}
_xnum = (int)(_localWidth / CellSize);
_ynum = (int)(_localHeight / CellSize);
_bodyMap = new List<Body>[_xnum, _ynum];
// make sure to mark the dirty area to an infinitely small box
_dirtyArea = new AABB(new Vector2(float.MaxValue, float.MaxValue), new Vector2(float.MinValue, float.MinValue));
}
/// <summary>
/// Apply a texture to the terrain using the specified TerrainTester.
/// </summary>
/// <param name="texture">Texture to apply.</param>
/// <param name="position">Top left position of the texture relative to the terrain.</param>
/// <param name="tester">Delegate method used to determine what colors should be included in the terrain.</param>
public void ApplyTexture(Texture2D texture, Vector2 position, TerrainTester tester)
{
Color[] colorData = new Color[texture.Width * texture.Height];
texture.GetData(colorData);
for (int y = (int)position.Y; y < texture.Height + (int)position.Y; y++)
{
for (int x = (int)position.X; x < texture.Width + (int)position.X; x++)
{
if (x >= 0 && x < _localWidth && y >= 0 && y < _localHeight)
{
bool inside = tester(colorData[((y - (int)position.Y) * texture.Width) + (x - (int)position.X)]);
if (!inside)
_terrainMap[x, y] = 1;
else
_terrainMap[x, y] = -1;
}
}
}
// generate terrain
for (int gy = 0; gy < _ynum; gy++)
{
for (int gx = 0; gx < _xnum; gx++)
{
//remove old terrain object at grid cell
if (_bodyMap[gx, gy] != null)
{
for (int i = 0; i < _bodyMap[gx, gy].Count; i++)
{
World.RemoveBody(_bodyMap[gx, gy][i]);
}
}
_bodyMap[gx, gy] = null;
//generate new one
GenerateTerrain(gx, gy);
}
}
}
/// <summary>
/// Apply a texture to the terrain using the specified TerrainTester.
/// </summary>
/// <param name="position">Top left position of the texture relative to the terrain.</param>
public void ApplyData(sbyte[,] data, Vector2 position)
{
for (int y = (int)position.Y; y < data.GetUpperBound(1) + (int)position.Y; y++)
{
for (int x = (int)position.X; x < data.GetUpperBound(0) + (int)position.X; x++)
{
if (x >= 0 && x < _localWidth && y >= 0 && y < _localHeight)
{
_terrainMap[x, y] = data[x, y];
}
}
}
// generate terrain
for (int gy = 0; gy < _ynum; gy++)
{
for (int gx = 0; gx < _xnum; gx++)
{
//remove old terrain object at grid cell
if (_bodyMap[gx, gy] != null)
{
for (int i = 0; i < _bodyMap[gx, gy].Count; i++)
{
World.RemoveBody(_bodyMap[gx, gy][i]);
}
}
_bodyMap[gx, gy] = null;
//generate new one
GenerateTerrain(gx, gy);
}
}
}
/// <summary>
/// Convert a texture to an sbtye array compatible with ApplyData().
/// </summary>
/// <param name="texture">Texture to convert.</param>
/// <param name="tester"></param>
/// <returns></returns>
public static sbyte[,] ConvertTextureToData(Texture2D texture, TerrainTester tester)
{
sbyte[,] data = new sbyte[texture.Width, texture.Height];
Color[] colorData = new Color[texture.Width * texture.Height];
texture.GetData(colorData);
for (int y = 0; y < texture.Height; y++)
{
for (int x = 0; x < texture.Width; x++)
{
bool inside = tester(colorData[(y * texture.Width) + x]);
if (!inside)
data[x, y] = 1;
else
data[x, y] = -1;
}
}
return data;
}
/// <summary>
/// Modify a single point in the terrain.
/// </summary>
/// <param name="location">World location to modify. Automatically clipped.</param>
/// <param name="value">-1 = inside terrain, 1 = outside terrain</param>
public void ModifyTerrain(Vector2 location, sbyte value)
{
// find local position
// make position local to map space
Vector2 p = location - _topLeft;
// find map position for each axis
p.X = p.X * _localWidth / Width;
p.Y = p.Y * -_localHeight / Height;
if (p.X >= 0 && p.X < _localWidth && p.Y >= 0 && p.Y < _localHeight)
{
_terrainMap[(int)p.X, (int)p.Y] = value;
// expand dirty area
if (p.X < _dirtyArea.LowerBound.X) _dirtyArea.LowerBound.X = p.X;
if (p.X > _dirtyArea.UpperBound.X) _dirtyArea.UpperBound.X = p.X;
if (p.Y < _dirtyArea.LowerBound.Y) _dirtyArea.LowerBound.Y = p.Y;
if (p.Y > _dirtyArea.UpperBound.Y) _dirtyArea.UpperBound.Y = p.Y;
}
}
/// <summary>
/// Regenerate the terrain.
/// </summary>
public void RegenerateTerrain()
{
//iterate effected cells
var gx0 = (int)(_dirtyArea.LowerBound.X / CellSize);
var gx1 = (int)(_dirtyArea.UpperBound.X / CellSize) + 1;
if (gx0 < 0) gx0 = 0;
if (gx1 > _xnum) gx1 = _xnum;
var gy0 = (int)(_dirtyArea.LowerBound.Y / CellSize);
var gy1 = (int)(_dirtyArea.UpperBound.Y / CellSize) + 1;
if (gy0 < 0) gy0 = 0;
if (gy1 > _ynum) gy1 = _ynum;
for (int gx = gx0; gx < gx1; gx++)
{
for (int gy = gy0; gy < gy1; gy++)
{
//remove old terrain object at grid cell
if (_bodyMap[gx, gy] != null)
{
for (int i = 0; i < _bodyMap[gx, gy].Count; i++)
{
World.RemoveBody(_bodyMap[gx, gy][i]);
}
}
_bodyMap[gx, gy] = null;
//generate new one
GenerateTerrain(gx, gy);
}
}
_dirtyArea = new AABB(new Vector2(float.MaxValue, float.MaxValue), new Vector2(float.MinValue, float.MinValue));
}
private void GenerateTerrain(int gx, int gy)
{
float ax = gx * CellSize;
float ay = gy * CellSize;
List<Vertices> polys = MarchingSquares.DetectSquares(new AABB(new Vector2(ax, ay), new Vector2(ax + CellSize, ay + CellSize)), SubCellSize, SubCellSize, _terrainMap, Iterations, true);
if (polys.Count == 0) return;
_bodyMap[gx, gy] = new List<Body>();
// create the scale vector
Vector2 scale = new Vector2(1f / PointsPerUnit, 1f / -PointsPerUnit);
// create physics object for this grid cell
foreach (var item in polys)
{
// does this need to be negative?
item.Scale(ref scale);
item.Translate(ref _topLeft);
item.ForceCounterClockWise();
Vertices p = FarseerPhysics.Common.PolygonManipulation.SimplifyTools.CollinearSimplify(item);
List<Vertices> decompPolys = new List<Vertices>();
switch (Decomposer)
{
case Decomposer.Bayazit:
decompPolys = Decomposition.BayazitDecomposer.ConvexPartition(p);
break;
case Decomposer.CDT:
decompPolys = Decomposition.CDTDecomposer.ConvexPartition(p);
break;
case Decomposer.Earclip:
decompPolys = Decomposition.EarclipDecomposer.ConvexPartition(p);
break;
case Decomposer.Flipcode:
decompPolys = Decomposition.FlipcodeDecomposer.ConvexPartition(p);
break;
case Decomposer.Seidel:
decompPolys = Decomposition.SeidelDecomposer.ConvexPartition(p, 0.001f);
break;
default:
break;
}
foreach (Vertices poly in decompPolys)
{
if (poly.Count > 2)
_bodyMap[gx, gy].Add(BodyFactory.CreatePolygon(World, poly, 1));
}
}
}
}
}

View File

@@ -0,0 +1,800 @@
using System.Collections.Generic;
using FarseerPhysics.Collision;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common
{
// Ported by Matthew Bettcher - Feb 2011
/*
Copyright (c) 2010, Luca Deltodesco
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 the nape project 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 HOLDER 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.
*/
public static class MarchingSquares
{
/// <summary>
/// Marching squares over the given domain using the mesh defined via the dimensions
/// (wid,hei) to build a set of polygons such that f(x,y) less than 0, using the given number
/// 'bin' for recursive linear inteprolation along cell boundaries.
///
/// if 'comb' is true, then the polygons will also be composited into larger possible concave
/// polygons.
/// </summary>
/// <param name="domain"></param>
/// <param name="cellWidth"></param>
/// <param name="cellHeight"></param>
/// <param name="f"></param>
/// <param name="lerpCount"></param>
/// <param name="combine"></param>
/// <returns></returns>
public static List<Vertices> DetectSquares(AABB domain, float cellWidth, float cellHeight, sbyte[,] f,
int lerpCount, bool combine)
{
CxFastList<GeomPoly> ret = new CxFastList<GeomPoly>();
List<Vertices> verticesList = new List<Vertices>();
//NOTE: removed assignments as they were not used.
List<GeomPoly> polyList;
GeomPoly gp;
int xn = (int)(domain.Extents.X * 2 / cellWidth);
bool xp = xn == (domain.Extents.X * 2 / cellWidth);
int yn = (int)(domain.Extents.Y * 2 / cellHeight);
bool yp = yn == (domain.Extents.Y * 2 / cellHeight);
if (!xp) xn++;
if (!yp) yn++;
sbyte[,] fs = new sbyte[xn + 1, yn + 1];
GeomPolyVal[,] ps = new GeomPolyVal[xn + 1, yn + 1];
//populate shared function lookups.
for (int x = 0; x < xn + 1; x++)
{
int x0;
if (x == xn) x0 = (int)domain.UpperBound.X;
else x0 = (int)(x * cellWidth + domain.LowerBound.X);
for (int y = 0; y < yn + 1; y++)
{
int y0;
if (y == yn) y0 = (int)domain.UpperBound.Y;
else y0 = (int)(y * cellHeight + domain.LowerBound.Y);
fs[x, y] = f[x0, y0];
}
}
//generate sub-polys and combine to scan lines
for (int y = 0; y < yn; y++)
{
float y0 = y * cellHeight + domain.LowerBound.Y;
float y1;
if (y == yn - 1) y1 = domain.UpperBound.Y;
else y1 = y0 + cellHeight;
GeomPoly pre = null;
for (int x = 0; x < xn; x++)
{
float x0 = x * cellWidth + domain.LowerBound.X;
float x1;
if (x == xn - 1) x1 = domain.UpperBound.X;
else x1 = x0 + cellWidth;
gp = new GeomPoly();
int key = MarchSquare(f, fs, ref gp, x, y, x0, y0, x1, y1, lerpCount);
if (gp.Length != 0)
{
if (combine && pre != null && (key & 9) != 0)
{
combLeft(ref pre, ref gp);
gp = pre;
}
else
ret.Add(gp);
ps[x, y] = new GeomPolyVal(gp, key);
}
else
gp = null;
pre = gp;
}
}
if (!combine)
{
polyList = ret.GetListOfElements();
foreach (GeomPoly poly in polyList)
{
verticesList.Add(new Vertices(poly.Points.GetListOfElements()));
}
return verticesList;
}
//combine scan lines together
for (int y = 1; y < yn; y++)
{
int x = 0;
while (x < xn)
{
GeomPolyVal p = ps[x, y];
//skip along scan line if no polygon exists at this point
if (p == null)
{
x++;
continue;
}
//skip along if current polygon cannot be combined above.
if ((p.Key & 12) == 0)
{
x++;
continue;
}
//skip along if no polygon exists above.
GeomPolyVal u = ps[x, y - 1];
if (u == null)
{
x++;
continue;
}
//skip along if polygon above cannot be combined with.
if ((u.Key & 3) == 0)
{
x++;
continue;
}
float ax = x * cellWidth + domain.LowerBound.X;
float ay = y * cellHeight + domain.LowerBound.Y;
CxFastList<Vector2> bp = p.GeomP.Points;
CxFastList<Vector2> ap = u.GeomP.Points;
//skip if it's already been combined with above polygon
if (u.GeomP == p.GeomP)
{
x++;
continue;
}
//combine above (but disallow the hole thingies
CxFastListNode<Vector2> bi = bp.Begin();
while (Square(bi.Elem().Y - ay) > Settings.Epsilon || bi.Elem().X < ax) bi = bi.Next();
//NOTE: Unused
//Vector2 b0 = bi.elem();
Vector2 b1 = bi.Next().Elem();
if (Square(b1.Y - ay) > Settings.Epsilon)
{
x++;
continue;
}
bool brk = true;
CxFastListNode<Vector2> ai = ap.Begin();
while (ai != ap.End())
{
if (VecDsq(ai.Elem(), b1) < Settings.Epsilon)
{
brk = false;
break;
}
ai = ai.Next();
}
if (brk)
{
x++;
continue;
}
CxFastListNode<Vector2> bj = bi.Next().Next();
if (bj == bp.End()) bj = bp.Begin();
while (bj != bi)
{
ai = ap.Insert(ai, bj.Elem()); // .clone()
bj = bj.Next();
if (bj == bp.End()) bj = bp.Begin();
u.GeomP.Length++;
}
//u.p.simplify(float.Epsilon,float.Epsilon);
//
ax = x + 1;
while (ax < xn)
{
GeomPolyVal p2 = ps[(int)ax, y];
if (p2 == null || p2.GeomP != p.GeomP)
{
ax++;
continue;
}
p2.GeomP = u.GeomP;
ax++;
}
ax = x - 1;
while (ax >= 0)
{
GeomPolyVal p2 = ps[(int)ax, y];
if (p2 == null || p2.GeomP != p.GeomP)
{
ax--;
continue;
}
p2.GeomP = u.GeomP;
ax--;
}
ret.Remove(p.GeomP);
p.GeomP = u.GeomP;
x = (int)((bi.Next().Elem().X - domain.LowerBound.X) / cellWidth) + 1;
//x++; this was already commented out!
}
}
polyList = ret.GetListOfElements();
foreach (GeomPoly poly in polyList)
{
verticesList.Add(new Vertices(poly.Points.GetListOfElements()));
}
return verticesList;
}
#region Private Methods
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/** Linearly interpolate between (x0 to x1) given a value at these coordinates (v0 and v1)
such as to approximate value(return) = 0
**/
private static int[] _lookMarch = {
0x00, 0xE0, 0x38, 0xD8, 0x0E, 0xEE, 0x36, 0xD6, 0x83, 0x63, 0xBB, 0x5B, 0x8D,
0x6D, 0xB5, 0x55
};
private static float Lerp(float x0, float x1, float v0, float v1)
{
float dv = v0 - v1;
float t;
if (dv * dv < Settings.Epsilon)
t = 0.5f;
else t = v0 / dv;
return x0 + t * (x1 - x0);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/** Recursive linear interpolation for use in marching squares **/
private static float Xlerp(float x0, float x1, float y, float v0, float v1, sbyte[,] f, int c)
{
float xm = Lerp(x0, x1, v0, v1);
if (c == 0)
return xm;
sbyte vm = f[(int)xm, (int)y];
if (v0 * vm < 0)
return Xlerp(x0, xm, y, v0, vm, f, c - 1);
return Xlerp(xm, x1, y, vm, v1, f, c - 1);
}
/** Recursive linear interpolation for use in marching squares **/
private static float Ylerp(float y0, float y1, float x, float v0, float v1, sbyte[,] f, int c)
{
float ym = Lerp(y0, y1, v0, v1);
if (c == 0)
return ym;
sbyte vm = f[(int)x, (int)ym];
if (v0 * vm < 0)
return Ylerp(y0, ym, x, v0, vm, f, c - 1);
return Ylerp(ym, y1, x, vm, v1, f, c - 1);
}
/** Square value for use in marching squares **/
private static float Square(float x)
{
return x * x;
}
private static float VecDsq(Vector2 a, Vector2 b)
{
Vector2 d = a - b;
return d.X * d.X + d.Y * d.Y;
}
private static float VecCross(Vector2 a, Vector2 b)
{
return a.X * b.Y - a.Y * b.X;
}
/** Look-up table to relate polygon key with the vertices that should be used for
the sub polygon in marching squares
**/
/** Perform a single celled marching square for for the given cell defined by (x0,y0) (x1,y1)
using the function f for recursive interpolation, given the look-up table 'fs' of
the values of 'f' at cell vertices with the result to be stored in 'poly' given the actual
coordinates of 'ax' 'ay' in the marching squares mesh.
**/
private static int MarchSquare(sbyte[,] f, sbyte[,] fs, ref GeomPoly poly, int ax, int ay, float x0, float y0,
float x1, float y1, int bin)
{
//key lookup
int key = 0;
sbyte v0 = fs[ax, ay];
if (v0 < 0) key |= 8;
sbyte v1 = fs[ax + 1, ay];
if (v1 < 0) key |= 4;
sbyte v2 = fs[ax + 1, ay + 1];
if (v2 < 0) key |= 2;
sbyte v3 = fs[ax, ay + 1];
if (v3 < 0) key |= 1;
int val = _lookMarch[key];
if (val != 0)
{
CxFastListNode<Vector2> pi = null;
for (int i = 0; i < 8; i++)
{
Vector2 p;
if ((val & (1 << i)) != 0)
{
if (i == 7 && (val & 1) == 0)
poly.Points.Add(p = new Vector2(x0, Ylerp(y0, y1, x0, v0, v3, f, bin)));
else
{
if (i == 0) p = new Vector2(x0, y0);
else if (i == 2) p = new Vector2(x1, y0);
else if (i == 4) p = new Vector2(x1, y1);
else if (i == 6) p = new Vector2(x0, y1);
else if (i == 1) p = new Vector2(Xlerp(x0, x1, y0, v0, v1, f, bin), y0);
else if (i == 5) p = new Vector2(Xlerp(x0, x1, y1, v3, v2, f, bin), y1);
else if (i == 3) p = new Vector2(x1, Ylerp(y0, y1, x1, v1, v2, f, bin));
else p = new Vector2(x0, Ylerp(y0, y1, x0, v0, v3, f, bin));
pi = poly.Points.Insert(pi, p);
}
poly.Length++;
}
}
//poly.simplify(float.Epsilon,float.Epsilon);
}
return key;
}
/** Used in polygon composition to composit polygons into scan lines
Combining polya and polyb into one super-polygon stored in polya.
**/
private static void combLeft(ref GeomPoly polya, ref GeomPoly polyb)
{
CxFastList<Vector2> ap = polya.Points;
CxFastList<Vector2> bp = polyb.Points;
CxFastListNode<Vector2> ai = ap.Begin();
CxFastListNode<Vector2> bi = bp.Begin();
Vector2 b = bi.Elem();
CxFastListNode<Vector2> prea = null;
while (ai != ap.End())
{
Vector2 a = ai.Elem();
if (VecDsq(a, b) < Settings.Epsilon)
{
//ignore shared vertex if parallel
if (prea != null)
{
Vector2 a0 = prea.Elem();
b = bi.Next().Elem();
Vector2 u = a - a0;
//vec_new(u); vec_sub(a.p.p, a0.p.p, u);
Vector2 v = b - a;
//vec_new(v); vec_sub(b.p.p, a.p.p, v);
float dot = VecCross(u, v);
if (dot * dot < Settings.Epsilon)
{
ap.Erase(prea, ai);
polya.Length--;
ai = prea;
}
}
//insert polyb into polya
bool fst = true;
CxFastListNode<Vector2> preb = null;
while (!bp.Empty())
{
Vector2 bb = bp.Front();
bp.Pop();
if (!fst && !bp.Empty())
{
ai = ap.Insert(ai, bb);
polya.Length++;
preb = ai;
}
fst = false;
}
//ignore shared vertex if parallel
ai = ai.Next();
Vector2 a1 = ai.Elem();
ai = ai.Next();
if (ai == ap.End()) ai = ap.Begin();
Vector2 a2 = ai.Elem();
Vector2 a00 = preb.Elem();
Vector2 uu = a1 - a00;
//vec_new(u); vec_sub(a1.p, a0.p, u);
Vector2 vv = a2 - a1;
//vec_new(v); vec_sub(a2.p, a1.p, v);
float dot1 = VecCross(uu, vv);
if (dot1 * dot1 < Settings.Epsilon)
{
ap.Erase(preb, preb.Next());
polya.Length--;
}
return;
}
prea = ai;
ai = ai.Next();
}
}
#endregion
#region CxFastList from nape physics
#region Nested type: CxFastList
/// <summary>
/// Designed as a complete port of CxFastList from CxStd.
/// </summary>
internal class CxFastList<T>
{
// first node in the list
private CxFastListNode<T> _head;
private int _count;
/// <summary>
/// Iterator to start of list (O(1))
/// </summary>
public CxFastListNode<T> Begin()
{
return _head;
}
/// <summary>
/// Iterator to end of list (O(1))
/// </summary>
public CxFastListNode<T> End()
{
return null;
}
/// <summary>
/// Returns first element of list (O(1))
/// </summary>
public T Front()
{
return _head.Elem();
}
/// <summary>
/// add object to list (O(1))
/// </summary>
public CxFastListNode<T> Add(T value)
{
CxFastListNode<T> newNode = new CxFastListNode<T>(value);
if (_head == null)
{
newNode._next = null;
_head = newNode;
_count++;
return newNode;
}
newNode._next = _head;
_head = newNode;
_count++;
return newNode;
}
/// <summary>
/// remove object from list, returns true if an element was removed (O(n))
/// </summary>
public bool Remove(T value)
{
CxFastListNode<T> head = _head;
CxFastListNode<T> prev = _head;
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
if (head != null)
{
if (value != null)
{
do
{
// if we are on the value to be removed
if (comparer.Equals(head._elt, value))
{
// then we need to patch the list
// check to see if we are removing the _head
if (head == _head)
{
_head = head._next;
_count--;
return true;
}
else
{
// were not at the head
prev._next = head._next;
_count--;
return true;
}
}
// cache the current as the previous for the next go around
prev = head;
head = head._next;
} while (head != null);
}
}
return false;
}
/// <summary>
/// pop element from head of list (O(1)) Note: this does not return the object popped!
/// There is good reason to this, and it regards the Alloc list variants which guarantee
/// objects are released to the object pool. You do not want to retrieve an element
/// through pop or else that object may suddenly be used by another piece of code which
/// retrieves it from the object pool.
/// </summary>
public CxFastListNode<T> Pop()
{
return Erase(null, _head);
}
/// <summary>
/// insert object after 'node' returning an iterator to the inserted object.
/// </summary>
public CxFastListNode<T> Insert(CxFastListNode<T> node, T value)
{
if (node == null)
{
return Add(value);
}
CxFastListNode<T> newNode = new CxFastListNode<T>(value);
CxFastListNode<T> nextNode = node._next;
newNode._next = nextNode;
node._next = newNode;
_count++;
return newNode;
}
/// <summary>
/// removes the element pointed to by 'node' with 'prev' being the previous iterator,
/// returning an iterator to the element following that of 'node' (O(1))
/// </summary>
public CxFastListNode<T> Erase(CxFastListNode<T> prev, CxFastListNode<T> node)
{
// cache the node after the node to be removed
CxFastListNode<T> nextNode = node._next;
if (prev != null)
prev._next = nextNode;
else if (_head != null)
_head = _head._next;
else
return null;
_count--;
return nextNode;
}
/// <summary>
/// whether the list is empty (O(1))
/// </summary>
public bool Empty()
{
if (_head == null)
return true;
return false;
}
/// <summary>
/// computes size of list (O(n))
/// </summary>
public int Size()
{
CxFastListNode<T> i = Begin();
int count = 0;
do
{
count++;
} while (i.Next() != null);
return count;
}
/// <summary>
/// empty the list (O(1) if CxMixList, O(n) otherwise)
/// </summary>
public void Clear()
{
CxFastListNode<T> head = _head;
while (head != null)
{
CxFastListNode<T> node2 = head;
head = head._next;
node2._next = null;
}
_head = null;
_count = 0;
}
/// <summary>
/// returns true if 'value' is an element of the list (O(n))
/// </summary>
public bool Has(T value)
{
return (Find(value) != null);
}
// Non CxFastList Methods
public CxFastListNode<T> Find(T value)
{
// start at head
CxFastListNode<T> head = _head;
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
if (head != null)
{
if (value != null)
{
do
{
if (comparer.Equals(head._elt, value))
{
return head;
}
head = head._next;
} while (head != _head);
}
else
{
do
{
if (head._elt == null)
{
return head;
}
head = head._next;
} while (head != _head);
}
}
return null;
}
public List<T> GetListOfElements()
{
List<T> list = new List<T>();
CxFastListNode<T> iter = Begin();
if (iter != null)
{
do
{
list.Add(iter._elt);
iter = iter._next;
} while (iter != null);
}
return list;
}
}
#endregion
#region Nested type: CxFastListNode
internal class CxFastListNode<T>
{
internal T _elt;
internal CxFastListNode<T> _next;
public CxFastListNode(T obj)
{
_elt = obj;
}
public T Elem()
{
return _elt;
}
public CxFastListNode<T> Next()
{
return _next;
}
}
#endregion
#endregion
#region Internal Stuff
#region Nested type: GeomPoly
internal class GeomPoly
{
public int Length;
public CxFastList<Vector2> Points;
public GeomPoly()
{
Points = new CxFastList<Vector2>();
Length = 0;
}
}
#endregion
#region Nested type: GeomPolyVal
private class GeomPolyVal
{
/** Associated polygon at coordinate **/
/** Key of original sub-polygon **/
public int Key;
public GeomPoly GeomP;
public GeomPolyVal(GeomPoly geomP, int K)
{
GeomP = geomP;
Key = K;
}
}
#endregion
#endregion
}
}

File diff suppressed because it is too large Load Diff

955
axios/Common/Vertices.cs Normal file
View File

@@ -0,0 +1,955 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using FarseerPhysics.Collision;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common
{
#if !(XBOX360)
[DebuggerDisplay("Count = {Count} Vertices = {ToString()}")]
#endif
public class Vertices : List<Vector2>
{
public Vertices()
{
}
public Vertices(int capacity)
{
Capacity = capacity;
}
public Vertices(Vector2[] vector2)
{
for (int i = 0; i < vector2.Length; i++)
{
Add(vector2[i]);
}
}
public Vertices(IList<Vector2> vertices)
{
for (int i = 0; i < vertices.Count; i++)
{
Add(vertices[i]);
}
}
/// <summary>
/// Nexts the index.
/// </summary>
/// <param name="index">The index.</param>
/// <returns></returns>
public int NextIndex(int index)
{
if (index == Count - 1)
{
return 0;
}
return index + 1;
}
public Vector2 NextVertex(int index)
{
return this[NextIndex(index)];
}
/// <summary>
/// Gets the previous index.
/// </summary>
/// <param name="index">The index.</param>
/// <returns></returns>
public int PreviousIndex(int index)
{
if (index == 0)
{
return Count - 1;
}
return index - 1;
}
public Vector2 PreviousVertex(int index)
{
return this[PreviousIndex(index)];
}
/// <summary>
/// Gets the signed area.
/// </summary>
/// <returns></returns>
public float GetSignedArea()
{
int i;
float area = 0;
for (i = 0; i < Count; i++)
{
int j = (i + 1) % Count;
area += this[i].X * this[j].Y;
area -= this[i].Y * this[j].X;
}
area /= 2.0f;
return area;
}
/// <summary>
/// Gets the area.
/// </summary>
/// <returns></returns>
public float GetArea()
{
int i;
float area = 0;
for (i = 0; i < Count; i++)
{
int j = (i + 1) % Count;
area += this[i].X * this[j].Y;
area -= this[i].Y * this[j].X;
}
area /= 2.0f;
return (area < 0 ? -area : area);
}
/// <summary>
/// Gets the centroid.
/// </summary>
/// <returns></returns>
public Vector2 GetCentroid()
{
// Same algorithm is used by Box2D
Vector2 c = Vector2.Zero;
float area = 0.0f;
const float inv3 = 1.0f / 3.0f;
Vector2 pRef = Vector2.Zero;
for (int i = 0; i < Count; ++i)
{
// Triangle vertices.
Vector2 p1 = pRef;
Vector2 p2 = this[i];
Vector2 p3 = i + 1 < Count ? this[i + 1] : this[0];
Vector2 e1 = p2 - p1;
Vector2 e2 = p3 - p1;
float D = MathUtils.Cross(e1, e2);
float triangleArea = 0.5f * D;
area += triangleArea;
// Area weighted centroid
c += triangleArea * inv3 * (p1 + p2 + p3);
}
// Centroid
c *= 1.0f / area;
return c;
}
/// <summary>
/// Gets the radius based on area.
/// </summary>
/// <returns></returns>
public float GetRadius()
{
float area = GetSignedArea();
double radiusSqrd = (double)area / MathHelper.Pi;
if (radiusSqrd < 0)
{
radiusSqrd *= -1;
}
return (float)Math.Sqrt(radiusSqrd);
}
/// <summary>
/// Returns an AABB for vertex.
/// </summary>
/// <returns></returns>
public AABB GetCollisionBox()
{
AABB aabb;
Vector2 lowerBound = new Vector2(float.MaxValue, float.MaxValue);
Vector2 upperBound = new Vector2(float.MinValue, float.MinValue);
for (int i = 0; i < Count; ++i)
{
if (this[i].X < lowerBound.X)
{
lowerBound.X = this[i].X;
}
if (this[i].X > upperBound.X)
{
upperBound.X = this[i].X;
}
if (this[i].Y < lowerBound.Y)
{
lowerBound.Y = this[i].Y;
}
if (this[i].Y > upperBound.Y)
{
upperBound.Y = this[i].Y;
}
}
aabb.LowerBound = lowerBound;
aabb.UpperBound = upperBound;
return aabb;
}
public void Translate(Vector2 vector)
{
Translate(ref vector);
}
/// <summary>
/// Translates the vertices with the specified vector.
/// </summary>
/// <param name="vector">The vector.</param>
public void Translate(ref Vector2 vector)
{
for (int i = 0; i < Count; i++)
this[i] = Vector2.Add(this[i], vector);
}
/// <summary>
/// Scales the vertices with the specified vector.
/// </summary>
/// <param name="value">The Value.</param>
public void Scale(ref Vector2 value)
{
for (int i = 0; i < Count; i++)
this[i] = Vector2.Multiply(this[i], value);
}
/// <summary>
/// Rotate the vertices with the defined value in radians.
/// </summary>
/// <param name="value">The amount to rotate by in radians.</param>
public void Rotate(float value)
{
Matrix rotationMatrix;
Matrix.CreateRotationZ(value, out rotationMatrix);
for (int i = 0; i < Count; i++)
this[i] = Vector2.Transform(this[i], rotationMatrix);
}
/// <summary>
/// Assuming the polygon is simple; determines whether the polygon is convex.
/// NOTE: It will also return false if the input contains colinear edges.
/// </summary>
/// <returns>
/// <c>true</c> if it is convex; otherwise, <c>false</c>.
/// </returns>
public bool IsConvex()
{
// Ensure the polygon is convex and the interior
// is to the left of each edge.
for (int i = 0; i < Count; ++i)
{
int i1 = i;
int i2 = i + 1 < Count ? i + 1 : 0;
Vector2 edge = this[i2] - this[i1];
for (int j = 0; j < Count; ++j)
{
// Don't check vertices on the current edge.
if (j == i1 || j == i2)
{
continue;
}
Vector2 r = this[j] - this[i1];
float s = edge.X * r.Y - edge.Y * r.X;
if (s <= 0.0f)
return false;
}
}
return true;
}
public bool IsCounterClockWise()
{
//We just return true for lines
if (Count < 3)
return true;
return (GetSignedArea() > 0.0f);
}
/// <summary>
/// Forces counter clock wise order.
/// </summary>
public void ForceCounterClockWise()
{
if (!IsCounterClockWise())
{
Reverse();
}
}
/// <summary>
/// Check for edge crossings
/// </summary>
/// <returns></returns>
public bool IsSimple()
{
for (int i = 0; i < Count; ++i)
{
int iplus = (i + 1 > Count - 1) ? 0 : i + 1;
Vector2 a1 = new Vector2(this[i].X, this[i].Y);
Vector2 a2 = new Vector2(this[iplus].X, this[iplus].Y);
for (int j = i + 1; j < Count; ++j)
{
int jplus = (j + 1 > Count - 1) ? 0 : j + 1;
Vector2 b1 = new Vector2(this[j].X, this[j].Y);
Vector2 b2 = new Vector2(this[jplus].X, this[jplus].Y);
Vector2 temp;
if (LineTools.LineIntersect2(a1, a2, b1, b2, out temp))
{
return false;
}
}
}
return true;
}
//TODO: Test
//Implementation found here: http://www.gamedev.net/community/forums/topic.asp?topic_id=548477
public bool IsSimple2()
{
for (int i = 0; i < Count; ++i)
{
if (i < Count - 1)
{
for (int h = i + 1; h < Count; ++h)
{
// Do two vertices lie on top of one another?
if (this[i] == this[h])
{
return true;
}
}
}
int j = (i + 1) % Count;
Vector2 iToj = this[j] - this[i];
Vector2 iTojNormal = new Vector2(iToj.Y, -iToj.X);
// i is the first vertex and j is the second
int startK = (j + 1) % Count;
int endK = (i - 1 + Count) % Count;
endK += startK < endK ? 0 : startK + 1;
int k = startK;
Vector2 iTok = this[k] - this[i];
bool onLeftSide = Vector2.Dot(iTok, iTojNormal) >= 0;
Vector2 prevK = this[k];
++k;
for (; k <= endK; ++k)
{
int modK = k % Count;
iTok = this[modK] - this[i];
if (onLeftSide != Vector2.Dot(iTok, iTojNormal) >= 0)
{
Vector2 prevKtoK = this[modK] - prevK;
Vector2 prevKtoKNormal = new Vector2(prevKtoK.Y, -prevKtoK.X);
if ((Vector2.Dot(this[i] - prevK, prevKtoKNormal) >= 0) !=
(Vector2.Dot(this[j] - prevK, prevKtoKNormal) >= 0))
{
return true;
}
}
onLeftSide = Vector2.Dot(iTok, iTojNormal) > 0;
prevK = this[modK];
}
}
return false;
}
// From Eric Jordan's convex decomposition library
/// <summary>
/// Checks if polygon is valid for use in Box2d engine.
/// Last ditch effort to ensure no invalid polygons are
/// added to world geometry.
///
/// Performs a full check, for simplicity, convexity,
/// orientation, minimum angle, and volume. This won't
/// be very efficient, and a lot of it is redundant when
/// other tools in this section are used.
/// </summary>
/// <returns></returns>
public bool CheckPolygon()
{
int error = -1;
if (Count < 3 || Count > Settings.MaxPolygonVertices)
{
error = 0;
}
if (!IsConvex())
{
error = 1;
}
if (!IsSimple())
{
error = 2;
}
if (GetArea() < Settings.Epsilon)
{
error = 3;
}
//Compute normals
Vector2[] normals = new Vector2[Count];
Vertices vertices = new Vertices(Count);
for (int i = 0; i < Count; ++i)
{
vertices.Add(new Vector2(this[i].X, this[i].Y));
int i1 = i;
int i2 = i + 1 < Count ? i + 1 : 0;
Vector2 edge = new Vector2(this[i2].X - this[i1].X, this[i2].Y - this[i1].Y);
normals[i] = MathUtils.Cross(edge, 1.0f);
normals[i].Normalize();
}
//Required side checks
for (int i = 0; i < Count; ++i)
{
int iminus = (i == 0) ? Count - 1 : i - 1;
//Parallel sides check
float cross = MathUtils.Cross(normals[iminus], normals[i]);
cross = MathUtils.Clamp(cross, -1.0f, 1.0f);
float angle = (float)Math.Asin(cross);
if (angle <= Settings.AngularSlop)
{
error = 4;
break;
}
//Too skinny check
for (int j = 0; j < Count; ++j)
{
if (j == i || j == (i + 1) % Count)
{
continue;
}
float s = Vector2.Dot(normals[i], vertices[j] - vertices[i]);
if (s >= -Settings.LinearSlop)
{
error = 5;
}
}
Vector2 centroid = vertices.GetCentroid();
Vector2 n1 = normals[iminus];
Vector2 n2 = normals[i];
Vector2 v = vertices[i] - centroid;
Vector2 d = new Vector2();
d.X = Vector2.Dot(n1, v); // - toiSlop;
d.Y = Vector2.Dot(n2, v); // - toiSlop;
// Shifting the edge inward by toiSlop should
// not cause the plane to pass the centroid.
if ((d.X < 0.0f) || (d.Y < 0.0f))
{
error = 6;
}
}
if (error != -1)
{
Debug.WriteLine("Found invalid polygon, ");
switch (error)
{
case 0:
Debug.WriteLine(string.Format("must have between 3 and {0} vertices.\n",
Settings.MaxPolygonVertices));
break;
case 1:
Debug.WriteLine("must be convex.\n");
break;
case 2:
Debug.WriteLine("must be simple (cannot intersect itself).\n");
break;
case 3:
Debug.WriteLine("area is too small.\n");
break;
case 4:
Debug.WriteLine("sides are too close to parallel.\n");
break;
case 5:
Debug.WriteLine("polygon is too thin.\n");
break;
case 6:
Debug.WriteLine("core shape generation would move edge past centroid (too thin).\n");
break;
default:
Debug.WriteLine("don't know why.\n");
break;
}
}
return error != -1;
}
// From Eric Jordan's convex decomposition library
/// <summary>
/// Trace the edge of a non-simple polygon and return a simple polygon.
///
/// Method:
/// Start at vertex with minimum y (pick maximum x one if there are two).
/// We aim our "lastDir" vector at (1.0, 0)
/// We look at the two rays going off from our start vertex, and follow whichever
/// has the smallest angle (in -Pi . Pi) wrt lastDir ("rightest" turn)
/// Loop until we hit starting vertex:
/// We add our current vertex to the list.
/// We check the seg from current vertex to next vertex for intersections
/// - if no intersections, follow to next vertex and continue
/// - if intersections, pick one with minimum distance
/// - if more than one, pick one with "rightest" next point (two possibilities for each)
/// </summary>
/// <param name="verts">The vertices.</param>
/// <returns></returns>
public Vertices TraceEdge(Vertices verts)
{
PolyNode[] nodes = new PolyNode[verts.Count * verts.Count];
//overkill, but sufficient (order of mag. is right)
int nNodes = 0;
//Add base nodes (raw outline)
for (int i = 0; i < verts.Count; ++i)
{
Vector2 pos = new Vector2(verts[i].X, verts[i].Y);
nodes[i].Position = pos;
++nNodes;
int iplus = (i == verts.Count - 1) ? 0 : i + 1;
int iminus = (i == 0) ? verts.Count - 1 : i - 1;
nodes[i].AddConnection(nodes[iplus]);
nodes[i].AddConnection(nodes[iminus]);
}
//Process intersection nodes - horribly inefficient
bool dirty = true;
int counter = 0;
while (dirty)
{
dirty = false;
for (int i = 0; i < nNodes; ++i)
{
for (int j = 0; j < nodes[i].NConnected; ++j)
{
for (int k = 0; k < nNodes; ++k)
{
if (k == i || nodes[k] == nodes[i].Connected[j]) continue;
for (int l = 0; l < nodes[k].NConnected; ++l)
{
if (nodes[k].Connected[l] == nodes[i].Connected[j] ||
nodes[k].Connected[l] == nodes[i]) continue;
//Check intersection
Vector2 intersectPt;
bool crosses = LineTools.LineIntersect(nodes[i].Position, nodes[i].Connected[j].Position,
nodes[k].Position, nodes[k].Connected[l].Position,
out intersectPt);
if (crosses)
{
dirty = true;
//Destroy and re-hook connections at crossing point
PolyNode connj = nodes[i].Connected[j];
PolyNode connl = nodes[k].Connected[l];
nodes[i].Connected[j].RemoveConnection(nodes[i]);
nodes[i].RemoveConnection(connj);
nodes[k].Connected[l].RemoveConnection(nodes[k]);
nodes[k].RemoveConnection(connl);
nodes[nNodes] = new PolyNode(intersectPt);
nodes[nNodes].AddConnection(nodes[i]);
nodes[i].AddConnection(nodes[nNodes]);
nodes[nNodes].AddConnection(nodes[k]);
nodes[k].AddConnection(nodes[nNodes]);
nodes[nNodes].AddConnection(connj);
connj.AddConnection(nodes[nNodes]);
nodes[nNodes].AddConnection(connl);
connl.AddConnection(nodes[nNodes]);
++nNodes;
goto SkipOut;
}
}
}
}
}
SkipOut:
++counter;
}
//Collapse duplicate points
bool foundDupe = true;
int nActive = nNodes;
while (foundDupe)
{
foundDupe = false;
for (int i = 0; i < nNodes; ++i)
{
if (nodes[i].NConnected == 0) continue;
for (int j = i + 1; j < nNodes; ++j)
{
if (nodes[j].NConnected == 0) continue;
Vector2 diff = nodes[i].Position - nodes[j].Position;
if (diff.LengthSquared() <= Settings.Epsilon * Settings.Epsilon)
{
if (nActive <= 3)
return new Vertices();
//printf("Found dupe, %d left\n",nActive);
--nActive;
foundDupe = true;
PolyNode inode = nodes[i];
PolyNode jnode = nodes[j];
//Move all of j's connections to i, and orphan j
int njConn = jnode.NConnected;
for (int k = 0; k < njConn; ++k)
{
PolyNode knode = jnode.Connected[k];
Debug.Assert(knode != jnode);
if (knode != inode)
{
inode.AddConnection(knode);
knode.AddConnection(inode);
}
knode.RemoveConnection(jnode);
}
jnode.NConnected = 0;
}
}
}
}
//Now walk the edge of the list
//Find node with minimum y value (max x if equal)
float minY = float.MaxValue;
float maxX = -float.MaxValue;
int minYIndex = -1;
for (int i = 0; i < nNodes; ++i)
{
if (nodes[i].Position.Y < minY && nodes[i].NConnected > 1)
{
minY = nodes[i].Position.Y;
minYIndex = i;
maxX = nodes[i].Position.X;
}
else if (nodes[i].Position.Y == minY && nodes[i].Position.X > maxX && nodes[i].NConnected > 1)
{
minYIndex = i;
maxX = nodes[i].Position.X;
}
}
Vector2 origDir = new Vector2(1.0f, 0.0f);
Vector2[] resultVecs = new Vector2[4 * nNodes];
// nodes may be visited more than once, unfortunately - change to growable array!
int nResultVecs = 0;
PolyNode currentNode = nodes[minYIndex];
PolyNode startNode = currentNode;
Debug.Assert(currentNode.NConnected > 0);
PolyNode nextNode = currentNode.GetRightestConnection(origDir);
if (nextNode == null)
{
Vertices vertices = new Vertices(nResultVecs);
for (int i = 0; i < nResultVecs; ++i)
{
vertices.Add(resultVecs[i]);
}
return vertices;
}
// Borked, clean up our mess and return
resultVecs[0] = startNode.Position;
++nResultVecs;
while (nextNode != startNode)
{
if (nResultVecs > 4 * nNodes)
{
Debug.Assert(false);
}
resultVecs[nResultVecs++] = nextNode.Position;
PolyNode oldNode = currentNode;
currentNode = nextNode;
nextNode = currentNode.GetRightestConnection(oldNode);
if (nextNode == null)
{
Vertices vertices = new Vertices(nResultVecs);
for (int i = 0; i < nResultVecs; ++i)
{
vertices.Add(resultVecs[i]);
}
return vertices;
}
// There was a problem, so jump out of the loop and use whatever garbage we've generated so far
}
return new Vertices();
}
private class PolyNode
{
private const int MaxConnected = 32;
/*
* Given sines and cosines, tells if A's angle is less than B's on -Pi, Pi
* (in other words, is A "righter" than B)
*/
public PolyNode[] Connected = new PolyNode[MaxConnected];
public int NConnected;
public Vector2 Position;
public PolyNode(Vector2 pos)
{
Position = pos;
NConnected = 0;
}
private bool IsRighter(float sinA, float cosA, float sinB, float cosB)
{
if (sinA < 0)
{
if (sinB > 0 || cosA <= cosB) return true;
else return false;
}
else
{
if (sinB < 0 || cosA <= cosB) return false;
else return true;
}
}
public void AddConnection(PolyNode toMe)
{
Debug.Assert(NConnected < MaxConnected);
// Ignore duplicate additions
for (int i = 0; i < NConnected; ++i)
{
if (Connected[i] == toMe) return;
}
Connected[NConnected] = toMe;
++NConnected;
}
public void RemoveConnection(PolyNode fromMe)
{
bool isFound = false;
int foundIndex = -1;
for (int i = 0; i < NConnected; ++i)
{
if (fromMe == Connected[i])
{
//.position == connected[i].position){
isFound = true;
foundIndex = i;
break;
}
}
Debug.Assert(isFound);
--NConnected;
for (int i = foundIndex; i < NConnected; ++i)
{
Connected[i] = Connected[i + 1];
}
}
public PolyNode GetRightestConnection(PolyNode incoming)
{
if (NConnected == 0) Debug.Assert(false); // This means the connection graph is inconsistent
if (NConnected == 1)
{
//b2Assert(false);
// Because of the possibility of collapsing nearby points,
// we may end up with "spider legs" dangling off of a region.
// The correct behavior here is to turn around.
return incoming;
}
Vector2 inDir = Position - incoming.Position;
float inLength = inDir.Length();
inDir.Normalize();
Debug.Assert(inLength > Settings.Epsilon);
PolyNode result = null;
for (int i = 0; i < NConnected; ++i)
{
if (Connected[i] == incoming) continue;
Vector2 testDir = Connected[i].Position - Position;
float testLengthSqr = testDir.LengthSquared();
testDir.Normalize();
Debug.Assert(testLengthSqr >= Settings.Epsilon * Settings.Epsilon);
float myCos = Vector2.Dot(inDir, testDir);
float mySin = MathUtils.Cross(inDir, testDir);
if (result != null)
{
Vector2 resultDir = result.Position - Position;
resultDir.Normalize();
float resCos = Vector2.Dot(inDir, resultDir);
float resSin = MathUtils.Cross(inDir, resultDir);
if (IsRighter(mySin, myCos, resSin, resCos))
{
result = Connected[i];
}
}
else
{
result = Connected[i];
}
}
Debug.Assert(result != null);
return result;
}
public PolyNode GetRightestConnection(Vector2 incomingDir)
{
Vector2 diff = Position - incomingDir;
PolyNode temp = new PolyNode(diff);
PolyNode res = GetRightestConnection(temp);
Debug.Assert(res != null);
return res;
}
}
public override string ToString()
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < Count; i++)
{
builder.Append(this[i].ToString());
if (i < Count - 1)
{
builder.Append(" ");
}
}
return builder.ToString();
}
/// <summary>
/// Projects to axis.
/// </summary>
/// <param name="axis">The axis.</param>
/// <param name="min">The min.</param>
/// <param name="max">The max.</param>
public void ProjectToAxis(ref Vector2 axis, out float min, out float max)
{
// To project a point on an axis use the dot product
float dotProduct = Vector2.Dot(axis, this[0]);
min = dotProduct;
max = dotProduct;
for (int i = 0; i < Count; i++)
{
dotProduct = Vector2.Dot(this[i], axis);
if (dotProduct < min)
{
min = dotProduct;
}
else
{
if (dotProduct > max)
{
max = dotProduct;
}
}
}
}
/// <summary>
/// Winding number test for a point in a polygon.
/// </summary>
/// See more info about the algorithm here: http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm
/// <param name="point">The point to be tested.</param>
/// <returns>-1 if the winding number is zero and the point is outside
/// the polygon, 1 if the point is inside the polygon, and 0 if the point
/// is on the polygons edge.</returns>
public int PointInPolygon(ref Vector2 point)
{
// Winding number
int wn = 0;
// Iterate through polygon's edges
for (int i = 0; i < Count; i++)
{
// Get points
Vector2 p1 = this[i];
Vector2 p2 = this[NextIndex(i)];
// Test if a point is directly on the edge
Vector2 edge = p2 - p1;
float area = MathUtils.Area(ref p1, ref p2, ref point);
if (area == 0f && Vector2.Dot(point - p1, edge) >= 0f && Vector2.Dot(point - p2, edge) <= 0f)
{
return 0;
}
// Test edge for intersection with ray from point
if (p1.Y <= point.Y)
{
if (p2.Y > point.Y && area > 0f)
{
++wn;
}
}
else
{
if (p2.Y <= point.Y && area < 0f)
{
--wn;
}
}
}
return (wn == 0 ? -1 : 1);
}
/// <summary>
/// Compute the sum of the angles made between the test point and each pair of points making up the polygon.
/// If this sum is 2pi then the point is an interior point, if 0 then the point is an exterior point.
/// ref: http://ozviz.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/ - Solution 2
/// </summary>
public bool PointInPolygonAngle(ref Vector2 point)
{
double angle = 0;
// Iterate through polygon's edges
for (int i = 0; i < Count; i++)
{
// Get points
Vector2 p1 = this[i] - point;
Vector2 p2 = this[NextIndex(i)] - point;
angle += MathUtils.VectorAngle(ref p1, ref p2);
}
if (Math.Abs(angle) < Math.PI)
{
return false;
}
return true;
}
}
}