/// <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); }
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> /// 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 bool MakeHull(Mesh mesh, Transform tr, string path, ref ConvexData data) { GiftWrap gw = new GiftWrap(); gw.Reset(); List <Vector3> vecVertex = new List <Vector3>(); Vector3 vmin = Vector3.zero; Vector3 vmax = Vector3.zero; Bounds bound = mesh.bounds; vmin = bound.min; vmax = bound.max; Vector3[] verts = mesh.vertices; for (int j = 0; j < mesh.vertexCount; ++j) { Vector3 wp = verts[j]; if (null != tr) { wp = tr.TransformPoint(wp); } bool bnear = false; for (int k = 0; k < (int)vecVertex.Count; k++) { if ((wp - vecVertex[k]).magnitude < CLOSE_VERTEX_DISTANCE_THRESH) { bnear = true; break; } } if (!bnear) { vecVertex.Add(wp); } } int numallvert = vecVertex.Count; gw.SetVertexes(vecVertex); gw.ComputeConvexHull(); path += "/" + System.DateTime.Now.ToString("dd-MM-yy-HH-mm-ss") + "_vts.txt"; if (gw.ExceptionOccur()) { gw.SaveVerticesToFile(path); gw.Reset(); return(false); } ConvexPolytope cp = new ConvexPolytope(); cp.Init(gw, (vmax - vmin).magnitude); if (cp.ExceptionOccur && cp.MinPatchNum > 20) { gw.SaveVerticesToFile(path); return(false); } int patchnum = Mathf.Min(cp.OriginPatchNum, Mathf.Max(10, cp.MinPatchNum)); if (patchnum > MAX_FACE_IN_HULL) { gw.Reset(); return(false); } else { cp.Goto(Mathf.Min(patchnum, MAX_FACE_IN_HULL)); } gw.Reset(); data.Reset(); cp.ExportCHData(data); return(true); }
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); } }
public bool Init(GiftWrap gw, float error) { //重载一个Init的方法,避免顺序耦合 ErrorBase = error; return(Init(gw)); }
public bool Init(GiftWrap gw) { if (gw.ExceptionOccur()) { return(false); } Reset(); //重置为初始状态 Centroid = gw.Centroid; //质心置为初始化gw的质心 //分情况考虑构造CConvexPolytope if (gw.HullType == GiftWrap.HULL_TYPE.HULL_3D) //3D Hull的情况 { int[] map = new int[gw.LstVertex.Count]; //构造一个映射表 int count = 0; //添加有效顶点&构造映射表 int i; for (i = 0; i < gw.LstVertex.Count; i++) { map[i] = -1; if (gw.LstExtremeVertex[i]) { //当前点有效,用其初始化v,并将其插入到顶点列表 Vector3 v = gw.LstVertex[i]; LstVertecies.Add(v); LstVertexInfo.Add(new VertexInfo()); map[i] = count++; } } //构造面片 //添加三角形面片 for (i = 0; i < gw.LstFaces.Count; i++) { Face f = gw.LstFaces[i]; if (!f.InPolygon) //不属于任何多边形,添加 { Vector3 v1 = gw.LstVertex[f.v1]; Vector3 v2 = gw.LstVertex[f.v2]; Vector3 v3 = gw.LstVertex[f.v3]; Patch pat = new Patch(this); pat.Set(v1, v2, v3); //几何信息 //依次向Neighbors中添加元素 VPNeighbor vpn = new VPNeighbor(); List <VPNeighbor> lstNeighbor = pat.LstNeighbors; vpn.Vid = map[f.v1]; //这里必须作一个映射 lstNeighbor.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = map[f.v2]; lstNeighbor.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = map[f.v3]; lstNeighbor.Add(vpn); //各顶点度数增1 LstVertexInfo[map[f.v1]].Degree++; LstVertexInfo[map[f.v2]].Degree++; LstVertexInfo[map[f.v3]].Degree++; //添加到链表 LstPatches.Add(pat); } } //添加多边形面片 for (i = 0; i < gw.LstPlanes.Count; i++) { List <int> planes = gw.LstPlanes[i]; //取前三个点构造平面几何信息 Vector3 v1 = gw.LstVertex[planes[0]]; Vector3 v2 = gw.LstVertex[planes[1]]; Vector3 v3 = gw.LstVertex[planes[2]]; Patch pat = new Patch(this); pat.Set(v1, v2, v3); //几何信息 //依次向Neighbors中添加元素 VPNeighbor vpn = new VPNeighbor(); List <VPNeighbor> lstNeighbors = pat.LstNeighbors; for (int j = 0; j < planes.Count; j++) { vpn = new VPNeighbor(); vpn.Vid = map[planes[j]]; //这里必须作一个映射 lstNeighbors.Add(vpn); LstVertexInfo[vpn.Vid].Degree++; //顶点度数增1 } //添加到链表 LstPatches.Add(pat); } } else { //说明是2D Hull的情况 List <int> lstCHVs = gw.GetCHVertecies(); if (lstCHVs == null) { return(false); } if (lstCHVs.Count < 3) //至少是一个三角形 { return(false); } //顶点信息 VertexInfo vInfo = new VertexInfo(); vInfo.Degree = 3; //直棱拄所有顶点的面度数均为3 HalfSpace planeOut = new HalfSpace(); HalfSpace planeIn = new HalfSpace(); //取前三个点构造平面几何信息 Vector3 v1 = gw.LstVertex[lstCHVs[0]]; Vector3 v2 = gw.LstVertex[lstCHVs[1]]; Vector3 v3 = gw.LstVertex[lstCHVs[2]]; //构造两个平面PlaneOut和PlaneIn,分别表示顶面和底面 planeOut.Set(v1, v2, v3); planeIn.Normal = -planeOut.Normal; planeIn.Dist = -planeOut.Dist; planeIn.Translate(HULL2D_HALF_THICKNESS); planeOut.Translate(HULL2D_HALF_THICKNESS); Vector3 vOutNormal = planeOut.Normal; Vector3 vInNormal = planeIn.Normal; //分别求出PlaneOut,PlaneIn上的两点 Vector3 vOut = v1 + HULL2D_HALF_THICKNESS * vOutNormal; Vector3 vIn = v1 + HULL2D_HALF_THICKNESS * vInNormal; //构造顶点及顶点信息 int i; for (i = 0; i < lstCHVs.Count; i++) { //同时添加底面和顶面的一个顶点 Vector3 vec1 = gw.LstVertex[lstCHVs[i]]; Vector3 vec2 = vec1; if (i < 3) { vec1 += HULL2D_HALF_THICKNESS * vOutNormal; vec2 += HULL2D_HALF_THICKNESS * vInNormal; } else { Vector3 vDiff = vec1 - vOut; vec1 -= Vector3.Dot(vDiff, vOutNormal) * vOutNormal; vDiff = vec2 - vIn; vec2 -= Vector3.Dot(vDiff, vInNormal) * vInNormal; } LstVertecies.Add(vec1); LstVertecies.Add(vec2); //相应的,添加两个顶点信息 LstVertexInfo.Add(vInfo); LstVertexInfo.Add(vInfo); } //开始构造平面面片 //向外的面 Patch pat = new Patch(this); pat.Normal = vOutNormal; //几何信息 pat.Dist = planeOut.Dist; //依次向Neighbors中添加元素 VPNeighbor vpn = new VPNeighbor(); List <VPNeighbor> lstNeighbors1 = pat.LstNeighbors; for (i = 0; i < lstCHVs.Count; i++) { vpn = new VPNeighbor(); vpn.Vid = 2 * i; lstNeighbors1.Add(vpn); } //添加到链表 LstPatches.Add(pat); //向内的面 pat = new Patch(this); pat.Normal = vInNormal; //几何信息 pat.Dist = planeIn.Dist; //依次向Neighbors中添加元素 List <VPNeighbor> lstNeighbors2 = pat.LstNeighbors; //顶面按照逆序添加 for (i = lstCHVs.Count - 1; i >= 0; i--) { vpn = new VPNeighbor(); vpn.Vid = 2 * i + 1; lstNeighbors2.Add(vpn); } //添加到链表 LstPatches.Add(pat); //开始添加各个侧面 for (i = 0; i < lstCHVs.Count; i++) { pat = new Patch(this); List <VPNeighbor> lstNeighbors = pat.LstNeighbors; //每个侧面都是一个矩形 if (i < lstCHVs.Count - 1) { v1 = LstVertecies[2 * i + 2]; v2 = LstVertecies[2 * i]; v3 = LstVertecies[2 * i + 1]; pat.Set(v1, v2, v3); vpn = new VPNeighbor(); vpn.Vid = 2 * i; lstNeighbors.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = 2 * i + 1; lstNeighbors.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = 2 * i + 3; lstNeighbors.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = 2 * i + 2; lstNeighbors.Add(vpn); } else { //最后一个矩形的情况比较特殊 v1 = LstVertecies[0]; v2 = LstVertecies[2 * i]; v3 = LstVertecies[2 * i + 1]; pat.Set(v1, v2, v3); vpn = new VPNeighbor(); vpn.Vid = 2 * i; lstNeighbors.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = 2 * i + 1; lstNeighbors.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = 1; lstNeighbors.Add(vpn); vpn = new VPNeighbor(); vpn.Vid = 0; lstNeighbors.Add(vpn); } LstPatches.Add(pat); } } OriginPatchNum = LstPatches.Count; CurVNum = LstVertecies.Count; //初始化删除误差 if (mLstRemovedError == null) { mLstRemovedError = new List <float>(OriginPatchNum + 1); } mLstRemovedError.Clear(); for (int i = 0; i < OriginPatchNum + 1; i++) { mLstRemovedError.Add(-1.0f); //0,1,2,3都置为无效值 } ExceptionOccur = false; //计算每个patch的邻域patch ComputePatchNeighbors(); //HalfSpace::SetDistThresh(1e-3); //恢复到缺省的阈值 //寻找最小删除误差对应的面片 SearchLeastErrorPatch(); //开始简化,简化到头 ReduceAll(); return(true); }