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

This commit is contained in:
Regalis
2015-07-08 11:37:47 +03:00
parent 3af9b8183b
commit d56f7f3f77
155 changed files with 39772 additions and 261 deletions
@@ -0,0 +1,217 @@
using System.Collections.Generic;
using System.Diagnostics;
using FarseerPhysics.Collision.Shapes;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.PolygonManipulation
{
public static class CuttingTools
{
//Cutting a shape into two is based on the work of Daid and his prototype BoxCutter: http://www.box2d.org/forum/viewtopic.php?f=3&t=1473
/// <summary>
/// Split a fixture into 2 vertice collections using the given entry and exit-point.
/// </summary>
/// <param name="fixture">The Fixture to split</param>
/// <param name="entryPoint">The entry point - The start point</param>
/// <param name="exitPoint">The exit point - The end point</param>
/// <param name="first">The first collection of vertexes</param>
/// <param name="second">The second collection of vertexes</param>
public static void SplitShape(Fixture fixture, Vector2 entryPoint, Vector2 exitPoint, out Vertices first, out Vertices second)
{
Vector2 localEntryPoint = fixture.Body.GetLocalPoint(ref entryPoint);
Vector2 localExitPoint = fixture.Body.GetLocalPoint(ref exitPoint);
PolygonShape shape = fixture.Shape as PolygonShape;
//We can only cut polygons at the moment
if (shape == null)
{
first = new Vertices();
second = new Vertices();
return;
}
//Offset the entry and exit points if they are too close to the vertices
foreach (Vector2 vertex in shape.Vertices)
{
if (vertex.Equals(localEntryPoint))
localEntryPoint -= new Vector2(0, Settings.Epsilon);
if (vertex.Equals(localExitPoint))
localExitPoint += new Vector2(0, Settings.Epsilon);
}
Vertices vertices = new Vertices(shape.Vertices);
Vertices[] newPolygon = new Vertices[2];
for (int i = 0; i < newPolygon.Length; i++)
{
newPolygon[i] = new Vertices(vertices.Count);
}
int[] cutAdded = { -1, -1 };
int last = -1;
for (int i = 0; i < vertices.Count; i++)
{
int n;
//Find out if this vertex is on the old or new shape.
if (Vector2.Dot(MathUtils.Cross(localExitPoint - localEntryPoint, 1), vertices[i] - localEntryPoint) > Settings.Epsilon)
n = 0;
else
n = 1;
if (last != n)
{
//If we switch from one shape to the other add the cut vertices.
if (last == 0)
{
Debug.Assert(cutAdded[0] == -1);
cutAdded[0] = newPolygon[last].Count;
newPolygon[last].Add(localExitPoint);
newPolygon[last].Add(localEntryPoint);
}
if (last == 1)
{
Debug.Assert(cutAdded[last] == -1);
cutAdded[last] = newPolygon[last].Count;
newPolygon[last].Add(localEntryPoint);
newPolygon[last].Add(localExitPoint);
}
}
newPolygon[n].Add(vertices[i]);
last = n;
}
//Add the cut in case it has not been added yet.
if (cutAdded[0] == -1)
{
cutAdded[0] = newPolygon[0].Count;
newPolygon[0].Add(localExitPoint);
newPolygon[0].Add(localEntryPoint);
}
if (cutAdded[1] == -1)
{
cutAdded[1] = newPolygon[1].Count;
newPolygon[1].Add(localEntryPoint);
newPolygon[1].Add(localExitPoint);
}
for (int n = 0; n < 2; n++)
{
Vector2 offset;
if (cutAdded[n] > 0)
{
offset = (newPolygon[n][cutAdded[n] - 1] - newPolygon[n][cutAdded[n]]);
}
else
{
offset = (newPolygon[n][newPolygon[n].Count - 1] - newPolygon[n][0]);
}
offset.Normalize();
if (!offset.IsValid())
offset = Vector2.One;
newPolygon[n][cutAdded[n]] += Settings.Epsilon * offset;
if (cutAdded[n] < newPolygon[n].Count - 2)
{
offset = (newPolygon[n][cutAdded[n] + 2] - newPolygon[n][cutAdded[n] + 1]);
}
else
{
offset = (newPolygon[n][0] - newPolygon[n][newPolygon[n].Count - 1]);
}
offset.Normalize();
if (!offset.IsValid())
offset = Vector2.One;
newPolygon[n][cutAdded[n] + 1] += Settings.Epsilon * offset;
}
first = newPolygon[0];
second = newPolygon[1];
}
/// <summary>
/// This is a high-level function to cuts fixtures inside the given world, using the start and end points.
/// Note: We don't support cutting when the start or end is inside a shape.
/// </summary>
/// <param name="world">The world.</param>
/// <param name="start">The startpoint.</param>
/// <param name="end">The endpoint.</param>
/// <returns>True if the cut was performed.</returns>
public static bool Cut(World world, Vector2 start, Vector2 end)
{
List<Fixture> fixtures = new List<Fixture>();
List<Vector2> entryPoints = new List<Vector2>();
List<Vector2> exitPoints = new List<Vector2>();
//We don't support cutting when the start or end is inside a shape.
if (world.TestPoint(start) != null || world.TestPoint(end) != null)
return false;
//Get the entry points
world.RayCast((f, p, n, fr) =>
{
fixtures.Add(f);
entryPoints.Add(p);
return 1;
}, start, end);
//Reverse the ray to get the exitpoints
world.RayCast((f, p, n, fr) =>
{
exitPoints.Add(p);
return 1;
}, end, start);
//We only have a single point. We need at least 2
if (entryPoints.Count + exitPoints.Count < 2)
return false;
for (int i = 0; i < fixtures.Count; i++)
{
// can't cut circles or edges yet !
if (fixtures[i].Shape.ShapeType != ShapeType.Polygon)
continue;
if (fixtures[i].Body.BodyType != BodyType.Static)
{
//Split the shape up into two shapes
Vertices first;
Vertices second;
SplitShape(fixtures[i], entryPoints[i], exitPoints[i], out first, out second);
//Delete the original shape and create two new. Retain the properties of the body.
if (first.CheckPolygon() == PolygonError.NoError)
{
Body firstFixture = BodyFactory.CreatePolygon(world, first, fixtures[i].Shape.Density, fixtures[i].Body.Position);
firstFixture.Rotation = fixtures[i].Body.Rotation;
firstFixture.LinearVelocity = fixtures[i].Body.LinearVelocity;
firstFixture.AngularVelocity = fixtures[i].Body.AngularVelocity;
firstFixture.BodyType = BodyType.Dynamic;
}
if (second.CheckPolygon() == PolygonError.NoError)
{
Body secondFixture = BodyFactory.CreatePolygon(world, second, fixtures[i].Shape.Density, fixtures[i].Body.Position);
secondFixture.Rotation = fixtures[i].Body.Rotation;
secondFixture.LinearVelocity = fixtures[i].Body.LinearVelocity;
secondFixture.AngularVelocity = fixtures[i].Body.AngularVelocity;
secondFixture.BodyType = BodyType.Dynamic;
}
world.RemoveBody(fixtures[i].Body);
}
}
return true;
}
}
}
@@ -0,0 +1,226 @@
/*
* C# Version Ported by Matt Bettcher and Ian Qvist 2009-2010
*
* Original C++ Version Copyright (c) 2007 Eric Jordan
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.PolygonManipulation
{
/// <summary>
/// Combines a list of triangles into a list of convex polygons.
/// Starts with a seed triangle, keep adding triangles to it until you can't add any more without making the polygon non-convex.
/// </summary>
public static class SimpleCombiner
{
/// <summary>
/// Combine a list of triangles into a list of convex polygons.
///
/// Note: This only works on triangles.
/// </summary>
///<param name="triangles">The triangles.</param>
///<param name="maxPolys">The maximun number of polygons to return.</param>
///<param name="tolerance">The tolerance</param>
public static List<Vertices> PolygonizeTriangles(List<Vertices> triangles, int maxPolys = int.MaxValue, float tolerance = 0.001f)
{
if (triangles.Count <= 0)
return triangles;
List<Vertices> polys = new List<Vertices>();
bool[] covered = new bool[triangles.Count];
for (int i = 0; i < triangles.Count; ++i)
{
covered[i] = false;
//Check here for degenerate triangles
Vertices triangle = triangles[i];
Vector2 a = triangle[0];
Vector2 b = triangle[1];
Vector2 c = triangle[2];
if ((a.X == b.X && a.Y == b.Y) || (b.X == c.X && b.Y == c.Y) || (a.X == c.X && a.Y == c.Y))
covered[i] = true;
}
int polyIndex = 0;
bool notDone = true;
while (notDone)
{
int currTri = -1;
for (int i = 0; i < triangles.Count; ++i)
{
if (covered[i])
continue;
currTri = i;
break;
}
if (currTri == -1)
{
notDone = false;
}
else
{
Vertices poly = new Vertices(3);
for (int i = 0; i < 3; i++)
{
poly.Add(triangles[currTri][i]);
}
covered[currTri] = true;
int index = 0;
for (int i = 0; i < 2 * triangles.Count; ++i, ++index)
{
while (index >= triangles.Count) index -= triangles.Count;
if (covered[index])
{
continue;
}
Vertices newP = AddTriangle(triangles[index], poly);
if (newP == null)
continue; // is this right
if (newP.Count > Settings.MaxPolygonVertices)
continue;
if (newP.IsConvex())
{
//Or should it be IsUsable? Maybe re-write IsConvex to apply the angle threshold from Box2d
poly = new Vertices(newP);
covered[index] = true;
}
}
//We have a maximum of polygons that we need to keep under.
if (polyIndex < maxPolys)
{
SimplifyTools.MergeParallelEdges(poly, tolerance);
//If identical points are present, a triangle gets
//borked by the MergeParallelEdges function, hence
//the vertex number check
if (poly.Count >= 3)
polys.Add(new Vertices(poly));
else
Debug.WriteLine("Skipping corrupt poly.");
}
if (poly.Count >= 3)
polyIndex++; //Must be outside (polyIndex < polysLength) test
}
}
//TODO: Add sanity check
//Remove empty vertice collections
for (int i = polys.Count - 1; i >= 0; i--)
{
if (polys[i].Count == 0)
polys.RemoveAt(i);
}
return polys;
}
private static Vertices AddTriangle(Vertices t, Vertices vertices)
{
// First, find vertices that connect
int firstP = -1;
int firstT = -1;
int secondP = -1;
int secondT = -1;
for (int i = 0; i < vertices.Count; i++)
{
if (t[0].X == vertices[i].X && t[0].Y == vertices[i].Y)
{
if (firstP == -1)
{
firstP = i;
firstT = 0;
}
else
{
secondP = i;
secondT = 0;
}
}
else if (t[1].X == vertices[i].X && t[1].Y == vertices[i].Y)
{
if (firstP == -1)
{
firstP = i;
firstT = 1;
}
else
{
secondP = i;
secondT = 1;
}
}
else if (t[2].X == vertices[i].X && t[2].Y == vertices[i].Y)
{
if (firstP == -1)
{
firstP = i;
firstT = 2;
}
else
{
secondP = i;
secondT = 2;
}
}
}
// Fix ordering if first should be last vertex of poly
if (firstP == 0 && secondP == vertices.Count - 1)
{
firstP = vertices.Count - 1;
secondP = 0;
}
// Didn't find it
if (secondP == -1)
{
return null;
}
// Find tip index on triangle
int tipT = 0;
if (tipT == firstT || tipT == secondT)
tipT = 1;
if (tipT == firstT || tipT == secondT)
tipT = 2;
Vertices result = new Vertices(vertices.Count + 1);
for (int i = 0; i < vertices.Count; i++)
{
result.Add(vertices[i]);
if (i == firstP)
result.Add(t[tipT]);
}
return result;
}
}
}
@@ -0,0 +1,302 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.PolygonManipulation
{
/// <summary>
/// Provides a set of tools to simplify polygons in various ways.
/// </summary>
public static class SimplifyTools
{
/// <summary>
/// Removes all collinear points on the polygon.
/// </summary>
/// <param name="vertices">The polygon that needs simplification.</param>
/// <param name="collinearityTolerance">The collinearity tolerance.</param>
/// <returns>A simplified polygon.</returns>
public static Vertices CollinearSimplify(Vertices vertices, float collinearityTolerance = 0)
{
if (vertices.Count <= 3)
return vertices;
Vertices simplified = new Vertices(vertices.Count);
for (int i = 0; i < vertices.Count; i++)
{
Vector2 prev = vertices.PreviousVertex(i);
Vector2 current = vertices[i];
Vector2 next = vertices.NextVertex(i);
//If they collinear, continue
if (MathUtils.IsCollinear(ref prev, ref current, ref next, collinearityTolerance))
continue;
simplified.Add(current);
}
return simplified;
}
/// <summary>
/// Ramer-Douglas-Peucker polygon simplification algorithm. This is the general recursive version that does not use the
/// speed-up technique by using the Melkman convex hull.
///
/// If you pass in 0, it will remove all collinear points.
/// </summary>
/// <returns>The simplified polygon</returns>
public static Vertices DouglasPeuckerSimplify(Vertices vertices, float distanceTolerance)
{
if (vertices.Count <= 3)
return vertices;
bool[] usePoint = new bool[vertices.Count];
for (int i = 0; i < vertices.Count; i++)
usePoint[i] = true;
SimplifySection(vertices, 0, vertices.Count - 1, usePoint, distanceTolerance);
Vertices simplified = new Vertices(vertices.Count);
for (int i = 0; i < vertices.Count; i++)
{
if (usePoint[i])
simplified.Add(vertices[i]);
}
return simplified;
}
private static void SimplifySection(Vertices vertices, int i, int j, bool[] usePoint, float distanceTolerance)
{
if ((i + 1) == j)
return;
Vector2 a = vertices[i];
Vector2 b = vertices[j];
double maxDistance = -1.0;
int maxIndex = i;
for (int k = i + 1; k < j; k++)
{
Vector2 point = vertices[k];
double distance = LineTools.DistanceBetweenPointAndLineSegment(ref point, ref a, ref b);
if (distance > maxDistance)
{
maxDistance = distance;
maxIndex = k;
}
}
if (maxDistance <= distanceTolerance)
{
for (int k = i + 1; k < j; k++)
{
usePoint[k] = false;
}
}
else
{
SimplifySection(vertices, i, maxIndex, usePoint, distanceTolerance);
SimplifySection(vertices, maxIndex, j, usePoint, distanceTolerance);
}
}
/// <summary>
/// Merges all parallel edges in the list of vertices
/// </summary>
/// <param name="vertices">The vertices.</param>
/// <param name="tolerance">The tolerance.</param>
public static Vertices MergeParallelEdges(Vertices vertices, float tolerance)
{
//From Eric Jordan's convex decomposition library
if (vertices.Count <= 3)
return vertices; //Can't do anything useful here to a triangle
bool[] mergeMe = new bool[vertices.Count];
int newNVertices = vertices.Count;
//Gather points to process
for (int i = 0; i < vertices.Count; ++i)
{
int lower = (i == 0) ? (vertices.Count - 1) : (i - 1);
int middle = i;
int upper = (i == vertices.Count - 1) ? (0) : (i + 1);
float dx0 = vertices[middle].X - vertices[lower].X;
float dy0 = vertices[middle].Y - vertices[lower].Y;
float dx1 = vertices[upper].Y - vertices[middle].X;
float dy1 = vertices[upper].Y - vertices[middle].Y;
float norm0 = (float)Math.Sqrt(dx0 * dx0 + dy0 * dy0);
float norm1 = (float)Math.Sqrt(dx1 * dx1 + dy1 * dy1);
if (!(norm0 > 0.0f && norm1 > 0.0f) && newNVertices > 3)
{
//Merge identical points
mergeMe[i] = true;
--newNVertices;
}
dx0 /= norm0;
dy0 /= norm0;
dx1 /= norm1;
dy1 /= norm1;
float cross = dx0 * dy1 - dx1 * dy0;
float dot = dx0 * dx1 + dy0 * dy1;
if (Math.Abs(cross) < tolerance && dot > 0 && newNVertices > 3)
{
mergeMe[i] = true;
--newNVertices;
}
else
mergeMe[i] = false;
}
if (newNVertices == vertices.Count || newNVertices == 0)
return vertices;
int currIndex = 0;
//Copy the vertices to a new list and clear the old
Vertices newVertices = new Vertices(newNVertices);
for (int i = 0; i < vertices.Count; ++i)
{
if (mergeMe[i] || newNVertices == 0 || currIndex == newNVertices)
continue;
Debug.Assert(currIndex < newNVertices);
newVertices.Add(vertices[i]);
++currIndex;
}
return newVertices;
}
/// <summary>
/// Merges the identical points in the polygon.
/// </summary>
/// <param name="vertices">The vertices.</param>
public static Vertices MergeIdenticalPoints(Vertices vertices)
{
HashSet<Vector2> unique = new HashSet<Vector2>();
foreach (Vector2 vertex in vertices)
{
unique.Add(vertex);
}
return new Vertices(unique);
}
/// <summary>
/// Reduces the polygon by distance.
/// </summary>
/// <param name="vertices">The vertices.</param>
/// <param name="distance">The distance between points. Points closer than this will be removed.</param>
public static Vertices ReduceByDistance(Vertices vertices, float distance)
{
if (vertices.Count <= 3)
return vertices;
float distance2 = distance * distance;
Vertices simplified = new Vertices(vertices.Count);
for (int i = 0; i < vertices.Count; i++)
{
Vector2 current = vertices[i];
Vector2 next = vertices.NextVertex(i);
//If they are closer than the distance, continue
if ((next - current).LengthSquared() <= distance2)
continue;
simplified.Add(current);
}
return simplified;
}
/// <summary>
/// Reduces the polygon by removing the Nth vertex in the vertices list.
/// </summary>
/// <param name="vertices">The vertices.</param>
/// <param name="nth">The Nth point to remove. Example: 5.</param>
/// <returns></returns>
public static Vertices ReduceByNth(Vertices vertices, int nth)
{
if (vertices.Count <= 3)
return vertices;
if (nth == 0)
return vertices;
Vertices simplified = new Vertices(vertices.Count);
for (int i = 0; i < vertices.Count; i++)
{
if (i % nth == 0)
continue;
simplified.Add(vertices[i]);
}
return simplified;
}
/// <summary>
/// Simplify the polygon by removing all points that in pairs of 3 have an area less than the tolerance.
///
/// Pass in 0 as tolerance, and it will only remove collinear points.
/// </summary>
/// <param name="vertices"></param>
/// <param name="areaTolerance"></param>
/// <returns></returns>
public static Vertices ReduceByArea(Vertices vertices, float areaTolerance)
{
//From physics2d.net
if (vertices.Count <= 3)
return vertices;
if (areaTolerance < 0)
throw new ArgumentOutOfRangeException("areaTolerance", "must be equal to or greater than zero.");
Vertices simplified = new Vertices(vertices.Count);
Vector2 v3;
Vector2 v1 = vertices[vertices.Count - 2];
Vector2 v2 = vertices[vertices.Count - 1];
areaTolerance *= 2;
for (int i = 0; i < vertices.Count; ++i, v2 = v3)
{
v3 = i == vertices.Count - 1 ? simplified[0] : vertices[i];
float old1;
MathUtils.Cross(ref v1, ref v2, out old1);
float old2;
MathUtils.Cross(ref v2, ref v3, out old2);
float new1;
MathUtils.Cross(ref v1, ref v3, out new1);
if (Math.Abs(new1 - (old1 + old2)) > areaTolerance)
{
simplified.Add(v2);
v1 = v2;
}
}
return simplified;
}
}
}
@@ -0,0 +1,514 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
namespace FarseerPhysics.Common.PolygonManipulation
{
internal enum PolyClipType
{
Intersect,
Union,
Difference
}
public enum PolyClipError
{
None,
DegeneratedOutput,
NonSimpleInput,
BrokenResult
}
//Clipper contributed by Helge Backhaus
public static class YuPengClipper
{
private const float ClipperEpsilonSquared = 1.192092896e-07f;
public static List<Vertices> Union(Vertices polygon1, Vertices polygon2, out PolyClipError error)
{
return Execute(polygon1, polygon2, PolyClipType.Union, out error);
}
public static List<Vertices> Difference(Vertices polygon1, Vertices polygon2, out PolyClipError error)
{
return Execute(polygon1, polygon2, PolyClipType.Difference, out error);
}
public static List<Vertices> Intersect(Vertices polygon1, Vertices polygon2, out PolyClipError error)
{
return Execute(polygon1, polygon2, PolyClipType.Intersect, out error);
}
/// <summary>
/// Implements "A new algorithm for Boolean operations on general polygons"
/// available here: http://liama.ia.ac.cn/wiki/_media/user:dong:dong_cg_05.pdf
/// Merges two polygons, a subject and a clip with the specified operation. Polygons may not be
/// self-intersecting.
///
/// Warning: May yield incorrect results or even crash if polygons contain collinear points.
/// </summary>
/// <param name="subject">The subject polygon.</param>
/// <param name="clip">The clip polygon, which is added,
/// substracted or intersected with the subject</param>
/// <param name="clipType">The operation to be performed. Either
/// Union, Difference or Intersection.</param>
/// <param name="error">The error generated (if any)</param>
/// <returns>A list of closed polygons, which make up the result of the clipping operation.
/// Outer contours are ordered counter clockwise, holes are ordered clockwise.</returns>
private static List<Vertices> Execute(Vertices subject, Vertices clip, PolyClipType clipType, out PolyClipError error)
{
Debug.Assert(subject.IsSimple() && clip.IsSimple(), "Non simple input!", "Input polygons must be simple (cannot intersect themselves).");
// Copy polygons
Vertices slicedSubject;
Vertices slicedClip;
// Calculate the intersection and touch points between
// subject and clip and add them to both
CalculateIntersections(subject, clip, out slicedSubject, out slicedClip);
// Translate polygons into upper right quadrant
// as the algorithm depends on it
Vector2 lbSubject = subject.GetAABB().LowerBound;
Vector2 lbClip = clip.GetAABB().LowerBound;
Vector2 translate;
Vector2.Min(ref lbSubject, ref lbClip, out translate);
translate = Vector2.One - translate;
if (translate != Vector2.Zero)
{
slicedSubject.Translate(ref translate);
slicedClip.Translate(ref translate);
}
// Enforce counterclockwise contours
slicedSubject.ForceCounterClockWise();
slicedClip.ForceCounterClockWise();
List<Edge> subjectSimplices;
List<float> subjectCoeff;
List<Edge> clipSimplices;
List<float> clipCoeff;
// Build simplical chains from the polygons and calculate the
// the corresponding coefficients
CalculateSimplicalChain(slicedSubject, out subjectCoeff, out subjectSimplices);
CalculateSimplicalChain(slicedClip, out clipCoeff, out clipSimplices);
List<Edge> resultSimplices;
// Determine the characteristics function for all non-original edges
// in subject and clip simplical chain and combine the edges contributing
// to the result, depending on the clipType
CalculateResultChain(subjectCoeff, subjectSimplices, clipCoeff, clipSimplices, clipType,
out resultSimplices);
List<Vertices> result;
// Convert result chain back to polygon(s)
error = BuildPolygonsFromChain(resultSimplices, out result);
// Reverse the polygon translation from the beginning
// and remove collinear points from output
translate *= -1f;
for (int i = 0; i < result.Count; ++i)
{
result[i].Translate(ref translate);
SimplifyTools.CollinearSimplify(result[i]);
}
return result;
}
/// <summary>
/// Calculates all intersections between two polygons.
/// </summary>
/// <param name="polygon1">The first polygon.</param>
/// <param name="polygon2">The second polygon.</param>
/// <param name="slicedPoly1">Returns the first polygon with added intersection points.</param>
/// <param name="slicedPoly2">Returns the second polygon with added intersection points.</param>
private static void CalculateIntersections(Vertices polygon1, Vertices polygon2,
out Vertices slicedPoly1, out Vertices slicedPoly2)
{
slicedPoly1 = new Vertices(polygon1);
slicedPoly2 = new Vertices(polygon2);
// Iterate through polygon1's edges
for (int i = 0; i < polygon1.Count; i++)
{
// Get edge vertices
Vector2 a = polygon1[i];
Vector2 b = polygon1[polygon1.NextIndex(i)];
// Get intersections between this edge and polygon2
for (int j = 0; j < polygon2.Count; j++)
{
Vector2 c = polygon2[j];
Vector2 d = polygon2[polygon2.NextIndex(j)];
Vector2 intersectionPoint;
// Check if the edges intersect
if (LineTools.LineIntersect(a, b, c, d, out intersectionPoint))
{
// calculate alpha values for sorting multiple intersections points on a edge
float alpha;
// Insert intersection point into first polygon
alpha = GetAlpha(a, b, intersectionPoint);
if (alpha > 0f && alpha < 1f)
{
int index = slicedPoly1.IndexOf(a) + 1;
while (index < slicedPoly1.Count &&
GetAlpha(a, b, slicedPoly1[index]) <= alpha)
{
++index;
}
slicedPoly1.Insert(index, intersectionPoint);
}
// Insert intersection point into second polygon
alpha = GetAlpha(c, d, intersectionPoint);
if (alpha > 0f && alpha < 1f)
{
int index = slicedPoly2.IndexOf(c) + 1;
while (index < slicedPoly2.Count &&
GetAlpha(c, d, slicedPoly2[index]) <= alpha)
{
++index;
}
slicedPoly2.Insert(index, intersectionPoint);
}
}
}
}
// Check for very small edges
for (int i = 0; i < slicedPoly1.Count; ++i)
{
int iNext = slicedPoly1.NextIndex(i);
//If they are closer than the distance remove vertex
if ((slicedPoly1[iNext] - slicedPoly1[i]).LengthSquared() <= ClipperEpsilonSquared)
{
slicedPoly1.RemoveAt(i);
--i;
}
}
for (int i = 0; i < slicedPoly2.Count; ++i)
{
int iNext = slicedPoly2.NextIndex(i);
//If they are closer than the distance remove vertex
if ((slicedPoly2[iNext] - slicedPoly2[i]).LengthSquared() <= ClipperEpsilonSquared)
{
slicedPoly2.RemoveAt(i);
--i;
}
}
}
/// <summary>
/// Calculates the simplical chain corresponding to the input polygon.
/// </summary>
/// <remarks>Used by method <c>Execute()</c>.</remarks>
private static void CalculateSimplicalChain(Vertices poly, out List<float> coeff,
out List<Edge> simplicies)
{
simplicies = new List<Edge>();
coeff = new List<float>();
for (int i = 0; i < poly.Count; ++i)
{
simplicies.Add(new Edge(poly[i], poly[poly.NextIndex(i)]));
coeff.Add(CalculateSimplexCoefficient(Vector2.Zero, poly[i], poly[poly.NextIndex(i)]));
}
}
/// <summary>
/// Calculates the characteristics function for all edges of
/// the given simplical chains and builds the result chain.
/// </summary>
/// <remarks>Used by method <c>Execute()</c>.</remarks>
private static void CalculateResultChain(List<float> poly1Coeff, List<Edge> poly1Simplicies,
List<float> poly2Coeff, List<Edge> poly2Simplicies,
PolyClipType clipType, out List<Edge> resultSimplices)
{
resultSimplices = new List<Edge>();
for (int i = 0; i < poly1Simplicies.Count; ++i)
{
float edgeCharacter = 0;
if (poly2Simplicies.Contains(poly1Simplicies[i]))
{
edgeCharacter = 1f;
}
else if (poly2Simplicies.Contains(-poly1Simplicies[i]) && clipType == PolyClipType.Union)
{
edgeCharacter = 1f;
}
else
{
for (int j = 0; j < poly2Simplicies.Count; ++j)
{
if (!poly2Simplicies.Contains(-poly1Simplicies[i]))
{
edgeCharacter += CalculateBeta(poly1Simplicies[i].GetCenter(),
poly2Simplicies[j], poly2Coeff[j]);
}
}
}
if (clipType == PolyClipType.Intersect)
{
if (edgeCharacter == 1f)
{
resultSimplices.Add(poly1Simplicies[i]);
}
}
else
{
if (edgeCharacter == 0f)
{
resultSimplices.Add(poly1Simplicies[i]);
}
}
}
for (int i = 0; i < poly2Simplicies.Count; ++i)
{
float edgeCharacter = 0f;
if (!resultSimplices.Contains(poly2Simplicies[i]) &&
!resultSimplices.Contains(-poly2Simplicies[i]))
{
if (poly1Simplicies.Contains(-poly2Simplicies[i]) && clipType == PolyClipType.Union)
{
edgeCharacter = 1f;
}
else
{
edgeCharacter = 0f;
for (int j = 0; j < poly1Simplicies.Count; ++j)
{
if (!poly1Simplicies.Contains(poly2Simplicies[i]) && !poly1Simplicies.Contains(-poly2Simplicies[i]))
{
edgeCharacter += CalculateBeta(poly2Simplicies[i].GetCenter(),
poly1Simplicies[j], poly1Coeff[j]);
}
}
if (clipType == PolyClipType.Intersect || clipType == PolyClipType.Difference)
{
if (edgeCharacter == 1f)
{
resultSimplices.Add(-poly2Simplicies[i]);
}
}
else
{
if (edgeCharacter == 0f)
{
resultSimplices.Add(poly2Simplicies[i]);
}
}
}
}
}
}
/// <summary>
/// Calculates the polygon(s) from the result simplical chain.
/// </summary>
/// <remarks>Used by method <c>Execute()</c>.</remarks>
private static PolyClipError BuildPolygonsFromChain(List<Edge> simplicies, out List<Vertices> result)
{
result = new List<Vertices>();
PolyClipError errVal = PolyClipError.None;
while (simplicies.Count > 0)
{
Vertices output = new Vertices();
output.Add(simplicies[0].EdgeStart);
output.Add(simplicies[0].EdgeEnd);
simplicies.RemoveAt(0);
bool closed = false;
int index = 0;
int count = simplicies.Count; // Needed to catch infinite loops
while (!closed && simplicies.Count > 0)
{
if (VectorEqual(output[output.Count - 1], simplicies[index].EdgeStart))
{
if (VectorEqual(simplicies[index].EdgeEnd, output[0]))
{
closed = true;
}
else
{
output.Add(simplicies[index].EdgeEnd);
}
simplicies.RemoveAt(index);
--index;
}
else if (VectorEqual(output[output.Count - 1], simplicies[index].EdgeEnd))
{
if (VectorEqual(simplicies[index].EdgeStart, output[0]))
{
closed = true;
}
else
{
output.Add(simplicies[index].EdgeStart);
}
simplicies.RemoveAt(index);
--index;
}
if (!closed)
{
if (++index == simplicies.Count)
{
if (count == simplicies.Count)
{
result = new List<Vertices>();
Debug.WriteLine("Undefined error while building result polygon(s).");
return PolyClipError.BrokenResult;
}
index = 0;
count = simplicies.Count;
}
}
}
if (output.Count < 3)
{
errVal = PolyClipError.DegeneratedOutput;
Debug.WriteLine("Degenerated output polygon produced (vertices < 3).");
}
result.Add(output);
}
return errVal;
}
/// <summary>
/// Needed to calculate the characteristics function of a simplex.
/// </summary>
/// <remarks>Used by method <c>CalculateEdgeCharacter()</c>.</remarks>
private static float CalculateBeta(Vector2 point, Edge e, float coefficient)
{
float result = 0f;
if (PointInSimplex(point, e))
{
result = coefficient;
}
if (PointOnLineSegment(Vector2.Zero, e.EdgeStart, point) ||
PointOnLineSegment(Vector2.Zero, e.EdgeEnd, point))
{
result = .5f * coefficient;
}
return result;
}
/// <summary>
/// Needed for sorting multiple intersections points on the same edge.
/// </summary>
/// <remarks>Used by method <c>CalculateIntersections()</c>.</remarks>
private static float GetAlpha(Vector2 start, Vector2 end, Vector2 point)
{
return (point - start).LengthSquared() / (end - start).LengthSquared();
}
/// <summary>
/// Returns the coefficient of a simplex.
/// </summary>
/// <remarks>Used by method <c>CalculateSimplicalChain()</c>.</remarks>
private static float CalculateSimplexCoefficient(Vector2 a, Vector2 b, Vector2 c)
{
float isLeft = MathUtils.Area(ref a, ref b, ref c);
if (isLeft < 0f)
{
return -1f;
}
if (isLeft > 0f)
{
return 1f;
}
return 0f;
}
/// <summary>
/// Winding number test for a point in a simplex.
/// </summary>
/// <param name="point">The point to be tested.</param>
/// <param name="edge">The edge that the point is tested against.</param>
/// <returns>False if the winding number is even and the point is outside
/// the simplex and True otherwise.</returns>
private static bool PointInSimplex(Vector2 point, Edge edge)
{
Vertices polygon = new Vertices();
polygon.Add(Vector2.Zero);
polygon.Add(edge.EdgeStart);
polygon.Add(edge.EdgeEnd);
return (polygon.PointInPolygon(ref point) == 1);
}
/// <summary>
/// Tests if a point lies on a line segment.
/// </summary>
/// <remarks>Used by method <c>CalculateBeta()</c>.</remarks>
private static bool PointOnLineSegment(Vector2 start, Vector2 end, Vector2 point)
{
Vector2 segment = end - start;
return MathUtils.Area(ref start, ref end, ref point) == 0f &&
Vector2.Dot(point - start, segment) >= 0f &&
Vector2.Dot(point - end, segment) <= 0f;
}
private static bool VectorEqual(Vector2 vec1, Vector2 vec2)
{
return (vec2 - vec1).LengthSquared() <= ClipperEpsilonSquared;
}
#region Nested type: Edge
/// <summary>Specifies an Edge. Edges are used to represent simplicies in simplical chains</summary>
private sealed class Edge
{
public Edge(Vector2 edgeStart, Vector2 edgeEnd)
{
EdgeStart = edgeStart;
EdgeEnd = edgeEnd;
}
public Vector2 EdgeStart { get; private set; }
public Vector2 EdgeEnd { get; private set; }
public Vector2 GetCenter()
{
return (EdgeStart + EdgeEnd) / 2f;
}
public static Edge operator -(Edge e)
{
return new Edge(e.EdgeEnd, e.EdgeStart);
}
public override bool Equals(Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to Point return false.
return Equals(obj as Edge);
}
public bool Equals(Edge e)
{
// If parameter is null return false:
if (e == null)
{
return false;
}
// Return true if the fields match
return VectorEqual(EdgeStart, e.EdgeStart) && VectorEqual(EdgeEnd, e.EdgeEnd);
}
public override int GetHashCode()
{
return EdgeStart.GetHashCode() ^ EdgeEnd.GetHashCode();
}
}
#endregion
}
}