Polygons
+ {
+ get { return _polygons; }
+ }
+
+ public void Add(Polygon p)
+ {
+ _polygons.Add(p);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/CDT/Sets/ConstrainedPointSet.cs b/src/Box2DNet/Common/Decomposition/CDT/Sets/ConstrainedPointSet.cs
new file mode 100644
index 0000000..8230c0f
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/CDT/Sets/ConstrainedPointSet.cs
@@ -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 Box2DNet.Common.Decomposition.CDT.Sets
+{
+ /*
+ * Extends the PointSet by adding some Constraints on how it will be triangulated
+ * 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.
+ *
+ *
+ *
+ * @author Thomas Åhlén, thahlen@gmail.com
+ */
+
+ internal class ConstrainedPointSet : PointSet
+ {
+ private List _constrainedPointList;
+
+ public ConstrainedPointSet(List 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 must be part of given PointSet!
+ */
+
+ public ConstrainedPointSet(List points, IEnumerable constraints)
+ : base(points)
+ {
+ _constrainedPointList = new List();
+ _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.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
+ * 1. Check's if there any constraint edges are crossing or collinear
+ * 2.
+ * @return
+ */
+
+ public bool isValid()
+ {
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/CDT/Sets/PointSet.cs b/src/Box2DNet/Common/Decomposition/CDT/Sets/PointSet.cs
new file mode 100644
index 0000000..f2ecb6b
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/CDT/Sets/PointSet.cs
@@ -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 Box2DNet.Common.Decomposition.CDT.Delaunay;
+
+namespace Box2DNet.Common.Decomposition.CDT.Sets
+{
+ internal class PointSet : Triangulatable
+ {
+ public PointSet(List points)
+ {
+ Points = new List(points);
+ }
+
+ #region Triangulatable Members
+
+ public IList Points { get; private set; }
+ public IList Triangles { get; private set; }
+
+ public virtual TriangulationMode TriangulationMode
+ {
+ get { return TriangulationMode.Unconstrained; }
+ }
+
+ public void AddTriangle(DelaunayTriangle t)
+ {
+ Triangles.Add(t);
+ }
+
+ public void AddTriangles(IEnumerable 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(Points.Count);
+ }
+ else
+ {
+ Triangles.Clear();
+ }
+ tcx.Points.AddRange(Points);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/CDT/TriangulationConstraint.cs b/src/Box2DNet/Common/Decomposition/CDT/TriangulationConstraint.cs
new file mode 100644
index 0000000..80bb15b
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/CDT/TriangulationConstraint.cs
@@ -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 Box2DNet.Common.Decomposition.CDT
+{
+ internal class TriangulationConstraint
+ {
+ public TriangulationPoint P;
+ public TriangulationPoint Q;
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/CDT/TriangulationContext.cs b/src/Box2DNet/Common/Decomposition/CDT/TriangulationContext.cs
new file mode 100644
index 0000000..59f20bc
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/CDT/TriangulationContext.cs
@@ -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 Box2DNet.Common.Decomposition.CDT.Delaunay;
+
+namespace Box2DNet.Common.Decomposition.CDT
+{
+ internal abstract class TriangulationContext
+ {
+ public readonly List Points = new List(200);
+ public readonly List Triangles = new List();
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/CDT/TriangulationMode.cs b/src/Box2DNet/Common/Decomposition/CDT/TriangulationMode.cs
new file mode 100644
index 0000000..c91bf16
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/CDT/TriangulationMode.cs
@@ -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 Box2DNet.Common.Decomposition.CDT
+{
+ internal enum TriangulationMode
+ {
+ Unconstrained,
+ Constrained,
+ Polygon
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/CDT/TriangulationPoint.cs b/src/Box2DNet/Common/Decomposition/CDT/TriangulationPoint.cs
new file mode 100644
index 0000000..c13923f
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/CDT/TriangulationPoint.cs
@@ -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 Box2DNet.Common.Decomposition.CDT.Delaunay.Sweep;
+
+namespace Box2DNet.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 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();
+ }
+ Edges.Add(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/CDT/TriangulationUtil.cs b/src/Box2DNet/Common/Decomposition/CDT/TriangulationUtil.cs
new file mode 100644
index 0000000..f737192
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/CDT/TriangulationUtil.cs
@@ -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 Box2DNet.Common.Decomposition.CDT
+{
+ /**
+ * @author Thomas Åhlén, thahlen@gmail.com
+ */
+
+ internal class TriangulationUtil
+ {
+ public static double EPSILON = 1e-12;
+
+ ///
+ /// Requirements:
+ /// 1. a,b and c form a triangle.
+ /// 2. a and d is know to be on opposite side of bc
+ ///
+ /// a
+ /// +
+ /// / \
+ /// / \
+ /// b/ \c
+ /// +-------+
+ /// / B \
+ /// / \
+ ///
+ /// 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
+ ///
+ /// triangle point, opposite d
+ /// triangle point
+ /// triangle point
+ /// point opposite a
+ /// true if d is inside circle, false if on circle edge
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/CDT/Util/FixedArray3.cs b/src/Box2DNet/Common/Decomposition/CDT/Util/FixedArray3.cs
new file mode 100644
index 0000000..3de628b
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/CDT/Util/FixedArray3.cs
@@ -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 Box2DNet.Common.Decomposition.CDT.Util
+{
+ internal struct FixedArray3 : IEnumerable 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 Members
+
+ public IEnumerator 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 Enumerate()
+ {
+ for (int i = 0; i < 3; ++i) yield return this[i];
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/CDT/Util/FixedBitArray3.cs b/src/Box2DNet/Common/Decomposition/CDT/Util/FixedBitArray3.cs
new file mode 100644
index 0000000..99f6d46
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/CDT/Util/FixedBitArray3.cs
@@ -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 Box2DNet.Common.Decomposition.CDT.Util
+{
+ internal struct FixedBitArray3 : IEnumerable
+ {
+ 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 Members
+
+ public IEnumerator 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 Enumerate()
+ {
+ for (int i = 0; i < 3; ++i) yield return this[i];
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/CDT/Util/PointGenerator.cs b/src/Box2DNet/Common/Decomposition/CDT/Util/PointGenerator.cs
new file mode 100644
index 0000000..f988c50
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/CDT/Util/PointGenerator.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+
+namespace Box2DNet.Common.Decomposition.CDT.Util
+{
+ internal class PointGenerator
+ {
+ private static readonly Random RNG = new Random();
+
+ public static List UniformDistribution(int n, double scale)
+ {
+ List points = new List();
+ 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 UniformGrid(int n, double scale)
+ {
+ double x = 0;
+ double size = scale/n;
+ double halfScale = 0.5*scale;
+
+ List points = new List();
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/CDT/Util/PolygonGenerator.cs b/src/Box2DNet/Common/Decomposition/CDT/Util/PolygonGenerator.cs
new file mode 100644
index 0000000..20b46b5
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/CDT/Util/PolygonGenerator.cs
@@ -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 Box2DNet.Common.Decomposition.CDT.Polygon;
+
+namespace Box2DNet.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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/CDTDecomposer.cs b/src/Box2DNet/Common/Decomposition/CDTDecomposer.cs
new file mode 100644
index 0000000..ca7fce1
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/CDTDecomposer.cs
@@ -0,0 +1,75 @@
+/*
+* Farseer Physics Engine:
+* Copyright (c) 2012 Ian Qvist
+*/
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using Box2DNet.Common.Decomposition.CDT;
+using Box2DNet.Common.Decomposition.CDT.Delaunay;
+using Box2DNet.Common.Decomposition.CDT.Delaunay.Sweep;
+using Box2DNet.Common.Decomposition.CDT.Polygon;
+using Microsoft.Xna.Framework;
+
+namespace Box2DNet.Common.Decomposition
+{
+ ///
+ /// 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/
+ ///
+ internal static class CDTDecomposer
+ {
+ ///
+ /// Decompose the polygon into several smaller non-concave polygon.
+ ///
+ public static List 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 results = new List();
+
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/EarclipDecomposer.cs b/src/Box2DNet/Common/Decomposition/EarclipDecomposer.cs
new file mode 100644
index 0000000..72fded4
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/EarclipDecomposer.cs
@@ -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 Box2DNet.Common.Decomposition
+{
+ ///
+ /// 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/
+ ///
+ internal static class EarclipDecomposer
+ {
+ //box2D rev 32 - for details, see http://www.box2d.org/forum/viewtopic.php?f=4&t=83&start=50
+
+ ///
+ /// Decompose the polygon into several smaller non-concave polygon.
+ /// Each resulting polygon will have no more than Settings.MaxPolygonVertices vertices.
+ ///
+ /// The vertices.
+ /// The tolerance.
+ public static List ConvexPartition(Vertices vertices, float tolerance = 0.001f)
+ {
+ Debug.Assert(vertices.Count > 3);
+ Debug.Assert(!vertices.IsCounterClockWise());
+
+ return TriangulatePolygon(vertices, tolerance);
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ /// Only works on simple polygons.
+ ///
+ private static List 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();
+
+ List results = new List();
+
+ //Recurse and split on pinch points
+ Vertices pA, pB;
+ Vertices pin = new Vertices(vertices);
+ if (ResolvePinchPoint(pin, out pA, out pB, tolerance))
+ {
+ List mergeA = TriangulatePolygon(pA, tolerance);
+ List 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;
+ }
+
+ ///
+ /// 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...
+ ///
+ /// The pin.
+ /// The pout A.
+ /// The pout B.
+ ///
+ 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;
+ }
+
+ ///
+ /// Fix for obnoxious behavior for the % operator for negative numbers...
+ ///
+ /// The x.
+ /// The modulus.
+ ///
+ private static int Remainder(int x, int modulus)
+ {
+ int rem = x % modulus;
+ while (rem < 0)
+ {
+ rem += modulus;
+ }
+ return rem;
+ }
+
+ ///
+ /// Checks if vertex i is the tip of an ear in polygon defined by xv[] and yv[].
+ ///
+ /// The i.
+ /// The xv.
+ /// The yv.
+ /// Length of the xv.
+ ///
+ /// Assumes clockwise orientation of polygon.
+ ///
+ ///
+ /// true if the specified i is ear; otherwise, false.
+ ///
+ 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));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/FlipcodeDecomposer.cs b/src/Box2DNet/Common/Decomposition/FlipcodeDecomposer.cs
new file mode 100644
index 0000000..1d3df2d
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/FlipcodeDecomposer.cs
@@ -0,0 +1,152 @@
+using System.Collections.Generic;
+using System.Diagnostics;
+using Microsoft.Xna.Framework;
+
+namespace Box2DNet.Common.Decomposition
+{
+ ///
+ /// 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
+ ///
+ internal static class FlipcodeDecomposer
+ {
+ private static Vector2 _tmpA;
+ private static Vector2 _tmpB;
+ private static Vector2 _tmpC;
+
+ ///
+ /// Decompose the polygon into triangles.
+ ///
+ /// Properties:
+ /// - Only works on counter clockwise polygons
+ ///
+ ///
+ /// The list of points describing the polygon
+ public static List 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 result = new List();
+
+ 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();
+ }
+
+ // Three consecutive vertices in current polygon,
+ 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;
+ }
+
+ ///
+ /// Check if the point P is inside the triangle defined by
+ /// the points A, B, C
+ ///
+ /// The A point.
+ /// The B point.
+ /// The C point.
+ /// The point to be tested.
+ /// True if the point is inside the triangle
+ 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));
+ }
+
+ ///
+ /// Cut a the contour and add a triangle into V to describe the
+ /// location of the cut
+ ///
+ /// The list of points defining the polygon
+ /// The index of the first point
+ /// The index of the second point
+ /// The index of the third point
+ /// The number of elements in the array.
+ /// The array to populate with indicies of triangles.
+ /// True if a triangle was found
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/Seidel/Edge.cs b/src/Box2DNet/Common/Decomposition/Seidel/Edge.cs
new file mode 100644
index 0000000..4bf01b8
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/Seidel/Edge.cs
@@ -0,0 +1,60 @@
+using System.Collections.Generic;
+
+namespace Box2DNet.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 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();
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/Seidel/MonotoneMountain.cs b/src/Box2DNet/Common/Decomposition/Seidel/MonotoneMountain.cs
new file mode 100644
index 0000000..283fce7
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/Seidel/MonotoneMountain.cs
@@ -0,0 +1,166 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace Box2DNet.Common.Decomposition.Seidel
+{
+ internal class MonotoneMountain
+ {
+ // Almost Pi!
+ private const float PiSlop = 3.1f;
+
+ // Triangles that constitute the mountain
+ public List> Triangles;
+ private HashSet _convexPoints;
+ private Point _head;
+
+ // Monotone mountain points
+ private List _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();
+ _monoPoly = new List();
+ Triangles = new List>();
+ }
+
+ // 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 e = _convexPoints.GetEnumerator();
+ e.MoveNext();
+ Point ear = e.Current;
+
+ _convexPoints.Remove(ear);
+ Point a = ear.Prev;
+ Point b = ear;
+ Point c = ear.Next;
+ List triangle = new List(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;
+ }
+ }
+}
diff --git a/src/Box2DNet/Common/Decomposition/Seidel/Node.cs b/src/Box2DNet/Common/Decomposition/Seidel/Node.cs
new file mode 100644
index 0000000..d082353
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/Seidel/Node.cs
@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+
+namespace Box2DNet.Common.Decomposition.Seidel
+{
+ // Node for a Directed Acyclic graph (DAG)
+ internal abstract class Node
+ {
+ protected Node LeftChild;
+ public List ParentList;
+ protected Node RightChild;
+
+ protected Node(Node left, Node right)
+ {
+ ParentList = new List();
+ 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);
+ }
+ }
+}
diff --git a/src/Box2DNet/Common/Decomposition/Seidel/Point.cs b/src/Box2DNet/Common/Decomposition/Seidel/Point.cs
new file mode 100644
index 0000000..c82ce8d
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/Seidel/Point.cs
@@ -0,0 +1,61 @@
+namespace Box2DNet.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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/Seidel/QueryGraph.cs b/src/Box2DNet/Common/Decomposition/Seidel/QueryGraph.cs
new file mode 100644
index 0000000..0dbf2ca
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/Seidel/QueryGraph.cs
@@ -0,0 +1,78 @@
+using System.Collections.Generic;
+
+namespace Box2DNet.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 FollowEdge(Edge edge)
+ {
+ List trapezoids = new List();
+ 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);
+ }
+ }
+}
diff --git a/src/Box2DNet/Common/Decomposition/Seidel/Sink.cs b/src/Box2DNet/Common/Decomposition/Seidel/Sink.cs
new file mode 100644
index 0000000..c7e7177
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/Seidel/Sink.cs
@@ -0,0 +1,27 @@
+namespace Box2DNet.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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/Seidel/Trapezoid.cs b/src/Box2DNet/Common/Decomposition/Seidel/Trapezoid.cs
new file mode 100644
index 0000000..2478de7
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/Seidel/Trapezoid.cs
@@ -0,0 +1,123 @@
+using System.Collections.Generic;
+
+namespace Box2DNet.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 GetVertices()
+ {
+ List verts = new List(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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/Seidel/TrapezoidalMap.cs b/src/Box2DNet/Common/Decomposition/Seidel/TrapezoidalMap.cs
new file mode 100644
index 0000000..d8335b4
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/Seidel/TrapezoidalMap.cs
@@ -0,0 +1,195 @@
+using System.Collections.Generic;
+
+namespace Box2DNet.Common.Decomposition.Seidel
+{
+ internal class TrapezoidalMap
+ {
+ // Trapezoid container
+ public HashSet 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();
+ _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 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/Seidel/Triangulator.cs b/src/Box2DNet/Common/Decomposition/Seidel/Triangulator.cs
new file mode 100644
index 0000000..5cad47d
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/Seidel/Triangulator.cs
@@ -0,0 +1,203 @@
+using System;
+using System.Collections.Generic;
+
+namespace Box2DNet.Common.Decomposition.Seidel
+{
+ internal class Triangulator
+ {
+ // Trapezoid decomposition list
+ public List Trapezoids;
+ public List> Triangles;
+
+ // Initialize trapezoidal map and query structure
+ private Trapezoid _boundingBox;
+ private List _edgeList;
+ private QueryGraph _queryGraph;
+ private float _sheer = 0.001f;
+ private TrapezoidalMap _trapezoidalMap;
+ private List _xMonoPoly;
+
+ public Triangulator(List polyLine, float sheer)
+ {
+ _sheer = sheer;
+ Triangles = new List>();
+ Trapezoids = new List();
+ _xMonoPoly = new List();
+ _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 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 points = new List(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 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 InitEdges(List points)
+ {
+ List edges = new List();
+
+ 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 OrderSegments(List edgeInput)
+ {
+ // Ignore vertical segments!
+ List edges = new List();
+
+ 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(IList 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/Seidel/XNode.cs b/src/Box2DNet/Common/Decomposition/Seidel/XNode.cs
new file mode 100644
index 0000000..19a4abe
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/Seidel/XNode.cs
@@ -0,0 +1,21 @@
+namespace Box2DNet.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
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/Seidel/YNode.cs b/src/Box2DNet/Common/Decomposition/Seidel/YNode.cs
new file mode 100644
index 0000000..c1c6f26
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/Seidel/YNode.cs
@@ -0,0 +1,29 @@
+namespace Box2DNet.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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/SeidelDecomposer.cs b/src/Box2DNet/Common/Decomposition/SeidelDecomposer.cs
new file mode 100644
index 0000000..9678ea8
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/SeidelDecomposer.cs
@@ -0,0 +1,109 @@
+/*
+* Farseer Physics Engine:
+* Copyright (c) 2012 Ian Qvist
+*/
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using Box2DNet.Common.Decomposition.Seidel;
+using Microsoft.Xna.Framework;
+using Point = Box2DNet.Common.Decomposition.Seidel.Point;
+
+namespace Box2DNet.Common.Decomposition
+{
+ ///
+ /// 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
+ ///
+ internal static class SeidelDecomposer
+ {
+ ///
+ /// Decompose the polygon into several smaller non-concave polygons.
+ ///
+ /// The polygon to decompose.
+ /// The sheer to use if you get bad results, try using a higher value.
+ /// A list of triangles
+ public static List ConvexPartition(Vertices vertices, float sheer = 0.001f)
+ {
+ Debug.Assert(vertices.Count > 3);
+
+ List compatList = new List(vertices.Count);
+
+ foreach (Vector2 vertex in vertices)
+ {
+ compatList.Add(new Point(vertex.X, vertex.Y));
+ }
+
+ Triangulator t = new Triangulator(compatList, sheer);
+
+ List list = new List();
+
+ foreach (List 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;
+ }
+
+ ///
+ /// Decompose the polygon into several smaller non-concave polygons.
+ ///
+ /// The polygon to decompose.
+ /// The sheer to use if you get bad results, try using a higher value.
+ /// A list of trapezoids
+ public static List ConvexPartitionTrapezoid(Vertices vertices, float sheer = 0.001f)
+ {
+ List compatList = new List(vertices.Count);
+
+ foreach (Vector2 vertex in vertices)
+ {
+ compatList.Add(new Point(vertex.X, vertex.Y));
+ }
+
+ Triangulator t = new Triangulator(compatList, sheer);
+
+ List list = new List();
+
+ foreach (Trapezoid trapezoid in t.Trapezoids)
+ {
+ Vertices verts = new Vertices();
+
+ List points = trapezoid.GetVertices();
+ foreach (Point point in points)
+ {
+ verts.Add(new Vector2(point.X, point.Y));
+ }
+
+ list.Add(verts);
+ }
+
+ return list;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Decomposition/Triangulate.cs b/src/Box2DNet/Common/Decomposition/Triangulate.cs
new file mode 100644
index 0000000..fca9c03
--- /dev/null
+++ b/src/Box2DNet/Common/Decomposition/Triangulate.cs
@@ -0,0 +1,172 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Box2DNet.Common.ConvexHull;
+
+namespace Box2DNet.Common.Decomposition
+{
+ public enum TriangulationAlgorithm
+ {
+ ///
+ /// 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.
+ ///
+ Earclip,
+
+ ///
+ /// 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.
+ ///
+ Bayazit,
+
+ ///
+ /// Convex decomposition algorithm created by unknown
+ ///
+ /// Properties:
+ /// - No support for holes
+ /// - Very fast
+ /// - Only works on simple polygons
+ /// - Only works on counter clockwise polygons
+ ///
+ Flipcode,
+
+ ///
+ /// 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.
+ ///
+ Seidel,
+ SeidelTrapezoids,
+
+ ///
+ /// 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.
+ ///
+ Delauny
+ }
+
+ public static class Triangulate
+ {
+ public static List ConvexPartition(Vertices vertices, TriangulationAlgorithm algorithm, bool discardAndFixInvalid = true, float tolerance = 0.001f)
+ {
+ if (vertices.Count <= 3)
+ return new List { vertices };
+
+ List 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;
+ }
+ }
+}
diff --git a/src/Box2DNet/Common/FixedArray.cs b/src/Box2DNet/Common/FixedArray.cs
new file mode 100644
index 0000000..623a5d4
--- /dev/null
+++ b/src/Box2DNet/Common/FixedArray.cs
@@ -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 Box2DNet.Common
+{
+ public struct FixedArray2
+ {
+ 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
+ {
+ 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
+ {
+ 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
+ {
+ 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();
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/HashSet.cs b/src/Box2DNet/Common/HashSet.cs
new file mode 100644
index 0000000..2ce561a
--- /dev/null
+++ b/src/Box2DNet/Common/HashSet.cs
@@ -0,0 +1,78 @@
+#if WINDOWS_PHONE || XBOX
+
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Box2DNet.Common
+{
+ public class HashSet : ICollection
+ {
+ private Dictionary _dict;
+
+ public HashSet(int capacity)
+ {
+ _dict = new Dictionary(capacity);
+ }
+
+ public HashSet()
+ {
+ _dict = new Dictionary();
+ }
+
+ #region ICollection 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 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
\ No newline at end of file
diff --git a/src/Box2DNet/Common/LineTools.cs b/src/Box2DNet/Common/LineTools.cs
new file mode 100644
index 0000000..f54e89b
--- /dev/null
+++ b/src/Box2DNet/Common/LineTools.cs
@@ -0,0 +1,287 @@
+using System;
+using Box2DNet.Collision;
+using Microsoft.Xna.Framework;
+
+namespace Box2DNet.Common
+{
+ ///
+ /// Collection of helper methods for misc collisions.
+ /// Does float tolerance and line collisions with lines and AABBs.
+ ///
+ 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
+ ///
+ ///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.
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// This method detects if two line segments (or lines) intersect,
+ /// and, if so, the point of intersection. Use the and
+ /// 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
+ ///
+ /// The first point of the first line segment.
+ /// The second point of the first line segment.
+ /// The first point of the second line segment.
+ /// The second point of the second line segment.
+ /// This is set to the intersection
+ /// point if an intersection is detected.
+ /// Set this to true to require that the
+ /// intersection point be on the first line segment.
+ /// Set this to true to require that the
+ /// intersection point be on the second line segment.
+ /// True if an intersection is detected, false otherwise.
+ 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;
+ }
+
+ ///
+ /// This method detects if two line segments (or lines) intersect,
+ /// and, if so, the point of intersection. Use the and
+ /// 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
+ ///
+ /// The first point of the first line segment.
+ /// The second point of the first line segment.
+ /// The first point of the second line segment.
+ /// The second point of the second line segment.
+ /// This is set to the intersection
+ /// point if an intersection is detected.
+ /// Set this to true to require that the
+ /// intersection point be on the first line segment.
+ /// Set this to true to require that the
+ /// intersection point be on the second line segment.
+ /// True if an intersection is detected, false otherwise.
+ 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);
+ }
+
+ ///
+ /// 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).
+ ///
+ /// The first point of the first line segment.
+ /// The second point of the first line segment.
+ /// The first point of the second line segment.
+ /// The second point of the second line segment.
+ /// This is set to the intersection
+ /// point if an intersection is detected.
+ /// True if an intersection is detected, false otherwise.
+ 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);
+ }
+
+ ///
+ /// 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).
+ ///
+ /// The first point of the first line segment.
+ /// The second point of the first line segment.
+ /// The first point of the second line segment.
+ /// The second point of the second line segment.
+ /// This is set to the intersection
+ /// point if an intersection is detected.
+ /// True if an intersection is detected, false otherwise.
+ 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);
+ }
+
+ ///
+ /// 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)
+ ///
+ /// The first point of the line segment to test
+ /// The second point of the line segment to test.
+ /// The vertices, as described above
+ 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;
+ }
+
+ ///
+ /// Get all intersections between a line segment and an AABB.
+ ///
+ /// The first point of the line segment to test
+ /// The second point of the line segment to test.
+ /// The AABB that is used for testing intersection.
+ public static Vertices LineSegmentAABBIntersect(ref Vector2 point1, ref Vector2 point2, AABB aabb)
+ {
+ return LineSegmentVerticesIntersect(ref point1, ref point2, aabb.Vertices);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Mar33.cs b/src/Box2DNet/Common/Mar33.cs
deleted file mode 100644
index eaed306..0000000
--- a/src/Box2DNet/Common/Mar33.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- Box2DNet Copyright (c) 2018 codeyu https://github.com/codeyu/Box2DNet
- Box2D original C++ version Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-*/
-
-//r175
-
-using System; using System.Numerics;
-using System.Collections.Generic;
-using System.Text;
-
-
-namespace Box2DNet.Common
-{
- ///
- /// A 3-by-3 matrix. Stored in column-major order.
- ///
- public struct Mat33
- {
- ///
- /// Construct this matrix using columns.
- ///
- public Mat33(Vector3 c1, Vector3 c2, Vector3 c3)
- {
- Col1 = c1;
- Col2 = c2;
- Col3 = c3;
- }
-
- ///
- /// Set this matrix to all zeros.
- ///
- public void SetZero()
- {
- Col1 = Vector3.Zero;
- Col2 = Vector3.Zero;
- Col3 = Vector3.Zero;
- }
-
- ///
- /// Solve A * x = b, where b is a column vector. This is more efficient
- /// than computing the inverse in one-shot cases.
- ///
- public Vector3 Solve33(Vector3 b)
- {
- float det = Vector3.Dot(Col1, Vector3.Cross(Col2, Col3));
- Box2DNetDebug.Assert(det != 0.0f);
- det = 1.0f / det;
- Vector3 x = new Vector3();
- x.X = det * Vector3.Dot(b, Vector3.Cross(Col2, Col3));
- x.Y = det * Vector3.Dot(Col1, Vector3.Cross(b, Col3));
- x.Z = det * Vector3.Dot(Col1, Vector3.Cross(Col2, b));
- return x;
- }
-
- ///
- /// 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.
- ///
- public Vector2 Solve22(Vector2 b)
- {
- float a11 = Col1.X, a12 = Col2.X, a21 = Col1.Y, a22 = Col2.Y;
- float det = a11 * a22 - a12 * a21;
- Box2DNetDebug.Assert(det != 0.0f);
- det = 1.0f / det;
- Vector2 x = new Vector2();
- x.X = det * (a22 * b.X - a12 * b.Y);
- x.Y = det * (a11 * b.Y - a21 * b.X);
- return x;
- }
-
- public Vector3 Col1;
- public Vector3 Col2;
- public Vector3 Col3;
- }
-}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Mat22.cs b/src/Box2DNet/Common/Mat22.cs
deleted file mode 100644
index 978e75a..0000000
--- a/src/Box2DNet/Common/Mat22.cs
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- Box2DNet Copyright (c) 2018 codeyu https://github.com/codeyu/Box2DNet
- Box2D original C++ version Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-*/
-
-using System; using System.Numerics;
-using System.Collections.Generic;
-using System.Text;
-
-
-namespace Box2DNet.Common
-{
- ///
- /// A 2-by-2 matrix. Stored in column-major order.
- ///
- public struct Mat22
- {
- public Vector2 Col1;
- public Vector2 Col2;
-
- ///
- /// Construct this matrix using columns.
- ///
- public Mat22(Vector2 c1, Vector2 c2)
- {
- Col1 = c1;
- Col2 = c2;
- }
-
- ///
- /// Construct this matrix using scalars.
- ///
- public Mat22(float a11, float a12, float a21, float a22)
- {
- Col1.X = a11; Col1.Y = a21;
- Col2.X = a12; Col2.Y = a22;
- }
-
- ///
- /// Construct this matrix using an angle.
- /// This matrix becomes an orthonormal rotation matrix.
- ///
- public Mat22(float angle)
- {
- float c = (float)System.Math.Cos(angle);
- float s = (float)System.Math.Sin(angle);
- Col1.X = c; Col2.X = -s;
- Col1.Y = s; Col2.Y = c;
- }
-
- ///
- /// Initialize this matrix using columns.
- ///
- public void Set(Vector2 c1, Vector2 c2)
- {
- Col1 = c1;
- Col2 = c2;
- }
-
- ///
- /// Initialize this matrix using an angle.
- /// This matrix becomes an orthonormal rotation matrix.
- ///
- public void Set(float angle)
- {
- float c = (float)System.Math.Cos(angle);
- float s = (float)System.Math.Sin(angle);
- Col1.X = c; Col2.X = -s;
- Col1.Y = s; Col2.Y = c;
- }
-
- ///
- /// Extract the angle from this matrix (assumed to be a rotation matrix).
- ///
- public float GetAngle()
- {
- return Math.Atan2(Col1.Y, Col1.X);
- }
-
- public Vector2 Multiply(Vector2 vector) {
- return new Vector2(Col1.X * vector.Y + Col2.X * vector.Y, Col1.Y * vector.X + Col2.Y * vector.Y);
- }
-
- ///
- /// Compute the inverse of this matrix, such that inv(A) * A = identity.
- ///
- public Mat22 GetInverse()
- {
- float a = Col1.X, b = Col2.X, c = Col1.Y, d = Col2.Y;
- Mat22 B = new Mat22();
- float det = a * d - b * c;
- Box2DNetDebug.Assert(det != 0.0f);
- det = 1.0f / det;
- B.Col1.X = det * d; B.Col2.X = -det * b;
- B.Col1.Y = -det * c; B.Col2.Y = det * a;
- return B;
- }
-
- ///
- /// Solve A * x = b, where b is a column vector. This is more efficient
- /// than computing the inverse in one-shot cases.
- ///
- public Vector2 Solve(Vector2 b)
- {
- float a11 = Col1.X, a12 = Col2.X, a21 = Col1.Y, a22 = Col2.Y;
- float det = a11 * a22 - a12 * a21;
- Box2DNetDebug.Assert(det != 0.0f);
- det = 1.0f / det;
- Vector2 x = new Vector2();
- x.X = det * (a22 * b.X - a12 * b.Y);
- x.Y = det * (a11 * b.Y - a21 * b.X);
- return x;
- }
-
- public static Mat22 Identity { get { return new Mat22(1, 0, 0, 1); } }
-
- public static Mat22 operator +(Mat22 A, Mat22 B)
- {
- Mat22 C = new Mat22();
- C.Set(A.Col1 + B.Col1, A.Col2 + B.Col2);
- return C;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Math.cs b/src/Box2DNet/Common/Math.cs
index 17b5f4d..780db59 100644
--- a/src/Box2DNet/Common/Math.cs
+++ b/src/Box2DNet/Common/Math.cs
@@ -1,291 +1,815 @@
-/*
- Box2DNet Copyright (c) 2018 codeyu https://github.com/codeyu/Box2DNet
- Box2D original C++ version Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-*/
-
-using System; using System.Numerics;
-using System.Collections.Generic;
-using System.Text;
-
-
-using Random = System.Random;
-
-namespace Box2DNet.Common
-{
- public static class Vector2Extension
- {
- public static float Rad2Deg = (float)360.0f / (float)(System.Math.PI * 2);
- public static Vector3 ToVector3(this Vector2 vector)
- {
- return new Vector3(vector.X, vector.Y, 0.0f);
- }
-
- public static bool IsValid(this Vector2 vector)
- {
- return Math.IsValid(vector.X) && Math.IsValid(vector.Y);
- }
-
- public static float Cross(this Vector2 vector, Vector2 other)
- {
- return vector.X * other.Y - vector.Y * other.X;
- }
-
- public static Vector2 CrossScalarPostMultiply(this Vector2 vector, float s)
- {
- return new Vector2(s * vector.Y, -s * vector.X);
- }
-
- public static Vector2 CrossScalarPreMultiply(this Vector2 vector, float s)
- {
- return new Vector2(-s * vector.Y, s * vector.X);
- }
-
- public static Vector2 Abs(this Vector2 vector) {
- return new Vector2(Math.Abs(vector.X), Math.Abs(vector.Y));
- }
- }
-
- public static class Vector3Extension
- {
- public static Vector2 ToVector2(this Vector3 vector)
- {
- return new Vector2(vector.X, vector.Y);
- }
- }
-
- public static class QuaternionExtension
- {
- public static Quaternion FromAngle2D(float radians)
- {
- return Quaternion.CreateFromAxisAngle(new Vector3(0, 0, 1), radians * ((float)360.0f / (float)(System.Math.PI * 2)));
- }
- }
-
- public class Math
- {
- public static readonly ushort USHRT_MAX = 0xffff;
- public static readonly byte UCHAR_MAX = 0xff;
- public static readonly int RAND_LIMIT = 32767;
-
- ///
- /// This function is used to ensure that a floating point number is
- /// not a NaN or infinity.
- ///
- public static bool IsValid(float x)
- {
- return !(float.IsNaN(x) || float.IsNegativeInfinity(x) || float.IsPositiveInfinity(x));
- }
-
- [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)]
- public struct Convert
- {
- [System.Runtime.InteropServices.FieldOffset(0)]
- public float x;
-
- [System.Runtime.InteropServices.FieldOffset(0)]
- public int i;
- }
-
-
-#if USE_MATRIX_FOR_ROTATION
- public static Mat22 AngleToRotation(float angle)
- {
- return new Mat22(angle);
- }
-#else
- public static Quaternion AngleToRotation(float angle)
- {
- return QuaternionExtension.FromAngle2D(angle);
- }
-#endif
-
- ///
- /// This is a approximate yet fast inverse square-root.
- ///
- public static float InvSqrt(float x)
- {
- Convert convert = new Convert();
- 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 float Clamp(float f, float min, float max) {
- if (f < min)
- return min;
- else if (f > max)
- return max;
- else return f;
- }
- public static float Rad2Deg = 57.29578f;
- public static float Epsilon = 1.401298E-45f;
- public static float Sqrt(float x)
- {
- return Math.Sqrt(x);
- }
- public static float Distance(Vector2 v1, Vector2 v2) {
- return (float)System.Math.Sqrt(System.Math.Pow(v2.X - v1.X, 2) + System.Math.Pow(v2.Y - v1.Y, 2));
- }
-
- ///
- /// Random floating point number in range [lo, hi]
- ///
-
- ///
- /// "Next Largest Power of 2
- /// Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm
- /// that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with
- /// the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next
- /// largest power of 2. For a 32-bit value:"
- ///
- public static uint NextPowerOfTwo(uint x)
- {
- x |= (x >> 1);
- x |= (x >> 2);
- x |= (x >> 4);
- x |= (x >> 8);
- x |= (x >> 16);
- return x + 1;
- }
-
- public static bool IsPowerOfTwo(uint x)
- {
- bool result = x > 0 && (x & (x - 1)) == 0;
- return result;
- }
-
- public static float Abs(float a)
- {
- return a > 0.0f ? a : -a;
- }
-
- public static Vector2 Abs(Vector2 a)
- {
- return new Vector2(Math.Abs(a.X), Math.Abs(a.Y));
- }
-
- public static Mat22 Abs(Mat22 A)
- {
- Mat22 B = new Mat22();
- B.Set(Math.Abs(A.Col1), Math.Abs(A.Col2));
- return B;
- }
-
- public static float Min(float a, float b)
- {
- return a < b ? a : b;
- }
-
- public static int Min(int a, int b)
- {
- return a < b ? a : b;
- }
-
- public static float Max(float a, float b)
- {
- return a > b ? a : b;
- }
-
- public static int Max(int a, int b)
- {
- return a > b ? a : b;
- }
-
- public static Vector2 Clamp(Vector2 a, Vector2 low, Vector2 high)
- {
- return Vector2.Max(low, Vector2.Min(a, high));
- }
-
- public static void Swap(ref T a, ref T b)
- {
- T tmp = a;
- a = b;
- b = tmp;
- }
-
- ///
- /// Multiply a matrix times a vector. If a rotation matrix is provided,
- /// then this Transforms the vector from one frame to another.
- ///
- public static Vector2 Mul(Mat22 A, Vector2 v)
- {
- return new Vector2(A.Col1.X * v.X + A.Col2.X * v.Y, A.Col1.Y * v.X + A.Col2.Y * v.Y);
- }
-
- ///
- /// Multiply a matrix transpose times a vector. If a rotation matrix is provided,
- /// then this Transforms the vector from one frame to another (inverse Transform).
- ///
- public static Vector2 MulT(Mat22 A, Vector2 v)
- {
- return new Vector2(Vector2.Dot(v, A.Col1), Vector2.Dot(v, A.Col2));
- }
-
- ///
- /// A * B
- ///
- public static Mat22 Mul(Mat22 A, Mat22 B)
- {
- Mat22 C = new Mat22();
- C.Set(Math.Mul(A, B.Col1), Math.Mul(A, B.Col2));
- return C;
- }
-
- ///
- /// A^T * B
- ///
- public static Mat22 MulT(Mat22 A, Mat22 B)
- {
- Vector2 c1 = new Vector2(Vector2.Dot(A.Col1, B.Col1), Vector2.Dot(A.Col2, B.Col1));
- Vector2 c2 = new Vector2(Vector2.Dot(A.Col1, B.Col2), Vector2.Dot(A.Col2, B.Col2));
- return new Mat22(c1, c2);
- }
-
- public static Vector2 Mul(Transform T, Vector2 v)
- {
-#if USE_MATRIX_FOR_ROTATION
- return T.position + Math.Mul(T.R, v);
-#else
- return T.position + T.TransformDirection(v);
-#endif
- }
-
- public static Vector2 MulT(Transform T, Vector2 v)
- {
-#if USE_MATRIX_FOR_ROTATION
- return Math.MulT(T.R, v - T.position);
-#else
- return T.InverseTransformDirection(v - T.position);
-#endif
- }
-
- ///
- /// Multiply a matrix times a vector.
- ///
- public static Vector3 Mul(Mat33 A, Vector3 v)
- {
- return v.X * A.Col1 + v.Y * A.Col2 + v.Z * A.Col3;
- }
-
- public static float Atan2(float y, float x)
- {
- return (float)System.Math.Atan2(y, x);
- }
- }
+/*
+* 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 Box2DNet.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(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);
+ }
+
+ ///
+ /// This function is used to ensure that a floating point number is
+ /// not a NaN or infinity.
+ ///
+ /// The x.
+ ///
+ /// true if the specified x is valid; otherwise, false.
+ ///
+ 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);
+ }
+
+ ///
+ /// This is a approximate yet fast inverse square-root.
+ ///
+ /// The x.
+ ///
+ 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;
+ }
+
+ ///
+ /// 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
+ ///
+ 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);
+ }
+
+ ///
+ /// Returns a positive number if c is to the left of the line going from a to b.
+ ///
+ /// Positive number if point is left, negative if point is right,
+ /// and 0 if points are collinear.
+ public static float Area(Vector2 a, Vector2 b, Vector2 c)
+ {
+ return Area(ref a, ref b, ref c);
+ }
+
+ ///
+ /// Returns a positive number if c is to the left of the line going from a to b.
+ ///
+ /// Positive number if point is left, negative if point is right,
+ /// and 0 if points are collinear.
+ 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);
+ }
+
+ ///
+ /// Determines if three vertices are collinear (ie. on a straight line)
+ ///
+ /// First vertex
+ /// Second vertex
+ /// Third vertex
+ /// The tolerance
+ ///
+ 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;
+ }
+
+ ///
+ /// Checks if a floating point Value is equal to another,
+ /// within a certain tolerance.
+ ///
+ /// The first floating point Value.
+ /// The second floating point Value.
+ /// The floating point tolerance.
+ /// True if the values are "equal", false otherwise.
+ public static bool FloatEquals(float value1, float value2, float delta)
+ {
+ return FloatInRange(value1, value2 - delta, value2 + delta);
+ }
+
+ ///
+ /// Checks if a floating point Value is within a specified
+ /// range of values (inclusive).
+ ///
+ /// The Value to check.
+ /// The minimum Value.
+ /// The maximum Value.
+ /// True if the Value is within the range specified,
+ /// false otherwise.
+ 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);
+ }
+ }
+
+ ///
+ /// A 2-by-2 matrix. Stored in column-major order.
+ ///
+ public struct Mat22
+ {
+ public Vector2 ex, ey;
+
+ ///
+ /// Construct this matrix using columns.
+ ///
+ /// The c1.
+ /// The c2.
+ public Mat22(Vector2 c1, Vector2 c2)
+ {
+ ex = c1;
+ ey = c2;
+ }
+
+ ///
+ /// Construct this matrix using scalars.
+ ///
+ /// The a11.
+ /// The a12.
+ /// The a21.
+ /// The a22.
+ 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;
+ }
+ }
+
+ ///
+ /// Initialize this matrix using columns.
+ ///
+ /// The c1.
+ /// The c2.
+ public void Set(Vector2 c1, Vector2 c2)
+ {
+ ex = c1;
+ ey = c2;
+ }
+
+ ///
+ /// Set this to the identity matrix.
+ ///
+ public void SetIdentity()
+ {
+ ex.X = 1.0f;
+ ey.X = 0.0f;
+ ex.Y = 0.0f;
+ ey.Y = 1.0f;
+ }
+
+ ///
+ /// Set this matrix to all zeros.
+ ///
+ public void SetZero()
+ {
+ ex.X = 0.0f;
+ ey.X = 0.0f;
+ ex.Y = 0.0f;
+ ey.Y = 0.0f;
+ }
+
+ ///
+ /// Solve A * x = b, where b is a column vector. This is more efficient
+ /// than computing the inverse in one-shot cases.
+ ///
+ /// The b.
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// A 3-by-3 matrix. Stored in column-major order.
+ ///
+ public struct Mat33
+ {
+ public Vector3 ex, ey, ez;
+
+ ///
+ /// Construct this matrix using columns.
+ ///
+ /// The c1.
+ /// The c2.
+ /// The c3.
+ public Mat33(Vector3 c1, Vector3 c2, Vector3 c3)
+ {
+ ex = c1;
+ ey = c2;
+ ez = c3;
+ }
+
+ ///
+ /// Set this matrix to all zeros.
+ ///
+ public void SetZero()
+ {
+ ex = Vector3.Zero;
+ ey = Vector3.Zero;
+ ez = Vector3.Zero;
+ }
+
+ ///
+ /// Solve A * x = b, where b is a column vector. This is more efficient
+ /// than computing the inverse in one-shot cases.
+ ///
+ /// The b.
+ ///
+ 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)));
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The b.
+ ///
+ 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);
+ }
+ }
+
+ ///
+ /// Rotation
+ ///
+ public struct Rot
+ {
+ /// Sine and cosine
+ public float s, c;
+
+ ///
+ /// Initialize from an angle in radians
+ ///
+ /// Angle in radians
+ public Rot(float angle)
+ {
+ // TODO_ERIN optimize
+ s = (float)Math.Sin(angle);
+ c = (float)Math.Cos(angle);
+ }
+
+ ///
+ /// Set using an angle in radians.
+ ///
+ ///
+ public void Set(float angle)
+ {
+ //FPE: Optimization
+ if (angle == 0)
+ {
+ s = 0;
+ c = 1;
+ }
+ else
+ {
+ // TODO_ERIN optimize
+ s = (float)Math.Sin(angle);
+ c = (float)Math.Cos(angle);
+ }
+ }
+
+ ///
+ /// Set to the identity rotation
+ ///
+ public void SetIdentity()
+ {
+ s = 0.0f;
+ c = 1.0f;
+ }
+
+ ///
+ /// Get the angle in radians
+ ///
+ public float GetAngle()
+ {
+ return (float)Math.Atan2(s, c);
+ }
+
+ ///
+ /// Get the x-axis
+ ///
+ public Vector2 GetXAxis()
+ {
+ return new Vector2(c, s);
+ }
+
+ ///
+ /// Get the y-axis
+ ///
+ public Vector2 GetYAxis()
+ {
+ return new Vector2(-s, c);
+ }
+ }
+
+ ///
+ /// A transform contains translation and rotation. It is used to represent
+ /// the position and orientation of rigid frames.
+ ///
+ public struct Transform
+ {
+ public Vector2 p;
+ public Rot q;
+
+ ///
+ /// Initialize using a position vector and a rotation matrix.
+ ///
+ /// The position.
+ /// The r.
+ public Transform(ref Vector2 position, ref Rot rotation)
+ {
+ p = position;
+ q = rotation;
+ }
+
+ ///
+ /// Set this to the identity transform.
+ ///
+ public void SetIdentity()
+ {
+ p = Vector2.Zero;
+ q.SetIdentity();
+ }
+
+ ///
+ /// Set this based on the position and angle.
+ ///
+ /// The position.
+ /// The angle.
+ public void Set(Vector2 position, float angle)
+ {
+ p = position;
+ q.Set(angle);
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ public struct Sweep
+ {
+ ///
+ /// World angles
+ ///
+ public float A;
+
+ public float A0;
+
+ ///
+ /// Fraction of the current time step in the range [0,1]
+ /// c0 and a0 are the positions at alpha0.
+ ///
+ public float Alpha0;
+
+ ///
+ /// Center world positions
+ ///
+ public Vector2 C;
+
+ public Vector2 C0;
+
+ ///
+ /// Local center of mass position
+ ///
+ public Vector2 LocalCenter;
+
+ ///
+ /// Get the interpolated transform at a specific time.
+ ///
+ /// The transform.
+ /// beta is a factor in [0,1], where 0 indicates alpha0.
+ 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);
+ }
+
+ ///
+ /// Advance the sweep forward, yielding a new initial state.
+ ///
+ /// new initial time..
+ 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;
+ }
+
+ ///
+ /// Normalize the angles.
+ ///
+ public void Normalize()
+ {
+ float d = MathHelper.TwoPi * (float)Math.Floor(A0 / MathHelper.TwoPi);
+ A0 -= d;
+ A -= d;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/Path.cs b/src/Box2DNet/Common/Path.cs
new file mode 100644
index 0000000..b2dff99
--- /dev/null
+++ b/src/Box2DNet/Common/Path.cs
@@ -0,0 +1,337 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Xna.Framework;
+
+namespace Box2DNet.Common
+{
+ //Contributed by Matthew Bettcher
+
+ ///
+ /// Path:
+ /// Very similar to Vertices, but this
+ /// class contains vectors describing
+ /// control points on a Catmull-Rom
+ /// curve.
+ ///
+ public class Path
+ {
+ ///
+ /// All the points that makes up the curve
+ ///
+ public List ControlPoints;
+
+ private float _deltaT;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Path()
+ {
+ ControlPoints = new List();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The vertices to created the path from.
+ public Path(Vector2[] vertices)
+ {
+ ControlPoints = new List(vertices.Length);
+
+ for (int i = 0; i < vertices.Length; i++)
+ {
+ Add(vertices[i]);
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The vertices to created the path from.
+ public Path(IList vertices)
+ {
+ ControlPoints = new List(vertices.Count);
+ for (int i = 0; i < vertices.Count; i++)
+ {
+ Add(vertices[i]);
+ }
+ }
+
+ ///
+ /// True if the curve is closed.
+ ///
+ /// true if closed; otherwise, false.
+ public bool Closed { get; set; }
+
+ ///
+ /// Gets the next index of a controlpoint
+ ///
+ /// The index.
+ ///
+ public int NextIndex(int index)
+ {
+ if (index == ControlPoints.Count - 1)
+ {
+ return 0;
+ }
+ return index + 1;
+ }
+
+ ///
+ /// Gets the previous index of a controlpoint
+ ///
+ /// The index.
+ ///
+ public int PreviousIndex(int index)
+ {
+ if (index == 0)
+ {
+ return ControlPoints.Count - 1;
+ }
+ return index - 1;
+ }
+
+ ///
+ /// Translates the control points by the specified vector.
+ ///
+ /// The vector.
+ public void Translate(ref Vector2 vector)
+ {
+ for (int i = 0; i < ControlPoints.Count; i++)
+ ControlPoints[i] = Vector2.Add(ControlPoints[i], vector);
+ }
+
+ ///
+ /// Scales the control points by the specified vector.
+ ///
+ /// The Value.
+ public void Scale(ref Vector2 value)
+ {
+ for (int i = 0; i < ControlPoints.Count; i++)
+ ControlPoints[i] = Vector2.Multiply(ControlPoints[i], value);
+ }
+
+ ///
+ /// Rotate the control points by the defined value in radians.
+ ///
+ /// The amount to rotate by in radians.
+ 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();
+ }
+
+ ///
+ /// Returns a set of points defining the
+ /// curve with the specifed number of divisions
+ /// between each control point.
+ ///
+ /// Number of divisions between each control point.
+ ///
+ 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;
+ }
+
+ ///
+ /// Gets the normal for the given time.
+ ///
+ /// The time
+ /// The normal.
+ 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 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 SubdivideEvenly(int divisions)
+ {
+ List verts = new List();
+
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Box2DNet/Common/PathManager.cs b/src/Box2DNet/Common/PathManager.cs
new file mode 100644
index 0000000..1f9d6d6
--- /dev/null
+++ b/src/Box2DNet/Common/PathManager.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Collections.Generic;
+using Box2DNet.Collision.Shapes;
+using Box2DNet.Common.Decomposition;
+using Box2DNet.Dynamics;
+using Box2DNet.Dynamics.Joints;
+using Microsoft.Xna.Framework;
+
+namespace Box2DNet.Common
+{
+ ///
+ /// An easy to use manager for creating paths.
+ ///
+ public static class PathManager
+ {
+ #region LinkType enum
+
+ public enum LinkType
+ {
+ Revolute,
+ Slider
+ }
+
+ #endregion
+
+ //Contributed by Matthew Bettcher
+
+ ///
+ /// Convert a path into a set of edges and attaches them to the specified body.
+ /// Note: use only for static edges.
+ ///
+ /// The path.
+ /// The body.
+ /// The subdivisions.
+ 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]));
+ }
+ }
+ }
+
+ ///
+ /// Convert a closed path into a polygon.
+ /// Convex decomposition is automatically performed.
+ ///
+ /// The path.
+ /// The body.
+ /// The density.
+ /// The subdivisions.
+ 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 verts = path.GetVertices(subdivisions);
+
+ List