private ConvexHull2Test() { _pointCloud1 = new Vertices(PointCount); for (int i = 0; i < PointCount; i++) { float x = Rand.RandomFloat(-10, 10); float y = Rand.RandomFloat(-10, 10); _pointCloud1.Add(new Vector2(x, y)); } _pointCloud2 = new Vertices(_pointCloud1); _pointCloud3 = new Vertices(_pointCloud1); //Melkman DOES NOT work on point clouds. It only works on simple polygons. _pointCloud1.Translate(new Vector2(-20, 30)); _melkman = Melkman.GetConvexHull(_pointCloud1); //Giftwrap works on point clouds _pointCloud2.Translate(new Vector2(20, 30)); _giftWrap = GiftWrap.GetConvexHull(_pointCloud2); //Chain hull also works on point clouds _pointCloud3.Translate(new Vector2(20, 10)); _chainHull = ChainHull.GetConvexHull(_pointCloud3); }
/// <summary> /// Describes whether validate polygon /// </summary> /// <param name="polygon">The polygon</param> /// <returns>The bool</returns> private static bool ValidatePolygon(Vertices polygon) { PolygonError errorCode = polygon.CheckPolygon(); if (errorCode == PolygonError.InvalidAmountOfVertices || errorCode == PolygonError.AreaTooSmall || errorCode == PolygonError.SideTooSmall || errorCode == PolygonError.NotSimple) { return(false); } if ( errorCode == PolygonError .NotCounterClockWise) //NotCounterCloseWise is the last check in CheckPolygon(), thus we don't need to call ValidatePolygon again. { polygon.Reverse(); } if (errorCode == PolygonError.NotConvex) { polygon = GiftWrap.GetConvexHull(polygon); return(ValidatePolygon(polygon)); } return(true); }
/// <summary> /// Updates data for the convex hull of the polygon. /// </summary> private void UpdateConvexHull() { // compute convex hull Vertices hull = GiftWrap.GetConvexHull(Polygon.Vertices); // update polygon lines convexHullLines = new PointF[hull.Count]; for (int i = 0; i < hull.Count; ++i) { convexHullLines[i] = new PointF(hull[i].X, hull[i].Y); } // update valid/invalid vertices for (int i = 0; i < vertices.Count; ++i) { int index = hull.IndexOf(Polygon.Vertices[i]); if (index == -1) { vertices[i] = new KeyValuePair <PointF, bool>(vertices[i].Key, false); } else { vertices[i] = new KeyValuePair <PointF, bool>(vertices[i].Key, true); } } }
/// <summary> /// sets the vertices without copying over the data from verts to the local List. /// </summary> /// <param name="verts">Verts.</param> public void SetVerticesNoCopy(Vertices verts) { Debug.Assert(verts.Count >= 3 && verts.Count <= Settings.MaxPolygonVertices); _vertices = verts; if (Settings.UseConvexHullPolygons) { // FPE note: This check is required as the GiftWrap algorithm early exits on triangles // So instead of giftwrapping a triangle, we just force it to be clock wise. if (_vertices.Count <= 3) { _vertices.ForceCounterClockWise(); } else { _vertices = GiftWrap.GetConvexHull(_vertices); } } if (_normals == null) { _normals = new Vertices(_vertices.Count); } else { _normals.Clear(); } // Compute normals. Ensure the edges have non-zero length. for (var i = 0; i < _vertices.Count; ++i) { var next = i + 1 < _vertices.Count ? i + 1 : 0; var edge = _vertices[next] - _vertices[i]; Debug.Assert(edge.LengthSquared() > Settings.Epsilon * Settings.Epsilon); // FPE optimization: Normals.Add(MathHelper.Cross(edge, 1.0f)); var temp = new Vector2(edge.Y, -edge.X); Nez.Vector2Ext.Normalize(ref temp); _normals.Add(temp); } // Compute the polygon mass data ComputeProperties(); }
/// <summary> /// Makes convex hull from the polygon. /// </summary> public void MakeConvexHull() { // compute convex hull Vertices hull = GiftWrap.GetConvexHull(Polygon.Vertices); // remove all unused vertices from convex hull for (int i = 0; i < Polygon.Vertices.Count; ++i) { int index = hull.IndexOf(Polygon.Vertices[i]); if (index == -1) { Polygon.Vertices.RemoveAt(i); --i; } } // update data UpdateVertices(); }
public static List <Vertices> ConvexPartition(Vertices vertices, TriangulationAlgorithm algorithm, bool discardAndFixInvalid = true, float tolerance = 0.001f) { if (vertices.Count <= 3) { return new List <Vertices> { vertices } } ; List <Vertices> results; switch (algorithm) { case TriangulationAlgorithm.Earclip: if (Settings.SkipSanityChecks) { Debug.Assert(!vertices.IsCounterClockWise(), "The Earclip algorithm expects the polygon to be clockwise."); } else { if (vertices.IsCounterClockWise()) { Vertices temp = new Vertices(vertices); temp.Reverse(); results = EarclipDecomposer.ConvexPartition(temp, tolerance); } else { results = EarclipDecomposer.ConvexPartition(vertices, tolerance); } } break; case TriangulationAlgorithm.Bayazit: if (Settings.SkipSanityChecks) { Debug.Assert(vertices.IsCounterClockWise(), "The polygon is not counter clockwise. This is needed for Bayazit to work correctly."); } else { if (!vertices.IsCounterClockWise()) { Vertices temp = new Vertices(vertices); temp.Reverse(); results = BayazitDecomposer.ConvexPartition(temp); } else { results = BayazitDecomposer.ConvexPartition(vertices); } } break; case TriangulationAlgorithm.Flipcode: if (Settings.SkipSanityChecks) { Debug.Assert(vertices.IsCounterClockWise(), "The polygon is not counter clockwise. This is needed for Bayazit to work correctly."); } else { if (!vertices.IsCounterClockWise()) { Vertices temp = new Vertices(vertices); temp.Reverse(); results = FlipcodeDecomposer.ConvexPartition(temp); } else { results = FlipcodeDecomposer.ConvexPartition(vertices); } } break; case TriangulationAlgorithm.Seidel: results = SeidelDecomposer.ConvexPartition(vertices, tolerance); break; case TriangulationAlgorithm.SeidelTrapezoids: results = SeidelDecomposer.ConvexPartitionTrapezoid(vertices, tolerance); break; case TriangulationAlgorithm.Delauny: results = CDTDecomposer.ConvexPartition(vertices); break; default: throw new ArgumentOutOfRangeException("algorithm"); } if (discardAndFixInvalid) { for (int i = results.Count - 1; i >= 0; i--) { Vertices polygon = results[i]; PolygonError errorCode = polygon.CheckPolygon(); if (errorCode == PolygonError.InvalidAmountOfVertices || errorCode == PolygonError.AreaTooSmall || errorCode == PolygonError.SideTooSmall || errorCode == PolygonError.NotSimple) { results.RemoveAt(i); } else if (errorCode == PolygonError.NotCounterClockWise) { polygon.Reverse(); } else if (errorCode == PolygonError.NotConvex) { results[i] = GiftWrap.GetConvexHull(polygon); } } } return(results); } }