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