public override void ApplyPolygonList(List <DTPolygon> dtPolygonList) { if (dtPolygon == dtPolygonList[0]) { return; } dtPolygon = dtPolygonList[0]; DTProfilerMarkers.Triangulation.Begin(); DTMesh triMesh = GetTriangulator().PolygonToMesh(dtPolygon); DTProfilerMarkers.Triangulation.End(); DTConvexPolygroup triPolygroup = triMesh.ToPolygroup(); // Collider from polygon DTProfilerMarkers.HertelMehlhorn.Begin(); DTConvexPolygroup hmPolygroup = HertelMehlhorn.PolyPartitionHM.Instance.ExecuteToPolygroup(triPolygroup); DTProfilerMarkers.HertelMehlhorn.End(); DTProfilerMarkers.ApplyCollider.Begin(); ApplyCollider(hmPolygroup); DTProfilerMarkers.ApplyCollider.End(); // Create mesh from triangulated polygon ApplyRenderMesh(triMesh); }
public List <DTPolygon> Subtract(DTPolygon subject, DTPolygon clippingPolygon) { if (!DTUtility.BoundsCheck(subject.Contour, clippingPolygon.Contour)) { // There is no overlap at all, so output a copy of the subject polygon return(new List <DTPolygon>() { new DTPolygon(new List <Vector2>(subject.Contour)) }); } clipper.Clear(); // Add subject polygon paths clipper.AddPath(subject.Contour.ToIntPointList(), PolyType.ptSubject, true); foreach (var hole in subject.Holes) { clipper.AddPath(hole.ToIntPointList(), PolyType.ptSubject, true); } // Add clipping polygon paths clipper.AddPath(clippingPolygon.Contour.ToIntPointList(), PolyType.ptClip, true); foreach (var hole in clippingPolygon.Holes) { clipper.AddPath(hole.ToIntPointList(), PolyType.ptClip, true); } // Execute subtraction and store result in a PolyTree so that we can easily identify holes PolyTree clipperOutput = new PolyTree(); clipper.Execute(ClipType.ctDifference, clipperOutput, PolyFillType.pftEvenOdd, PolyFillType.pftNonZero); // Convert Polytree into list of DTPolygons return(clipperOutput.ToDTPolygons()); }
public override void ApplyPolygonList(List <DTPolygon> dtPolygonList) { if (dtPolygon == dtPolygonList[0]) { return; } dtPolygon = dtPolygonList[0]; DTProfilerMarkers.Triangulation.Begin(); DTMesh dtMesh = GetTriangulator().PolygonToMesh(dtPolygon); DTProfilerMarkers.Triangulation.End(); // Collider from polygon DTProfilerMarkers.HertelMehlhorn.Begin(); DTMesh hmMesh = HertelMehlhorn.CustomHM.Instance.ExecuteToMesh(dtMesh); DTProfilerMarkers.HertelMehlhorn.End(); DTProfilerMarkers.ApplyCollider.Begin(); ApplyCollider(hmMesh); DTProfilerMarkers.ApplyCollider.End(); // Create mesh from triangulated polygon ApplyRenderMesh(dtMesh); }
// Removes unnecessary vertices public static DTPolygon Simplify(this DTPolygon inPoly) { DTProfilerMarkers.SimplifyPolygon.Begin(); if (inPoly.Contour.Count == 0) { DTProfilerMarkers.SimplifyPolygon.End(); return(null); } var simplifiedContour = inPoly.Contour.Simplify(); if (simplifiedContour == null) { DTProfilerMarkers.SimplifyPolygon.End(); return(null); } List <List <Vector2> > simplifiedHoles = new List <List <Vector2> >(); foreach (var hole in inPoly.Holes) { var simplifiedHole = hole.Simplify(); if (simplifiedContour.Count != 0) { simplifiedHoles.Add(simplifiedHole); } } DTPolygon simplifiedPolygon = new DTPolygon(simplifiedContour, simplifiedHoles); DTProfilerMarkers.SimplifyPolygon.End(); return(simplifiedPolygon); }
public DTMesh PolygonToMesh(DTPolygon subject) { // Mark any unmarked holes in the contour, otherwise Triangle.NET won't handle them properly subject = DTUtility.IdentifyHoles(subject); if (subject.Contour.Count < 3) { return(new DTMesh()); } // Don't triangulate if this is already a triangle else if (subject.Contour.Count == 3 && subject.Holes.Count == 0) { return(new DTMesh(subject.Contour, new List <List <int> >() { new List <int>() { 0, 1, 2 } })); } // If the polygon is convex, use simple fanning technique to triangulate else if (subject.IsConvex()) { DTMesh mesh = new DTMesh(subject.Contour, new List <List <int> >()); for (int i = 1; i < subject.Contour.Count - 1; i++) { mesh.Partitions.Add(new List <int>() { 0, i, i + 1 }); } return(mesh); } // Format polygon input and execute Polygon polygon = new Polygon(); polygon.Add(new Contour(subject.Contour.ToVertexList()), false); foreach (var hole in subject.Holes) { if (hole.Count >= 3) { try { polygon.Add(new Contour(hole.ToVertexList()), true); } catch (Exception) { } } } DTProfilerMarkers.TriangleNet.Begin(); IMesh triangleNetOutput = polygon.Triangulate(); ++callCount; DTProfilerMarkers.TriangleNet.End(); // Convert Triangle.NET output into DTMesh List <Vector2> vertices = triangleNetOutput.Vertices.ToVector2List(); List <List <int> > triangles = triangleNetOutput.Triangles.ToPartitionList(); return(new DTMesh(vertices, triangles)); }
private static DTPolygon Translate(DTPolygon poly, Vector2 t) { return(new DTPolygon( poly.Contour.Select(v => { return v + t; }).ToList(), poly.Holes.Select(hole => { return hole.Select(v => { return v + t; }).ToList(); }).ToList())); }
public static void DO_Subtraction(IPolygonSubtractor sub, DO_Tri_Tri dtObject, float x, float y) { DTPolygon box = new DTPolygon(square.Contour, L(smallSquare.Contour)); dtObject.transform.position = new Vector3(x, y, 0); dtObject.ApplyPolygonList(L(box)); Explosion explosion = new Explosion(x + 1, y + 1, 0.2f, 8); IterEE.ExecuteExplosions(L(explosion), L(dtObject), sub); }
public static T CreateRingObject <T> (bool hasHole = true, float radius = 1.0f, int numEdges = 24) where T : DestructibleObject { float variationAmplitude = 0.0f * radius; float angleStep = 2 * Mathf.PI / numEdges; List <Vector2> exterior = new List <Vector2>(); List <Vector2> hole = new List <Vector2>(); for (int i = 0; i < numEdges; ++i) { float rOuter = UnityEngine.Random.Range(radius - variationAmplitude, radius + variationAmplitude); float rInner = UnityEngine.Random.Range(radius - variationAmplitude, radius + variationAmplitude) * 0.5f; Vector2 dirOuter = new Vector2(Mathf.Cos(i * angleStep), Mathf.Sin(i * angleStep)); Vector2 dirInner = new Vector2(dirOuter.x, -dirOuter.y); // CW exterior exterior.Add(dirOuter * rOuter); // CCW hole if (hasHole) { hole.Add(dirInner * rInner); } } DTPolygon polygon; if (hasHole) { polygon = new DTPolygon(exterior, new List <List <Vector2> >() { hole }); } else { polygon = new DTPolygon(exterior); } GameObject go = new GameObject(); T destructibleObject = go.AddComponent <T>(); destructibleObject.ApplyPolygonList(new List <DTPolygon> { polygon }); return(destructibleObject); }
public static void ConvexBasic(IPolygonSubtractor sub) { // Basic corner clips (produces an 'L' shape) DTPolygon clip0 = Translate(square, V(-1, -1)); // bottom left DTPolygon clip1 = Translate(square, V(1, -1)); // bottom right DTPolygon clip2 = Translate(square, V(1, 1)); // top right DTPolygon clip3 = Translate(square, V(-1, 1)); // top left // Basic edge clips (produces a 'C' shape) DTPolygon clip4 = Translate(narrowRect, V(0, -1)); // bottom DTPolygon clip5 = Translate(shortRect, V(1, 0)); // right DTPolygon clip6 = Translate(narrowRect, V(0, 1)); // top DTPolygon clip7 = Translate(shortRect, V(-1, 0)); // left // Diamond clip (produces 4 triangles) DTPolygon clip8 = P(V(0, -1.5f), V(1.5f, 0), V(0, 1.5f), V(-1.5f, 0)); // start with bottom vertex // Basic corner clips (produces an 'L' shape) List <DTPolygon> expected0 = L(P(V(0, 0), V(0, -1), V(1, -1), V(1, 1), V(-1, 1), V(-1, 0))); List <DTPolygon> expected1 = L(P(V(-1, -1), V(0, -1), V(0, 0), V(1, 0), V(1, 1), V(-1, 1))); List <DTPolygon> expected2 = L(P(V(-1, -1), V(1, -1), V(1, 0), V(0, 0), V(0, 1), V(-1, 1))); List <DTPolygon> expected3 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(0, 1), V(0, 0), V(-1, 0))); // Basic edge clips (produces a 'C' shape) List <DTPolygon> expected4 = L(P(V(-1, -1), V(-0.5f, -1), V(-0.5f, 0), V(0.5f, 0), V(0.5f, -1), V(1, -1), V(1, 1), V(-1, 1))); List <DTPolygon> expected5 = L(P(V(-1, -1), V(1, -1), V(1, -0.5f), V(0, -0.5f), V(0, 0.5f), V(1, 0.5f), V(1, 1), V(-1, 1))); List <DTPolygon> expected6 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(0.5f, 1), V(0.5f, 0), V(-0.5f, 0), V(-0.5f, 1), V(-1, 1))); List <DTPolygon> expected7 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(-1, 1), V(-1, 0.5f), V(0, 0.5f), V(0, -0.5f), V(-1, -0.5f))); // Diamond clip (produces 4 triangles) List <DTPolygon> expected8 = L( P(V(-1, -0.5f), V(-1, -1), V(-0.5f, -1)), P(V(0.5f, -1), V(1, -1), V(1, -0.5f)), P(V(1, 0.5f), V(1, 1), V(0.5f, 1)), P(V(-0.5f, 1), V(-1, 1), V(-1, 0.5f)) ); // Complete removal List <DTPolygon> empty = new List <DTPolygon>(); VerifyPolygons(sub.Subtract(square, clip0), expected0); VerifyPolygons(sub.Subtract(square, clip1), expected1); VerifyPolygons(sub.Subtract(square, clip2), expected2); VerifyPolygons(sub.Subtract(square, clip3), expected3); VerifyPolygons(sub.Subtract(square, clip4), expected4); VerifyPolygons(sub.Subtract(square, clip5), expected5); VerifyPolygons(sub.Subtract(square, clip6), expected6); VerifyPolygons(sub.Subtract(square, clip7), expected7); VerifyPolygons(sub.Subtract(square, clip8), expected8); // Complete removal VerifyPolygons(sub.Subtract(smallSquare, square), empty); }
public Explosion(float x, float y, float radius, int numPoints) { Position = new Vector2(x, y); Radius = radius; List <Vector2> circle = new List <Vector2>(); float step = 2 * Mathf.PI / numPoints; for (int i = 0; i < numPoints; ++i) { float angle = i * step; circle.Add(new Vector2(x + radius * Mathf.Cos(angle), y + radius * Mathf.Sin(angle))); } DTPolygon = new DTPolygon(circle); }
protected void ApplyCollider(DTPolygon dtPolygon) { PolygonCollider2D polygonCollider = GetComponent <PolygonCollider2D>(); polygonCollider.pathCount = 1 + dtPolygon.Holes.Count; polygonCollider.SetPath(0, dtPolygon.Contour); for (int i = 0; i < dtPolygon.Holes.Count; i++) { polygonCollider.SetPath(i + 1, dtPolygon.Holes[i]); } if (GetComponent <Rigidbody2D>().mass < MassCutoff) { Destroy(gameObject); } }
public override void ApplyPolygonList(List <DTPolygon> dtPolygonList) { if (dtPolygon == dtPolygonList[0]) { return; } dtPolygon = dtPolygonList[0]; // Collider from polygon DTProfilerMarkers.ApplyCollider.Begin(); ApplyCollider(dtPolygon); DTProfilerMarkers.ApplyCollider.End(); // Create mesh from triangulated polygon DTProfilerMarkers.Triangulation.Begin(); DTMesh dtMesh = GetTriangulator().PolygonToMesh(dtPolygon); DTProfilerMarkers.Triangulation.End(); ApplyRenderMesh(dtMesh); }
public DTConvexPolygroup PolygonToTriangleList(DTPolygon subject) { // Mark any unmarked holes in the contour, otherwise Triangle.NET won't handle them properly subject = DTUtility.IdentifyHoles(subject); if (subject.Contour.Count < 3) { return(new DTConvexPolygroup()); } // Don't triangulate if this is already a triangle else if (subject.Contour.Count == 3 && subject.Holes.Count == 0) { return(new DTConvexPolygroup(new List <DTPolygon>() { subject })); } // Format polygon input and execute Polygon polygon = new Polygon(); polygon.Add(new Contour(subject.Contour.ToVertexList()), false); foreach (var hole in subject.Holes) { if (hole.Count >= 3) { try { polygon.Add(new Contour(hole.ToVertexList()), true); } catch (Exception) {} } } DTProfilerMarkers.TriangleNet.Begin(); IMesh triangleNetOutput = polygon.Triangulate(); ++callCount; DTProfilerMarkers.TriangleNet.End(); // Convert Triangle.NET output into poly group return(new DTConvexPolygroup(triangleNetOutput.Triangles.Select(t => t.ToVertexList()).ToList())); }
public DTMesh PolygonToMesh(DTPolygon subject) { return(PolygonToTriangleList(subject).ToMesh()); }
public DTConvexPolygroup PolygonToTriangleList(DTPolygon subject) { Triangulate_EC(subject.ToTPPLPolyList(), out TPPLPolyList triangles); return(triangles.ToPolygroup()); }
public static void ConvexCasesProducingHoles(IPolygonSubtractor sub) { // Diamond hole in center DTPolygon clip0 = smallDiamond; // Diamond holes touching edge DTPolygon clip1 = Translate(smallDiamond, V(0, -0.5f)); // bottom DTPolygon clip2 = Translate(smallDiamond, V(0.5f, 0)); // right DTPolygon clip3 = Translate(smallDiamond, V(0, 0.5f)); // top DTPolygon clip4 = Translate(smallDiamond, V(-0.5f, 0)); // left // Exact diamond clip (produces 4 triangles) DTPolygon clip5 = diamond; // Diamond hole in center List <DTPolygon> expected0 = L(new DTPolygon( square.Contour, L(smallDiamond.Contour.AsEnumerable().Reverse().ToList()) )); // Diamond holes touching edge List <DTPolygon> expected1 = L(P(V(-1, -1), V(0, -1), V(-0.5f, -0.5f), V(0, 0), V(0.5f, -0.5f), V(0, -1), V(1, -1), V(1, 1), V(-1, 1))); List <DTPolygon> expected1Hole = L(new DTPolygon( square.Contour, L(clip1.Contour.AsEnumerable().Reverse().ToList()) )); List <DTPolygon> expected2 = L(P(V(-1, -1), V(1, -1), V(1, 0), V(0.5f, -0.5f), V(0, 0), V(0.5f, 0.5f), V(1, 0), V(1, 1), V(-1, 1))); List <DTPolygon> expected2Hole = L(new DTPolygon( square.Contour, L(clip2.Contour.AsEnumerable().Reverse().ToList()) )); List <DTPolygon> expected3 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(0, 1), V(0.5f, 0.5f), V(0, 0), V(-0.5f, 0.5f), V(0, 1), V(-1, 1))); List <DTPolygon> expected3Hole = L(new DTPolygon( square.Contour, L(clip3.Contour.AsEnumerable().Reverse().ToList()) )); List <DTPolygon> expected4 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(-1, 1), V(-1, 0), V(-0.5f, 0.5f), V(0, 0), V(-0.5f, -0.5f), V(-1, 0))); List <DTPolygon> expected4Hole = L(new DTPolygon( square.Contour, L(clip4.Contour.AsEnumerable().Reverse().ToList()) )); // Exact diamond clip (produces 4 triangles) List <DTPolygon> expected5 = L( P(V(-1, 0), V(-1, -1), V(0, -1)), P(V(0, -1), V(1, -1), V(1, 0)), P(V(1, 0), V(1, 1), V(0, 1)), P(V(0, 1), V(-1, 1), V(-1, 0)) ); List <DTPolygon> expected5Hole = L(new DTPolygon( square.Contour, L(clip5.Contour.AsEnumerable().Reverse().ToList()) )); List <DTPolygon> expected5Single0 = L(P(V(-1, -1), V(0, -1), V(-1, 0), V(0, 1), V(1, 0), V(0, -1), V(1, -1), V(1, 1), V(-1, 1))); List <DTPolygon> expected5Single1 = L(P(V(-1, -1), V(1, -1), V(1, 0), V(0, -1), V(-1, 0), V(0, 1), V(1, 0), V(1, 1), V(-1, 1))); List <DTPolygon> expected5Single2 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(0, 1), V(1, 0), V(0, -1), V(-1, 0), V(0, 1), V(-1, 1))); List <DTPolygon> expected5Single3 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(-1, 1), V(-1, 0), V(0, 1), V(1, 0), V(0, -1), V(-1, 0))); VerifyPolygons(sub.Subtract(square, clip0), expected0); VerifyPolygons(sub.Subtract(square, clip1), expected1, expected1Hole); VerifyPolygons(sub.Subtract(square, clip2), expected2, expected2Hole); VerifyPolygons(sub.Subtract(square, clip3), expected3, expected3Hole); VerifyPolygons(sub.Subtract(square, clip4), expected4, expected4Hole); VerifyPolygons(sub.Subtract(square, clip5), expected5, expected5Hole, expected5Single0, expected5Single1, expected5Single2, expected5Single3); }
public static void ConvexDegenerateNoIntersection(IPolygonSubtractor sub) { // Half-overlapping edges DTPolygon clip0 = Translate(diamond, V(0.5f, -1.5f)); // bottom right edge shared, Q shifted down-left DTPolygon clip1 = Translate(diamond, V(1.5f, -0.5f)); // bottom right edge shared, Q shifted up-right DTPolygon clip2 = Translate(diamond, V(1.5f, 0.5f)); // top right edge shared, Q shifted down-right DTPolygon clip3 = Translate(diamond, V(0.5f, 1.5f)); // top right edge shared, Q shifted up-left DTPolygon clip4 = Translate(diamond, V(-0.5f, 1.5f)); // top left edge shared, Q shifted up-right DTPolygon clip5 = Translate(diamond, V(-1.5f, 0.5f)); // top left edge shared, Q shifted down-left DTPolygon clip6 = Translate(diamond, V(-1.5f, -0.5f)); // bottom left edge shared, Q shifted up-left DTPolygon clip7 = Translate(diamond, V(-0.5f, -1.5f)); // bottom left edge shared, Q shifted down-right // P edge completely inside Q edge DTPolygon clip8 = Translate(diamond, V(0.75f, -0.75f)); // bottom right edge inside Q edge DTPolygon clip9 = Translate(diamond, V(0.75f, 0.75f)); // top right edge inside Q edge DTPolygon clip10 = Translate(diamond, V(-0.75f, 0.75f)); // top left edge inside Q edge DTPolygon clip11 = Translate(diamond, V(-0.75f, -0.75f)); // bottom left edge inside Q edge // P edge completely contains Q edge DTPolygon clip12 = Translate(smallDiamond, V(0.75f, -0.75f)); // bottom right edge contains Q edge DTPolygon clip13 = Translate(smallDiamond, V(0.75f, 0.75f)); // top right edge contains Q edge DTPolygon clip14 = Translate(smallDiamond, V(-0.75f, 0.75f)); // top left edge contains Q edge DTPolygon clip15 = Translate(smallDiamond, V(-0.75f, -0.75f)); // bottom left edge contains Q edge // P edge and Q edge are the same DTPolygon clip16 = Translate(diamond, V(1, -1)); // bottom right edge same DTPolygon clip17 = Translate(diamond, V(1, 1)); // top right edge same DTPolygon clip18 = Translate(diamond, V(-1, 1)); // top left edge same DTPolygon clip19 = Translate(diamond, V(-1, -1)); // bottom left edge same // Single shared vertex (octagon) DTPolygon clip20 = Translate(octagon, V(-2, -2)); // octagon, bottom left vertex same DTPolygon clip21 = Translate(octagon, V(2, -2)); // octagon, bottom right vertex same DTPolygon clip22 = Translate(octagon, V(2, 2)); // octagon, top left vertex same DTPolygon clip23 = Translate(octagon, V(-2, 2)); // octagon, top right vertex same // Single shared vertex (diamond) DTPolygon clip24 = Translate(diamond, V(-1, -2)); // diamond top, square bottom left vertex same DTPolygon clip25 = Translate(diamond, V(1, -2)); // diamond top, square bottom right vertex same DTPolygon clip26 = Translate(diamond, V(-1, 2)); // diamond bottom, square top left vertex same DTPolygon clip27 = Translate(diamond, V(1, 2)); // diamond bottom, square top right vertex same // Q single vertex inside P edge DTPolygon clip28 = Translate(square, V(1.5f, -1.5f)); // bottom right edge contains Q vertex DTPolygon clip29 = Translate(square, V(1.5f, 1.5f)); // top right edge contains Q vertex DTPolygon clip30 = Translate(square, V(-1.5f, 1.5f)); // top left edge contains Q vertex DTPolygon clip31 = Translate(square, V(-1.5f, -1.5f)); // bottom left edge contains Q vertex // P single vertex inside Q edge DTPolygon clip32 = Translate(diamond, V(-1.5f, -1.5f)); // bottom left vertex in Q edge DTPolygon clip33 = Translate(diamond, V(1.5f, -1.5f)); // bottom right vertex in Q edge DTPolygon clip34 = Translate(diamond, V(1.5f, 1.5f)); // top right vertex in Q edge DTPolygon clip35 = Translate(diamond, V(-1.5f, 1.5f)); // top left vertex in Q edge List <DTPolygon> expectedSquare = L(square); List <DTPolygon> expectedDiamond = L(diamond); List <DTPolygon> expectedSmallDiamond = L(smallDiamond); List <DTPolygon> expectedOctagon = L(octagon); VerifyPolygons(sub.Subtract(diamond, clip0), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip1), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip2), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip3), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip4), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip5), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip6), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip7), expectedDiamond); VerifyPolygons(sub.Subtract(smallDiamond, clip8), expectedSmallDiamond); VerifyPolygons(sub.Subtract(smallDiamond, clip9), expectedSmallDiamond); VerifyPolygons(sub.Subtract(smallDiamond, clip10), expectedSmallDiamond); VerifyPolygons(sub.Subtract(smallDiamond, clip11), expectedSmallDiamond); VerifyPolygons(sub.Subtract(diamond, clip12), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip13), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip14), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip15), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip16), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip17), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip18), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip19), expectedDiamond); VerifyPolygons(sub.Subtract(octagon, clip20), expectedOctagon); VerifyPolygons(sub.Subtract(octagon, clip21), expectedOctagon); VerifyPolygons(sub.Subtract(octagon, clip22), expectedOctagon); VerifyPolygons(sub.Subtract(octagon, clip23), expectedOctagon); VerifyPolygons(sub.Subtract(octagon, clip24), expectedOctagon); VerifyPolygons(sub.Subtract(octagon, clip25), expectedOctagon); VerifyPolygons(sub.Subtract(octagon, clip26), expectedOctagon); VerifyPolygons(sub.Subtract(octagon, clip27), expectedOctagon); VerifyPolygons(sub.Subtract(diamond, clip28), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip29), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip30), expectedDiamond); VerifyPolygons(sub.Subtract(diamond, clip31), expectedDiamond); VerifyPolygons(sub.Subtract(octagon, clip32), expectedOctagon); VerifyPolygons(sub.Subtract(octagon, clip33), expectedOctagon); VerifyPolygons(sub.Subtract(octagon, clip34), expectedOctagon); VerifyPolygons(sub.Subtract(octagon, clip35), expectedOctagon); }
public static void ConvexDegenerateIntersection(IPolygonSubtractor sub) { // Shared corners (produces an 'L' shape) DTPolygon clip0 = Translate(smallSquare, V(-0.5f, -0.5f)); // bottom left DTPolygon clip1 = Translate(smallSquare, V(0.5f, -0.5f)); // bottom right DTPolygon clip2 = Translate(smallSquare, V(0.5f, 0.5f)); // top right DTPolygon clip3 = Translate(smallSquare, V(-0.5f, 0.5f)); // top left // Q edge inside P edge (produces a 'C' shape) DTPolygon clip4 = Translate(smallSquare, V(0, -0.5f)); // bottom DTPolygon clip5 = Translate(smallSquare, V(0.5f, 0)); // right DTPolygon clip6 = Translate(smallSquare, V(0, 0.5f)); // top DTPolygon clip7 = Translate(smallSquare, V(-0.5f, 0)); // left // Half same DTPolygon clip8 = Translate(shortRect, V(0, -0.5f)); // bottom half DTPolygon clip9 = Translate(narrowRect, V(0.5f, 0)); // right half DTPolygon clip10 = Translate(shortRect, V(0, 0.5f)); // top half DTPolygon clip11 = Translate(narrowRect, V(-0.5f, 0)); // left half // Diamond on corners DTPolygon clip12 = Translate(diamond, V(-1, -1)); // bottom left DTPolygon clip13 = Translate(diamond, V(1, -1)); // bottom right DTPolygon clip14 = Translate(diamond, V(1, 1)); // top right DTPolygon clip15 = Translate(diamond, V(-1, 1)); // top left // Diamond on edges DTPolygon clip16 = Translate(diamond, V(0, -1)); // bottom DTPolygon clip17 = Translate(diamond, V(1, 0)); // right DTPolygon clip18 = Translate(diamond, V(0, 1)); // top DTPolygon clip19 = Translate(diamond, V(-1, 0)); // left // Basic corner clips (produces an 'L' shape) List <DTPolygon> expected0 = L(P(V(0, 0), V(0, -1), V(1, -1), V(1, 1), V(-1, 1), V(-1, 0))); List <DTPolygon> expected1 = L(P(V(-1, -1), V(0, -1), V(0, 0), V(1, 0), V(1, 1), V(-1, 1))); List <DTPolygon> expected2 = L(P(V(-1, -1), V(1, -1), V(1, 0), V(0, 0), V(0, 1), V(-1, 1))); List <DTPolygon> expected3 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(0, 1), V(0, 0), V(-1, 0))); // Basic edge clips (produces a 'C' shape) List <DTPolygon> expected4 = L(P(V(-1, -1), V(-0.5f, -1), V(-0.5f, 0), V(0.5f, 0), V(0.5f, -1), V(1, -1), V(1, 1), V(-1, 1))); List <DTPolygon> expected5 = L(P(V(-1, -1), V(1, -1), V(1, -0.5f), V(0, -0.5f), V(0, 0.5f), V(1, 0.5f), V(1, 1), V(-1, 1))); List <DTPolygon> expected6 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(0.5f, 1), V(0.5f, 0), V(-0.5f, 0), V(-0.5f, 1), V(-1, 1))); List <DTPolygon> expected7 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(-1, 1), V(-1, 0.5f), V(0, 0.5f), V(0, -0.5f), V(-1, -0.5f))); // Half same List <DTPolygon> expected8 = L(P(V(-1, 0), V(1, 0), V(1, 1), V(-1, 1))); List <DTPolygon> expected9 = L(P(V(-1, -1), V(0, -1), V(0, 1), V(-1, 1))); List <DTPolygon> expected10 = L(P(V(-1, -1), V(1, -1), V(1, 0), V(-1, 0))); List <DTPolygon> expected11 = L(P(V(0, -1), V(1, -1), V(1, 1), V(0, 1))); // Diamond on corners List <DTPolygon> expected12 = L(P(V(0, -1), V(1, -1), V(1, 1), V(-1, 1), V(-1, 0))); List <DTPolygon> expected13 = L(P(V(-1, -1), V(0, -1), V(1, 0), V(1, 1), V(-1, 1))); List <DTPolygon> expected14 = L(P(V(-1, -1), V(1, -1), V(1, 0), V(0, 1), V(-1, 1))); List <DTPolygon> expected15 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(0, 1), V(-1, 0))); // Diamond on edges List <DTPolygon> expected16 = L(P(V(-1, -1), V(0, 0), V(1, -1), V(1, 1), V(-1, 1))); List <DTPolygon> expected17 = L(P(V(-1, -1), V(1, -1), V(0, 0), V(1, 1), V(-1, 1))); List <DTPolygon> expected18 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(0, 0), V(-1, 1))); List <DTPolygon> expected19 = L(P(V(-1, -1), V(1, -1), V(1, 1), V(-1, 1), V(0, 0))); // Complete removal List <DTPolygon> empty = new List <DTPolygon>(); // Complete removal VerifyPolygons(sub.Subtract(square, square), empty); VerifyPolygons(sub.Subtract(square, clip0), expected0); VerifyPolygons(sub.Subtract(square, clip1), expected1); VerifyPolygons(sub.Subtract(square, clip2), expected2); VerifyPolygons(sub.Subtract(square, clip3), expected3); VerifyPolygons(sub.Subtract(square, clip4), expected4); VerifyPolygons(sub.Subtract(square, clip5), expected5); VerifyPolygons(sub.Subtract(square, clip6), expected6); VerifyPolygons(sub.Subtract(square, clip7), expected7); VerifyPolygons(sub.Subtract(square, clip8), expected8); VerifyPolygons(sub.Subtract(square, clip9), expected9); VerifyPolygons(sub.Subtract(square, clip10), expected10); VerifyPolygons(sub.Subtract(square, clip11), expected11); VerifyPolygons(sub.Subtract(square, clip12), expected12); VerifyPolygons(sub.Subtract(square, clip13), expected13); VerifyPolygons(sub.Subtract(square, clip14), expected14); VerifyPolygons(sub.Subtract(square, clip15), expected15); VerifyPolygons(sub.Subtract(square, clip16), expected16); VerifyPolygons(sub.Subtract(square, clip17), expected17); VerifyPolygons(sub.Subtract(square, clip18), expected18); VerifyPolygons(sub.Subtract(square, clip19), expected19); }
public static DTPolygon IdentifyHoles(this DTPolygon inPoly) { DTProfilerMarkers.IdentifyHoles.Begin(); List <Vector2> workingContour = new List <Vector2>(inPoly.Contour); List <List <Vector2> > loops = new List <List <Vector2> >(); // Repeat until all loops are found while (workingContour.Count > 0) { // Simplify contour to remove edges that overlap the previous edge workingContour = workingContour.Simplify(); if (workingContour == null) { break; } Dictionary <Vector2, int> vertices = new Dictionary <Vector2, int>(new ApproximateVector2Comparer()); for (int i = 0; i < workingContour.Count; ++i) { Vector2 v = workingContour[i]; // If this is a repeated vertex, we've found a loop if (vertices.ContainsKey(v)) { // Separate this loop from the working contour int start = vertices[v]; int count = i - start; loops.Add(workingContour.GetRange(start, count)); workingContour.RemoveRange(start, count); vertices[v] = i; // Stop so that we can simplify the polygon and start from the beginning to find the next loop break; } vertices[v] = i; // Add the final remaining loop if (vertices.Count == workingContour.Count) { if (loops.Count == 0) { // If there were no other loops, then the contour does not contain unidentified holes DTProfilerMarkers.IdentifyHoles.End(); return(inPoly); } loops.Add(workingContour); workingContour = new List <Vector2>(); } } } // Start with holes already in the original polygon DTPolygon outPoly = new DTPolygon(); foreach (var hole in inPoly.Holes) { outPoly.Holes.Add(hole); } // We now have a list of loops. Check which ones are inside the others, and mark those as holes as well. for (int i = 0; i < loops.Count; ++i) { if (loops[i] == null) { continue; } for (int j = i + 1; j < loops.Count; ++j) { if (loops[j] == null) { continue; } if (QuickPolyInPoly(loops[i], loops[j])) { loops[i].SetCW(); outPoly.Holes.Add(loops[i]); loops[i] = null; break; } else if (QuickPolyInPoly(loops[j], loops[i])) { loops[j].SetCW(); outPoly.Holes.Add(loops[j]); loops[j] = null; break; } } } // Join any remaining loops and make the result the contour of the new polygon for (int i = 1; i < loops.Count; ++i) { if (loops[i] != null) { loops[i].SetCCW(); outPoly.Contour = JoinContours(outPoly.Contour, loops[i]); } } DTProfilerMarkers.IdentifyHoles.End(); return(outPoly); }
// Returns true if the contour is convex and there are no holes. Flat angles are allowed. public static bool IsConvex(this DTPolygon poly) { return(poly.Holes.Count == 0 && poly.Contour.IsConvex()); }
public List <DTPolygon> Subtract(DTPolygon subject, DTPolygon clippingPolygon) { SubtractInternal(subject.Contour, clippingPolygon.Contour, out List <DTPolygon> outputPolygons); return(outputPolygons); }
// Returns true if the subject polygon was modified at all private bool SubtractInternal(List <Vector2> subject, List <Vector2> clippingPolygon, out List <DTPolygon> outputPolygons) { if (!DTUtility.BoundsCheck(subject, clippingPolygon)) { // There is no overlap at all, so output a copy of the subject polygon outputPolygons = new List <DTPolygon>() { new DTPolygon(new List <Vector2>(subject)) }; return(false); } polyP = subject; polyQ = clippingPolygon; pIndex = 0; qIndex = 0; // The first entry intersection point of the first output polgon. If we return to this point, break the loop bool firstIntersectionFound = false; int? firstIntersectionPIndex = null; int? firstIntersectionQIndex = null; // List of disjoint output polygons after subtraction outputPolygons = new List <DTPolygon>(); // Working output polygon vertices polygonBegin = null; List <Vector2> pVertices = new List <Vector2>(); List <Vector2> qVertices = new List <Vector2>(); for (int i = 0; i <= 2 * (polyP.Count + polyQ.Count); ++i) { if (polygonBegin.HasValue) { Vector2?exitIntersection = ExitIntersection(); if (exitIntersection.HasValue) { // Exit intersection point for output polygon: construct output polygon DTPolygon poly = new DTPolygon(); poly.Contour.Add(polygonBegin.Value); poly.Contour.AddRange(pVertices); poly.Contour.Add(exitIntersection.Value); poly.Contour.AddRange(qVertices.AsEnumerable().Reverse()); // Simplify polygon poly = poly.Simplify(); if (poly != null) { outputPolygons.Add(poly); } // Clear working polygon vertices polygonBegin = null; pVertices.Clear(); qVertices.Clear(); } } else { Vector2?entranceIntersection = EntranceIntersection(); if (entranceIntersection.HasValue) { // Loop exit condition: revisiting first intersection if (firstIntersectionFound && ModP(pIndex) == ModP(firstIntersectionPIndex.Value) && ModQ(qIndex) == ModQ(firstIntersectionQIndex.Value)) { break; } // Entry intersection point for output polygon polygonBegin = entranceIntersection.Value; pVertices.Clear(); qVertices.Clear(); // Keep track of this point if it is the entry intersection for the 1st output polygon if (!firstIntersectionFound) { firstIntersectionFound = true; firstIntersectionPIndex = pIndex; firstIntersectionQIndex = qIndex; } } } void advanceP() { if (polygonBegin.HasValue) { pVertices.Add(P); } ++pIndex; } void advanceQ() { if (polygonBegin.HasValue) { qVertices.Add(Q); } ++qIndex; } float pSide = (P - QPrev).Cross(QEdge); float qSide = (Q - PPrev).Cross(PEdge); float cross = QEdge.Cross(PEdge); if (cross <= 0) { // QEdge heading inward if (qSide < 0) { // Q inside P's half-plane, heading away from PEdge advanceP(); } else { // Q outside P's half-plane or on P's line, heading toward PEdge advanceQ(); } } else { // QEdge heading outward if (pSide < 0) { // P inside Q's half-plane, heading away from QEdge advanceQ(); } else { // P outside Q's half-plane or on Q's line, heading toward QEdge advanceP(); } } } // There were no intersections, so either one poly is entirely contained within the other, or there is no overlap at all if (outputPolygons.Count == 0) { if (polyP.Inside(polyQ)) { // P is entirely within Q, so do nothing. The entire polygon has been subtracted return(true); } else if (polyQ.Inside(polyP)) { // Q is entirely within P, so output a copy of P, with Q (reversed) set as a hole outputPolygons.Add(new DTPolygon( new List <Vector2>(polyP), new List <List <Vector2> >() { polyQ.AsEnumerable().Reverse().ToList() })); return(true); } else { if (polyP.Simplify() == polyQ.Simplify()) { // The polygons are equal, so do nothing. The entire polygon has been subtracted return(true); } else { // There is no overlap at all, so output a copy of P outputPolygons.Add(new DTPolygon(new List <Vector2>(polyP))); return(false); } } } return(true); }