Server job assigning logic, submarine movement syncing, submarine collision improvements, spawnpoints in levels

This commit is contained in:
Regalis
2015-07-08 11:37:47 +03:00
parent 3af9b8183b
commit d56f7f3f77
155 changed files with 39772 additions and 261 deletions
@@ -0,0 +1,143 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.ConvexHull
{
/// <summary>
/// Andrew's Monotone Chain Convex Hull algorithm.
/// Used to get the convex hull of a point cloud.
///
/// Source: http://www.softsurfer.com/Archive/algorithm_0109/algorithm_0109.htm
/// </summary>
public static class ChainHull
{
//Copyright 2001, softSurfer (www.softsurfer.com)
private static PointComparer _pointComparer = new PointComparer();
/// <summary>
/// Returns the convex hull from the given vertices..
/// </summary>
public static Vertices GetConvexHull(Vertices vertices)
{
if (vertices.Count <= 3)
return vertices;
Vertices pointSet = new Vertices(vertices);
//Sort by X-axis
pointSet.Sort(_pointComparer);
Vector2[] h = new Vector2[pointSet.Count];
Vertices res;
int 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
const int minmin = 0;
float xmin = pointSet[0].X;
for (i = 1; i < pointSet.Count; i++)
{
if (pointSet[i].X != xmin)
break;
}
// degenerate case: all x-coords == xmin
int minmax = i - 1;
if (minmax == pointSet.Count - 1)
{
h[++top] = pointSet[minmin];
if (pointSet[minmax].Y != pointSet[minmin].Y) // a nontrivial segment
h[++top] = pointSet[minmax];
h[++top] = pointSet[minmin]; // add polygon endpoint
res = new Vertices(top + 1);
for (int j = 0; j < top + 1; j++)
{
res.Add(h[j]);
}
return res;
}
top = -1;
// Get the indices of points with max x-coord and min|max y-coord
int maxmax = pointSet.Count - 1;
float xmax = pointSet[pointSet.Count - 1].X;
for (i = pointSet.Count - 2; i >= 0; i--)
{
if (pointSet[i].X != xmax)
break;
}
int maxmin = i + 1;
// Compute the lower hull on the stack H
h[++top] = pointSet[minmin]; // push minmin point onto stack
i = minmax;
while (++i <= maxmin)
{
// the lower line joins P[minmin] with P[maxmin]
if (MathUtils.Area(pointSet[minmin], pointSet[maxmin], pointSet[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], pointSet[i]) > 0)
break; // P[i] is a new hull vertex
top--; // pop top point off stack
}
h[++top] = pointSet[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] = pointSet[maxmax]; // push maxmax point onto stack
int bot = top;
i = maxmin;
while (--i >= minmax)
{
// the upper line joins P[maxmax] with P[minmax]
if (MathUtils.Area(pointSet[maxmax], pointSet[minmax], pointSet[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], pointSet[i]) > 0)
break; // P[i] is a new hull vertex
top--; // pop top point off stack
}
h[++top] = pointSet[i]; // push P[i] onto stack
}
if (minmax != minmin)
h[++top] = pointSet[minmin]; // push joining endpoint onto stack
res = new Vertices(top + 1);
for (int j = 0; j < top + 1; j++)
{
res.Add(h[j]);
}
return res;
}
private 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);
}
}
}
}
@@ -0,0 +1,90 @@
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.ConvexHull
{
/// <summary>
/// Giftwrap convex hull algorithm.
/// O(nh) time complexity, where n is the number of points and h is the number of points on the convex hull.
///
/// See http://en.wikipedia.org/wiki/Gift_wrapping_algorithm for more details.
/// </summary>
public static class GiftWrap
{
//Extracted from Box2D
/// <summary>
/// Returns the convex hull from the given vertices.
/// </summary>
/// <param name="vertices">The vertices.</param>
public static Vertices GetConvexHull(Vertices vertices)
{
if (vertices.Count <= 3)
return vertices;
// Find the right most point on the hull
int i0 = 0;
float x0 = vertices[0].X;
for (int i = 1; i < vertices.Count; ++i)
{
float x = vertices[i].X;
if (x > x0 || (x == x0 && vertices[i].Y < vertices[i0].Y))
{
i0 = i;
x0 = x;
}
}
int[] hull = new int[vertices.Count];
int m = 0;
int ih = i0;
for (; ; )
{
hull[m] = ih;
int ie = 0;
for (int j = 1; j < vertices.Count; ++j)
{
if (ie == ih)
{
ie = j;
continue;
}
Vector2 r = vertices[ie] - vertices[hull[m]];
Vector2 v = vertices[j] - vertices[hull[m]];
float c = MathUtils.Cross(ref r, ref v);
if (c < 0.0f)
{
ie = j;
}
// Collinearity check
if (c == 0.0f && v.LengthSquared() > r.LengthSquared())
{
ie = j;
}
}
++m;
ih = ie;
if (ie == i0)
{
break;
}
}
Vertices result = new Vertices(m);
// Copy vertices.
for (int i = 0; i < m; ++i)
{
result.Add(vertices[hull[i]]);
}
return result;
}
}
}
@@ -0,0 +1,132 @@
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.ConvexHull
{
/// <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>
public static class Melkman
{
//Melkman based convex hull algorithm contributed by Cowdozer
/// <summary>
/// Returns a convex hull from the given vertices.
/// </summary>
/// <returns>A convex hull in counter clockwise winding order.</returns>
public static Vertices GetConvexHull(Vertices vertices)
{
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
//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];
}
}
int qfm1 = qf == 0 ? deque.Length - 1 : qf - 1;
int qbm1 = qb == deque.Length - 1 ? 0 : 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
if (qb < qf)
{
Vertices convexHull = new Vertices(qf);
for (int i = qb; i < qf; i++)
convexHull.Add(deque[i]);
return convexHull;
}
else
{
Vertices convexHull = new Vertices(qf + deque.Length);
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;
}
}
}
}
+273
View File
@@ -0,0 +1,273 @@
#if !XNA && !WINDOWS_PHONE && !XBOX && !ANDROID
#region License
/*
MIT License
Copyright © 2006 The Mono.Xna Team
All rights reserved.
Authors:
Olivier Dufour (Duff)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#endregion License
using System;
namespace Microsoft.Xna.Framework
{
public enum CurveLoopType
{
Constant,
Cycle,
CycleOffset,
Oscillate,
Linear
}
public enum CurveContinuity
{
Smooth,
Step
}
public enum CurveTangent
{
Flat,
Linear,
Smooth
}
public class Curve
{
#region Private Fields
private CurveKeyCollection keys;
private CurveLoopType postLoop;
private CurveLoopType preLoop;
#endregion Private Fields
#region Public Properties
public bool IsConstant
{
get { return keys.Count <= 1; }
}
public CurveKeyCollection Keys
{
get { return keys; }
}
public CurveLoopType PostLoop
{
get { return postLoop; }
set { postLoop = value; }
}
public CurveLoopType PreLoop
{
get { return preLoop; }
set { preLoop = value; }
}
#endregion Public Properties
#region Public Constructors
public Curve()
{
keys = new CurveKeyCollection();
}
#endregion Public Constructors
#region Public Methods
public void ComputeTangent(int keyIndex, CurveTangent tangentInType, CurveTangent tangentOutType)
{
throw new NotImplementedException();
}
public void ComputeTangent(int keyIndex, CurveTangent tangentType)
{
ComputeTangent(keyIndex, tangentType, tangentType);
}
public void ComputeTangents(CurveTangent tangentInType, CurveTangent tangentOutType)
{
throw new NotImplementedException();
}
public void ComputeTangents(CurveTangent tangentType)
{
ComputeTangents(tangentType, tangentType);
}
public Curve Clone()
{
Curve curve = new Curve();
curve.keys = keys.Clone();
curve.preLoop = preLoop;
curve.postLoop = postLoop;
return curve;
}
public float Evaluate(float position)
{
CurveKey first = keys[0];
CurveKey last = keys[keys.Count - 1];
if (position < first.Position)
{
switch (PreLoop)
{
case CurveLoopType.Constant:
//constant
return first.Value;
case CurveLoopType.Linear:
// linear y = a*x +b with a tangeant of last point
return first.Value - first.TangentIn*(first.Position - position);
case CurveLoopType.Cycle:
//start -> end / start -> end
int cycle = GetNumberOfCycle(position);
float virtualPos = position - (cycle*(last.Position - first.Position));
return GetCurvePosition(virtualPos);
case CurveLoopType.CycleOffset:
//make the curve continue (with no step) so must up the curve each cycle of delta(value)
cycle = GetNumberOfCycle(position);
virtualPos = position - (cycle*(last.Position - first.Position));
return (GetCurvePosition(virtualPos) + cycle*(last.Value - first.Value));
case CurveLoopType.Oscillate:
//go back on curve from end and target start
// start-> end / end -> start
cycle = GetNumberOfCycle(position);
if (0 == cycle%2f) //if pair
virtualPos = position - (cycle*(last.Position - first.Position));
else
virtualPos = last.Position - position + first.Position +
(cycle*(last.Position - first.Position));
return GetCurvePosition(virtualPos);
}
}
else if (position > last.Position)
{
int cycle;
switch (PostLoop)
{
case CurveLoopType.Constant:
//constant
return last.Value;
case CurveLoopType.Linear:
// linear y = a*x +b with a tangeant of last point
return last.Value + first.TangentOut*(position - last.Position);
case CurveLoopType.Cycle:
//start -> end / start -> end
cycle = GetNumberOfCycle(position);
float virtualPos = position - (cycle*(last.Position - first.Position));
return GetCurvePosition(virtualPos);
case CurveLoopType.CycleOffset:
//make the curve continue (with no step) so must up the curve each cycle of delta(value)
cycle = GetNumberOfCycle(position);
virtualPos = position - (cycle*(last.Position - first.Position));
return (GetCurvePosition(virtualPos) + cycle*(last.Value - first.Value));
case CurveLoopType.Oscillate:
//go back on curve from end and target start
// start-> end / end -> start
cycle = GetNumberOfCycle(position);
virtualPos = position - (cycle*(last.Position - first.Position));
if (0 == cycle%2f) //if pair
virtualPos = position - (cycle*(last.Position - first.Position));
else
virtualPos = last.Position - position + first.Position +
(cycle*(last.Position - first.Position));
return GetCurvePosition(virtualPos);
}
}
//in curve
return GetCurvePosition(position);
}
#endregion Public Methods
#region Private Methods
private int GetNumberOfCycle(float position)
{
float cycle = (position - keys[0].Position)/(keys[keys.Count - 1].Position - keys[0].Position);
if (cycle < 0f)
cycle--;
return (int) cycle;
}
private float GetCurvePosition(float position)
{
//only for position in curve
CurveKey prev = keys[0];
CurveKey next;
for (int i = 1; i < keys.Count; i++)
{
next = Keys[i];
if (next.Position >= position)
{
if (prev.Continuity == CurveContinuity.Step)
{
if (position >= 1f)
{
return next.Value;
}
return prev.Value;
}
float t = (position - prev.Position)/(next.Position - prev.Position); //to have t in [0,1]
float ts = t*t;
float tss = ts*t;
//After a lot of search on internet I have found all about spline function
// and bezier (phi'sss ancien) but finaly use hermite curve
//http://en.wikipedia.org/wiki/Cubic_Hermite_spline
//P(t) = (2*t^3 - 3t^2 + 1)*P0 + (t^3 - 2t^2 + t)m0 + (-2t^3 + 3t^2)P1 + (t^3-t^2)m1
//with P0.value = prev.value , m0 = prev.tangentOut, P1= next.value, m1 = next.TangentIn
return (2*tss - 3*ts + 1f)*prev.Value + (tss - 2*ts + t)*prev.TangentOut + (3*ts - 2*tss)*next.Value +
(tss - ts)*next.TangentIn;
}
prev = next;
}
return 0f;
}
#endregion
}
}
#endif
@@ -0,0 +1,167 @@
#if !XNA && !WINDOWS_PHONE && !XBOX && !ANDROID
#region License
/*
MIT License
Copyright © 2006 The Mono.Xna Team
All rights reserved.
Authors:
Olivier Dufour (Duff)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#endregion License
using System;
namespace Microsoft.Xna.Framework
{
public class CurveKey : IEquatable<CurveKey>, IComparable<CurveKey>
{
#region Private Fields
private CurveContinuity continuity;
private float position;
private float tangentIn;
private float tangentOut;
private float value;
#endregion Private Fields
#region Properties
public CurveContinuity Continuity
{
get { return continuity; }
set { continuity = value; }
}
public float Position
{
get { return position; }
}
public float TangentIn
{
get { return tangentIn; }
set { tangentIn = value; }
}
public float TangentOut
{
get { return tangentOut; }
set { tangentOut = value; }
}
public float Value
{
get { return value; }
set { this.value = value; }
}
#endregion
#region Constructors
public CurveKey(float position, float value)
: this(position, value, 0, 0, CurveContinuity.Smooth)
{
}
public CurveKey(float position, float value, float tangentIn, float tangentOut)
: this(position, value, tangentIn, tangentOut, CurveContinuity.Smooth)
{
}
public CurveKey(float position, float value, float tangentIn, float tangentOut, CurveContinuity continuity)
{
this.position = position;
this.value = value;
this.tangentIn = tangentIn;
this.tangentOut = tangentOut;
this.continuity = continuity;
}
#endregion Constructors
#region Public Methods
#region IComparable<CurveKey> Members
public int CompareTo(CurveKey other)
{
return position.CompareTo(other.position);
}
#endregion
#region IEquatable<CurveKey> Members
public bool Equals(CurveKey other)
{
return (this == other);
}
#endregion
public static bool operator !=(CurveKey a, CurveKey b)
{
return !(a == b);
}
public static bool operator ==(CurveKey a, CurveKey b)
{
if (Equals(a, null))
return Equals(b, null);
if (Equals(b, null))
return Equals(a, null);
return (a.position == b.position)
&& (a.value == b.value)
&& (a.tangentIn == b.tangentIn)
&& (a.tangentOut == b.tangentOut)
&& (a.continuity == b.continuity);
}
public CurveKey Clone()
{
return new CurveKey(position, value, tangentIn, tangentOut, continuity);
}
public override bool Equals(object obj)
{
return (obj is CurveKey) ? ((CurveKey) obj) == this : false;
}
public override int GetHashCode()
{
return position.GetHashCode() ^ value.GetHashCode() ^ tangentIn.GetHashCode() ^
tangentOut.GetHashCode() ^ continuity.GetHashCode();
}
#endregion
}
}
#endif
@@ -0,0 +1,176 @@
#if !XNA && !WINDOWS_PHONE && !XBOX && !ANDROID
#region License
/*
MIT License
Copyright © 2006 The Mono.Xna Team
All rights reserved.
Authors:
Olivier Dufour (Duff)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#endregion License
using System;
using System.Collections;
using System.Collections.Generic;
namespace Microsoft.Xna.Framework
{
public class CurveKeyCollection : ICollection<CurveKey>, IEnumerable<CurveKey>, IEnumerable
{
#region Private Fields
private List<CurveKey> innerlist;
private bool isReadOnly = false;
#endregion Private Fields
#region Properties
public CurveKey this[int index]
{
get { return innerlist[index]; }
set
{
if (value == null)
throw new ArgumentNullException();
if (index >= innerlist.Count)
throw new IndexOutOfRangeException();
if (innerlist[index].Position == value.Position)
innerlist[index] = value;
else
{
innerlist.RemoveAt(index);
innerlist.Add(value);
}
}
}
public int Count
{
get { return innerlist.Count; }
}
public bool IsReadOnly
{
get { return isReadOnly; }
}
#endregion Properties
#region Constructors
public CurveKeyCollection()
{
innerlist = new List<CurveKey>();
}
#endregion Constructors
#region Public Methods
public void Add(CurveKey item)
{
if (item == null)
throw new ArgumentNullException("Value cannot be null.", (Exception) null);
if (innerlist.Count == 0)
{
innerlist.Add(item);
return;
}
for (int i = 0; i < innerlist.Count; i++)
{
if (item.Position < innerlist[i].Position)
{
innerlist.Insert(i, item);
return;
}
}
innerlist.Add(item);
}
public void Clear()
{
innerlist.Clear();
}
public bool Contains(CurveKey item)
{
return innerlist.Contains(item);
}
public void CopyTo(CurveKey[] array, int arrayIndex)
{
innerlist.CopyTo(array, arrayIndex);
}
public IEnumerator<CurveKey> GetEnumerator()
{
return innerlist.GetEnumerator();
}
public bool Remove(CurveKey item)
{
return innerlist.Remove(item);
}
IEnumerator IEnumerable.GetEnumerator()
{
return innerlist.GetEnumerator();
}
public CurveKeyCollection Clone()
{
CurveKeyCollection ckc = new CurveKeyCollection();
foreach (CurveKey key in innerlist)
ckc.Add(key);
return ckc;
}
public int IndexOf(CurveKey item)
{
return innerlist.IndexOf(item);
}
public void RemoveAt(int index)
{
if (index != Count && index > -1)
innerlist.RemoveAt(index);
else
throw new ArgumentOutOfRangeException(
"Index was out of range. Must be non-negative and less than the size of the collection.\r\nParameter name: index",
(Exception) null);
}
#endregion Public Methods
}
}
#endif
@@ -0,0 +1,243 @@
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.Decomposition
{
//From phed rev 36: http://code.google.com/p/phed/source/browse/trunk/Polygon.cpp
/// <summary>
/// Convex decomposition algorithm created by Mark Bayazit (http://mnbayazit.com/)
///
/// Properties:
/// - Tries to decompose using polygons instead of triangles.
/// - Tends to produce optimal results with low processing time.
/// - Running time is O(nr), n = number of vertices, r = reflex vertices.
/// - Does not support holes.
///
/// For more information about this algorithm, see http://mnbayazit.com/406/bayazit
/// </summary>
internal static class BayazitDecomposer
{
/// <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.
/// </summary>
public static List<Vertices> ConvexPartition(Vertices vertices)
{
Debug.Assert(vertices.Count > 3);
Debug.Assert(vertices.IsCounterClockWise());
return TriangulatePolygon(vertices);
}
private static List<Vertices> TriangulatePolygon(Vertices vertices)
{
List<Vertices> list = new List<Vertices>();
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))
{
float upperDist;
float lowerDist = upperDist = float.MaxValue;
for (int j = 0; j < vertices.Count; ++j)
{
// if line intersects with an edge
float d;
Vector2 p;
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 p = ((lowerInt + upperInt) / 2);
lowerPoly = Copy(i, upperIndex, vertices);
lowerPoly.Add(p);
upperPoly = Copy(lowerIndex, i, vertices);
upperPoly.Add(p);
}
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(TriangulatePolygon(lowerPoly));
list.AddRange(TriangulatePolygon(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(TriangulatePolygon(lowerPoly));
list.AddRange(TriangulatePolygon(upperPoly));
}
else
list.Add(vertices);
return list;
}
private static Vector2 At(int i, Vertices vertices)
{
int s = vertices.Count;
return vertices[i < 0 ? s - 1 - ((-i - 1) % s) : i % s];
}
private static Vertices Copy(int i, int j, Vertices vertices)
{
while (j < i)
j += vertices.Count;
Vertices p = new Vertices(j);
for (; i <= j; ++i)
{
p.Add(At(i, vertices));
}
return p;
}
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;
}
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;
}
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Delaunay.Sweep;
using FarseerPhysics.Common.Decomposition.CDT.Util;
namespace FarseerPhysics.Common.Decomposition.CDT.Delaunay
{
internal 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 Util.FixedArray3<DelaunayTriangle> Neighbors;
/** Has this triangle been marked as an interior triangle? */
public Util.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;
}
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Delaunay.Sweep
{
/**
* @author Thomas Åhlen (thahlen@gmail.com)
*/
internal 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 FarseerPhysics.Common.Decomposition.CDT.Delaunay.Sweep
{
internal class AdvancingFrontNode
{
public AdvancingFrontNode Next;
public TriangulationPoint Point;
public AdvancingFrontNode Prev;
public DelaunayTriangle Triangle;
public double Value;
public AdvancingFrontNode(TriangulationPoint point)
{
Point = point;
Value = point.X;
}
public bool HasNext
{
get { return Next != null; }
}
public bool HasPrev
{
get { return Prev != null; }
}
}
}
File diff suppressed because it is too large Load Diff
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Delaunay.Sweep
{
internal 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);
}
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Delaunay.Sweep
{
/**
*
* @author Thomas Åhlén, thahlen@gmail.com
*
*/
internal 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 FarseerPhysics.Common.Decomposition.CDT.Delaunay.Sweep
{
internal 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 FarseerPhysics.Common.Decomposition.CDT.Delaunay.Sweep
{
internal class PointOnEdgeException : NotImplementedException
{
public PointOnEdgeException(string message)
: base(message)
{
}
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Delaunay;
namespace FarseerPhysics.Common.Decomposition.CDT
{
internal 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();
}
}
@@ -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
{
internal enum Orientation
{
CW,
CCW,
Collinear
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Delaunay;
namespace FarseerPhysics.Common.Decomposition.CDT.Polygon
{
internal 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);
}
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Polygon
{
internal class PolygonPoint : TriangulationPoint
{
public PolygonPoint(double x, double y) : base(x, y)
{
}
public PolygonPoint Next { get; set; }
public PolygonPoint Previous { get; set; }
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Polygon
{
internal 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);
}
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.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
*/
internal class ConstrainedPointSet : PointSet
{
private List<TriangulationPoint> _constrainedPointList;
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;
}
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Delaunay;
namespace FarseerPhysics.Common.Decomposition.CDT.Sets
{
internal 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
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT
{
internal class TriangulationConstraint
{
public TriangulationPoint P;
public TriangulationPoint Q;
}
}
@@ -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 System.Runtime.CompilerServices;
using FarseerPhysics.Common.Decomposition.CDT.Delaunay;
namespace FarseerPhysics.Common.Decomposition.CDT
{
internal abstract class TriangulationContext
{
public readonly List<TriangulationPoint> Points = new List<TriangulationPoint>(200);
public readonly List<DelaunayTriangle> Triangles = new List<DelaunayTriangle>();
private int _stepTime = -1;
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;
}
}
}
@@ -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
{
internal enum TriangulationMode
{
Unconstrained,
Constrained,
Polygon
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Delaunay.Sweep;
namespace FarseerPhysics.Common.Decomposition.CDT
{
internal 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);
}
}
}
@@ -0,0 +1,175 @@
/* 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
{
/**
* @author Thomas Åhlén, thahlen@gmail.com
*/
internal 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;
}
*/
public static bool InScanArea(TriangulationPoint pa, TriangulationPoint pb, TriangulationPoint pc, TriangulationPoint pd)
{
double oadb = (pa.X - pb.X) * (pd.Y - pb.Y) - (pd.X - pb.X) * (pa.Y - pb.Y);
if (oadb >= -EPSILON)
{
return false;
}
double oadc = (pa.X - pc.X) * (pd.Y - pc.Y) - (pd.X - pc.X) * (pa.Y - pc.Y);
if (oadc <= EPSILON)
{
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;
}
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Util
{
internal 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];
}
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Util
{
internal 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];
}
}
}
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
namespace FarseerPhysics.Common.Decomposition.CDT.Util
{
internal 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;
}
}
}
@@ -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 FarseerPhysics.Common.Decomposition.CDT.Polygon;
namespace FarseerPhysics.Common.Decomposition.CDT.Util
{
internal 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);
}
}
}
@@ -0,0 +1,75 @@
/*
* Farseer Physics Engine:
* Copyright (c) 2012 Ian Qvist
*/
using System.Collections.Generic;
using System.Diagnostics;
using FarseerPhysics.Common.Decomposition.CDT;
using FarseerPhysics.Common.Decomposition.CDT.Delaunay;
using FarseerPhysics.Common.Decomposition.CDT.Delaunay.Sweep;
using FarseerPhysics.Common.Decomposition.CDT.Polygon;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.Decomposition
{
/// <summary>
/// 2D constrained Delaunay triangulation algorithm.
/// Based on the paper "Sweep-line algorithm for constrained Delaunay triangulation" by V. Domiter and and B. Zalik
///
/// Properties:
/// - Creates triangles with a large interior angle.
/// - Supports holes
/// - Generate a lot of garbage due to incapsulation of the Poly2Tri library.
/// - Running time is O(n^2), n = number of vertices.
/// - Does not care about winding order.
///
/// Source: http://code.google.com/p/poly2tri/
/// </summary>
internal static class CDTDecomposer
{
/// <summary>
/// Decompose the polygon into several smaller non-concave polygon.
/// </summary>
public static List<Vertices> ConvexPartition(Vertices vertices)
{
Debug.Assert(vertices.Count > 3);
Polygon poly = new Polygon();
foreach (Vector2 vertex in vertices)
poly.Points.Add(new TriangulationPoint(vertex.X, vertex.Y));
if (vertices.Holes != null)
{
foreach (Vertices holeVertices in vertices.Holes)
{
Polygon hole = new Polygon();
foreach (Vector2 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;
}
}
}
@@ -0,0 +1,403 @@
/*
* 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 System.Diagnostics;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.Decomposition
{
/// <summary>
/// Convex decomposition algorithm using ear clipping
///
/// Properties:
/// - Only works on simple polygons.
/// - Does not support holes.
/// - Running time is O(n^2), n = number of vertices.
///
/// Source: http://www.ewjordan.com/earClip/
/// </summary>
internal static class EarclipDecomposer
{
//box2D rev 32 - for details, see http://www.box2d.org/forum/viewtopic.php?f=4&t=83&start=50
/// <summary>
/// Decompose the polygon into several smaller non-concave polygon.
/// Each resulting polygon will have no more than Settings.MaxPolygonVertices vertices.
/// </summary>
/// <param name="vertices">The vertices.</param>
/// <param name="tolerance">The tolerance.</param>
public static List<Vertices> ConvexPartition(Vertices vertices, float tolerance = 0.001f)
{
Debug.Assert(vertices.Count > 3);
Debug.Assert(!vertices.IsCounterClockWise());
return TriangulatePolygon(vertices, tolerance);
}
/// <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.
/// </summary>
/// <remarks>
/// Only works on simple polygons.
/// </remarks>
private static List<Vertices> TriangulatePolygon(Vertices vertices, float tolerance)
{
//FPE note: Check is needed as invalid triangles can be returned in recursive calls.
if (vertices.Count < 3)
return new List<Vertices>();
List<Vertices> results = new List<Vertices>();
//Recurse and split on pinch points
Vertices pA, pB;
Vertices pin = new Vertices(vertices);
if (ResolvePinchPoint(pin, out pA, out pB, tolerance))
{
List<Vertices> mergeA = TriangulatePolygon(pA, tolerance);
List<Vertices> mergeB = TriangulatePolygon(pB, tolerance);
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 Vertices(mergeA[i]));
}
for (int i = 0; i < mergeB.Count; ++i)
{
results.Add(new Vertices(mergeB[i]));
}
return results;
}
Vertices[] buffer = new Vertices[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(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 Vertices(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>
/// <param name="tolerance"></param>
private static bool ResolvePinchPoint(Vertices pin, out Vertices poutA, out Vertices poutB, float tolerance)
{
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) < tolerance && Math.Abs(pin[i].Y - pin[j].Y) < tolerance && 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;
}
/// <summary>
/// Checks if vertex i is the tip of an ear in polygon defined by xv[] and yv[].
/// </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>
/// <remarks>
/// Assumes clockwise orientation of polygon.
/// </remarks>
/// <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;
}
private class Triangle : Vertices
{
//Constructor automatically fixes orientation to ccw
public Triangle(float x1, float y1, float x2, float y2, float x3, float y3)
{
float cross = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1);
if (cross > 0)
{
Add(new Vector2(x1, y1));
Add(new Vector2(x2, y2));
Add(new Vector2(x3, y3));
}
else
{
Add(new Vector2(x1, y1));
Add(new Vector2(x3, y3));
Add(new Vector2(x2, y2));
}
}
public bool IsInside(float x, float y)
{
Vector2 a = this[0];
Vector2 b = this[1];
Vector2 c = this[2];
if (x < a.X && x < b.X && x < c.X) return false;
if (x > a.X && x > b.X && x > c.X) return false;
if (y < a.Y && y < b.Y && y < c.Y) return false;
if (y > a.Y && y > b.Y && y > c.Y) return false;
float vx2 = x - a.X;
float vy2 = y - a.Y;
float vx1 = b.X - a.X;
float vy1 = b.Y - a.Y;
float vx0 = c.X - a.X;
float vy0 = c.Y - a.Y;
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));
}
}
}
}
@@ -0,0 +1,152 @@
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.Decomposition
{
/// <summary>
/// Convex decomposition algorithm created by unknown
///
/// Properties:
/// - No support for holes
/// - Very fast
/// - Only works on simple polygons
/// - Only works on counter clockwise polygons
///
/// More information: http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
/// </summary>
internal static class FlipcodeDecomposer
{
private static Vector2 _tmpA;
private static Vector2 _tmpB;
private static Vector2 _tmpC;
/// <summary>
/// Decompose the polygon into triangles.
///
/// Properties:
/// - Only works on counter clockwise polygons
///
/// </summary>
/// <param name="vertices">The list of points describing the polygon</param>
public static List<Vertices> ConvexPartition(Vertices vertices)
{
Debug.Assert(vertices.Count > 3);
Debug.Assert(vertices.IsCounterClockWise());
int[] polygon = new int[vertices.Count];
for (int v = 0; v < vertices.Count; v++)
polygon[v] = v;
int nv = vertices.Count;
// 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 = vertices[polygon[u]];
_tmpB = vertices[polygon[v]];
_tmpC = vertices[polygon[w]];
if (Snip(vertices, u, v, w, nv, polygon))
{
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++)
{
polygon[s] = polygon[t];
}
nv--;
// Reset error detection counter
count = 2 * nv;
}
}
return result;
}
/// <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;
}
}
}
@@ -0,0 +1,60 @@
using System.Collections.Generic;
namespace FarseerPhysics.Common.Decomposition.Seidel
{
internal class Edge
{
// Pointers used for building trapezoidal map
public Trapezoid Above;
public float B;
public Trapezoid Below;
// Montone mountain points
public HashSet<Point> MPoints;
public Point P;
public Point Q;
// Slope of the line (m)
public float Slope;
public Edge(Point p, Point q)
{
P = p;
Q = q;
if (q.X - p.X != 0)
Slope = (q.Y - p.Y) / (q.X - p.X);
else
Slope = 0;
B = p.Y - (p.X * Slope);
Above = null;
Below = null;
MPoints = new HashSet<Point>();
MPoints.Add(p);
MPoints.Add(q);
}
public bool IsAbove(Point point)
{
return P.Orient2D(Q, point) < 0;
}
public bool IsBelow(Point point)
{
return P.Orient2D(Q, point) > 0;
}
public void AddMpoint(Point point)
{
foreach (Point mp in MPoints)
{
if (!mp.Neq(point))
return;
}
MPoints.Add(point);
}
}
}
@@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace FarseerPhysics.Common.Decomposition.Seidel
{
internal class MonotoneMountain
{
// Almost Pi!
private const float PiSlop = 3.1f;
// Triangles that constitute the mountain
public List<List<Point>> Triangles;
private HashSet<Point> _convexPoints;
private Point _head;
// Monotone mountain points
private List<Point> _monoPoly;
// Used to track which side of the line we are on
private bool _positive;
private int _size;
private Point _tail;
public MonotoneMountain()
{
_size = 0;
_tail = null;
_head = null;
_positive = false;
_convexPoints = new HashSet<Point>();
_monoPoly = new List<Point>();
Triangles = new List<List<Point>>();
}
// Append a point to the list
public void Add(Point point)
{
if (_size == 0)
{
_head = point;
_size = 1;
}
else if (_size == 1)
{
// Keep repeat points out of the list
_tail = point;
_tail.Prev = _head;
_head.Next = _tail;
_size = 2;
}
else
{
// Keep repeat points out of the list
_tail.Next = point;
point.Prev = _tail;
_tail = point;
_size += 1;
}
}
// Remove a point from the list
public void Remove(Point point)
{
Point next = point.Next;
Point prev = point.Prev;
point.Prev.Next = next;
point.Next.Prev = prev;
_size -= 1;
}
// Partition a x-monotone mountain into triangles O(n)
// See "Computational Geometry in C", 2nd edition, by Joseph O'Rourke, page 52
public void Process()
{
// Establish the proper sign
_positive = AngleSign();
// create monotone polygon - for dubug purposes
GenMonoPoly();
// Initialize internal angles at each nonbase vertex
// Link strictly convex vertices into a list, ignore reflex vertices
Point p = _head.Next;
while (p.Neq(_tail))
{
float a = Angle(p);
// If the point is almost colinear with it's neighbor, remove it!
if (a >= PiSlop || a <= -PiSlop || a == 0.0f)
Remove(p);
else if (IsConvex(p))
_convexPoints.Add(p);
p = p.Next;
}
Triangulate();
}
private void Triangulate()
{
while (_convexPoints.Count != 0)
{
IEnumerator<Point> e = _convexPoints.GetEnumerator();
e.MoveNext();
Point ear = e.Current;
_convexPoints.Remove(ear);
Point a = ear.Prev;
Point b = ear;
Point c = ear.Next;
List<Point> triangle = new List<Point>(3);
triangle.Add(a);
triangle.Add(b);
triangle.Add(c);
Triangles.Add(triangle);
// Remove ear, update angles and convex list
Remove(ear);
if (Valid(a))
_convexPoints.Add(a);
if (Valid(c))
_convexPoints.Add(c);
}
Debug.Assert(_size <= 3, "Triangulation bug, please report");
}
private bool Valid(Point p)
{
return p.Neq(_head) && p.Neq(_tail) && IsConvex(p);
}
// Create the monotone polygon
private void GenMonoPoly()
{
Point p = _head;
while (p != null)
{
_monoPoly.Add(p);
p = p.Next;
}
}
private float Angle(Point p)
{
Point a = (p.Next - p);
Point b = (p.Prev - p);
return (float)Math.Atan2(a.Cross(b), a.Dot(b));
}
private bool AngleSign()
{
Point a = (_head.Next - _head);
Point b = (_tail - _head);
return Math.Atan2(a.Cross(b), a.Dot(b)) >= 0;
}
// Determines if the inslide angle is convex or reflex
private bool IsConvex(Point p)
{
if (_positive != (Angle(p) >= 0))
return false;
return true;
}
}
}
@@ -0,0 +1,41 @@
using System.Collections.Generic;
namespace FarseerPhysics.Common.Decomposition.Seidel
{
// Node for a Directed Acyclic graph (DAG)
internal abstract class Node
{
protected Node LeftChild;
public List<Node> ParentList;
protected Node RightChild;
protected Node(Node left, Node right)
{
ParentList = new List<Node>();
LeftChild = left;
RightChild = right;
if (left != null)
left.ParentList.Add(this);
if (right != null)
right.ParentList.Add(this);
}
public abstract Sink Locate(Edge s);
// Replace a node in the graph with this node
// Make sure parent pointers are updated
public void Replace(Node node)
{
foreach (Node parent in node.ParentList)
{
// Select the correct node to replace (left or right child)
if (parent.LeftChild == node)
parent.LeftChild = this;
else
parent.RightChild = this;
}
ParentList.AddRange(node.ParentList);
}
}
}
@@ -0,0 +1,61 @@
namespace FarseerPhysics.Common.Decomposition.Seidel
{
internal class Point
{
// Pointers to next and previous points in Monontone Mountain
public Point Next, Prev;
public float X, Y;
public Point(float x, float y)
{
X = x;
Y = y;
Next = null;
Prev = null;
}
public static Point operator -(Point p1, Point p2)
{
return new Point(p1.X - p2.X, p1.Y - p2.Y);
}
public static Point operator +(Point p1, Point p2)
{
return new Point(p1.X + p2.X, p1.Y + p2.Y);
}
public static Point operator -(Point p1, float f)
{
return new Point(p1.X - f, p1.Y - f);
}
public static Point operator +(Point p1, float f)
{
return new Point(p1.X + f, p1.Y + f);
}
public float Cross(Point p)
{
return X * p.Y - Y * p.X;
}
public float Dot(Point p)
{
return X * p.X + Y * p.Y;
}
public bool Neq(Point p)
{
return p.X != X || p.Y != Y;
}
public float Orient2D(Point pb, Point pc)
{
float acx = X - pc.X;
float bcx = pb.X - pc.X;
float acy = Y - pc.Y;
float bcy = pb.Y - pc.Y;
return acx * bcy - acy * bcx;
}
}
}
@@ -0,0 +1,78 @@
using System.Collections.Generic;
namespace FarseerPhysics.Common.Decomposition.Seidel
{
// Directed Acyclic graph (DAG)
// See "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
internal class QueryGraph
{
private Node _head;
public QueryGraph(Node head)
{
_head = head;
}
private Trapezoid Locate(Edge edge)
{
return _head.Locate(edge).Trapezoid;
}
public List<Trapezoid> FollowEdge(Edge edge)
{
List<Trapezoid> trapezoids = new List<Trapezoid>();
trapezoids.Add(Locate(edge));
int j = 0;
while (edge.Q.X > trapezoids[j].RightPoint.X)
{
if (edge.IsAbove(trapezoids[j].RightPoint))
{
trapezoids.Add(trapezoids[j].UpperRight);
}
else
{
trapezoids.Add(trapezoids[j].LowerRight);
}
j += 1;
}
return trapezoids;
}
private void Replace(Sink sink, Node node)
{
if (sink.ParentList.Count == 0)
_head = node;
else
node.Replace(sink);
}
public void Case1(Sink sink, Edge edge, Trapezoid[] tList)
{
YNode yNode = new YNode(edge, Sink.Isink(tList[1]), Sink.Isink(tList[2]));
XNode qNode = new XNode(edge.Q, yNode, Sink.Isink(tList[3]));
XNode pNode = new XNode(edge.P, Sink.Isink(tList[0]), qNode);
Replace(sink, pNode);
}
public void Case2(Sink sink, Edge edge, Trapezoid[] tList)
{
YNode yNode = new YNode(edge, Sink.Isink(tList[1]), Sink.Isink(tList[2]));
XNode pNode = new XNode(edge.P, Sink.Isink(tList[0]), yNode);
Replace(sink, pNode);
}
public void Case3(Sink sink, Edge edge, Trapezoid[] tList)
{
YNode yNode = new YNode(edge, Sink.Isink(tList[0]), Sink.Isink(tList[1]));
Replace(sink, yNode);
}
public void Case4(Sink sink, Edge edge, Trapezoid[] tList)
{
YNode yNode = new YNode(edge, Sink.Isink(tList[0]), Sink.Isink(tList[1]));
XNode qNode = new XNode(edge.Q, yNode, Sink.Isink(tList[2]));
Replace(sink, qNode);
}
}
}
@@ -0,0 +1,27 @@
namespace FarseerPhysics.Common.Decomposition.Seidel
{
internal class Sink : Node
{
public Trapezoid Trapezoid;
private Sink(Trapezoid trapezoid)
: base(null, null)
{
Trapezoid = trapezoid;
trapezoid.Sink = this;
}
public static Sink Isink(Trapezoid trapezoid)
{
if (trapezoid.Sink == null)
return new Sink(trapezoid);
return trapezoid.Sink;
}
public override Sink Locate(Edge edge)
{
return this;
}
}
}
@@ -0,0 +1,123 @@
using System.Collections.Generic;
namespace FarseerPhysics.Common.Decomposition.Seidel
{
internal class Trapezoid
{
public Edge Bottom;
public bool Inside;
public Point LeftPoint;
// Neighbor pointers
public Trapezoid LowerLeft;
public Trapezoid LowerRight;
public Point RightPoint;
public Sink Sink;
public Edge Top;
public Trapezoid UpperLeft;
public Trapezoid UpperRight;
public Trapezoid(Point leftPoint, Point rightPoint, Edge top, Edge bottom)
{
LeftPoint = leftPoint;
RightPoint = rightPoint;
Top = top;
Bottom = bottom;
UpperLeft = null;
UpperRight = null;
LowerLeft = null;
LowerRight = null;
Inside = true;
Sink = null;
}
// Update neighbors to the left
public void UpdateLeft(Trapezoid ul, Trapezoid ll)
{
UpperLeft = ul;
if (ul != null) ul.UpperRight = this;
LowerLeft = ll;
if (ll != null) ll.LowerRight = this;
}
// Update neighbors to the right
public void UpdateRight(Trapezoid ur, Trapezoid lr)
{
UpperRight = ur;
if (ur != null) ur.UpperLeft = this;
LowerRight = lr;
if (lr != null) lr.LowerLeft = this;
}
// Update neighbors on both sides
public void UpdateLeftRight(Trapezoid ul, Trapezoid ll, Trapezoid ur, Trapezoid lr)
{
UpperLeft = ul;
if (ul != null) ul.UpperRight = this;
LowerLeft = ll;
if (ll != null) ll.LowerRight = this;
UpperRight = ur;
if (ur != null) ur.UpperLeft = this;
LowerRight = lr;
if (lr != null) lr.LowerLeft = this;
}
// Recursively trim outside neighbors
public void TrimNeighbors()
{
if (Inside)
{
Inside = false;
if (UpperLeft != null) UpperLeft.TrimNeighbors();
if (LowerLeft != null) LowerLeft.TrimNeighbors();
if (UpperRight != null) UpperRight.TrimNeighbors();
if (LowerRight != null) LowerRight.TrimNeighbors();
}
}
// Determines if this point lies inside the trapezoid
public bool Contains(Point point)
{
return (point.X > LeftPoint.X && point.X < RightPoint.X && Top.IsAbove(point) && Bottom.IsBelow(point));
}
public List<Point> GetVertices()
{
List<Point> verts = new List<Point>(4);
verts.Add(LineIntersect(Top, LeftPoint.X));
verts.Add(LineIntersect(Bottom, LeftPoint.X));
verts.Add(LineIntersect(Bottom, RightPoint.X));
verts.Add(LineIntersect(Top, RightPoint.X));
return verts;
}
private Point LineIntersect(Edge edge, float x)
{
float y = edge.Slope * x + edge.B;
return new Point(x, y);
}
// Add points to monotone mountain
public void AddPoints()
{
if (LeftPoint != Bottom.P)
{
Bottom.AddMpoint(LeftPoint);
}
if (RightPoint != Bottom.Q)
{
Bottom.AddMpoint(RightPoint);
}
if (LeftPoint != Top.P)
{
Top.AddMpoint(LeftPoint);
}
if (RightPoint != Top.Q)
{
Top.AddMpoint(RightPoint);
}
}
}
}
@@ -0,0 +1,195 @@
using System.Collections.Generic;
namespace FarseerPhysics.Common.Decomposition.Seidel
{
internal class TrapezoidalMap
{
// Trapezoid container
public HashSet<Trapezoid> Map;
// Bottom segment that spans multiple trapezoids
private Edge _bCross;
// Top segment that spans multiple trapezoids
private Edge _cross;
// AABB margin
private float _margin;
public TrapezoidalMap()
{
Map = new HashSet<Trapezoid>();
_margin = 50.0f;
_bCross = null;
_cross = null;
}
public void Clear()
{
_bCross = null;
_cross = null;
}
// Case 1: segment completely enclosed by trapezoid
// break trapezoid into 4 smaller trapezoids
public Trapezoid[] Case1(Trapezoid t, Edge e)
{
Trapezoid[] trapezoids = new Trapezoid[4];
trapezoids[0] = new Trapezoid(t.LeftPoint, e.P, t.Top, t.Bottom);
trapezoids[1] = new Trapezoid(e.P, e.Q, t.Top, e);
trapezoids[2] = new Trapezoid(e.P, e.Q, e, t.Bottom);
trapezoids[3] = new Trapezoid(e.Q, t.RightPoint, t.Top, t.Bottom);
trapezoids[0].UpdateLeft(t.UpperLeft, t.LowerLeft);
trapezoids[1].UpdateLeftRight(trapezoids[0], null, trapezoids[3], null);
trapezoids[2].UpdateLeftRight(null, trapezoids[0], null, trapezoids[3]);
trapezoids[3].UpdateRight(t.UpperRight, t.LowerRight);
return trapezoids;
}
// Case 2: Trapezoid contains point p, q lies outside
// break trapezoid into 3 smaller trapezoids
public Trapezoid[] Case2(Trapezoid t, Edge e)
{
Point rp;
if (e.Q.X == t.RightPoint.X)
rp = e.Q;
else
rp = t.RightPoint;
Trapezoid[] trapezoids = new Trapezoid[3];
trapezoids[0] = new Trapezoid(t.LeftPoint, e.P, t.Top, t.Bottom);
trapezoids[1] = new Trapezoid(e.P, rp, t.Top, e);
trapezoids[2] = new Trapezoid(e.P, rp, e, t.Bottom);
trapezoids[0].UpdateLeft(t.UpperLeft, t.LowerLeft);
trapezoids[1].UpdateLeftRight(trapezoids[0], null, t.UpperRight, null);
trapezoids[2].UpdateLeftRight(null, trapezoids[0], null, t.LowerRight);
_bCross = t.Bottom;
_cross = t.Top;
e.Above = trapezoids[1];
e.Below = trapezoids[2];
return trapezoids;
}
// Case 3: Trapezoid is bisected
public Trapezoid[] Case3(Trapezoid t, Edge e)
{
Point lp;
if (e.P.X == t.LeftPoint.X)
lp = e.P;
else
lp = t.LeftPoint;
Point rp;
if (e.Q.X == t.RightPoint.X)
rp = e.Q;
else
rp = t.RightPoint;
Trapezoid[] trapezoids = new Trapezoid[2];
if (_cross == t.Top)
{
trapezoids[0] = t.UpperLeft;
trapezoids[0].UpdateRight(t.UpperRight, null);
trapezoids[0].RightPoint = rp;
}
else
{
trapezoids[0] = new Trapezoid(lp, rp, t.Top, e);
trapezoids[0].UpdateLeftRight(t.UpperLeft, e.Above, t.UpperRight, null);
}
if (_bCross == t.Bottom)
{
trapezoids[1] = t.LowerLeft;
trapezoids[1].UpdateRight(null, t.LowerRight);
trapezoids[1].RightPoint = rp;
}
else
{
trapezoids[1] = new Trapezoid(lp, rp, e, t.Bottom);
trapezoids[1].UpdateLeftRight(e.Below, t.LowerLeft, null, t.LowerRight);
}
_bCross = t.Bottom;
_cross = t.Top;
e.Above = trapezoids[0];
e.Below = trapezoids[1];
return trapezoids;
}
// Case 4: Trapezoid contains point q, p lies outside
// break trapezoid into 3 smaller trapezoids
public Trapezoid[] Case4(Trapezoid t, Edge e)
{
Point lp;
if (e.P.X == t.LeftPoint.X)
lp = e.P;
else
lp = t.LeftPoint;
Trapezoid[] trapezoids = new Trapezoid[3];
if (_cross == t.Top)
{
trapezoids[0] = t.UpperLeft;
trapezoids[0].RightPoint = e.Q;
}
else
{
trapezoids[0] = new Trapezoid(lp, e.Q, t.Top, e);
trapezoids[0].UpdateLeft(t.UpperLeft, e.Above);
}
if (_bCross == t.Bottom)
{
trapezoids[1] = t.LowerLeft;
trapezoids[1].RightPoint = e.Q;
}
else
{
trapezoids[1] = new Trapezoid(lp, e.Q, e, t.Bottom);
trapezoids[1].UpdateLeft(e.Below, t.LowerLeft);
}
trapezoids[2] = new Trapezoid(e.Q, t.RightPoint, t.Top, t.Bottom);
trapezoids[2].UpdateLeftRight(trapezoids[0], trapezoids[1], t.UpperRight, t.LowerRight);
return trapezoids;
}
// Create an AABB around segments
public Trapezoid BoundingBox(List<Edge> edges)
{
Point max = edges[0].P + _margin;
Point min = edges[0].Q - _margin;
foreach (Edge e in edges)
{
if (e.P.X > max.X) max = new Point(e.P.X + _margin, max.Y);
if (e.P.Y > max.Y) max = new Point(max.X, e.P.Y + _margin);
if (e.Q.X > max.X) max = new Point(e.Q.X + _margin, max.Y);
if (e.Q.Y > max.Y) max = new Point(max.X, e.Q.Y + _margin);
if (e.P.X < min.X) min = new Point(e.P.X - _margin, min.Y);
if (e.P.Y < min.Y) min = new Point(min.X, e.P.Y - _margin);
if (e.Q.X < min.X) min = new Point(e.Q.X - _margin, min.Y);
if (e.Q.Y < min.Y) min = new Point(min.X, e.Q.Y - _margin);
}
Edge top = new Edge(new Point(min.X, max.Y), new Point(max.X, max.Y));
Edge bottom = new Edge(new Point(min.X, min.Y), new Point(max.X, min.Y));
Point left = bottom.P;
Point right = top.Q;
return new Trapezoid(left, right, top, bottom);
}
}
}
@@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
namespace FarseerPhysics.Common.Decomposition.Seidel
{
internal class Triangulator
{
// Trapezoid decomposition list
public List<Trapezoid> Trapezoids;
public List<List<Point>> Triangles;
// Initialize trapezoidal map and query structure
private Trapezoid _boundingBox;
private List<Edge> _edgeList;
private QueryGraph _queryGraph;
private float _sheer = 0.001f;
private TrapezoidalMap _trapezoidalMap;
private List<MonotoneMountain> _xMonoPoly;
public Triangulator(List<Point> polyLine, float sheer)
{
_sheer = sheer;
Triangles = new List<List<Point>>();
Trapezoids = new List<Trapezoid>();
_xMonoPoly = new List<MonotoneMountain>();
_edgeList = InitEdges(polyLine);
_trapezoidalMap = new TrapezoidalMap();
_boundingBox = _trapezoidalMap.BoundingBox(_edgeList);
_queryGraph = new QueryGraph(Sink.Isink(_boundingBox));
Process();
}
// Build the trapezoidal map and query graph
private void Process()
{
foreach (Edge edge in _edgeList)
{
List<Trapezoid> traps = _queryGraph.FollowEdge(edge);
// Remove trapezoids from trapezoidal Map
foreach (Trapezoid t in traps)
{
_trapezoidalMap.Map.Remove(t);
bool cp = t.Contains(edge.P);
bool cq = t.Contains(edge.Q);
Trapezoid[] tList;
if (cp && cq)
{
tList = _trapezoidalMap.Case1(t, edge);
_queryGraph.Case1(t.Sink, edge, tList);
}
else if (cp && !cq)
{
tList = _trapezoidalMap.Case2(t, edge);
_queryGraph.Case2(t.Sink, edge, tList);
}
else if (!cp && !cq)
{
tList = _trapezoidalMap.Case3(t, edge);
_queryGraph.Case3(t.Sink, edge, tList);
}
else
{
tList = _trapezoidalMap.Case4(t, edge);
_queryGraph.Case4(t.Sink, edge, tList);
}
// Add new trapezoids to map
foreach (Trapezoid y in tList)
{
_trapezoidalMap.Map.Add(y);
}
}
_trapezoidalMap.Clear();
}
// Mark outside trapezoids
foreach (Trapezoid t in _trapezoidalMap.Map)
{
MarkOutside(t);
}
// Collect interior trapezoids
foreach (Trapezoid t in _trapezoidalMap.Map)
{
if (t.Inside)
{
Trapezoids.Add(t);
t.AddPoints();
}
}
// Generate the triangles
CreateMountains();
}
// Build a list of x-monotone mountains
private void CreateMountains()
{
foreach (Edge edge in _edgeList)
{
if (edge.MPoints.Count > 2)
{
MonotoneMountain mountain = new MonotoneMountain();
// Sorting is a perfromance hit. Literature says this can be accomplised in
// linear time, although I don't see a way around using traditional methods
// when using a randomized incremental algorithm
// Insertion sort is one of the fastest algorithms for sorting arrays containing
// fewer than ten elements, or for lists that are already mostly sorted.
List<Point> points = new List<Point>(edge.MPoints);
points.Sort((p1, p2) => p1.X.CompareTo(p2.X));
foreach (Point p in points)
mountain.Add(p);
// Triangulate monotone mountain
mountain.Process();
// Extract the triangles into a single list
foreach (List<Point> t in mountain.Triangles)
{
Triangles.Add(t);
}
_xMonoPoly.Add(mountain);
}
}
}
// Mark the outside trapezoids surrounding the polygon
private void MarkOutside(Trapezoid t)
{
if (t.Top == _boundingBox.Top || t.Bottom == _boundingBox.Bottom)
t.TrimNeighbors();
}
// Create segments and connect end points; update edge event pointer
private List<Edge> InitEdges(List<Point> points)
{
List<Edge> edges = new List<Edge>();
for (int i = 0; i < points.Count - 1; i++)
{
edges.Add(new Edge(points[i], points[i + 1]));
}
edges.Add(new Edge(points[0], points[points.Count - 1]));
return OrderSegments(edges);
}
private List<Edge> OrderSegments(List<Edge> edgeInput)
{
// Ignore vertical segments!
List<Edge> edges = new List<Edge>();
foreach (Edge e in edgeInput)
{
Point p = ShearTransform(e.P);
Point q = ShearTransform(e.Q);
// Point p must be to the left of point q
if (p.X > q.X)
{
edges.Add(new Edge(q, p));
}
else if (p.X < q.X)
{
edges.Add(new Edge(p, q));
}
}
// Randomized triangulation improves performance
// See Seidel's paper, or O'Rourke's book, p. 57
Shuffle(edges);
return edges;
}
private static void Shuffle<T>(IList<T> list)
{
Random rng = new Random();
int n = list.Count;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
// Prevents any two distinct endpoints from lying on a common vertical line, and avoiding
// the degenerate case. See Mark de Berg et al, Chapter 6.3
private Point ShearTransform(Point point)
{
return new Point(point.X + _sheer * point.Y, point.Y);
}
}
}
@@ -0,0 +1,21 @@
namespace FarseerPhysics.Common.Decomposition.Seidel
{
internal class XNode : Node
{
private Point _point;
public XNode(Point point, Node lChild, Node rChild)
: base(lChild, rChild)
{
_point = point;
}
public override Sink Locate(Edge edge)
{
if (edge.P.X >= _point.X)
return RightChild.Locate(edge); // Move to the right in the graph
return LeftChild.Locate(edge); // Move to the left in the graph
}
}
}
@@ -0,0 +1,29 @@
namespace FarseerPhysics.Common.Decomposition.Seidel
{
internal class YNode : Node
{
private Edge _edge;
public YNode(Edge edge, Node lChild, Node rChild)
: base(lChild, rChild)
{
_edge = edge;
}
public override Sink Locate(Edge edge)
{
if (_edge.IsAbove(edge.P))
return RightChild.Locate(edge); // Move down the graph
if (_edge.IsBelow(edge.P))
return LeftChild.Locate(edge); // Move up the graph
// s and segment share the same endpoint, p
if (edge.Slope < _edge.Slope)
return RightChild.Locate(edge); // Move down the graph
// Move up the graph
return LeftChild.Locate(edge);
}
}
}
@@ -0,0 +1,109 @@
/*
* Farseer Physics Engine:
* Copyright (c) 2012 Ian Qvist
*/
using System.Collections.Generic;
using System.Diagnostics;
using FarseerPhysics.Common.Decomposition.Seidel;
using Microsoft.Xna.Framework;
using Point = FarseerPhysics.Common.Decomposition.Seidel.Point;
namespace FarseerPhysics.Common.Decomposition
{
/// <summary>
/// Convex decomposition algorithm created by Raimund Seidel
///
/// Properties:
/// - Decompose the polygon into trapezoids, then triangulate.
/// - To use the trapezoid data, use ConvexPartitionTrapezoid()
/// - Generate a lot of garbage due to incapsulation of the Poly2Tri library.
/// - Running time is O(n log n), n = number of vertices.
/// - Running time is almost linear for most simple polygons.
/// - Does not care about winding order.
///
/// For more information, see Raimund Seidel's paper "A simple and fast incremental randomized
/// algorithm for computing trapezoidal decompositions and for triangulating polygons"
///
/// See also: "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
/// "Computational Geometry in C", 2nd edition, by Joseph O'Rourke
///
/// Original code from the Poly2Tri project by Mason Green.
/// http://code.google.com/p/poly2tri/source/browse?repo=archive#hg/scala/src/org/poly2tri/seidel
///
/// This implementation is from Dec 14, 2010
/// </summary>
internal static class SeidelDecomposer
{
/// <summary>
/// Decompose the polygon into several smaller non-concave polygons.
/// </summary>
/// <param name="vertices">The polygon to decompose.</param>
/// <param name="sheer">The sheer to use if you get bad results, try using a higher value.</param>
/// <returns>A list of triangles</returns>
public static List<Vertices> ConvexPartition(Vertices vertices, float sheer = 0.001f)
{
Debug.Assert(vertices.Count > 3);
List<Point> compatList = new List<Point>(vertices.Count);
foreach (Vector2 vertex in vertices)
{
compatList.Add(new Point(vertex.X, vertex.Y));
}
Triangulator t = new Triangulator(compatList, sheer);
List<Vertices> list = new List<Vertices>();
foreach (List<Point> triangle in t.Triangles)
{
Vertices outTriangles = new Vertices(triangle.Count);
foreach (Point outTriangle in triangle)
{
outTriangles.Add(new Vector2(outTriangle.X, outTriangle.Y));
}
list.Add(outTriangles);
}
return list;
}
/// <summary>
/// Decompose the polygon into several smaller non-concave polygons.
/// </summary>
/// <param name="vertices">The polygon to decompose.</param>
/// <param name="sheer">The sheer to use if you get bad results, try using a higher value.</param>
/// <returns>A list of trapezoids</returns>
public static List<Vertices> ConvexPartitionTrapezoid(Vertices vertices, float sheer = 0.001f)
{
List<Point> compatList = new List<Point>(vertices.Count);
foreach (Vector2 vertex in vertices)
{
compatList.Add(new Point(vertex.X, vertex.Y));
}
Triangulator t = new Triangulator(compatList, sheer);
List<Vertices> list = new List<Vertices>();
foreach (Trapezoid trapezoid in t.Trapezoids)
{
Vertices verts = new Vertices();
List<Point> points = trapezoid.GetVertices();
foreach (Point point in points)
{
verts.Add(new Vector2(point.X, point.Y));
}
list.Add(verts);
}
return list;
}
}
}
@@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using FarseerPhysics.Common.ConvexHull;
namespace FarseerPhysics.Common.Decomposition
{
public enum TriangulationAlgorithm
{
/// <summary>
/// Convex decomposition algorithm using ear clipping
///
/// Properties:
/// - Only works on simple polygons.
/// - Does not support holes.
/// - Running time is O(n^2), n = number of vertices.
/// </summary>
Earclip,
/// <summary>
/// Convex decomposition algorithm created by Mark Bayazit (http://mnbayazit.com/)
///
/// Properties:
/// - Tries to decompose using polygons instead of triangles.
/// - Tends to produce optimal results with low processing time.
/// - Running time is O(nr), n = number of vertices, r = reflex vertices.
/// - Does not support holes.
/// </summary>
Bayazit,
/// <summary>
/// Convex decomposition algorithm created by unknown
///
/// Properties:
/// - No support for holes
/// - Very fast
/// - Only works on simple polygons
/// - Only works on counter clockwise polygons
/// </summary>
Flipcode,
/// <summary>
/// Convex decomposition algorithm created by Raimund Seidel
///
/// Properties:
/// - Decompose the polygon into trapezoids, then triangulate.
/// - To use the trapezoid data, use ConvexPartitionTrapezoid()
/// - Generate a lot of garbage due to incapsulation of the Poly2Tri library.
/// - Running time is O(n log n), n = number of vertices.
/// - Running time is almost linear for most simple polygons.
/// - Does not care about winding order.
/// </summary>
Seidel,
SeidelTrapezoids,
/// <summary>
/// 2D constrained Delaunay triangulation algorithm.
/// Based on the paper "Sweep-line algorithm for constrained Delaunay triangulation" by V. Domiter and and B. Zalik
///
/// Properties:
/// - Creates triangles with a large interior angle.
/// - Supports holes
/// - Generate a lot of garbage due to incapsulation of the Poly2Tri library.
/// - Running time is O(n^2), n = number of vertices.
/// - Does not care about winding order.
/// </summary>
Delauny
}
public static class Triangulate
{
public static List<Vertices> ConvexPartition(Vertices vertices, TriangulationAlgorithm algorithm, bool discardAndFixInvalid = true, float tolerance = 0.001f)
{
if (vertices.Count <= 3)
return new List<Vertices> { vertices };
List<Vertices> results;
switch (algorithm)
{
case TriangulationAlgorithm.Earclip:
if (Settings.SkipSanityChecks)
Debug.Assert(!vertices.IsCounterClockWise(), "The Earclip algorithm expects the polygon to be clockwise.");
else
{
if (vertices.IsCounterClockWise())
{
Vertices temp = new Vertices(vertices);
temp.Reverse();
results = EarclipDecomposer.ConvexPartition(temp, tolerance);
}
else
results = EarclipDecomposer.ConvexPartition(vertices, tolerance);
}
break;
case TriangulationAlgorithm.Bayazit:
if (Settings.SkipSanityChecks)
Debug.Assert(vertices.IsCounterClockWise(), "The polygon is not counter clockwise. This is needed for Bayazit to work correctly.");
else
{
if (!vertices.IsCounterClockWise())
{
Vertices temp = new Vertices(vertices);
temp.Reverse();
results = BayazitDecomposer.ConvexPartition(temp);
}
else
results = BayazitDecomposer.ConvexPartition(vertices);
}
break;
case TriangulationAlgorithm.Flipcode:
if (Settings.SkipSanityChecks)
Debug.Assert(vertices.IsCounterClockWise(), "The polygon is not counter clockwise. This is needed for Bayazit to work correctly.");
else
{
if (!vertices.IsCounterClockWise())
{
Vertices temp = new Vertices(vertices);
temp.Reverse();
results = FlipcodeDecomposer.ConvexPartition(temp);
}
else
results = FlipcodeDecomposer.ConvexPartition(vertices);
}
break;
case TriangulationAlgorithm.Seidel:
results = SeidelDecomposer.ConvexPartition(vertices, tolerance);
break;
case TriangulationAlgorithm.SeidelTrapezoids:
results = SeidelDecomposer.ConvexPartitionTrapezoid(vertices, tolerance);
break;
case TriangulationAlgorithm.Delauny:
results = CDTDecomposer.ConvexPartition(vertices);
break;
default:
throw new ArgumentOutOfRangeException("algorithm");
}
if (discardAndFixInvalid)
{
for (int i = results.Count - 1; i >= 0; i--)
{
Vertices polygon = results[i];
if (!ValidatePolygon(polygon))
results.RemoveAt(i);
}
}
return results;
}
private static bool ValidatePolygon(Vertices polygon)
{
PolygonError errorCode = polygon.CheckPolygon();
if (errorCode == PolygonError.InvalidAmountOfVertices || errorCode == PolygonError.AreaTooSmall || errorCode == PolygonError.SideTooSmall || errorCode == PolygonError.NotSimple)
return false;
if (errorCode == PolygonError.NotCounterClockWise) //NotCounterCloseWise is the last check in CheckPolygon(), thus we don't need to call ValidatePolygon again.
polygon.Reverse();
if (errorCode == PolygonError.NotConvex)
{
polygon = GiftWrap.GetConvexHull(polygon);
return ValidatePolygon(polygon);
}
return true;
}
}
}
@@ -0,0 +1,224 @@
/*
* Farseer Physics Engine:
* Copyright (c) 2012 Ian Qvist
*
* Original source Box2D:
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* 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();
}
}
}
}
}
@@ -0,0 +1,78 @@
#if WINDOWS_PHONE || XBOX
using System.Collections;
using System.Collections.Generic;
namespace FarseerPhysics.Common
{
public class HashSet<T> : ICollection<T>
{
private Dictionary<T, byte> _dict;
public HashSet(int capacity)
{
_dict = new Dictionary<T, byte>(capacity);
}
public HashSet()
{
_dict = new Dictionary<T, byte>();
}
#region ICollection<T> Members
public void Add(T item)
{
// We don't care for the value in dictionary, only keys matter.
if (!_dict.ContainsKey(item))
_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)
{
foreach (var item in _dict.Keys)
{
array[arrayIndex++] = item;
}
}
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
@@ -0,0 +1,287 @@
using System;
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 DistanceBetweenPointAndLineSegment(ref Vector2 point, ref Vector2 start, ref Vector2 end)
{
if (start == end)
return Vector2.Distance(point, start);
Vector2 v = Vector2.Subtract(end, start);
Vector2 w = Vector2.Subtract(point, start);
float c1 = Vector2.Dot(w, v);
if (c1 <= 0) return Vector2.Distance(point, start);
float c2 = Vector2.Dot(v, v);
if (c2 <= c1) return Vector2.Distance(point, end);
float b = c1 / c2;
Vector2 pointOnLine = Vector2.Add(start, Vector2.Multiply(v, b));
return Vector2.Distance(point, 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>
public static bool LineIntersect2(ref Vector2 a0, ref Vector2 a1, ref Vector2 b0, ref 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>
public static Vertices LineSegmentVerticesIntersect(ref Vector2 point1, ref Vector2 point2, Vertices vertices)
{
Vertices intersectionPoints = new Vertices();
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);
}
}
return intersectionPoints;
}
/// <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>
public static Vertices LineSegmentAABBIntersect(ref Vector2 point1, ref Vector2 point2, AABB aabb)
{
return LineSegmentVerticesIntersect(ref point1, ref point2, aabb.Vertices);
}
}
}
+806
View File
@@ -0,0 +1,806 @@
/*
* Farseer Physics Engine:
* Copyright (c) 2012 Ian Qvist
*
* Original source Box2D:
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* 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(ref Vector2 a, ref Vector2 b)
{
return a.X * b.Y - a.Y * b.X;
}
public static float Cross(Vector2 a, Vector2 b)
{
return Cross(ref a, ref b);
}
/// Perform the cross product on two vectors.
public static Vector3 Cross(Vector3 a, Vector3 b)
{
return new Vector3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, 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 Mul(ref Mat22 A, Vector2 v)
{
return Mul(ref A, ref v);
}
public static Vector2 Mul(ref Mat22 A, ref Vector2 v)
{
return new Vector2(A.ex.X * v.X + A.ey.X * v.Y, A.ex.Y * v.X + A.ey.Y * v.Y);
}
public static Vector2 Mul(ref Transform T, Vector2 v)
{
return Mul(ref T, ref v);
}
public static Vector2 Mul(ref Transform T, ref Vector2 v)
{
float x = (T.q.c * v.X - T.q.s * v.Y) + T.p.X;
float y = (T.q.s * v.X + T.q.c * v.Y) + T.p.Y;
return new Vector2(x, y);
}
public static Vector2 MulT(ref Mat22 A, Vector2 v)
{
return MulT(ref A, ref v);
}
public static Vector2 MulT(ref Mat22 A, ref Vector2 v)
{
return new Vector2(v.X * A.ex.X + v.Y * A.ex.Y, v.X * A.ey.X + v.Y * A.ey.Y);
}
public static Vector2 MulT(ref Transform T, Vector2 v)
{
return MulT(ref T, ref v);
}
public static Vector2 MulT(ref Transform T, ref Vector2 v)
{
float px = v.X - T.p.X;
float py = v.Y - T.p.Y;
float x = (T.q.c * px + T.q.s * py);
float y = (-T.q.s * px + T.q.c * py);
return new Vector2(x, y);
}
// A^T * B
public static void MulT(ref Mat22 A, ref Mat22 B, out Mat22 C)
{
C = new Mat22();
C.ex.X = A.ex.X * B.ex.X + A.ex.Y * B.ex.Y;
C.ex.Y = A.ey.X * B.ex.X + A.ey.Y * B.ex.Y;
C.ey.X = A.ex.X * B.ey.X + A.ex.Y * B.ey.Y;
C.ey.Y = A.ey.X * B.ey.X + A.ey.Y * B.ey.Y;
}
/// Multiply a matrix times a vector.
public static Vector3 Mul(Mat33 A, Vector3 v)
{
return v.X * A.ex + v.Y * A.ey + v.Z * A.ez;
}
// v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p
// = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p
public static Transform Mul(Transform A, Transform B)
{
Transform C = new Transform();
C.q = Mul(A.q, B.q);
C.p = Mul(A.q, B.p) + A.p;
return C;
}
// v2 = A.q' * (B.q * v1 + B.p - A.p)
// = A.q' * B.q * v1 + A.q' * (B.p - A.p)
public static void MulT(ref Transform A, ref Transform B, out Transform C)
{
C = new Transform();
C.q = MulT(A.q, B.q);
C.p = MulT(A.q, B.p - A.p);
}
public static void Swap<T>(ref T a, ref T b)
{
T tmp = a;
a = b;
b = tmp;
}
/// Multiply a matrix times a vector.
public static Vector2 Mul22(Mat33 A, Vector2 v)
{
return new Vector2(A.ex.X * v.X + A.ey.X * v.Y, A.ex.Y * v.X + A.ey.Y * v.Y);
}
/// Multiply two rotations: q * r
public static Rot Mul(Rot q, Rot r)
{
// [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc]
// [qs qc] [rs rc] [qs*rc+qc*rs -qs*rs+qc*rc]
// s = qs * rc + qc * rs
// c = qc * rc - qs * rs
Rot qr;
qr.s = q.s * r.c + q.c * r.s;
qr.c = q.c * r.c - q.s * r.s;
return qr;
}
public static Vector2 MulT(Transform T, Vector2 v)
{
float px = v.X - T.p.X;
float py = v.Y - T.p.Y;
float x = (T.q.c * px + T.q.s * py);
float y = (-T.q.s * px + T.q.c * py);
return new Vector2(x, y);
}
/// Transpose multiply two rotations: qT * r
public static Rot MulT(Rot q, Rot r)
{
// [ qc qs] * [rc -rs] = [qc*rc+qs*rs -qc*rs+qs*rc]
// [-qs qc] [rs rc] [-qs*rc+qc*rs qs*rs+qc*rc]
// s = qc * rs - qs * rc
// c = qc * rc + qs * rs
Rot qr;
qr.s = q.c * r.s - q.s * r.c;
qr.c = q.c * r.c + q.s * r.s;
return qr;
}
// v2 = A.q' * (B.q * v1 + B.p - A.p)
// = A.q' * B.q * v1 + A.q' * (B.p - A.p)
public static Transform MulT(Transform A, Transform B)
{
Transform C = new Transform();
C.q = MulT(A.q, B.q);
C.p = MulT(A.q, B.p - A.p);
return C;
}
/// Rotate a vector
public static Vector2 Mul(Rot q, Vector2 v)
{
return new Vector2(q.c * v.X - q.s * v.Y, q.s * v.X + q.c * v.Y);
}
/// Inverse rotate a vector
public static Vector2 MulT(Rot q, Vector2 v)
{
return new Vector2(q.c * v.X + q.s * v.Y, -q.s * v.X + q.c * v.Y);
}
/// Get the skew vector such that dot(skew_vec, other) == cross(vec, other)
public static Vector2 Skew(Vector2 input)
{
return new Vector2(-input.Y, input.X);
}
/// <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);
}
/// Perform the dot product on two vectors.
public static float Dot(Vector3 a, Vector3 b)
{
return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
}
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>
/// <param name="tolerance">The tolerance</param>
/// <returns></returns>
public static bool IsCollinear(ref Vector2 a, ref Vector2 b, ref Vector2 c, float tolerance = 0)
{
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
public static Vector2 Mul(ref Rot rot, Vector2 axis)
{
return Mul(rot, axis);
}
public static Vector2 MulT(ref Rot rot, Vector2 axis)
{
return MulT(rot, axis);
}
}
/// <summary>
/// A 2-by-2 matrix. Stored in column-major order.
/// </summary>
public struct Mat22
{
public Vector2 ex, ey;
/// <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)
{
ex = c1;
ey = 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)
{
ex = new Vector2(a11, a21);
ey = new Vector2(a12, a22);
}
public Mat22 Inverse
{
get
{
float a = ex.X, b = ey.X, c = ex.Y, d = ey.Y;
float det = a * d - b * c;
if (det != 0.0f)
{
det = 1.0f / det;
}
Mat22 result = new Mat22();
result.ex.X = det * d;
result.ex.Y = -det * c;
result.ey.X = -det * b;
result.ey.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)
{
ex = c1;
ey = c2;
}
/// <summary>
/// Set this to the identity matrix.
/// </summary>
public void SetIdentity()
{
ex.X = 1.0f;
ey.X = 0.0f;
ex.Y = 0.0f;
ey.Y = 1.0f;
}
/// <summary>
/// Set this matrix to all zeros.
/// </summary>
public void SetZero()
{
ex.X = 0.0f;
ey.X = 0.0f;
ex.Y = 0.0f;
ey.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 = ex.X, a12 = ey.X, a21 = ex.Y, a22 = ey.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.ex = A.ex + B.ex;
R.ey = A.ey + B.ey;
}
}
/// <summary>
/// A 3-by-3 matrix. Stored in column-major order.
/// </summary>
public struct Mat33
{
public Vector3 ex, ey, ez;
/// <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)
{
ex = c1;
ey = c2;
ez = c3;
}
/// <summary>
/// Set this matrix to all zeros.
/// </summary>
public void SetZero()
{
ex = Vector3.Zero;
ey = Vector3.Zero;
ez = 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(ex, Vector3.Cross(ey, ez));
if (det != 0.0f)
{
det = 1.0f / det;
}
return new Vector3(det * Vector3.Dot(b, Vector3.Cross(ey, ez)), det * Vector3.Dot(ex, Vector3.Cross(b, ez)), det * Vector3.Dot(ex, Vector3.Cross(ey, 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 = ex.X, a12 = ey.X, a21 = ex.Y, a22 = ey.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));
}
/// Get the inverse of this matrix as a 2-by-2.
/// Returns the zero matrix if singular.
public void GetInverse22(ref Mat33 M)
{
float a = ex.X, b = ey.X, c = ex.Y, d = ey.Y;
float det = a * d - b * c;
if (det != 0.0f)
{
det = 1.0f / det;
}
M.ex.X = det * d; M.ey.X = -det * b; M.ex.Z = 0.0f;
M.ex.Y = -det * c; M.ey.Y = det * a; M.ey.Z = 0.0f;
M.ez.X = 0.0f; M.ez.Y = 0.0f; M.ez.Z = 0.0f;
}
/// Get the symmetric inverse of this matrix as a 3-by-3.
/// Returns the zero matrix if singular.
public void GetSymInverse33(ref Mat33 M)
{
float det = MathUtils.Dot(ex, MathUtils.Cross(ey, ez));
if (det != 0.0f)
{
det = 1.0f / det;
}
float a11 = ex.X, a12 = ey.X, a13 = ez.X;
float a22 = ey.Y, a23 = ez.Y;
float a33 = ez.Z;
M.ex.X = det * (a22 * a33 - a23 * a23);
M.ex.Y = det * (a13 * a23 - a12 * a33);
M.ex.Z = det * (a12 * a23 - a13 * a22);
M.ey.X = M.ex.Y;
M.ey.Y = det * (a11 * a33 - a13 * a13);
M.ey.Z = det * (a13 * a12 - a11 * a23);
M.ez.X = M.ex.Z;
M.ez.Y = M.ey.Z;
M.ez.Z = det * (a11 * a22 - a12 * a12);
}
}
/// <summary>
/// Rotation
/// </summary>
public struct Rot
{
/// Sine and cosine
public float s, c;
/// <summary>
/// Initialize from an angle in radians
/// </summary>
/// <param name="angle">Angle in radians</param>
public Rot(float angle)
{
// TODO_ERIN optimize
s = (float)Math.Sin(angle);
c = (float)Math.Cos(angle);
}
/// <summary>
/// Set using an angle in radians.
/// </summary>
/// <param name="angle"></param>
public void Set(float angle)
{
// TODO_ERIN optimize
s = (float)Math.Sin(angle);
c = (float)Math.Cos(angle);
}
/// <summary>
/// Set to the identity rotation
/// </summary>
public void SetIdentity()
{
s = 0.0f;
c = 1.0f;
}
/// <summary>
/// Get the angle in radians
/// </summary>
public float GetAngle()
{
return (float)Math.Atan2(s, c);
}
/// <summary>
/// Get the x-axis
/// </summary>
public Vector2 GetXAxis()
{
return new Vector2(c, s);
}
/// <summary>
/// Get the y-axis
/// </summary>
public Vector2 GetYAxis()
{
return new Vector2(-s, c);
}
}
/// <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 p;
public Rot q;
/// <summary>
/// Initialize using a position vector and a rotation matrix.
/// </summary>
/// <param name="position">The position.</param>
/// <param name="rotation">The r.</param>
public Transform(ref Vector2 position, ref Rot rotation)
{
p = position;
q = rotation;
}
/// <summary>
/// Set this to the identity transform.
/// </summary>
public void SetIdentity()
{
p = Vector2.Zero;
q.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)
{
p = position;
q.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="xfb">The transform.</param>
/// <param name="beta">beta is a factor in [0,1], where 0 indicates alpha0.</param>
public void GetTransform(out Transform xfb, float beta)
{
xfb = new Transform();
xfb.p.X = (1.0f - beta) * C0.X + beta * C.X;
xfb.p.Y = (1.0f - beta) * C0.Y + beta * C.Y;
float angle = (1.0f - beta) * A0 + beta * A;
xfb.q.Set(angle);
// Shift to origin
xfb.p -= MathUtils.Mul(xfb.q, 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 += beta * (C - C0);
A0 += beta * (A - A0);
Alpha0 = alpha;
}
/// <summary>
/// Normalize the angles.
/// </summary>
public void Normalize()
{
float d = MathHelper.TwoPi * (float)Math.Floor(A0 / MathHelper.TwoPi);
A0 -= d;
A -= d;
}
}
}
@@ -0,0 +1,158 @@
#if !XNA && !WINDOWS_PHONE && !XBOX && !ANDROID
#region License
/*
MIT License
Copyright © 2006 The Mono.Xna Team
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#endregion License
using System;
namespace Microsoft.Xna.Framework
{
public static class MathHelper
{
public const float E = (float) Math.E;
public const float Log10E = 0.4342945f;
public const float Log2E = 1.442695f;
public const float Pi = (float) Math.PI;
public const float PiOver2 = (float) (Math.PI/2.0);
public const float PiOver4 = (float) (Math.PI/4.0);
public const float TwoPi = (float) (Math.PI*2.0);
public static float Barycentric(float value1, float value2, float value3, float amount1, float amount2)
{
return value1 + (value2 - value1)*amount1 + (value3 - value1)*amount2;
}
public static float CatmullRom(float value1, float value2, float value3, float value4, float amount)
{
// Using formula from http://www.mvps.org/directx/articles/catmull/
// Internally using doubles not to lose precission
double amountSquared = amount*amount;
double amountCubed = amountSquared*amount;
return (float) (0.5*(2.0*value2 +
(value3 - value1)*amount +
(2.0*value1 - 5.0*value2 + 4.0*value3 - value4)*amountSquared +
(3.0*value2 - value1 - 3.0*value3 + value4)*amountCubed));
}
public static float Clamp(float value, float min, float max)
{
// First we check to see if we're greater than the max
value = (value > max) ? max : value;
// Then we check to see if we're less than the min.
value = (value < min) ? min : value;
// There's no check to see if min > max.
return value;
}
public static float Distance(float value1, float value2)
{
return Math.Abs(value1 - value2);
}
public static float Hermite(float value1, float tangent1, float value2, float tangent2, float amount)
{
// All transformed to double not to lose precission
// Otherwise, for high numbers of param:amount the result is NaN instead of Infinity
double v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount, result;
double sCubed = s*s*s;
double sSquared = s*s;
if (amount == 0f)
result = value1;
else if (amount == 1f)
result = value2;
else
result = (2*v1 - 2*v2 + t2 + t1)*sCubed +
(3*v2 - 3*v1 - 2*t1 - t2)*sSquared +
t1*s +
v1;
return (float) result;
}
public static float Lerp(float value1, float value2, float amount)
{
return value1 + (value2 - value1)*amount;
}
public static float Max(float value1, float value2)
{
return Math.Max(value1, value2);
}
public static float Min(float value1, float value2)
{
return Math.Min(value1, value2);
}
public static float SmoothStep(float value1, float value2, float amount)
{
// It is expected that 0 < amount < 1
// If amount < 0, return value1
// If amount > 1, return value2
float result = Clamp(amount, 0f, 1f);
result = Hermite(value1, 0f, value2, 0f, result);
return result;
}
public static float ToDegrees(float radians)
{
// This method uses double precission internally,
// though it returns single float
// Factor = 180 / pi
return (float) (radians*57.295779513082320876798154814105);
}
public static float ToRadians(float degrees)
{
// This method uses double precission internally,
// though it returns single float
// Factor = pi / 180
return (float) (degrees*0.017453292519943295769236907684886);
}
public static float WrapAngle(float angle)
{
angle = (float) Math.IEEERemainder((double) angle, 6.2831854820251465); //2xPi precission is double
if (angle <= -3.141593f)
{
angle += 6.283185f;
return angle;
}
if (angle > 3.141593f)
{
angle -= 6.283185f;
}
return angle;
}
}
}
#endif
File diff suppressed because it is too large Load Diff
+337
View File
@@ -0,0 +1,337 @@
using System;
using System.Collections.Generic;
using System.Text;
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>
public class Path
{
/// <summary>
/// All the points that makes up the curve
/// </summary>
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>
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;
}
}
}
@@ -0,0 +1,186 @@
using System;
using System.Collections.Generic;
using FarseerPhysics.Collision.Shapes;
using FarseerPhysics.Common.Decomposition;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common
{
/// <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)
{
ChainShape chain = new ChainShape(verts, true);
body.CreateFixture(chain);
}
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 = Triangulate.ConvexPartition(new Vertices(verts), TriangulationAlgorithm.Bayazit);
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 = null)
{
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;
b.UserData = userData;
foreach (Shape shape in shapes)
{
b.CreateFixture(shape);
}
bodyList.Add(b);
}
return bodyList;
}
/// <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);
}
/// <summary>
/// Moves the given body along the defined 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;
}
}
}
@@ -0,0 +1,133 @@
using FarseerPhysics.Dynamics;
namespace FarseerPhysics.Common.PhysicsLogic
{
/// <summary>
/// Contains filter data that can determine whether an object should be processed or not.
/// </summary>
public abstract class FilterData
{
/// <summary>
/// Disable the logic on specific categories.
/// Category.None by default.
/// </summary>
public Category DisabledOnCategories = Category.None;
/// <summary>
/// Disable the logic on specific groups
/// </summary>
public int DisabledOnGroup;
/// <summary>
/// Enable the logic on specific categories
/// Category.All by default.
/// </summary>
public Category EnabledOnCategories = Category.All;
/// <summary>
/// Enable the logic on specific groups.
/// </summary>
public int EnabledOnGroup;
/// <summary>
///
/// </summary>
/// <param name="body"></param>
/// <returns></returns>
public virtual bool IsActiveOn(Body body)
{
if (body == null || !body.Enabled || body.IsStatic)
return false;
if (body.FixtureList == null)
return false;
foreach (Fixture fixture in body.FixtureList)
{
//Disable
if ((fixture.CollisionGroup == DisabledOnGroup) && fixture.CollisionGroup != 0 && DisabledOnGroup != 0)
return false;
if ((fixture.CollisionCategories & DisabledOnCategories) != Category.None)
return false;
if (EnabledOnGroup != 0 || EnabledOnCategories != Category.All)
{
//Enable
if ((fixture.CollisionGroup == EnabledOnGroup) && fixture.CollisionGroup != 0 && EnabledOnGroup != 0)
return true;
if ((fixture.CollisionCategories & EnabledOnCategories) != Category.None &&
EnabledOnCategories != Category.All)
return true;
}
else
{
return true;
}
}
return false;
}
/// <summary>
/// Adds the category.
/// </summary>
/// <param name="category">The category.</param>
public void AddDisabledCategory(Category category)
{
DisabledOnCategories |= category;
}
/// <summary>
/// Removes the category.
/// </summary>
/// <param name="category">The category.</param>
public void RemoveDisabledCategory(Category category)
{
DisabledOnCategories &= ~category;
}
/// <summary>
/// Determines whether this body ignores the the specified controller.
/// </summary>
/// <param name="category">The category.</param>
/// <returns>
/// <c>true</c> if the object has the specified category; otherwise, <c>false</c>.
/// </returns>
public bool IsInDisabledCategory(Category category)
{
return (DisabledOnCategories & category) == category;
}
/// <summary>
/// Adds the category.
/// </summary>
/// <param name="category">The category.</param>
public void AddEnabledCategory(Category category)
{
EnabledOnCategories |= category;
}
/// <summary>
/// Removes the category.
/// </summary>
/// <param name="category">The category.</param>
public void RemoveEnabledCategory(Category category)
{
EnabledOnCategories &= ~category;
}
/// <summary>
/// Determines whether this body ignores the the specified controller.
/// </summary>
/// <param name="category">The category.</param>
/// <returns>
/// <c>true</c> if the object has the specified category; otherwise, <c>false</c>.
/// </returns>
public bool IsInEnabledInCategory(Category category)
{
return (EnabledOnCategories & category) == category;
}
}
}
@@ -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;
}
}
}
@@ -0,0 +1,418 @@
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
{
// Original Code by Steven Lu - see http://www.box2d.org/forum/viewtopic.php?f=3&t=1688
// Ported to Farseer 3.0 by Nicolás Hormazábal
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>
/// Creates a realistic explosion based on raycasting. Objects in the open will be affected, but objects behind
/// static bodies will not. A body that is half in cover, half in the open will get half the force applied to the end in
/// the open.
/// </summary>
public sealed class RealExplosion : 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 RayDataComparer _rdc;
public RealExplosion(World world)
: base(world, PhysicsLogicType.Explosion)
{
_rdc = new RayDataComparer();
_data = new List<ShapeData>();
}
/// <summary>
/// Activate the explosion at the specified position.
/// </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 list of bodies and the amount of force that was applied to them.</returns>
public Dictionary<Fixture, Vector2> Activate(Vector2 pos, float radius, float maxForce)
{
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;
return false;
}
containedShapes[containedShapeCount++] = fixture;
}
else
{
shapes[shapeCount++] = fixture;
}
// Continue the query.
return true;
}, ref aabb);
if (exit)
return new Dictionary<Fixture, Vector2>();
Dictionary<Fixture, Vector2> exploded = new Dictionary<Fixture, Vector2>(shapeCount + containedShapeCount);
// 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)
continue; // 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 fixture = 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;
hitClosest = true;
fixture = f;
return fr;
}, p1, p2);
//draws radius points
if ((hitClosest) && (fixture.Body.BodyType == BodyType.Dynamic))
{
if ((_data.Any()) && (_data.Last().Body == fixture.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 = fixture.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
if (exploded.ContainsKey(f))
exploded[f] += vectImp;
else
exploded.Add(f, vectImp);
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);
fix.Body.ApplyLinearImpulse(ref vectImp, ref hitPoint);
if (!exploded.ContainsKey(fix))
exploded.Add(fix, vectImp);
}
return exploded;
}
}
}
@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using FarseerPhysics.Collision;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.PhysicsLogic
{
/// <summary>
/// Creates a simple explosion that ignores other bodies hiding behind static bodies.
/// </summary>
public sealed class SimpleExplosion : PhysicsLogic
{
public SimpleExplosion(World world)
: base(world, PhysicsLogicType.Explosion)
{
Power = 1; //linear
}
/// <summary>
/// This is the power used in the power function. A value of 1 means the force
/// applied to bodies in the explosion is linear. A value of 2 means it is exponential.
/// </summary>
public float Power { get; set; }
/// <summary>
/// Activate the explosion at the specified position.
/// </summary>
/// <param name="pos">The position (center) of the explosion.</param>
/// <param name="radius">The radius of the explosion.</param>
/// <param name="force">The force applied</param>
/// <param name="maxForce">A maximum amount of force. When force gets over this value, it will be equal to maxForce</param>
/// <returns>A list of bodies and the amount of force that was applied to them.</returns>
public Dictionary<Body, Vector2> Activate(Vector2 pos, float radius, float force, float maxForce = float.MaxValue)
{
HashSet<Body> affectedBodies = new HashSet<Body>();
AABB aabb;
aabb.LowerBound = pos - new Vector2(radius);
aabb.UpperBound = pos + new Vector2(radius);
// Query the world for bodies within the radius.
World.QueryAABB(fixture =>
{
if (Vector2.Distance(fixture.Body.Position, pos) <= radius)
{
if (!affectedBodies.Contains(fixture.Body))
affectedBodies.Add(fixture.Body);
}
return true;
}, ref aabb);
return ApplyImpulse(pos, radius, force, maxForce, affectedBodies);
}
private Dictionary<Body, Vector2> ApplyImpulse(Vector2 pos, float radius, float force, float maxForce, HashSet<Body> overlappingBodies)
{
Dictionary<Body, Vector2> forces = new Dictionary<Body, Vector2>(overlappingBodies.Count);
foreach (Body overlappingBody in overlappingBodies)
{
if (IsActiveOn(overlappingBody))
{
float distance = Vector2.Distance(pos, overlappingBody.Position);
float forcePercent = GetPercent(distance, radius);
Vector2 forceVector = pos - overlappingBody.Position;
forceVector *= 1f / (float)Math.Sqrt(forceVector.X * forceVector.X + forceVector.Y * forceVector.Y);
forceVector *= MathHelper.Min(force * forcePercent, maxForce);
forceVector *= -1;
overlappingBody.ApplyLinearImpulse(forceVector);
forces.Add(overlappingBody, forceVector);
}
}
return forces;
}
private float GetPercent(float distance, float radius)
{
//(1-(distance/radius))^power-1
float percent = (float)Math.Pow(1 - ((distance - radius) / radius), Power) - 1;
if (float.IsNaN(percent))
return 0f;
return MathHelper.Clamp(percent, 0f, 1f);
}
}
}
@@ -0,0 +1,217 @@
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="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, 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;
//We can only cut polygons at the moment
if (shape == null)
{
first = new Vertices();
second = new Vertices();
return;
}
//Offset the entry and exit points if they are too close to the vertices
foreach (Vector2 vertex in shape.Vertices)
{
if (vertex.Equals(localEntryPoint))
localEntryPoint -= new Vector2(0, Settings.Epsilon);
if (vertex.Equals(localExitPoint))
localExitPoint += new Vector2(0, Settings.Epsilon);
}
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();
if (!offset.IsValid())
offset = Vector2.One;
newPolygon[n][cutAdded[n]] += Settings.Epsilon * 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();
if (!offset.IsValid())
offset = Vector2.One;
newPolygon[n][cutAdded[n] + 1] += Settings.Epsilon * 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>
/// <returns>True if the cut was performed.</returns>
public static bool Cut(World world, Vector2 start, Vector2 end)
{
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 false;
//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 false;
for (int i = 0; i < fixtures.Count; i++)
{
// can't cut circles or edges 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], out first, out second);
//Delete the original shape and create two new. Retain the properties of the body.
if (first.CheckPolygon() == PolygonError.NoError)
{
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 (second.CheckPolygon() == PolygonError.NoError)
{
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);
}
}
return true;
}
}
}
@@ -0,0 +1,226 @@
/*
* 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.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.PolygonManipulation
{
/// <summary>
/// Combines a list of triangles into a list of convex polygons.
/// Starts with a seed triangle, keep adding triangles to it until you can't add any more without making the polygon non-convex.
/// </summary>
public static class SimpleCombiner
{
/// <summary>
/// Combine a list of triangles into a list of convex polygons.
///
/// Note: This only works on triangles.
/// </summary>
///<param name="triangles">The triangles.</param>
///<param name="maxPolys">The maximun number of polygons to return.</param>
///<param name="tolerance">The tolerance</param>
public static List<Vertices> PolygonizeTriangles(List<Vertices> triangles, int maxPolys = int.MaxValue, float tolerance = 0.001f)
{
if (triangles.Count <= 0)
return triangles;
List<Vertices> polys = new List<Vertices>();
bool[] covered = new bool[triangles.Count];
for (int i = 0; i < triangles.Count; ++i)
{
covered[i] = false;
//Check here for degenerate triangles
Vertices triangle = triangles[i];
Vector2 a = triangle[0];
Vector2 b = triangle[1];
Vector2 c = triangle[2];
if ((a.X == b.X && a.Y == b.Y) || (b.X == c.X && b.Y == c.Y) || (a.X == c.X && a.Y == c.Y))
covered[i] = true;
}
int polyIndex = 0;
bool notDone = true;
while (notDone)
{
int currTri = -1;
for (int i = 0; i < triangles.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(triangles[currTri][i]);
}
covered[currTri] = true;
int index = 0;
for (int i = 0; i < 2 * triangles.Count; ++i, ++index)
{
while (index >= triangles.Count) index -= triangles.Count;
if (covered[index])
{
continue;
}
Vertices newP = AddTriangle(triangles[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
Debug.WriteLine("Skipping corrupt poly.");
}
if (poly.Count >= 3)
polyIndex++; //Must be outside (polyIndex < polysLength) test
}
}
//TODO: Add sanity check
//Remove empty vertice collections
for (int i = polys.Count - 1; i >= 0; i--)
{
if (polys[i].Count == 0)
polys.RemoveAt(i);
}
return polys;
}
private static Vertices AddTriangle(Vertices 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[0].X == vertices[i].X && t[0].Y == vertices[i].Y)
{
if (firstP == -1)
{
firstP = i;
firstT = 0;
}
else
{
secondP = i;
secondT = 0;
}
}
else if (t[1].X == vertices[i].X && t[1].Y == vertices[i].Y)
{
if (firstP == -1)
{
firstP = i;
firstT = 1;
}
else
{
secondP = i;
secondT = 1;
}
}
else if (t[2].X == vertices[i].X && t[2].Y == 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(t[tipT]);
}
return result;
}
}
}
@@ -0,0 +1,302 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.PolygonManipulation
{
/// <summary>
/// Provides a set of tools to simplify polygons in various ways.
/// </summary>
public static class SimplifyTools
{
/// <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 = 0)
{
if (vertices.Count <= 3)
return vertices;
Vertices simplified = new Vertices(vertices.Count);
for (int i = 0; i < vertices.Count; i++)
{
Vector2 prev = vertices.PreviousVertex(i);
Vector2 current = vertices[i];
Vector2 next = vertices.NextVertex(i);
//If they collinear, continue
if (MathUtils.IsCollinear(ref prev, ref current, ref next, collinearityTolerance))
continue;
simplified.Add(current);
}
return simplified;
}
/// <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)
{
if (vertices.Count <= 3)
return vertices;
bool[] usePoint = new bool[vertices.Count];
for (int i = 0; i < vertices.Count; i++)
usePoint[i] = true;
SimplifySection(vertices, 0, vertices.Count - 1, usePoint, distanceTolerance);
Vertices simplified = new Vertices(vertices.Count);
for (int i = 0; i < vertices.Count; i++)
{
if (usePoint[i])
simplified.Add(vertices[i]);
}
return simplified;
}
private static void SimplifySection(Vertices vertices, int i, int j, bool[] usePoint, float distanceTolerance)
{
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++)
{
Vector2 point = vertices[k];
double distance = LineTools.DistanceBetweenPointAndLineSegment(ref point, ref a, ref b);
if (distance > maxDistance)
{
maxDistance = distance;
maxIndex = k;
}
}
if (maxDistance <= distanceTolerance)
{
for (int k = i + 1; k < j; k++)
{
usePoint[k] = false;
}
}
else
{
SimplifySection(vertices, i, maxIndex, usePoint, distanceTolerance);
SimplifySection(vertices, maxIndex, j, usePoint, distanceTolerance);
}
}
/// <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 Vertices MergeParallelEdges(Vertices vertices, float tolerance)
{
//From Eric Jordan's convex decomposition library
if (vertices.Count <= 3)
return vertices; //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 vertices;
int currIndex = 0;
//Copy the vertices to a new list and clear the old
Vertices newVertices = new Vertices(newNVertices);
for (int i = 0; i < vertices.Count; ++i)
{
if (mergeMe[i] || newNVertices == 0 || currIndex == newNVertices)
continue;
Debug.Assert(currIndex < newNVertices);
newVertices.Add(vertices[i]);
++currIndex;
}
return newVertices;
}
/// <summary>
/// Merges the identical points in the polygon.
/// </summary>
/// <param name="vertices">The vertices.</param>
public static Vertices MergeIdenticalPoints(Vertices vertices)
{
HashSet<Vector2> unique = new HashSet<Vector2>();
foreach (Vector2 vertex in vertices)
{
unique.Add(vertex);
}
return new Vertices(unique);
}
/// <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 removed.</param>
public static Vertices ReduceByDistance(Vertices vertices, float distance)
{
if (vertices.Count <= 3)
return vertices;
float distance2 = distance * distance;
Vertices simplified = new Vertices(vertices.Count);
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() <= distance2)
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)
{
if (vertices.Count <= 3)
return vertices;
if (nth == 0)
return vertices;
Vertices simplified = new Vertices(vertices.Count);
for (int i = 0; i < vertices.Count; i++)
{
if (i % nth == 0)
continue;
simplified.Add(vertices[i]);
}
return simplified;
}
/// <summary>
/// Simplify the polygon by removing all points that in pairs of 3 have an area less than the tolerance.
///
/// Pass in 0 as tolerance, and it will only remove collinear points.
/// </summary>
/// <param name="vertices"></param>
/// <param name="areaTolerance"></param>
/// <returns></returns>
public static Vertices ReduceByArea(Vertices vertices, float areaTolerance)
{
//From physics2d.net
if (vertices.Count <= 3)
return vertices;
if (areaTolerance < 0)
throw new ArgumentOutOfRangeException("areaTolerance", "must be equal to or greater than zero.");
Vertices simplified = new Vertices(vertices.Count);
Vector2 v3;
Vector2 v1 = vertices[vertices.Count - 2];
Vector2 v2 = vertices[vertices.Count - 1];
areaTolerance *= 2;
for (int i = 0; i < vertices.Count; ++i, v2 = v3)
{
v3 = i == vertices.Count - 1 ? simplified[0] : vertices[i];
float old1;
MathUtils.Cross(ref v1, ref v2, out old1);
float old2;
MathUtils.Cross(ref v2, ref v3, out old2);
float new1;
MathUtils.Cross(ref v1, ref v3, out new1);
if (Math.Abs(new1 - (old1 + old2)) > areaTolerance)
{
simplified.Add(v2);
v1 = v2;
}
}
return simplified;
}
}
}
@@ -0,0 +1,514 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
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.GetAABB().LowerBound;
Vector2 lbClip = clip.GetAABB().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 = 0;
if (poly2Simplicies.Contains(poly1Simplicies[i]))
{
edgeCharacter = 1f;
}
else if (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)
{
float edgeCharacter = 0f;
if (!resultSimplices.Contains(poly2Simplicies[i]) &&
!resultSimplices.Contains(-poly2Simplicies[i]))
{
if (poly1Simplicies.Contains(-poly2Simplicies[i]) && clipType == PolyClipType.Union)
{
edgeCharacter = 1f;
}
else
{
edgeCharacter = 0f;
for (int j = 0; j < poly1Simplicies.Count; ++j)
{
if (!poly1Simplicies.Contains(poly2Simplicies[i]) && !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
}
}
@@ -0,0 +1,360 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using FarseerPhysics.Common.TextureTools;
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;
}
/// <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.p = center;
xf.q.Set(angle);
// Transform vertices
for (int i = 0; i < 4; ++i)
{
vertices[i] = MathUtils.Mul(ref xf, vertices[i]);
}
return vertices;
}
//Rounded rectangle contributed by Jonathan Smars - jsmars@gmail.com
/// <summary>
/// Creates a rounded rectangle with the specified width and height.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="xRadius">The rounding X radius.</param>
/// <param name="yRadius">The rounding Y radius.</param>
/// <param name="segments">The number of segments to subdivide the edges.</param>
/// <returns></returns>
public static Vertices CreateRoundedRectangle(float width, float height, float xRadius, float yRadius,
int segments)
{
if (yRadius > height / 2 || xRadius > width / 2)
throw new Exception("Rounding amount can't be more than half the height and width respectively.");
if (segments < 0)
throw new Exception("Segments must be zero or more.");
//We need at least 8 vertices to create a rounded rectangle
Debug.Assert(Settings.MaxPolygonVertices >= 8);
Vertices vertices = new Vertices();
if (segments == 0)
{
vertices.Add(new Vector2(width * .5f - xRadius, -height * .5f));
vertices.Add(new Vector2(width * .5f, -height * .5f + yRadius));
vertices.Add(new Vector2(width * .5f, height * .5f - yRadius));
vertices.Add(new Vector2(width * .5f - xRadius, height * .5f));
vertices.Add(new Vector2(-width * .5f + xRadius, height * .5f));
vertices.Add(new Vector2(-width * .5f, height * .5f - yRadius));
vertices.Add(new Vector2(-width * .5f, -height * .5f + yRadius));
vertices.Add(new Vector2(-width * .5f + xRadius, -height * .5f));
}
else
{
int numberOfEdges = (segments * 4 + 8);
float stepSize = MathHelper.TwoPi / (numberOfEdges - 4);
int perPhase = numberOfEdges / 4;
Vector2 posOffset = new Vector2(width / 2 - xRadius, height / 2 - yRadius);
vertices.Add(posOffset + new Vector2(xRadius, -yRadius + yRadius));
short phase = 0;
for (int i = 1; i < numberOfEdges; i++)
{
if (i - perPhase == 0 || i - perPhase * 3 == 0)
{
posOffset.X *= -1;
phase--;
}
else if (i - perPhase * 2 == 0)
{
posOffset.Y *= -1;
phase--;
}
vertices.Add(posOffset + new Vector2(xRadius * (float)Math.Cos(stepSize * -(i + phase)),
-yRadius * (float)Math.Sin(stepSize * -(i + phase))));
}
}
return vertices;
}
/// <summary>
/// Set this as a single edge.
/// </summary>
/// <param name="start">The first point.</param>
/// <param name="end">The second point.</param>
public static Vertices CreateLine(Vector2 start, Vector2 end)
{
Vertices vertices = new Vertices(2);
vertices.Add(start);
vertices.Add(end);
return vertices;
}
/// <summary>
/// Creates a circle with the specified radius and number of edges.
/// </summary>
/// <param name="radius">The radius.</param>
/// <param name="numberOfEdges">The number of edges. The more edges, the more it resembles a circle</param>
/// <returns></returns>
public static Vertices CreateCircle(float radius, int numberOfEdges)
{
return CreateEllipse(radius, radius, numberOfEdges);
}
/// <summary>
/// Creates a ellipse with the specified width, height and number of edges.
/// </summary>
/// <param name="xRadius">Width of the ellipse.</param>
/// <param name="yRadius">Height of the ellipse.</param>
/// <param name="numberOfEdges">The number of edges. The more edges, the more it resembles an ellipse</param>
/// <returns></returns>
public static Vertices CreateEllipse(float xRadius, float yRadius, int numberOfEdges)
{
Vertices vertices = new Vertices();
float stepSize = MathHelper.TwoPi / numberOfEdges;
vertices.Add(new Vector2(xRadius, 0));
for (int i = numberOfEdges - 1; i > 0; --i)
vertices.Add(new Vector2(xRadius * (float)Math.Cos(stepSize * i),
-yRadius * (float)Math.Sin(stepSize * i)));
return vertices;
}
public static Vertices CreateArc(float radians, int sides, float radius)
{
Debug.Assert(radians > 0, "The arc needs to be larger than 0");
Debug.Assert(sides > 1, "The arc needs to have more than 1 sides");
Debug.Assert(radius > 0, "The arc needs to have a radius larger than 0");
Vertices vertices = new Vertices();
float stepSize = radians / sides;
for (int i = sides - 1; i > 0; i--)
{
vertices.Add(new Vector2(radius * (float)Math.Cos(stepSize * i),
radius * (float)Math.Sin(stepSize * i)));
}
return vertices;
}
//Capsule contributed by Yobiv
/// <summary>
/// Creates an capsule with the specified height, radius and number of edges.
/// A capsule has the same form as a pill capsule.
/// </summary>
/// <param name="height">Height (inner height + 2 * radius) of the capsule.</param>
/// <param name="endRadius">Radius of the capsule ends.</param>
/// <param name="edges">The number of edges of the capsule ends. The more edges, the more it resembles an capsule</param>
/// <returns></returns>
public static Vertices CreateCapsule(float height, float endRadius, int edges)
{
if (endRadius >= height / 2)
throw new ArgumentException(
"The radius must be lower than height / 2. Higher values of radius would create a circle, and not a half circle.",
"endRadius");
return CreateCapsule(height, endRadius, edges, endRadius, edges);
}
/// <summary>
/// Creates an capsule with the specified height, radius and number of edges.
/// A capsule has the same form as a pill capsule.
/// </summary>
/// <param name="height">Height (inner height + radii) of the capsule.</param>
/// <param name="topRadius">Radius of the top.</param>
/// <param name="topEdges">The number of edges of the top. The more edges, the more it resembles an capsule</param>
/// <param name="bottomRadius">Radius of bottom.</param>
/// <param name="bottomEdges">The number of edges of the bottom. The more edges, the more it resembles an capsule</param>
/// <returns></returns>
public static Vertices CreateCapsule(float height, float topRadius, int topEdges, float bottomRadius,
int bottomEdges)
{
if (height <= 0)
throw new ArgumentException("Height must be longer than 0", "height");
if (topRadius <= 0)
throw new ArgumentException("The top radius must be more than 0", "topRadius");
if (topEdges <= 0)
throw new ArgumentException("Top edges must be more than 0", "topEdges");
if (bottomRadius <= 0)
throw new ArgumentException("The bottom radius must be more than 0", "bottomRadius");
if (bottomEdges <= 0)
throw new ArgumentException("Bottom edges must be more than 0", "bottomEdges");
if (topRadius >= height / 2)
throw new ArgumentException(
"The top radius must be lower than height / 2. Higher values of top radius would create a circle, and not a half circle.",
"topRadius");
if (bottomRadius >= height / 2)
throw new ArgumentException(
"The bottom radius must be lower than height / 2. Higher values of bottom radius would create a circle, and not a half circle.",
"bottomRadius");
Vertices vertices = new Vertices();
float newHeight = (height - topRadius - bottomRadius) * 0.5f;
// top
vertices.Add(new Vector2(topRadius, newHeight));
float stepSize = MathHelper.Pi / topEdges;
for (int i = 1; i < topEdges; i++)
{
vertices.Add(new Vector2(topRadius * (float)Math.Cos(stepSize * i),
topRadius * (float)Math.Sin(stepSize * i) + newHeight));
}
vertices.Add(new Vector2(-topRadius, newHeight));
// bottom
vertices.Add(new Vector2(-bottomRadius, -newHeight));
stepSize = MathHelper.Pi / bottomEdges;
for (int i = 1; i < bottomEdges; i++)
{
vertices.Add(new Vector2(-bottomRadius * (float)Math.Cos(stepSize * i),
-bottomRadius * (float)Math.Sin(stepSize * i) - newHeight));
}
vertices.Add(new Vector2(bottomRadius, -newHeight));
return vertices;
}
/// <summary>
/// Creates a gear shape with the specified radius and number of teeth.
/// </summary>
/// <param name="radius">The radius.</param>
/// <param name="numberOfTeeth">The number of teeth.</param>
/// <param name="tipPercentage">The tip percentage.</param>
/// <param name="toothHeight">Height of the tooth.</param>
/// <returns></returns>
public static Vertices CreateGear(float radius, int numberOfTeeth, float tipPercentage, float toothHeight)
{
Vertices vertices = new Vertices();
float stepSize = MathHelper.TwoPi / numberOfTeeth;
tipPercentage /= 100f;
MathHelper.Clamp(tipPercentage, 0f, 1f);
float toothTipStepSize = (stepSize / 2f) * tipPercentage;
float toothAngleStepSize = (stepSize - (toothTipStepSize * 2f)) / 2f;
for (int i = numberOfTeeth - 1; i >= 0; --i)
{
if (toothTipStepSize > 0f)
{
vertices.Add(
new Vector2(radius *
(float)Math.Cos(stepSize * i + toothAngleStepSize * 2f + toothTipStepSize),
-radius *
(float)Math.Sin(stepSize * i + toothAngleStepSize * 2f + toothTipStepSize)));
vertices.Add(
new Vector2((radius + toothHeight) *
(float)Math.Cos(stepSize * i + toothAngleStepSize + toothTipStepSize),
-(radius + toothHeight) *
(float)Math.Sin(stepSize * i + toothAngleStepSize + toothTipStepSize)));
}
vertices.Add(new Vector2((radius + toothHeight) *
(float)Math.Cos(stepSize * i + toothAngleStepSize),
-(radius + toothHeight) *
(float)Math.Sin(stepSize * i + toothAngleStepSize)));
vertices.Add(new Vector2(radius * (float)Math.Cos(stepSize * i),
-radius * (float)Math.Sin(stepSize * i)));
}
return vertices;
}
/// <summary>
/// Detects the vertices by analyzing the texture data.
/// </summary>
/// <param name="data">The texture data.</param>
/// <param name="width">The texture width.</param>
/// <returns></returns>
public static Vertices CreatePolygon(uint[] data, int width)
{
return TextureConverter.DetectVertices(data, width);
}
/// <summary>
/// Detects the vertices by analyzing the texture data.
/// </summary>
/// <param name="data">The texture data.</param>
/// <param name="width">The texture width.</param>
/// <param name="holeDetection">if set to <c>true</c> it will perform hole detection.</param>
/// <returns></returns>
public static Vertices CreatePolygon(uint[] data, int width, bool holeDetection)
{
return TextureConverter.DetectVertices(data, width, holeDetection);
}
/// <summary>
/// Detects the vertices by analyzing the texture data.
/// </summary>
/// <param name="data">The texture data.</param>
/// <param name="width">The texture width.</param>
/// <param name="hullTolerance">The hull tolerance.</param>
/// <param name="alphaTolerance">The alpha tolerance.</param>
/// <param name="multiPartDetection">if set to <c>true</c> it will perform multi part detection.</param>
/// <param name="holeDetection">if set to <c>true</c> it will perform hole detection.</param>
/// <returns></returns>
public static List<Vertices> CreatePolygon(uint[] data, int width, float hullTolerance,
byte alphaTolerance, bool multiPartDetection, bool holeDetection)
{
return TextureConverter.DetectVertices(data, width, hullTolerance, alphaTolerance,
multiPartDetection, holeDetection);
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,112 @@
#if SILVERLIGHT
using System;
namespace FarseerPhysics.Common
{
// Source: http://www.wiredprairie.us/blog/index.php/archives/723
/// <summary>
/// An emulation for the Stopwatch class for Windows Phone and Silverlight.
/// </summary>
public sealed class Stopwatch
{
private long _startTick;
private long _elapsed;
private bool _isRunning;
/// <summary>
/// Creates a new instance of the class and starts the watch immediately.
/// </summary>
/// <returns>An instance of Stopwatch, running.</returns>
public static Stopwatch StartNew()
{
Stopwatch sw = new Stopwatch();
sw.Start();
return sw;
}
/// <summary>
/// Creates an instance of the Stopwatch class.
/// </summary>
public Stopwatch() { }
/// <summary>
/// Completely resets and deactivates the timer.
/// </summary>
public void Reset()
{
_elapsed = 0;
_isRunning = false;
_startTick = 0;
}
/// <summary>
/// Begins the timer.
/// </summary>
public void Start()
{
if (!_isRunning)
{
_startTick = GetCurrentTicks();
_isRunning = true;
}
}
/// <summary>
/// Stops the current timer.
/// </summary>
public void Stop()
{
if (_isRunning)
{
_elapsed += GetCurrentTicks() - _startTick;
_isRunning = false;
}
}
/// <summary>
/// Gets a value indicating whether the instance is currently recording.
/// </summary>
public bool IsRunning
{
get { return _isRunning; }
}
/// <summary>
/// Gets the Elapsed time as a Timespan.
/// </summary>
public TimeSpan Elapsed
{
get { return TimeSpan.FromMilliseconds(ElapsedMilliseconds); }
}
/// <summary>
/// Gets the Elapsed time as the total number of milliseconds.
/// </summary>
public long ElapsedMilliseconds
{
get { return GetCurrentElapsedTicks() / TimeSpan.TicksPerMillisecond; }
}
/// <summary>
/// Gets the Elapsed time as the total number of ticks (which is faked
/// as Silverlight doesn't have a way to get at the actual "Ticks")
/// </summary>
public long ElapsedTicks
{
get { return GetCurrentElapsedTicks(); }
}
private long GetCurrentElapsedTicks()
{
return _elapsed + (IsRunning ? (GetCurrentTicks() - _startTick) : 0);
}
private long GetCurrentTicks()
{
// TickCount: Gets the number of milliseconds elapsed since the system started.
return Environment.TickCount * TimeSpan.TicksPerMillisecond;
}
}
}
#endif
@@ -0,0 +1,800 @@
using System.Collections.Generic;
using FarseerPhysics.Collision;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.TextureTools
{
// 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
}
}
@@ -0,0 +1,264 @@
using System.Collections.Generic;
using FarseerPhysics.Collision;
using FarseerPhysics.Common.Decomposition;
using FarseerPhysics.Common.PolygonManipulation;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.TextureTools
{
/// <summary>
/// Simple class to maintain a terrain. It can keep track
/// </summary>
public class Terrain
{
/// <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 TriangulationAlgorithm 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;
/// <summary>
/// Creates a new terrain.
/// </summary>
/// <param name="world">The World</param>
/// <param name="area">The area of the terrain.</param>
public Terrain(World world, AABB area)
{
World = world;
Width = area.Width;
Height = area.Height;
Center = area.Center;
}
/// <summary>
/// Creates a new terrain
/// </summary>
/// <param name="world">The World</param>
/// <param name="position">The position (center) of the terrain.</param>
/// <param name="width">The width of the terrain.</param>
/// <param name="height">The height of the terrain.</param>
public Terrain(World world, Vector2 position, float width, float height)
{
World = world;
Width = width;
Height = height;
Center = position;
}
/// <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 the specified texture data to the terrain.
/// </summary>
/// <param name="data"></param>
/// <param name="offset"></param>
public void ApplyData(sbyte[,] data, Vector2 offset = default(Vector2))
{
for (int x = 0; x < data.GetUpperBound(0); x++)
{
for (int y = 0; y < data.GetUpperBound(1); y++)
{
if (x + offset.X >= 0 && x + offset.X < _localWidth && y + offset.Y >= 0 && y + offset.Y < _localHeight)
{
_terrainMap[(int)(x + offset.X), (int)(y + offset.Y)] = data[x, y];
}
}
}
RemoveOldData(0, _xnum, 0, _ynum);
}
/// <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
int xStart = (int)(_dirtyArea.LowerBound.X / CellSize);
if (xStart < 0) xStart = 0;
int xEnd = (int)(_dirtyArea.UpperBound.X / CellSize) + 1;
if (xEnd > _xnum) xEnd = _xnum;
int yStart = (int)(_dirtyArea.LowerBound.Y / CellSize);
if (yStart < 0) yStart = 0;
int yEnd = (int)(_dirtyArea.UpperBound.Y / CellSize) + 1;
if (yEnd > _ynum) yEnd = _ynum;
RemoveOldData(xStart, xEnd, yStart, yEnd);
_dirtyArea = new AABB(new Vector2(float.MaxValue, float.MaxValue), new Vector2(float.MinValue, float.MinValue));
}
private void RemoveOldData(int xStart, int xEnd, int yStart, int yEnd)
{
for (int x = xStart; x < xEnd; x++)
{
for (int y = yStart; y < yEnd; y++)
{
//remove old terrain object at grid cell
if (_bodyMap[x, y] != null)
{
for (int i = 0; i < _bodyMap[x, y].Count; i++)
{
World.RemoveBody(_bodyMap[x, y][i]);
}
}
_bodyMap[x, y] = null;
//generate new one
GenerateTerrain(x, y);
}
}
}
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 (Vertices item in polys)
{
// does this need to be negative?
item.Scale(ref scale);
item.Translate(ref _topLeft);
Vertices simplified = SimplifyTools.CollinearSimplify(item);
List<Vertices> decompPolys = Triangulate.ConvexPartition(simplified, Decomposer);
foreach (Vertices poly in decompPolys)
{
if (poly.Count > 2)
_bodyMap[gx, gy].Add(BodyFactory.CreatePolygon(World, poly, 1));
}
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,588 @@
#if !XNA && !WINDOWS_PHONE && !XBOX && !ANDROID
#region License
/*
MIT License
Copyright © 2006 The Mono.Xna Team
All rights reserved.
Authors
* Alan McGovern
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#endregion License
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.Xna.Framework
{
[StructLayout(LayoutKind.Sequential)]
public struct Vector2 : IEquatable<Vector2>
{
#region Private Fields
private static Vector2 zeroVector = new Vector2(0f, 0f);
private static Vector2 unitVector = new Vector2(1f, 1f);
private static Vector2 unitXVector = new Vector2(1f, 0f);
private static Vector2 unitYVector = new Vector2(0f, 1f);
#endregion Private Fields
#region Public Fields
public float X;
public float Y;
#endregion Public Fields
#region Properties
public static Vector2 Zero
{
get { return zeroVector; }
}
public static Vector2 One
{
get { return unitVector; }
}
public static Vector2 UnitX
{
get { return unitXVector; }
}
public static Vector2 UnitY
{
get { return unitYVector; }
}
#endregion Properties
#region Constructors
/// <summary>
/// Constructor foe standard 2D vector.
/// </summary>
/// <param name="x">
/// A <see cref="System.Single"/>
/// </param>
/// <param name="y">
/// A <see cref="System.Single"/>
/// </param>
public Vector2(float x, float y)
{
X = x;
Y = y;
}
/// <summary>
/// Constructor for "square" vector.
/// </summary>
/// <param name="value">
/// A <see cref="System.Single"/>
/// </param>
public Vector2(float value)
{
X = value;
Y = value;
}
#endregion Constructors
#region Public Methods
public static void Reflect(ref Vector2 vector, ref Vector2 normal, out Vector2 result)
{
float dot = Dot(vector, normal);
result.X = vector.X - ((2f*dot)*normal.X);
result.Y = vector.Y - ((2f*dot)*normal.Y);
}
public static Vector2 Reflect(Vector2 vector, Vector2 normal)
{
Vector2 result;
Reflect(ref vector, ref normal, out result);
return result;
}
public static Vector2 Add(Vector2 value1, Vector2 value2)
{
value1.X += value2.X;
value1.Y += value2.Y;
return value1;
}
public static void Add(ref Vector2 value1, ref Vector2 value2, out Vector2 result)
{
result.X = value1.X + value2.X;
result.Y = value1.Y + value2.Y;
}
public static Vector2 Barycentric(Vector2 value1, Vector2 value2, Vector2 value3, float amount1, float amount2)
{
return new Vector2(
MathHelper.Barycentric(value1.X, value2.X, value3.X, amount1, amount2),
MathHelper.Barycentric(value1.Y, value2.Y, value3.Y, amount1, amount2));
}
public static void Barycentric(ref Vector2 value1, ref Vector2 value2, ref Vector2 value3, float amount1,
float amount2, out Vector2 result)
{
result = new Vector2(
MathHelper.Barycentric(value1.X, value2.X, value3.X, amount1, amount2),
MathHelper.Barycentric(value1.Y, value2.Y, value3.Y, amount1, amount2));
}
public static Vector2 CatmullRom(Vector2 value1, Vector2 value2, Vector2 value3, Vector2 value4, float amount)
{
return new Vector2(
MathHelper.CatmullRom(value1.X, value2.X, value3.X, value4.X, amount),
MathHelper.CatmullRom(value1.Y, value2.Y, value3.Y, value4.Y, amount));
}
public static void CatmullRom(ref Vector2 value1, ref Vector2 value2, ref Vector2 value3, ref Vector2 value4,
float amount, out Vector2 result)
{
result = new Vector2(
MathHelper.CatmullRom(value1.X, value2.X, value3.X, value4.X, amount),
MathHelper.CatmullRom(value1.Y, value2.Y, value3.Y, value4.Y, amount));
}
public static Vector2 Clamp(Vector2 value1, Vector2 min, Vector2 max)
{
return new Vector2(
MathHelper.Clamp(value1.X, min.X, max.X),
MathHelper.Clamp(value1.Y, min.Y, max.Y));
}
public static void Clamp(ref Vector2 value1, ref Vector2 min, ref Vector2 max, out Vector2 result)
{
result = new Vector2(
MathHelper.Clamp(value1.X, min.X, max.X),
MathHelper.Clamp(value1.Y, min.Y, max.Y));
}
/// <summary>
/// Returns float precison distanve between two vectors
/// </summary>
/// <param name="value1">
/// A <see cref="Vector2"/>
/// </param>
/// <param name="value2">
/// A <see cref="Vector2"/>
/// </param>
/// <returns>
/// A <see cref="System.Single"/>
/// </returns>
public static float Distance(Vector2 value1, Vector2 value2)
{
float result;
DistanceSquared(ref value1, ref value2, out result);
return (float) Math.Sqrt(result);
}
public static void Distance(ref Vector2 value1, ref Vector2 value2, out float result)
{
DistanceSquared(ref value1, ref value2, out result);
result = (float) Math.Sqrt(result);
}
public static float DistanceSquared(Vector2 value1, Vector2 value2)
{
float result;
DistanceSquared(ref value1, ref value2, out result);
return result;
}
public static void DistanceSquared(ref Vector2 value1, ref Vector2 value2, out float result)
{
result = (value1.X - value2.X)*(value1.X - value2.X) + (value1.Y - value2.Y)*(value1.Y - value2.Y);
}
/// <summary>
/// Devide first vector with the secund vector
/// </summary>
/// <param name="value1">
/// A <see cref="Vector2"/>
/// </param>
/// <param name="value2">
/// A <see cref="Vector2"/>
/// </param>
/// <returns>
/// A <see cref="Vector2"/>
/// </returns>
public static Vector2 Divide(Vector2 value1, Vector2 value2)
{
value1.X /= value2.X;
value1.Y /= value2.Y;
return value1;
}
public static void Divide(ref Vector2 value1, ref Vector2 value2, out Vector2 result)
{
result.X = value1.X/value2.X;
result.Y = value1.Y/value2.Y;
}
public static Vector2 Divide(Vector2 value1, float divider)
{
float factor = 1/divider;
value1.X *= factor;
value1.Y *= factor;
return value1;
}
public static void Divide(ref Vector2 value1, float divider, out Vector2 result)
{
float factor = 1/divider;
result.X = value1.X*factor;
result.Y = value1.Y*factor;
}
public static float Dot(Vector2 value1, Vector2 value2)
{
return value1.X*value2.X + value1.Y*value2.Y;
}
public static void Dot(ref Vector2 value1, ref Vector2 value2, out float result)
{
result = value1.X*value2.X + value1.Y*value2.Y;
}
public override bool Equals(object obj)
{
return (obj is Vector2) ? this == ((Vector2) obj) : false;
}
public bool Equals(Vector2 other)
{
return this == other;
}
public override int GetHashCode()
{
return (int) (X + Y);
}
public static Vector2 Hermite(Vector2 value1, Vector2 tangent1, Vector2 value2, Vector2 tangent2, float amount)
{
Vector2 result = new Vector2();
Hermite(ref value1, ref tangent1, ref value2, ref tangent2, amount, out result);
return result;
}
public static void Hermite(ref Vector2 value1, ref Vector2 tangent1, ref Vector2 value2, ref Vector2 tangent2,
float amount, out Vector2 result)
{
result.X = MathHelper.Hermite(value1.X, tangent1.X, value2.X, tangent2.X, amount);
result.Y = MathHelper.Hermite(value1.Y, tangent1.Y, value2.Y, tangent2.Y, amount);
}
public float Length()
{
float result;
DistanceSquared(ref this, ref zeroVector, out result);
return (float) Math.Sqrt(result);
}
public float LengthSquared()
{
float result;
DistanceSquared(ref this, ref zeroVector, out result);
return result;
}
public static Vector2 Lerp(Vector2 value1, Vector2 value2, float amount)
{
return new Vector2(
MathHelper.Lerp(value1.X, value2.X, amount),
MathHelper.Lerp(value1.Y, value2.Y, amount));
}
public static void Lerp(ref Vector2 value1, ref Vector2 value2, float amount, out Vector2 result)
{
result = new Vector2(
MathHelper.Lerp(value1.X, value2.X, amount),
MathHelper.Lerp(value1.Y, value2.Y, amount));
}
public static Vector2 Max(Vector2 value1, Vector2 value2)
{
return new Vector2(
MathHelper.Max(value1.X, value2.X),
MathHelper.Max(value1.Y, value2.Y));
}
public static void Max(ref Vector2 value1, ref Vector2 value2, out Vector2 result)
{
result = new Vector2(
MathHelper.Max(value1.X, value2.X),
MathHelper.Max(value1.Y, value2.Y));
}
public static Vector2 Min(Vector2 value1, Vector2 value2)
{
return new Vector2(
MathHelper.Min(value1.X, value2.X),
MathHelper.Min(value1.Y, value2.Y));
}
public static void Min(ref Vector2 value1, ref Vector2 value2, out Vector2 result)
{
result = new Vector2(
MathHelper.Min(value1.X, value2.X),
MathHelper.Min(value1.Y, value2.Y));
}
public static Vector2 Multiply(Vector2 value1, Vector2 value2)
{
value1.X *= value2.X;
value1.Y *= value2.Y;
return value1;
}
public static Vector2 Multiply(Vector2 value1, float scaleFactor)
{
value1.X *= scaleFactor;
value1.Y *= scaleFactor;
return value1;
}
public static void Multiply(ref Vector2 value1, float scaleFactor, out Vector2 result)
{
result.X = value1.X*scaleFactor;
result.Y = value1.Y*scaleFactor;
}
public static void Multiply(ref Vector2 value1, ref Vector2 value2, out Vector2 result)
{
result.X = value1.X*value2.X;
result.Y = value1.Y*value2.Y;
}
public static Vector2 Negate(Vector2 value)
{
value.X = -value.X;
value.Y = -value.Y;
return value;
}
public static void Negate(ref Vector2 value, out Vector2 result)
{
result.X = -value.X;
result.Y = -value.Y;
}
public void Normalize()
{
Normalize(ref this, out this);
}
public static Vector2 Normalize(Vector2 value)
{
Normalize(ref value, out value);
return value;
}
public static void Normalize(ref Vector2 value, out Vector2 result)
{
float factor;
DistanceSquared(ref value, ref zeroVector, out factor);
factor = 1f/(float) Math.Sqrt(factor);
result.X = value.X*factor;
result.Y = value.Y*factor;
}
public static Vector2 SmoothStep(Vector2 value1, Vector2 value2, float amount)
{
return new Vector2(
MathHelper.SmoothStep(value1.X, value2.X, amount),
MathHelper.SmoothStep(value1.Y, value2.Y, amount));
}
public static void SmoothStep(ref Vector2 value1, ref Vector2 value2, float amount, out Vector2 result)
{
result = new Vector2(
MathHelper.SmoothStep(value1.X, value2.X, amount),
MathHelper.SmoothStep(value1.Y, value2.Y, amount));
}
public static Vector2 Subtract(Vector2 value1, Vector2 value2)
{
value1.X -= value2.X;
value1.Y -= value2.Y;
return value1;
}
public static void Subtract(ref Vector2 value1, ref Vector2 value2, out Vector2 result)
{
result.X = value1.X - value2.X;
result.Y = value1.Y - value2.Y;
}
public static Vector2 Transform(Vector2 position, Matrix matrix)
{
Transform(ref position, ref matrix, out position);
return position;
}
public static void Transform(ref Vector2 position, ref Matrix matrix, out Vector2 result)
{
result = new Vector2((position.X*matrix.M11) + (position.Y*matrix.M21) + matrix.M41,
(position.X*matrix.M12) + (position.Y*matrix.M22) + matrix.M42);
}
public static void Transform(Vector2[] sourceArray, ref Matrix matrix, Vector2[] destinationArray)
{
throw new NotImplementedException();
}
public static void Transform(Vector2[] sourceArray, int sourceIndex, ref Matrix matrix,
Vector2[] destinationArray, int destinationIndex, int length)
{
throw new NotImplementedException();
}
public static Vector2 TransformNormal(Vector2 normal, Matrix matrix)
{
TransformNormal(ref normal, ref matrix, out normal);
return normal;
}
public static void TransformNormal(ref Vector2 normal, ref Matrix matrix, out Vector2 result)
{
result = new Vector2((normal.X*matrix.M11) + (normal.Y*matrix.M21),
(normal.X*matrix.M12) + (normal.Y*matrix.M22));
}
public static void TransformNormal(Vector2[] sourceArray, ref Matrix matrix, Vector2[] destinationArray)
{
throw new NotImplementedException();
}
public static void TransformNormal(Vector2[] sourceArray, int sourceIndex, ref Matrix matrix,
Vector2[] destinationArray, int destinationIndex, int length)
{
throw new NotImplementedException();
}
public override string ToString()
{
StringBuilder sb = new StringBuilder(24);
sb.Append("{X:");
sb.Append(X);
sb.Append(" Y:");
sb.Append(Y);
sb.Append("}");
return sb.ToString();
}
#endregion Public Methods
#region Operators
public static Vector2 operator -(Vector2 value)
{
value.X = -value.X;
value.Y = -value.Y;
return value;
}
public static bool operator ==(Vector2 value1, Vector2 value2)
{
return value1.X == value2.X && value1.Y == value2.Y;
}
public static bool operator !=(Vector2 value1, Vector2 value2)
{
return value1.X != value2.X || value1.Y != value2.Y;
}
public static Vector2 operator +(Vector2 value1, Vector2 value2)
{
value1.X += value2.X;
value1.Y += value2.Y;
return value1;
}
public static Vector2 operator -(Vector2 value1, Vector2 value2)
{
value1.X -= value2.X;
value1.Y -= value2.Y;
return value1;
}
public static Vector2 operator *(Vector2 value1, Vector2 value2)
{
value1.X *= value2.X;
value1.Y *= value2.Y;
return value1;
}
public static Vector2 operator *(Vector2 value, float scaleFactor)
{
value.X *= scaleFactor;
value.Y *= scaleFactor;
return value;
}
public static Vector2 operator *(float scaleFactor, Vector2 value)
{
value.X *= scaleFactor;
value.Y *= scaleFactor;
return value;
}
public static Vector2 operator /(Vector2 value1, Vector2 value2)
{
value1.X /= value2.X;
value1.Y /= value2.Y;
return value1;
}
public static Vector2 operator /(Vector2 value1, float divider)
{
float factor = 1/divider;
value1.X *= factor;
value1.Y *= factor;
return value1;
}
#endregion Operators
}
}
#endif
@@ -0,0 +1,648 @@
#if !XNA && !WINDOWS_PHONE && !XBOX && !ANDROID
#region License
/*
MIT License
Copyright © 2006 The Mono.Xna Team
All rights reserved.
Authors:
* Alan McGovern
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#endregion License
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.Xna.Framework
{
[StructLayout(LayoutKind.Sequential)]
public struct Vector3 : IEquatable<Vector3>
{
#region Private Fields
private static Vector3 zero = new Vector3(0f, 0f, 0f);
private static Vector3 one = new Vector3(1f, 1f, 1f);
private static Vector3 unitX = new Vector3(1f, 0f, 0f);
private static Vector3 unitY = new Vector3(0f, 1f, 0f);
private static Vector3 unitZ = new Vector3(0f, 0f, 1f);
private static Vector3 up = new Vector3(0f, 1f, 0f);
private static Vector3 down = new Vector3(0f, -1f, 0f);
private static Vector3 right = new Vector3(1f, 0f, 0f);
private static Vector3 left = new Vector3(-1f, 0f, 0f);
private static Vector3 forward = new Vector3(0f, 0f, -1f);
private static Vector3 backward = new Vector3(0f, 0f, 1f);
#endregion Private Fields
#region Public Fields
public float X;
public float Y;
public float Z;
#endregion Public Fields
#region Properties
public static Vector3 Zero
{
get { return zero; }
}
public static Vector3 One
{
get { return one; }
}
public static Vector3 UnitX
{
get { return unitX; }
}
public static Vector3 UnitY
{
get { return unitY; }
}
public static Vector3 UnitZ
{
get { return unitZ; }
}
public static Vector3 Up
{
get { return up; }
}
public static Vector3 Down
{
get { return down; }
}
public static Vector3 Right
{
get { return right; }
}
public static Vector3 Left
{
get { return left; }
}
public static Vector3 Forward
{
get { return forward; }
}
public static Vector3 Backward
{
get { return backward; }
}
#endregion Properties
#region Constructors
public Vector3(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public Vector3(float value)
{
X = value;
Y = value;
Z = value;
}
public Vector3(Vector2 value, float z)
{
X = value.X;
Y = value.Y;
Z = z;
}
#endregion Constructors
#region Public Methods
public static Vector3 Add(Vector3 value1, Vector3 value2)
{
value1.X += value2.X;
value1.Y += value2.Y;
value1.Z += value2.Z;
return value1;
}
public static void Add(ref Vector3 value1, ref Vector3 value2, out Vector3 result)
{
result.X = value1.X + value2.X;
result.Y = value1.Y + value2.Y;
result.Z = value1.Z + value2.Z;
}
public static Vector3 Barycentric(Vector3 value1, Vector3 value2, Vector3 value3, float amount1, float amount2)
{
return new Vector3(
MathHelper.Barycentric(value1.X, value2.X, value3.X, amount1, amount2),
MathHelper.Barycentric(value1.Y, value2.Y, value3.Y, amount1, amount2),
MathHelper.Barycentric(value1.Z, value2.Z, value3.Z, amount1, amount2));
}
public static void Barycentric(ref Vector3 value1, ref Vector3 value2, ref Vector3 value3, float amount1,
float amount2, out Vector3 result)
{
result = new Vector3(
MathHelper.Barycentric(value1.X, value2.X, value3.X, amount1, amount2),
MathHelper.Barycentric(value1.Y, value2.Y, value3.Y, amount1, amount2),
MathHelper.Barycentric(value1.Z, value2.Z, value3.Z, amount1, amount2));
}
public static Vector3 CatmullRom(Vector3 value1, Vector3 value2, Vector3 value3, Vector3 value4, float amount)
{
return new Vector3(
MathHelper.CatmullRom(value1.X, value2.X, value3.X, value4.X, amount),
MathHelper.CatmullRom(value1.Y, value2.Y, value3.Y, value4.Y, amount),
MathHelper.CatmullRom(value1.Z, value2.Z, value3.Z, value4.Z, amount));
}
public static void CatmullRom(ref Vector3 value1, ref Vector3 value2, ref Vector3 value3, ref Vector3 value4,
float amount, out Vector3 result)
{
result = new Vector3(
MathHelper.CatmullRom(value1.X, value2.X, value3.X, value4.X, amount),
MathHelper.CatmullRom(value1.Y, value2.Y, value3.Y, value4.Y, amount),
MathHelper.CatmullRom(value1.Z, value2.Z, value3.Z, value4.Z, amount));
}
public static Vector3 Clamp(Vector3 value1, Vector3 min, Vector3 max)
{
return new Vector3(
MathHelper.Clamp(value1.X, min.X, max.X),
MathHelper.Clamp(value1.Y, min.Y, max.Y),
MathHelper.Clamp(value1.Z, min.Z, max.Z));
}
public static void Clamp(ref Vector3 value1, ref Vector3 min, ref Vector3 max, out Vector3 result)
{
result = new Vector3(
MathHelper.Clamp(value1.X, min.X, max.X),
MathHelper.Clamp(value1.Y, min.Y, max.Y),
MathHelper.Clamp(value1.Z, min.Z, max.Z));
}
public static Vector3 Cross(Vector3 vector1, Vector3 vector2)
{
Cross(ref vector1, ref vector2, out vector1);
return vector1;
}
public static void Cross(ref Vector3 vector1, ref Vector3 vector2, out Vector3 result)
{
result = new Vector3(vector1.Y*vector2.Z - vector2.Y*vector1.Z,
-(vector1.X*vector2.Z - vector2.X*vector1.Z),
vector1.X*vector2.Y - vector2.X*vector1.Y);
}
public static float Distance(Vector3 vector1, Vector3 vector2)
{
float result;
DistanceSquared(ref vector1, ref vector2, out result);
return (float) Math.Sqrt(result);
}
public static void Distance(ref Vector3 value1, ref Vector3 value2, out float result)
{
DistanceSquared(ref value1, ref value2, out result);
result = (float) Math.Sqrt(result);
}
public static float DistanceSquared(Vector3 value1, Vector3 value2)
{
float result;
DistanceSquared(ref value1, ref value2, out result);
return result;
}
public static void DistanceSquared(ref Vector3 value1, ref Vector3 value2, out float result)
{
result = (value1.X - value2.X)*(value1.X - value2.X) +
(value1.Y - value2.Y)*(value1.Y - value2.Y) +
(value1.Z - value2.Z)*(value1.Z - value2.Z);
}
public static Vector3 Divide(Vector3 value1, Vector3 value2)
{
value1.X /= value2.X;
value1.Y /= value2.Y;
value1.Z /= value2.Z;
return value1;
}
public static Vector3 Divide(Vector3 value1, float value2)
{
float factor = 1/value2;
value1.X *= factor;
value1.Y *= factor;
value1.Z *= factor;
return value1;
}
public static void Divide(ref Vector3 value1, float divisor, out Vector3 result)
{
float factor = 1/divisor;
result.X = value1.X*factor;
result.Y = value1.Y*factor;
result.Z = value1.Z*factor;
}
public static void Divide(ref Vector3 value1, ref Vector3 value2, out Vector3 result)
{
result.X = value1.X/value2.X;
result.Y = value1.Y/value2.Y;
result.Z = value1.Z/value2.Z;
}
public static float Dot(Vector3 vector1, Vector3 vector2)
{
return vector1.X*vector2.X + vector1.Y*vector2.Y + vector1.Z*vector2.Z;
}
public static void Dot(ref Vector3 vector1, ref Vector3 vector2, out float result)
{
result = vector1.X*vector2.X + vector1.Y*vector2.Y + vector1.Z*vector2.Z;
}
public override bool Equals(object obj)
{
return (obj is Vector3) ? this == (Vector3) obj : false;
}
public bool Equals(Vector3 other)
{
return this == other;
}
public override int GetHashCode()
{
return (int) (X + Y + Z);
}
public static Vector3 Hermite(Vector3 value1, Vector3 tangent1, Vector3 value2, Vector3 tangent2, float amount)
{
Vector3 result = new Vector3();
Hermite(ref value1, ref tangent1, ref value2, ref tangent2, amount, out result);
return result;
}
public static void Hermite(ref Vector3 value1, ref Vector3 tangent1, ref Vector3 value2, ref Vector3 tangent2,
float amount, out Vector3 result)
{
result.X = MathHelper.Hermite(value1.X, tangent1.X, value2.X, tangent2.X, amount);
result.Y = MathHelper.Hermite(value1.Y, tangent1.Y, value2.Y, tangent2.Y, amount);
result.Z = MathHelper.Hermite(value1.Z, tangent1.Z, value2.Z, tangent2.Z, amount);
}
public float Length()
{
float result;
DistanceSquared(ref this, ref zero, out result);
return (float) Math.Sqrt(result);
}
public float LengthSquared()
{
float result;
DistanceSquared(ref this, ref zero, out result);
return result;
}
public static Vector3 Lerp(Vector3 value1, Vector3 value2, float amount)
{
return new Vector3(
MathHelper.Lerp(value1.X, value2.X, amount),
MathHelper.Lerp(value1.Y, value2.Y, amount),
MathHelper.Lerp(value1.Z, value2.Z, amount));
}
public static void Lerp(ref Vector3 value1, ref Vector3 value2, float amount, out Vector3 result)
{
result = new Vector3(
MathHelper.Lerp(value1.X, value2.X, amount),
MathHelper.Lerp(value1.Y, value2.Y, amount),
MathHelper.Lerp(value1.Z, value2.Z, amount));
}
public static Vector3 Max(Vector3 value1, Vector3 value2)
{
return new Vector3(
MathHelper.Max(value1.X, value2.X),
MathHelper.Max(value1.Y, value2.Y),
MathHelper.Max(value1.Z, value2.Z));
}
public static void Max(ref Vector3 value1, ref Vector3 value2, out Vector3 result)
{
result = new Vector3(
MathHelper.Max(value1.X, value2.X),
MathHelper.Max(value1.Y, value2.Y),
MathHelper.Max(value1.Z, value2.Z));
}
public static Vector3 Min(Vector3 value1, Vector3 value2)
{
return new Vector3(
MathHelper.Min(value1.X, value2.X),
MathHelper.Min(value1.Y, value2.Y),
MathHelper.Min(value1.Z, value2.Z));
}
public static void Min(ref Vector3 value1, ref Vector3 value2, out Vector3 result)
{
result = new Vector3(
MathHelper.Min(value1.X, value2.X),
MathHelper.Min(value1.Y, value2.Y),
MathHelper.Min(value1.Z, value2.Z));
}
public static Vector3 Multiply(Vector3 value1, Vector3 value2)
{
value1.X *= value2.X;
value1.Y *= value2.Y;
value1.Z *= value2.Z;
return value1;
}
public static Vector3 Multiply(Vector3 value1, float scaleFactor)
{
value1.X *= scaleFactor;
value1.Y *= scaleFactor;
value1.Z *= scaleFactor;
return value1;
}
public static void Multiply(ref Vector3 value1, float scaleFactor, out Vector3 result)
{
result.X = value1.X*scaleFactor;
result.Y = value1.Y*scaleFactor;
result.Z = value1.Z*scaleFactor;
}
public static void Multiply(ref Vector3 value1, ref Vector3 value2, out Vector3 result)
{
result.X = value1.X*value2.X;
result.Y = value1.Y*value2.Y;
result.Z = value1.Z*value2.Z;
}
public static Vector3 Negate(Vector3 value)
{
value = new Vector3(-value.X, -value.Y, -value.Z);
return value;
}
public static void Negate(ref Vector3 value, out Vector3 result)
{
result = new Vector3(-value.X, -value.Y, -value.Z);
}
public void Normalize()
{
Normalize(ref this, out this);
}
public static Vector3 Normalize(Vector3 vector)
{
Normalize(ref vector, out vector);
return vector;
}
public static void Normalize(ref Vector3 value, out Vector3 result)
{
float factor;
Distance(ref value, ref zero, out factor);
factor = 1f/factor;
result.X = value.X*factor;
result.Y = value.Y*factor;
result.Z = value.Z*factor;
}
public static Vector3 Reflect(Vector3 vector, Vector3 normal)
{
Vector3 result;
Reflect(ref vector, ref normal, out result);
return result;
}
public static void Reflect(ref Vector3 vector, ref Vector3 normal, out Vector3 result)
{
float dot = Dot(vector, normal);
result.X = vector.X - ((2f*dot)*normal.X);
result.Y = vector.Y - ((2f*dot)*normal.Y);
result.Z = vector.Z - ((2f*dot)*normal.Z);
}
public static Vector3 SmoothStep(Vector3 value1, Vector3 value2, float amount)
{
return new Vector3(
MathHelper.SmoothStep(value1.X, value2.X, amount),
MathHelper.SmoothStep(value1.Y, value2.Y, amount),
MathHelper.SmoothStep(value1.Z, value2.Z, amount));
}
public static void SmoothStep(ref Vector3 value1, ref Vector3 value2, float amount, out Vector3 result)
{
result = new Vector3(
MathHelper.SmoothStep(value1.X, value2.X, amount),
MathHelper.SmoothStep(value1.Y, value2.Y, amount),
MathHelper.SmoothStep(value1.Z, value2.Z, amount));
}
public static Vector3 Subtract(Vector3 value1, Vector3 value2)
{
value1.X -= value2.X;
value1.Y -= value2.Y;
value1.Z -= value2.Z;
return value1;
}
public static void Subtract(ref Vector3 value1, ref Vector3 value2, out Vector3 result)
{
result.X = value1.X - value2.X;
result.Y = value1.Y - value2.Y;
result.Z = value1.Z - value2.Z;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder(32);
sb.Append("{X:");
sb.Append(X);
sb.Append(" Y:");
sb.Append(Y);
sb.Append(" Z:");
sb.Append(Z);
sb.Append("}");
return sb.ToString();
}
public static Vector3 Transform(Vector3 position, Matrix matrix)
{
Transform(ref position, ref matrix, out position);
return position;
}
public static void Transform(ref Vector3 position, ref Matrix matrix, out Vector3 result)
{
result =
new Vector3((position.X*matrix.M11) + (position.Y*matrix.M21) + (position.Z*matrix.M31) + matrix.M41,
(position.X*matrix.M12) + (position.Y*matrix.M22) + (position.Z*matrix.M32) + matrix.M42,
(position.X*matrix.M13) + (position.Y*matrix.M23) + (position.Z*matrix.M33) + matrix.M43);
}
public static void Transform(Vector3[] sourceArray, ref Matrix matrix, Vector3[] destinationArray)
{
throw new NotImplementedException();
}
public static void Transform(Vector3[] sourceArray, int sourceIndex, ref Matrix matrix,
Vector3[] destinationArray, int destinationIndex, int length)
{
throw new NotImplementedException();
}
public static void TransformNormal(Vector3[] sourceArray, ref Matrix matrix, Vector3[] destinationArray)
{
throw new NotImplementedException();
}
public static void TransformNormal(Vector3[] sourceArray, int sourceIndex, ref Matrix matrix,
Vector3[] destinationArray, int destinationIndex, int length)
{
throw new NotImplementedException();
}
public static Vector3 TransformNormal(Vector3 normal, Matrix matrix)
{
TransformNormal(ref normal, ref matrix, out normal);
return normal;
}
public static void TransformNormal(ref Vector3 normal, ref Matrix matrix, out Vector3 result)
{
result = new Vector3((normal.X*matrix.M11) + (normal.Y*matrix.M21) + (normal.Z*matrix.M31),
(normal.X*matrix.M12) + (normal.Y*matrix.M22) + (normal.Z*matrix.M32),
(normal.X*matrix.M13) + (normal.Y*matrix.M23) + (normal.Z*matrix.M33));
}
#endregion Public methods
#region Operators
public static bool operator ==(Vector3 value1, Vector3 value2)
{
return value1.X == value2.X
&& value1.Y == value2.Y
&& value1.Z == value2.Z;
}
public static bool operator !=(Vector3 value1, Vector3 value2)
{
return !(value1 == value2);
}
public static Vector3 operator +(Vector3 value1, Vector3 value2)
{
value1.X += value2.X;
value1.Y += value2.Y;
value1.Z += value2.Z;
return value1;
}
public static Vector3 operator -(Vector3 value)
{
value = new Vector3(-value.X, -value.Y, -value.Z);
return value;
}
public static Vector3 operator -(Vector3 value1, Vector3 value2)
{
value1.X -= value2.X;
value1.Y -= value2.Y;
value1.Z -= value2.Z;
return value1;
}
public static Vector3 operator *(Vector3 value1, Vector3 value2)
{
value1.X *= value2.X;
value1.Y *= value2.Y;
value1.Z *= value2.Z;
return value1;
}
public static Vector3 operator *(Vector3 value, float scaleFactor)
{
value.X *= scaleFactor;
value.Y *= scaleFactor;
value.Z *= scaleFactor;
return value;
}
public static Vector3 operator *(float scaleFactor, Vector3 value)
{
value.X *= scaleFactor;
value.Y *= scaleFactor;
value.Z *= scaleFactor;
return value;
}
public static Vector3 operator /(Vector3 value1, Vector3 value2)
{
value1.X /= value2.X;
value1.Y /= value2.Y;
value1.Z /= value2.Z;
return value1;
}
public static Vector3 operator /(Vector3 value, float divider)
{
float factor = 1/divider;
value.X *= factor;
value.Y *= factor;
value.Z *= factor;
return value;
}
#endregion
}
}
#endif
@@ -0,0 +1,582 @@
/*
* Farseer Physics Engine:
* Copyright (c) 2012 Ian Qvist
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using FarseerPhysics.Collision;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common
{
public enum PolygonError
{
/// <summary>
/// There were no errors in the polygon
/// </summary>
NoError,
/// <summary>
/// Polygon must have between 3 and Settings.MaxPolygonVertices vertices.
/// </summary>
InvalidAmountOfVertices,
/// <summary>
/// Polygon must be simple. This means no overlapping edges.
/// </summary>
NotSimple,
/// <summary>
/// Polygon must have a counter clockwise winding.
/// </summary>
NotCounterClockWise,
/// <summary>
/// The polygon is concave, it needs to be convex.
/// </summary>
NotConvex,
/// <summary>
/// Polygon area is too small.
/// </summary>
AreaTooSmall,
/// <summary>
/// The polygon has a side that is too short.
/// </summary>
SideTooSmall
}
#if !(XBOX360)
[DebuggerDisplay("Count = {Count} Vertices = {ToString()}")]
#endif
public class Vertices : List<Vector2>
{
public Vertices() { }
public Vertices(int capacity) : base(capacity) { }
public Vertices(IEnumerable<Vector2> vertices)
{
AddRange(vertices);
}
internal bool AttachedToBody { get; set; }
/// <summary>
/// You can add holes to this collection.
/// It will get respected by some of the triangulation algoithms, but otherwise not used.
/// </summary>
public List<Vertices> Holes { get; set; }
/// <summary>
/// Gets the next index. Used for iterating all the edges with wrap-around.
/// </summary>
/// <param name="index">The current index</param>
public int NextIndex(int index)
{
return (index + 1 > Count - 1) ? 0 : index + 1;
}
/// <summary>
/// Gets the next vertex. Used for iterating all the edges with wrap-around.
/// </summary>
/// <param name="index">The current index</param>
public Vector2 NextVertex(int index)
{
return this[NextIndex(index)];
}
/// <summary>
/// Gets the previous index. Used for iterating all the edges with wrap-around.
/// </summary>
/// <param name="index">The current index</param>
public int PreviousIndex(int index)
{
return index - 1 < 0 ? Count - 1 : index - 1;
}
/// <summary>
/// Gets the previous vertex. Used for iterating all the edges with wrap-around.
/// </summary>
/// <param name="index">The current index</param>
public Vector2 PreviousVertex(int index)
{
return this[PreviousIndex(index)];
}
/// <summary>
/// Gets the signed area.
/// If the area is less than 0, it indicates that the polygon is clockwise winded.
/// </summary>
/// <returns>The signed area</returns>
public float GetSignedArea()
{
//The simplest polygon which can exist in the Euclidean plane has 3 sides.
if (Count < 3)
return 0;
int i;
float area = 0;
for (i = 0; i < Count; i++)
{
int j = (i + 1) % Count;
Vector2 vi = this[i];
Vector2 vj = this[j];
area += vi.X * vj.Y;
area -= vi.Y * vj.X;
}
area /= 2.0f;
return area;
}
/// <summary>
/// Gets the area.
/// </summary>
/// <returns></returns>
public float GetArea()
{
float area = GetSignedArea();
return (area < 0 ? -area : area);
}
/// <summary>
/// Gets the centroid.
/// </summary>
/// <returns></returns>
public Vector2 GetCentroid()
{
//The simplest polygon which can exist in the Euclidean plane has 3 sides.
if (Count < 3)
return new Vector2(float.NaN, float.NaN);
// Same algorithm is used by Box2D
Vector2 c = Vector2.Zero;
float area = 0.0f;
const float inv3 = 1.0f / 3.0f;
for (int i = 0; i < Count; ++i)
{
// Triangle vertices.
Vector2 current = this[i];
Vector2 next = (i + 1 < Count ? this[i + 1] : this[0]);
float triangleArea = 0.5f * (current.X * next.Y - current.Y * next.X);
area += triangleArea;
// Area weighted centroid
c += triangleArea * inv3 * (current + next);
}
// Centroid
c *= 1.0f / area;
return c;
}
/// <summary>
/// Returns an AABB that fully contains this polygon.
/// </summary>
public AABB GetAABB()
{
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;
}
/// <summary>
/// Translates the vertices with the specified vector.
/// </summary>
/// <param name="value">The value.</param>
public void Translate(Vector2 value)
{
Translate(ref value);
}
/// <summary>
/// Translates the vertices with the specified vector.
/// </summary>
/// <param name="value">The vector.</param>
public void Translate(ref Vector2 value)
{
Debug.Assert(!AttachedToBody, "Translating vertices that are used by a Body can result in unstable behavior. Use Body.Position instead.");
for (int i = 0; i < Count; i++)
this[i] = Vector2.Add(this[i], value);
if (Holes != null && Holes.Count > 0)
{
foreach (Vertices hole in Holes)
{
hole.Translate(ref value);
}
}
}
/// <summary>
/// Scales the vertices with the specified vector.
/// </summary>
/// <param name="value">The Value.</param>
public void Scale(Vector2 value)
{
Scale(ref value);
}
/// <summary>
/// Scales the vertices with the specified vector.
/// </summary>
/// <param name="value">The Value.</param>
public void Scale(ref Vector2 value)
{
Debug.Assert(!AttachedToBody, "Scaling vertices that are used by a Body can result in unstable behavior.");
for (int i = 0; i < Count; i++)
this[i] = Vector2.Multiply(this[i], value);
if (Holes != null && Holes.Count > 0)
{
foreach (Vertices hole in Holes)
{
hole.Scale(ref value);
}
}
}
/// <summary>
/// Rotate the vertices with the defined value in radians.
///
/// Warning: Using this method on an active set of vertices of a Body,
/// will cause problems with collisions. Use Body.Rotation instead.
/// </summary>
/// <param name="value">The amount to rotate by in radians.</param>
public void Rotate(float value)
{
Debug.Assert(!AttachedToBody, "Rotating vertices that are used by a Body can result in unstable behavior.");
float num1 = (float)Math.Cos(value);
float num2 = (float)Math.Sin(value);
for (int i = 0; i < Count; i++)
{
Vector2 position = this[i];
this[i] = new Vector2((position.X * num1 + position.Y * -num2), (position.X * num2 + position.Y * num1));
}
if (Holes != null && Holes.Count > 0)
{
foreach (Vertices hole in Holes)
{
hole.Rotate(value);
}
}
}
/// <summary>
/// Determines whether the polygon is convex.
/// O(n^2) running time.
///
/// Assumptions:
/// - The polygon is in counter clockwise order
/// - The polygon has no overlapping edges
/// </summary>
/// <returns>
/// <c>true</c> if it is convex; otherwise, <c>false</c>.
/// </returns>
public bool IsConvex()
{
//The simplest polygon which can exist in the Euclidean plane has 3 sides.
if (Count < 3)
return false;
//Triangles are always convex
if (Count == 3)
return true;
// Checks the polygon is convex and the interior is to the left of each edge.
for (int i = 0; i < Count; ++i)
{
int next = i + 1 < Count ? i + 1 : 0;
Vector2 edge = this[next] - this[i];
for (int j = 0; j < Count; ++j)
{
// Don't check vertices on the current edge.
if (j == i || j == next)
continue;
Vector2 r = this[j] - this[i];
float s = edge.X * r.Y - edge.Y * r.X;
if (s <= 0.0f)
return false;
}
}
return true;
}
/// <summary>
/// Indicates if the vertices are in counter clockwise order.
/// Warning: If the area of the polygon is 0, it is unable to determine the winding.
/// </summary>
public bool IsCounterClockWise()
{
//The simplest polygon which can exist in the Euclidean plane has 3 sides.
if (Count < 3)
return false;
return (GetSignedArea() > 0.0f);
}
/// <summary>
/// Forces the vertices to be counter clock wise order.
/// </summary>
public void ForceCounterClockWise()
{
//The simplest polygon which can exist in the Euclidean plane has 3 sides.
if (Count < 3)
return;
if (!IsCounterClockWise())
Reverse();
}
/// <summary>
/// Checks if the vertices forms an simple polygon by checking for edge crossings.
/// </summary>
public bool IsSimple()
{
//The simplest polygon which can exist in the Euclidean plane has 3 sides.
if (Count < 3)
return false;
for (int i = 0; i < Count; ++i)
{
Vector2 a1 = this[i];
Vector2 a2 = NextVertex(i);
for (int j = i + 1; j < Count; ++j)
{
Vector2 b1 = this[j];
Vector2 b2 = NextVertex(j);
Vector2 temp;
if (LineTools.LineIntersect2(ref a1, ref a2, ref b1, ref b2, out temp))
return false;
}
}
return true;
}
/// <summary>
/// Checks if the polygon is valid for use in the engine.
///
/// Performs a full check, for simplicity, convexity,
/// orientation, minimum angle, and volume.
///
/// From Eric Jordan's convex decomposition library
/// </summary>
/// <returns>PolygonError.NoError if there were no error.</returns>
public PolygonError CheckPolygon()
{
if (Count < 3 || Count > Settings.MaxPolygonVertices)
return PolygonError.InvalidAmountOfVertices;
if (!IsSimple())
return PolygonError.NotSimple;
if (GetArea() <= Settings.Epsilon)
return PolygonError.AreaTooSmall;
if (!IsConvex())
return PolygonError.NotConvex;
//Check if the sides are of adequate length.
for (int i = 0; i < Count; ++i)
{
int next = i + 1 < Count ? i + 1 : 0;
Vector2 edge = this[next] - this[i];
if (edge.LengthSquared() <= Settings.Epsilon*Settings.Epsilon)
{
return PolygonError.SideTooSmall;
}
}
if (!IsCounterClockWise())
return PolygonError.NotCounterClockWise;
return PolygonError.NoError;
}
/// <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;
}
/// <summary>
/// Transforms the polygon using the defined matrix.
/// </summary>
/// <param name="transform">The matrix to use as transformation.</param>
public void Transform(ref Matrix transform)
{
// Transform main polygon
for (int i = 0; i < Count; i++)
this[i] = Vector2.Transform(this[i], transform);
// Transform holes
if (Holes != null && Holes.Count > 0)
{
for (int i = 0; i < Holes.Count; i++)
{
Vector2[] temp = Holes[i].ToArray();
Vector2.Transform(temp, ref transform, temp);
Holes[i] = new Vertices(temp);
}
}
}
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();
}
}
}