private static Vector2 getCollisionPointClosest(PolygonSet p1, PolygonSet p2, Vector2 point) { Clipper clipper = new Clipper(); PolygonSet result = new PolygonSet(); clipper.AddPaths(p1, PolyType.ptClip, true); clipper.AddPaths(p2, PolyType.ptSubject, true); bool succeeded = clipper.Execute(ClipType.ctIntersection, result, PolyFillType.pftPositive, PolyFillType.pftPositive); clipper.Clear(); return getClosestPoint(result, new Vector2(point.X,point.Y)); }
//--------------------------------------------------------------------------- private void DrawBitmap(bool justClip = false) { if (!justClip) { if (rbTest2.Checked) GenerateAustPlusRandomEllipses((int)nudCount.Value); else GenerateRandomPolygon((int)nudCount.Value); } Cursor.Current = Cursors.WaitCursor; Graphics newgraphic; newgraphic = Graphics.FromImage(mybitmap); newgraphic.SmoothingMode = SmoothingMode.AntiAlias; newgraphic.Clear(Color.White); GraphicsPath path = new GraphicsPath(); if (rbNonZero.Checked) path.FillMode = FillMode.Winding; //draw subjects ... foreach (Polygon pg in subjects) { PointF[] pts = PolygonToPointFArray(pg, scale); path.AddPolygon(pts); pts = null; } Pen myPen = new Pen(Color.FromArgb(196, 0xC3, 0xC9, 0xCF), (float)0.6); SolidBrush myBrush = new SolidBrush(Color.FromArgb(127, 0xDD, 0xDD, 0xF0)); newgraphic.FillPath(myBrush, path); newgraphic.DrawPath(myPen, path); path.Reset(); //draw clips ... if (rbNonZero.Checked) path.FillMode = FillMode.Winding; foreach (Polygon pg in clips) { PointF[] pts = PolygonToPointFArray(pg, scale); path.AddPolygon(pts); pts = null; } myPen.Color = Color.FromArgb(196, 0xF9, 0xBE, 0xA6); myBrush.Color = Color.FromArgb(127, 0xFF, 0xE0, 0xE0); newgraphic.FillPath(myBrush, path); newgraphic.DrawPath(myPen, path); //do the clipping ... if ((clips.Count > 0 || subjects.Count > 0) && !rbNone.Checked) { Polygons solution2 = new Polygons(); Clipper c = new Clipper(); c.AddPolygons(subjects, PolyType.ptSubject); c.AddPolygons(clips, PolyType.ptClip); exSolution.Clear(); solution.Clear(); bool succeeded = c.Execute(GetClipType(), solution, GetPolyFillType(), GetPolyFillType()); if (succeeded) { myBrush.Color = Color.Black; path.Reset(); //It really shouldn't matter what FillMode is used for solution //polygons because none of the solution polygons overlap. //However, FillMode.Winding will show any orientation errors where //holes will be stroked (outlined) correctly but filled incorrectly ... path.FillMode = FillMode.Winding; //or for something fancy ... if (nudOffset.Value != 0) solution2 = Clipper.OffsetPolygons(solution, (double)nudOffset.Value * scale, JoinType.jtMiter); else solution2 = new Polygons(solution); foreach (Polygon pg in solution2) { PointF[] pts = PolygonToPointFArray(pg, scale); if (pts.Count() > 2) path.AddPolygon(pts); pts = null; } myBrush.Color = Color.FromArgb(127, 0x66, 0xEF, 0x7F); myPen.Color = Color.FromArgb(255, 0, 0x33, 0); myPen.Width = 1.0f; newgraphic.FillPath(myBrush, path); newgraphic.DrawPath(myPen, path); //now do some fancy testing ... Font f = new Font("Arial", 8); SolidBrush b = new SolidBrush(Color.Navy); double subj_area = 0, clip_area = 0, int_area = 0, union_area = 0; c.Clear(); c.AddPolygons(subjects, PolyType.ptSubject); c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) subj_area += Clipper.Area(pg); c.Clear(); c.AddPolygons(clips, PolyType.ptClip); c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) clip_area += Clipper.Area(pg); c.AddPolygons(subjects, PolyType.ptSubject); c.Execute(ClipType.ctIntersection, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) int_area += Clipper.Area(pg); c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) union_area += Clipper.Area(pg); StringFormat lftStringFormat = new StringFormat(); lftStringFormat.Alignment = StringAlignment.Near; lftStringFormat.LineAlignment = StringAlignment.Near; StringFormat rtStringFormat = new StringFormat(); rtStringFormat.Alignment = StringAlignment.Far; rtStringFormat.LineAlignment = StringAlignment.Near; Rectangle rec = new Rectangle(pictureBox1.ClientSize.Width - 108, pictureBox1.ClientSize.Height - 116, 104, 106); newgraphic.FillRectangle(new SolidBrush(Color.FromArgb(196, Color.WhiteSmoke)), rec); newgraphic.DrawRectangle(myPen, rec); rec.Inflate(new Size(-2, 0)); newgraphic.DrawString("Areas", f, b, rec, rtStringFormat); rec.Offset(new Point(0, 14)); newgraphic.DrawString("subj: ", f, b, rec, lftStringFormat); newgraphic.DrawString((subj_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 12)); newgraphic.DrawString("clip: ", f, b, rec, lftStringFormat); newgraphic.DrawString((clip_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 12)); newgraphic.DrawString("intersect: ", f, b, rec, lftStringFormat); newgraphic.DrawString((int_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 12)); newgraphic.DrawString("---------", f, b, rec, rtStringFormat); rec.Offset(new Point(0, 10)); newgraphic.DrawString("s + c - i: ", f, b, rec, lftStringFormat); newgraphic.DrawString(((subj_area + clip_area - int_area) / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 10)); newgraphic.DrawString("---------", f, b, rec, rtStringFormat); rec.Offset(new Point(0, 10)); newgraphic.DrawString("union: ", f, b, rec, lftStringFormat); newgraphic.DrawString((union_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 10)); newgraphic.DrawString("---------", f, b, rec, rtStringFormat); } //end if succeeded } //end if something to clip pictureBox1.Image = mybitmap; newgraphic.Dispose(); Cursor.Current = Cursors.Default; }
private PolyTree GetPolyTree(IEnumerable<LineStrip> lines, PolyFillType pft) { Paths polygons = new Paths(); Clipper c = new Clipper(); c.Clear(); foreach (var line in lines) { polygons.Add(LineStripToPolygon(line)); } polygons = Clipper.SimplifyPolygons(polygons, pft); c.AddPaths(polygons, PolyType.ptSubject, true); PolyTree tree = new PolyTree(); c.Execute(ClipType.ctUnion, tree); return tree; }
private PolyTree PolygonsToPolyTree(Paths polygons) { var tree = new PolyTree(); Clipper c = new Clipper(); c.Clear(); c.AddPaths(polygons, PolyType.ptSubject, true); c.Execute(ClipType.ctUnion, tree); return tree; }
public void SubtractFrom(Slice other) { Clipper c = new Clipper(); c.Clear(); c.AddPaths(PolyTreeToPolygons(other.polyTree), PolyType.ptSubject, true); c.AddPaths(PolyTreeToPolygons(polyTree), PolyType.ptClip, true); polyTree = new PolyTree(); c.Execute(ClipType.ctDifference, polyTree); }
public void Union(Slice other) { Clipper c = new Clipper(); c.Clear(); c.AddPaths(Clipper.PolyTreeToPaths(polyTree), PolyType.ptSubject, true); c.AddPaths(Clipper.PolyTreeToPaths(other.polyTree), PolyType.ptClip, true); polyTree = new PolyTree(); c.Execute(ClipType.ctUnion, polyTree); }
public bool Contains(Slice other) { // To contain another slice: // 1. Area of the union must be the same // 2. Area of this - other must be less float thisArea = this.Area(); Paths otherPolygons = Clipper.PolyTreeToPaths(other.polyTree); Paths thesePolygons = Clipper.PolyTreeToPaths(polyTree); Slice s = new Slice(this); Clipper c = new Clipper(); c.Clear(); c.AddPaths(thesePolygons, PolyType.ptSubject, true); c.AddPaths(otherPolygons, PolyType.ptClip, true); s.polyTree = new PolyTree(); c.Execute(ClipType.ctUnion, s.polyTree); float area_union = s.Area(); if (area_union > thisArea) { return false; } return true; //c.Clear(); //c.AddPaths(thesePolygons, PolyType.ptSubject, true); //c.AddPaths(otherPolygons, PolyType.ptClip, true); //s.polyTree = new PolyTree(); //c.Execute(ClipType.ctDifference, s.polyTree); //float area_difference = s.Area(); //if (area_difference < thisArea) //{ // return true; //} //return false; }
public void RemoveHoles(float maxPerimiter) { Paths keep = new Paths(); PolyNode node = polyTree.GetFirst(); while (node != null) { if (node.IsHole && node.ChildCount == 0) { var line = LineStripFromPolygon(node.Contour); float length = line.Length(LineStrip.Type.Closed); if (length < maxPerimiter) { // Remove it } else { keep.Add(node.Contour); } } else { keep.Add(node.Contour); } node = node.GetNext(); } Clipper c = new Clipper(); c.Clear(); c.AddPaths(keep, PolyType.ptSubject, true); polyTree = new PolyTree(); c.Execute(ClipType.ctUnion, polyTree); }
PushPolygonToArray(List<List<IntPoint>> subjects, List<List<IntPoint>> clips) { Polygons nextClip = new Polygons(); Polygons currentSubject = new Polygons(); Polygons currentClip = new Polygons(); Polygons currentArrayDraw = new Polygons(); Polygons currentArrayInvisible = new Polygons(); Clipper cTupleDraw = new Clipper(); Clipper cTupleInvisible = new Clipper(); //find polygons to be drawn currentSubject.Clear(); currentClip.Clear(); currentSubject = subjects; currentClip = clips; cTupleDraw.Clear(); cTupleDraw.AddPaths(currentSubject, PolyType.ptSubject, true); cTupleDraw.AddPaths(currentClip, PolyType.ptClip, true); currentArrayDraw.Clear(); bool succeededDraw = cTupleDraw.Execute(ClipType.ctDifference, currentArrayDraw, PolyFillType.pftNonZero, PolyFillType.pftNonZero); //find next clipped polygon nextClip.Clear(); bool succeededNextClip = cTupleDraw.Execute(ClipType.ctUnion, nextClip, PolyFillType.pftNonZero, PolyFillType.pftNonZero); //find invisible polygons cTupleInvisible.Clear(); cTupleInvisible.AddPaths(currentSubject, PolyType.ptSubject, true); cTupleInvisible.AddPaths(currentClip, PolyType.ptClip, true); currentArrayInvisible.Clear(); bool succeededInvisible = cTupleInvisible.Execute(ClipType.ctIntersection, currentArrayInvisible, PolyFillType.pftNonZero, PolyFillType.pftNonZero); return new Tuple<List<List<IntPoint>>, List<List<IntPoint>>, List<List<IntPoint>>> (currentArrayDraw, currentArrayInvisible, nextClip); }
//--------------------------------------------------------------------------- private void DrawBitmap(bool justClip = false) { Cursor.Current = Cursors.WaitCursor; try { if (!justClip) { //GenerateRandomPolygon(10); //GenerateTestPolygon(4); } using (Graphics newgraphic = Graphics.FromImage(mybitmap)) using (GraphicsPath path = new GraphicsPath()) { newgraphic.SmoothingMode = SmoothingMode.AntiAlias; newgraphic.Clear(Color.White); if (rbNonZero.Checked) path.FillMode = FillMode.Winding; //draw subjects ... foreach (Polygon pg in subjects) { PointF[] pts = PolygonToPointFArray(pg, scale); path.AddPolygon(pts); pts = null; } using (Pen myPen = new Pen(Color.FromArgb(196, 0xC3, 0xC9, 0xCF), (float)0.6)) using (SolidBrush myBrush = new SolidBrush(Color.FromArgb(127, 0xDD, 0xDD, 0xF0))) { newgraphic.FillPath(myBrush, path); newgraphic.DrawPath(myPen, path); path.Reset(); //draw clips ... if (rbNonZero.Checked) path.FillMode = FillMode.Winding; foreach (Polygon pg in clips) { PointF[] pts = PolygonToPointFArray(pg, scale); path.AddPolygon(pts); pts = null; } myPen.Color = Color.FromArgb(196, 0xF9, 0xBE, 0xA6); myBrush.Color = Color.FromArgb(127, 0xFF, 0xE0, 0xE0); newgraphic.FillPath(myBrush, path); newgraphic.DrawPath(myPen, path); //do the clipping ... if ((clips.Count > 0 || subjects.Count > 0) && !rbNone.Checked) { Polygons solution2 = new Polygons(); Clipper c = new Clipper(); c.AddPaths(subjects, PolyType.ptSubject, true); c.AddPaths(clips, PolyType.ptClip, true); solution.Clear(); //my codes ... bool succeeded = c.Execute(GetClipType(), solution, GetPolyFillType(), GetPolyFillType()); PrintVertice(solution); //if (rbIntersect.Checked) // Console.WriteLine("****END OF INTERSECT****"); //if (rbUnion.Checked) // Console.WriteLine("****END OF UNION****"); //if (rbDifference.Checked) // Console.WriteLine("****END OF DIFFERENCE****"); //else // Console.WriteLine("****END OF XOR****"); Tuple<List<List<IntPoint>>, List<List<IntPoint>>, List<List<IntPoint>>> testTuple; testTuple = PushPolygonToArray(subjects, clips); //my codes ... if (succeeded) { //SaveToFile("solution", solution); myBrush.Color = Color.Black; path.Reset(); //It really shouldn't matter what FillMode is used for solution //polygons because none of the solution polygons overlap. //However, FillMode.Winding will show any orientation errors where //holes will be stroked (outlined) correctly but filled incorrectly ... path.FillMode = FillMode.Winding; //or for something fancy ... if (nudOffset.Value != 0) { ClipperOffset co = new ClipperOffset(); co.AddPaths(solution, JoinType.jtRound, EndType.etClosedPolygon); co.Execute(ref solution2, (double)nudOffset.Value * scale); } else solution2 = new Polygons(solution); foreach (Polygon pg in solution2) { PointF[] pts = PolygonToPointFArray(pg, scale); if (pts.Count() > 2) path.AddPolygon(pts); pts = null; } myBrush.Color = Color.FromArgb(127, 0x66, 0xEF, 0x7F); myPen.Color = Color.FromArgb(255, 0, 0x33, 0); myPen.Width = 1.0f; newgraphic.FillPath(myBrush, path); newgraphic.DrawPath(myPen, path); //now do some fancy testing ... using (Font f = new Font("Arial", 8)) using (SolidBrush b = new SolidBrush(Color.Navy)) { double subj_area = 0, clip_area = 0, int_area = 0, union_area = 0; c.Clear(); c.AddPaths(subjects, PolyType.ptSubject, true); c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) subj_area += Clipper.Area(pg); c.Clear(); c.AddPaths(clips, PolyType.ptClip, true); c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) clip_area += Clipper.Area(pg); c.AddPaths(subjects, PolyType.ptSubject, true); c.Execute(ClipType.ctIntersection, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) int_area += Clipper.Area(pg); c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) union_area += Clipper.Area(pg); using (StringFormat lftStringFormat = new StringFormat()) using (StringFormat rtStringFormat = new StringFormat()) { lftStringFormat.Alignment = StringAlignment.Near; lftStringFormat.LineAlignment = StringAlignment.Near; rtStringFormat.Alignment = StringAlignment.Far; rtStringFormat.LineAlignment = StringAlignment.Near; Rectangle rec = new Rectangle(pictureBox1.ClientSize.Width - 108, pictureBox1.ClientSize.Height - 116, 104, 106); newgraphic.FillRectangle(new SolidBrush(Color.FromArgb(196, Color.WhiteSmoke)), rec); newgraphic.DrawRectangle(myPen, rec); rec.Inflate(new Size(-2, 0)); newgraphic.DrawString("Areas", f, b, rec, rtStringFormat); rec.Offset(new Point(0, 14)); newgraphic.DrawString("subj: ", f, b, rec, lftStringFormat); newgraphic.DrawString((subj_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 12)); newgraphic.DrawString("clip: ", f, b, rec, lftStringFormat); newgraphic.DrawString((clip_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 12)); newgraphic.DrawString("intersect: ", f, b, rec, lftStringFormat); newgraphic.DrawString((int_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 12)); newgraphic.DrawString("---------", f, b, rec, rtStringFormat); rec.Offset(new Point(0, 10)); newgraphic.DrawString("s + c - i: ", f, b, rec, lftStringFormat); newgraphic.DrawString(((subj_area + clip_area - int_area) / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 10)); newgraphic.DrawString("---------", f, b, rec, rtStringFormat); rec.Offset(new Point(0, 10)); newgraphic.DrawString("union: ", f, b, rec, lftStringFormat); newgraphic.DrawString((union_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new Point(0, 10)); newgraphic.DrawString("---------", f, b, rec, rtStringFormat); } } } //end if succeeded } //end if something to clip pictureBox1.Image = mybitmap; } } } finally { Cursor.Current = Cursors.Default; } }
private static bool CalculatePortalViews(IPortal portal, IPortal portalEnter, IList<IPortal> portals, Matrix4 viewMatrix, Vector2 viewPos, Vector2 viewPosPrevious, PortalView portalView, Matrix4 portalMatrix, List<Func<bool>> actionList) { const float AREA_EPSILON = 0.0001f; Clipper c = new Clipper(); //The clipper must be set to strictly simple. Otherwise polygons might have duplicate vertices which causes poly2tri to generate incorrect results. c.StrictlySimple = true; if (!_isPortalValid(portalEnter, portal, viewPos)) { return false; } Vector2[] fov = Vector2Ext.Transform(Portal.GetFov(portal, viewPos, 500, 3), portalMatrix); if (MathExt.GetArea(fov) < AREA_EPSILON) { return false; } List<IntPoint> pathFov = ClipperConvert.ToIntPoint(fov); var viewNew = new List<List<IntPoint>>(); c.AddPath(pathFov, PolyType.ptSubject, true); c.AddPaths(portalView.Paths, PolyType.ptClip, true); c.Execute(ClipType.ctIntersection, viewNew); c.Clear(); if (viewNew.Count <= 0) { return false; } c.AddPaths(viewNew, PolyType.ptSubject, true); foreach (IPortal other in portals) { if (other == portal) { continue; } if (!_isPortalValid(portalEnter, other, viewPos)) { continue; } //Skip this portal if it's inside the current portal's FOV. LineF portalLine = new LineF(Portal.GetWorldVerts(portal)); LineF portalOtherLine = new LineF(Portal.GetWorldVerts(other)); if (portalLine.IsInsideFOV(viewPos, portalOtherLine)) { continue; } Vector2[] otherFov = Vector2Ext.Transform(Portal.GetFov(other, viewPos, 500, 3), portalMatrix); if (MathExt.GetArea(otherFov) < AREA_EPSILON) { continue; } otherFov = MathExt.SetWinding(otherFov, true); List<IntPoint> otherPathFov = ClipperConvert.ToIntPoint(otherFov); c.AddPath(otherPathFov, PolyType.ptClip, true); } var viewNewer = new List<List<IntPoint>>(); c.Execute(ClipType.ctDifference, viewNewer, PolyFillType.pftNonZero, PolyFillType.pftNonZero); c.Clear(); if (viewNewer.Count <= 0) { return false; } Vector2 viewPosNew = Vector2Ext.Transform(viewPos, Portal.GetLinkedMatrix(portal, portal.Linked)); Vector2 viewPosPreviousNew = Vector2Ext.Transform(viewPosPrevious, Portal.GetLinkedMatrix(portal, portal.Linked)); Matrix4 portalMatrixNew = Portal.GetLinkedMatrix(portal.Linked, portal) * portalMatrix; Matrix4 viewMatrixNew = portalMatrixNew * viewMatrix; LineF[] lines = Portal.GetFovLines(portal, viewPos, 500); lines[0] = lines[0].Transform(portalMatrix); lines[1] = lines[1].Transform(portalMatrix); LineF[] linesPrevious = Portal.GetFovLines(portal, viewPosPrevious, 500); linesPrevious[0] = linesPrevious[0].Transform(portalMatrix); linesPrevious[1] = linesPrevious[1].Transform(portalMatrix); LineF portalWorldLine = new LineF(Portal.GetWorldVerts(portal)); portalWorldLine = portalWorldLine.Transform(portalMatrix); PortalView portalViewNew = new PortalView(portalView, viewMatrixNew, viewNewer, lines, linesPrevious, portalWorldLine); foreach (IPortal p in portals) { actionList.Add(() => CalculatePortalViews(p, portal, portals, viewMatrix, viewPosNew, viewPosPreviousNew, portalViewNew, portalMatrixNew, actionList) ); } return true; }
/// <summary> /// Returns a list of fixtures and their shapes with regions inside portals clipped away. /// This excludes portal fixtures and the shapes are in world coordinates. /// </summary> /// <param name="body"></param> /// <returns></returns> private static List<Tuple<Fixture, Vector2[]>> GetClippedFixtures(Body body) { BodyData data = GetData(body); /* If this body isn't colliding with any portals then we just return a list of * fixtures and vertices.*/ if (data.PortalCollisions().Count <= 0) { List<Tuple<Fixture, Vector2[]>> fixtures = new List<Tuple<Fixture, Vector2[]>>(); foreach (Fixture f in body.FixtureList) { fixtures.Add(new Tuple<Fixture, Vector2[]>(f, FixtureExt.GetWorldPoints(f))); } return fixtures; } Vector2 center = GetLocalOrigin(body); List<List<IntPoint>> clipPaths = new List<List<IntPoint>>(); foreach (IPortal p in data.PortalCollisions()) { Vector2[] verts = Portal.GetWorldVerts(p); float scale = 100; Vector2 v0 = verts[0] + (verts[1] - verts[0]).Normalized() * scale; Vector2 v1 = verts[1] - (verts[1] - verts[0]).Normalized() * scale; Vector2 depth = (verts[1] - verts[0]).PerpendicularLeft.Normalized() * scale; if (new LineF(v0, v1).GetSideOf(v1 + depth) == new LineF(v0, v1).GetSideOf(center)) { depth *= -1; } Vector2[] box = new Vector2[] { v0, v1, v1 + depth, v0 + depth }; box = MathExt.SetWinding(box, true); clipPaths.Add(ClipperConvert.ToIntPoint(box)); } List<Tuple<Fixture, Vector2[]>> clippedFixtures = new List<Tuple<Fixture, Vector2[]>>(); Clipper clipper = new Clipper(); foreach (Fixture f in body.FixtureList) { if (!FixtureExt.GetData(f).IsPortalParentless()) { continue; } clipper.Clear(); clipper.AddPaths(clipPaths, PolyType.ptClip, true); clipper.AddPath( ClipperConvert.ToIntPoint(FixtureExt.GetWorldPoints(f)), PolyType.ptSubject, true); List<List<IntPoint>> result = new List<List<IntPoint>>(); clipper.Execute(ClipType.ctDifference, result, PolyFillType.pftEvenOdd, PolyFillType.pftNonZero); Debug.Assert( result.Count <= 1, "This fixture is too large for the portal masking or something has gone wrong with the clipper."); if (result.Count > 0) { clippedFixtures.Add(new Tuple<Fixture, Vector2[]>(f, ClipperConvert.ToVector2(result[0]))); } } Debug.Assert(clippedFixtures.Count > 0); return clippedFixtures; }
private PolyTreeEdgesRetained PrepairPolyTree(Collider2D[] floorColliders, Collider2D[] wallColliders) { if(floorColliders.Length == 0) { throw new System.Exception("No colliders in the scene on the floor layer."); } PolyTreeEdgesRetained TreeAndEdges = new PolyTreeEdgesRetained(); TreeAndEdges.Edges = new List<List<IntPoint>>(); TreeAndEdges.Tree = new PolyTree(); Clipper finalClipper = new Clipper(); if (floorColliders.Length > 0) { Clipper tempC = new Clipper(); ClipperOffset tempCo = new ClipperOffset(); foreach (Collider2D d in floorColliders) { List<IntPoint> p = PolygonFromCollider2D(d, false); if (p != null && p.Count != 0) { if (ClipperLib.Clipper.Orientation(p)) p.Reverse(); tempC.AddPath(p, PolyType.ptSubject, true); } } List<List<IntPoint>> solution = new List<List<IntPoint>>(); tempC.Execute(ClipType.ctUnion, solution, PolyFillType.pftNonZero, PolyFillType.pftNonZero); tempC.Clear(); foreach (List<IntPoint> intPoints in solution) { tempCo.AddPath(intPoints, (JoinType)GenerationInformation.JoinType, EndType.etClosedPolygon); TreeAndEdges.Edges.Add(intPoints); } solution.Clear(); tempCo.Execute(ref solution, -GenerationInformation.ColliderPadding * GenerationInformation.CalculationScaleFactor); finalClipper.AddPaths(solution, PolyType.ptSubject, true); } if(wallColliders.Length > 0) { Clipper tempC = new Clipper(); ClipperOffset tempCo = new ClipperOffset(); foreach (Collider2D d in wallColliders) { List<IntPoint> p = PolygonFromCollider2D(d, false); if (p != null && p.Count != 0) { if (ClipperLib.Clipper.Orientation(p)) p.Reverse(); tempC.AddPath(p, PolyType.ptSubject, true); } } List<List<IntPoint>> solution = new List<List<IntPoint>>(); tempC.Execute(ClipType.ctUnion, solution, PolyFillType.pftNonZero, PolyFillType.pftNonZero); tempC.Clear(); foreach (List<IntPoint> intPoints in solution) { tempCo.AddPath(intPoints, (JoinType)GenerationInformation.JoinType, EndType.etClosedPolygon); TreeAndEdges.Edges.Add(intPoints); } solution.Clear(); tempCo.Execute(ref solution, GenerationInformation.ColliderPadding * GenerationInformation.CalculationScaleFactor); finalClipper.AddPaths(solution, PolyType.ptClip, true); } finalClipper.Execute(ClipType.ctDifference, TreeAndEdges.Tree, PolyFillType.pftPositive, PolyFillType.pftEvenOdd); return TreeAndEdges; }
public void UpdateMesh() { //clean up booleanUnionSolution.Clear (); //solutions.Clear (); EdgeCollider2D[] cols = GetComponents<EdgeCollider2D> (); foreach (EdgeCollider2D col in cols) { DestroyImmediate(col); } PolygonCollider2D[] polyCols = GetComponents<PolygonCollider2D> (); foreach (PolygonCollider2D polyCol in polyCols) { DestroyImmediate(polyCol); } //Wind the square to boolean from. levelArea.points.Clear (); levelArea.points.Add (new Point (-1000f, 1000f)); levelArea.points.Add (new Point (1000f, 1000f)); levelArea.points.Add (new Point (1000f, -1000f)); levelArea.points.Add (new Point (-1000f, -1000f)); ClipperLib.Clipper clipper = new ClipperLib.Clipper (); //Strictly simple is computationally expensive, but prevents points from appearing too close together and making Poly2Tri Shit the bed. clipper.StrictlySimple = true; foreach (Polygon polygon in booleanPolygons) { clipper.AddPath(polygon,ClipperLib.PolyType.ptClip, true); } clipper.Execute (ClipperLib.ClipType.ctUnion, booleanUnionSolution, ClipperLib.PolyFillType.pftNonZero,ClipperLib.PolyFillType.pftNonZero); clipper.Clear (); clipper.AddPath (levelArea, ClipperLib.PolyType.ptSubject, true); foreach (List<ClipperLib.IntPoint> polygon in booleanUnionSolution) { clipper.AddPath(polygon,ClipperLib.PolyType.ptClip, true); } List<List<ClipperLib.IntPoint>> solution = new List<List<ClipperLib.IntPoint>> (); clipper.Execute (ClipperLib.ClipType.ctDifference, solution, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero); mesh = Utils.PolygonsToMesh (solution.Select(x=> (Polygon)x).ToList()); solutions = solution.Select (x => (Polygon)x).ToList (); GetComponent<MeshFilter> ().sharedMesh = mesh; foreach (Polygon polygon in solution) { Vector2[] unityPoints = new Vector2[polygon.points.Count + 1]; for(int i = 0; i < polygon.points.Count; i++) { unityPoints[i] = new Vector2(polygon.points[i].x,polygon.points[i].y); } unityPoints[polygon.points.Count] = new Vector2(polygon.points[0].x,polygon.points[0].y); EdgeCollider2D collider = gameObject.AddComponent<EdgeCollider2D>(); collider.points = unityPoints; } solutionCount = booleanUnionSolution.Count; //No need to store polygons that we won't need to reference... booleanPolygons = booleanUnionSolution.Select(x=>(Polygon)x).ToList(); }
private void Slice(Plane p) { float epsilon = 0.01f; // TODO: compute proper epsilon value List<PolyLine> linePile = new List<PolyLine>(); // Pile of disconnected lines on the slice plane List<Vector3> all_points = new List<Vector3>(); foreach (Face f in this.faces) { PolyLine newLine = TrianglePlaneIntersect(f, p); // Only add lines with exactly 2 points - others are a no match or error if (newLine.points.Count() == 2 && (newLine.points[0] - newLine.points[1]).Length> epsilon) { linePile.Add(newLine); // Add the vertices to the all_points list - only need to add the first one, the tail will be the head of another point bool matched = false; foreach (Vector3 point in all_points) { if ((point - newLine.points[0]).Length < epsilon) { matched = true; break; } } if (!matched) { all_points.Add(newLine.points[0]); } } } // linePile is a unordered list of line segments. // If a line segment is oriented with point[0] on (0, 0, 0) and point[1] // somewhere on the positive Y axis, the solid object is in the direction of the positive x axis. // // p[1]xxxxxxxxxxxxxxxxxxxxxxxx // xx xx // xx <object over here> xx // xx xx // p[0]xxxxxxxxxxxxxxxxxxxxxxxx // List<PolyLine> newPolyLines = new List<PolyLine>(); for (int i = 0; i < linePile.Count(); i++) { int points = linePile[i].points.Count(); Vector3 v1 = linePile[i].points[0]; Vector3 v2 = linePile[i].points[1]; //DrawCone1(v1, v2); List<Vector3> points_on_line = new List<Vector3>(); foreach (Vector3 v in all_points) { if ((v1 - v).Length >= epsilon && (v2 - v).Length >= epsilon && DistanceToCylinder(v1, v2, v) < epsilon) { points_on_line.Add(v); } } points_on_line.Insert(0, v1); points_on_line.Add(v2); // Order from v1 to v2 var sorted = points_on_line.OrderBy(order_vec => (order_vec - v1).Length); PolyLine newPolyLine = new PolyLine(); foreach (Vector3 v in sorted) { if (newPolyLine.points.Count() == 0 || (newPolyLine.points[newPolyLine.points.Count() - 1] - v).Length > epsilon) { newPolyLine.points.Add(v); } } if (newPolyLine.points.Count() >= 2) { newPolyLines.Add(newPolyLine); } if (newPolyLine.points.Count() >= 3) { // Shouldn't get here! } } List<LinePointIndices> lpis = new List<LinePointIndices>(); List<Vector3> vertices = new List<Vector3>(); List<List<int>> v_lookup = new List<List<int>>(); foreach (PolyLine l in newPolyLines) { int lastIndex = -1; foreach (Vector3 pointVec in l.points) { int currentIndex = -1; for (int i = 0; i < vertices.Count(); i++) { float length = (vertices[i] - pointVec).Length; if (length < epsilon) { currentIndex = i; continue; } } if (currentIndex == -1) { vertices.Add(pointVec); v_lookup.Add(new List<int>()); currentIndex = vertices.Count() - 1; } if (lastIndex != -1 && lastIndex != currentIndex) { LinePointIndices line = new LinePointIndices(); bool already_matched = false; foreach (int line_index in v_lookup[lastIndex]) { LinePointIndices l2 = lpis[line_index]; if (l2.indices[1] == currentIndex) { already_matched = true; } } if (!already_matched) { line.indices.Add(lastIndex); line.indices.Add(currentIndex); lpis.Add(line); v_lookup[lastIndex].Add(lpis.Count() - 1); v_lookup[currentIndex].Add(lpis.Count() - 1); } } lastIndex = currentIndex; } } //List<Vector3> scaled = new List<Vector3>(); List<int> vector_indices_to_see = new List<int>(); foreach (Vector3 v in vertices) { //scaled.Add(v / 125); vector_indices_to_see.Add(vector_indices_to_see.Count()); } List<LinePointIndices> slices = new List<LinePointIndices>(); GL.PushMatrix(); GL.PointSize(10); List<int> seenVertices = new List<int>(); while(vector_indices_to_see.Count() > 0) { List<int> line_indices = v_lookup [vector_indices_to_see[0]]; vector_indices_to_see.RemoveAt(0); if (line_indices.Count() == 0) { continue; } LinePointIndices line = lpis[line_indices[0]]; // Only need to look at one line with this vertex LinePointIndices start_line = new LinePointIndices(); start_line.indices.Add(line.indices[0]); start_line.indices.Add(line.indices[1]); GL.Color3(Color.Green); DrawCone1(vertices[start_line.indices[0]], vertices[start_line.indices[1]]); LinePointIndices loop = FindLoop(seenVertices, p.normal, vertices, v_lookup, lpis, start_line); if (loop != null) { slices.Add(loop); GL.Color3(Color.LightBlue); GL.Begin(BeginMode.LineLoop); Vector3 add = new Vector3(0, 0, 0); foreach (int i in loop.indices) { vector_indices_to_see.RemoveAll(value => value == i); GL.Vertex3(vertices[i] + add); seenVertices.Add(i); //add += new Vector3(0, 0, 25); } GL.End(); //GL.Translate(new Vector3(0, 0, +100)); } //break; } GL.PointSize(1); GL.PopMatrix(); Vector3 normal = new Vector3(0, 0, 1); float toolRadius = 100; GL.LineWidth(1); List<IntPoint> boundingBox = new List<IntPoint>(); boundingBox.Add(new IntPoint(-1000, -1000)); boundingBox.Add(new IntPoint(3000, -1000)); boundingBox.Add(new IntPoint(3000, 3000)); boundingBox.Add(new IntPoint(-1000, 3000)); List<LineLoop> loops = new List<LineLoop>(); foreach (LinePointIndices l in slices) { LineStrip line = new LineStrip(); for (int i = 0; i < l.indices.Count (); i++) { line.Append(vertices[l.indices[i]]); } line.Append(vertices[l.indices[0]]); loops.Add(new LineLoop (line)); } if (loops.Count() > 0) { Vector3 up = new Vector3(0, 0, 1); if (Math.Abs (normal.Z) > 0.8) { up = new Vector3(1, 0, 0); } float distance = Vector3.Dot(loops[0].GetVertex(0), normal); Matrix4 transform = Matrix4.LookAt(normal * distance, normal * (distance - 1), up); Matrix4 inverseTransform = Matrix4.Invert(transform); Clipper c = new Clipper(); c.Clear(); try { // These loops go clockwise foreach (LineLoop loop in loops) { List<IntPoint> polygon = new List<IntPoint>(); foreach (Vector3 vertex in loop.Vertices) { Vector3 result = Vector3.Transform(vertex, transform); polygon.Add(new IntPoint((long)result.X, (long)result.Y)); } polygon.RemoveAt(0); c.AddPolygon(polygon, PolyType.ptClip); GL.PushMatrix(); GL.Translate(new Vector3(0, 0, 100)); //loop.Draw(); GL.PopMatrix(); } List<List<IntPoint>> union = new List<List<IntPoint>>(); bool r = c.Execute(ClipType.ctUnion, union, PolyFillType.pftNonZero, PolyFillType.pftNonZero); List<List<IntPoint>> with_offset = Clipper.OffsetPolygons(union, toolRadius, JoinType.jtSquare); List<List<IntPoint>> whatsLeft = Clipper.OffsetPolygons(with_offset, -toolRadius, JoinType.jtRound); List<LineStrip> strips = new List<LineStrip>(); foreach (List<IntPoint> polygon in with_offset) { LineStrip strip = new LineStrip(); foreach (IntPoint point in polygon) { strip.Append(Vector3.Transform(new Vector3(point.X, point.Y, 0.0f), inverseTransform)); } strip.Append(Vector3.Transform(new Vector3(polygon[0].X, polygon[0].Y, 0.0f), inverseTransform)); strips.Add(strip); //new LineLoop(strip).Draw(); } List<List<IntPoint>> removeArea = new List<List<IntPoint>>(); c.Clear(); c.AddPolygons(with_offset, PolyType.ptClip); c.AddPolygon(boundingBox, PolyType.ptSubject); List<List<IntPoint>> resultingPolygon = new List<List<IntPoint>>(); c.Execute(ClipType.ctDifference, removeArea, PolyFillType.pftNonZero, PolyFillType.pftNonZero); removeArea = Clipper.CleanPolygons(removeArea, toolRadius / 100); c.Clear(); c.AddPolygons(removeArea, PolyType.ptClip); PolyTree test = new PolyTree(); c.Execute(ClipType.ctUnion, test, PolyFillType.pftNonZero, PolyFillType.pftNonZero); //PolyNode pn = test.GetFirst(); //while (pn != null) //{ // if (pn.IsHole) // { // LineLoop l = new LineLoop(pn.Contour, inverseTransform); // l.Draw(); // } // pn = pn.GetNext(); //} List<Polygons> polys = FlattenPolyTree(test); //GL.PushMatrix(); foreach (Polygons polygons in polys) { //GL.Translate(new Vector3 (0, 0, 100)); //foreach (Polygon polygon in polygons) //{ // LineLoop l = new LineLoop(polygon, inverseTransform); // l.Draw(); //} List<Polygons> paths = ReducePolygon(polygons, toolRadius, inverseTransform); //IOrderedEnumerable<List<IntPoint>> ordered = paths.OrderBy(poly => Clipper.Area(poly)); GL.PushMatrix(); List<Polygons> paths2 = new List<Polygons>(); List<Polygons> paths3 = new List<Polygons>(); foreach (Polygons polygons2 in paths) { var newPolys = new Polygons(); foreach (Polygon poly in polygons2) { if (Clipper.Area(poly) > 0) { newPolys.Add(poly); } } paths2.Add(newPolys); //GL.Translate(new Vector3(0, 0, 100)); var newInnerPolys = new Polygons(); foreach (Polygon poly in polygons2) { if (paths3.Count() == 0) { //newInnerPoly } if (Clipper.Area(poly) < 0) { LineLoop l = new LineLoop(poly, inverseTransform); l.Draw(); } } } foreach (Polygons polygons2 in paths2) { GL.Translate(new Vector3(0, 0, 100)); foreach (Polygon poly in polygons2) { LineLoop l = new LineLoop(poly, inverseTransform); l.Draw(); } } GL.PopMatrix(); } //GL.PopMatrix(); double boundingBoxArea = Clipper.Area(boundingBox); // Outer Polygon // Inner Polygons //ReducePolygon(boundingBox, with_offset, toolRadius, inverseTransform); //strips = new List<LineStrip>(); //double area = 1; //int loopTimes = 0; //List<List<IntPoint>> cutPolygons = new List<List<IntPoint>>(); //List<Vector3> parentPoints = new List<Vector3>(); //GL.PushMatrix(); //while (removeArea.Count() > 0) //{ // List<Vector3> points = new List<Vector3>(); // foreach (List<IntPoint> polygon in removeArea) // { // double area = Clipper.Area(polygon); // // if (area > 0) // Bigger to Smaller // { // } // IntPoint[] newP = new IntPoint[polygon.Count()]; // polygon.CopyTo(newP); // cutPolygons.Add(new List<IntPoint>(newP)); // // // LineLoop l = new LineLoop(polygon, inverseTransform); // //l.Draw(); // points.AddRange(l.Vertices); // // //ReducePolygon(null, polygon, toolRadius, inverseTransform); // //area += Clipper.Area(polygon); // //LineStrip strip = new LineStrip(); // //foreach (IntPoint point in polygon) // //{ // // strip.Append(Vector3.Transform(new Vector3(point.X, point.Y, 0.0f), inverseTransform)); // //} // //strip.Append(Vector3.Transform(new Vector3(polygon[0].X, polygon[0].Y, 0.0f), inverseTransform)); // // //strips.Add(strip); // //new LineLoop(strip).Draw(); // } // // //GL.Color3(Color.Black); // //GL.Begin(BeginMode.Lines); // //foreach (Vector3 v in points) // //{ // // foreach (Vector3 v2 in parentPoints) // // { // // if ((v - v2).Length < toolRadius * 2) // // { // // GL.Vertex3(v); // // GL.Vertex3(v2); // // } // // } // //} // //GL.End(); // // parentPoints = points; // removeArea = Clipper.OffsetPolygons(removeArea, -toolRadius, JoinType.jtRound); // removeArea = Clipper.CleanPolygons(removeArea, toolRadius / 100); //} //GL.PopMatrix(); //IOrderedEnumerable<List<IntPoint>> ordered = cutPolygons.OrderBy(poly => Clipper.Area(poly)); // //GL.PushMatrix(); //foreach (List<IntPoint> poly in ordered) //{ // GL.Translate(new Vector3(0, 0, 100)); // LineLoop l = new LineLoop(poly, inverseTransform); // l.Draw(); //} //GL.PopMatrix(); ////strips = new List<LineStrip>(); //GL.Color3(Color.Red); //GL.LineWidth(2); //foreach (List<IntPoint> polygon in whatsLeft) //{ // LineStrip strip = new LineStrip(); // foreach (IntPoint point in polygon) // { // strip.Append(Vector3.Transform(new Vector3(point.X, point.Y, 0.0f), inverseTransform)); // } // strip.Append(Vector3.Transform(new Vector3(polygon[0].X, polygon[0].Y, 0.0f), inverseTransform)); // // strips.Add(strip); // new LineLoop(strip).Draw(); //} //GL.LineWidth(1); } catch (Exception e) { } } }