/// <summary> /// Creates a convex hull mesh for a set of points. /// </summary> /// <param name="points">The points.</param> /// <param name="vertexLimit"> /// The vertex limit. Must be greater than 0. Common values are 32 or 64. /// </param> /// <param name="skinWidth"> /// The skin width. Common values are 0.01 or 0.001. /// </param> /// <returns> /// The mesh of the convex hull or <see langword="null"/> if the point list is /// <see langword="null"/> or empty. /// </returns> /// <remarks> /// <para> /// The returned mesh describes the convex hull. All faces are convex polygons. /// </para> /// <para> /// If the created convex hull has more vertices than <paramref name="vertexLimit"/>, the hull /// will be simplified. The simplified hull is conservative, which means it contains all given /// <paramref name="points"/> and is less "tight" than the exact hull. It is possible that the /// simplified hull contains slightly more vertices than <paramref name="vertexLimit"/> (e.g. it /// is possible that for a vertex limit of 32 a hull with 34 vertices is returned). /// </para> /// <para> /// All planes of the convex hull are extruded by the <paramref name="skinWidth"/>. This can be /// used to increase or decrease the size of the convex hull. /// </para> /// </remarks> public static DcelMesh CreateConvexHull(IEnumerable<Vector3F> points, int vertexLimit, float skinWidth) { // Nothing to do for empty input. if (points == null) return null; ConvexHullBuilder builder = new ConvexHullBuilder(); builder.Grow(points, vertexLimit, skinWidth); if (builder.Type == ConvexHullType.Empty) return null; return builder.Mesh; }
private float GetConcavity(int vertexLimit, bool sampleVertices, bool sampleCenters) { // Initially we assume that the new vertices are simply the union of the islands' vertices. Vertices = IslandA.Vertices.Union(IslandB.Vertices).ToArray(); try { // Create hull mesh. // Incremental hull building. // Note: Commented out because this building the hull this way is less stable. //bool rebuild = true; //try //{ // if (IslandA.ConvexHullBuilder != null) // { // if (IslandB.ConvexHullBuilder == null || IslandA.Vertices.Length > IslandB.Vertices.Length) // { // ConvexHullBuilder = IslandA.ConvexHullBuilder.Clone(); // ConvexHullBuilder.Grow(IslandB.Vertices, vertexLimit, 0); // } // else // { // ConvexHullBuilder = IslandB.ConvexHullBuilder.Clone(); // ConvexHullBuilder.Grow(IslandA.Vertices, vertexLimit, 0); // } // rebuild = false; // } // else if (IslandB.ConvexHullBuilder != null) // { // ConvexHullBuilder = IslandB.ConvexHullBuilder.Clone(); // ConvexHullBuilder.Grow(IslandA.Vertices, vertexLimit, 0); // rebuild = false; // } //} //catch (GeometryException) //{ // rebuild = true; //} //if (rebuild) { try { ConvexHullBuilder = new ConvexHullBuilder(); ConvexHullBuilder.Grow(Vertices, vertexLimit, 0); } catch (GeometryException) { // Hull building failed. Try again with a randomized order. var random = new Random(1234567); // Fisher-Yates shuffle: for (int i = Vertices.Length - 1; i >= 1; i--) { var v = Vertices[i]; var j = random.NextInteger(0, i); Vertices[i] = Vertices[j]; Vertices[j] = v; } } } var hullMesh = ConvexHullBuilder.Mesh.ToTriangleMesh(); // Now, we have a reduced set of vertices. Vertices = hullMesh.Vertices.ToArray(); // For larger meshes we create an AabbTree as acceleration structure. AabbTree<int> partition = null; if (hullMesh.NumberOfTriangles > 12) { partition = new AabbTree<int> { GetAabbForItem = i => hullMesh.GetTriangle(i).Aabb, BottomUpBuildThreshold = 0, }; for (int i = 0; i < hullMesh.NumberOfTriangles; i++) partition.Add(i); partition.Update(true); } Aabb aabb = Aabb; float aabbExtent = aabb.Extent.Length; // Note: For a speed-up we could skip some ray tests in the next loop and only sample // a few vertices if there would be a lot of tests. // The next loop performs ray casts against the hull mesh to determine the maximum // concavity. We ensure that we make only one ray cast per vertex even if a vertex // is shared by many triangles. float maxConcavity = 0; foreach (var triangle in IslandA.Triangles.Union(IslandB.Triangles)) { if (sampleVertices) { for (int i = 0; i < triangle.Vertices.Length; i++) { // Each vertex can be shared by several triangles of the current islands. // Therefore, we check the edges that contain this vertex. If an edge neighbor // is in the same island, we make sure that the vertex concavity is computed only once // in the triangle with the smallest Id. var neighbor0 = triangle.Neighbors[(i + 1) % 3]; var neighbor1 = triangle.Neighbors[(i + 2) % 3]; if (neighbor0 != null && (neighbor0.Island == IslandA || neighbor0.Island == IslandB) && triangle.Id > neighbor0.Id) { // No need to test: The neighbor is in the same islands and this triangle Id is larger. continue; } if (neighbor1 != null && (neighbor1.Island == IslandA || neighbor1.Island == IslandB) && triangle.Id > neighbor1.Id) { // No need to test: The neighbor is in the same islands and this triangle Id is larger. continue; } var position = triangle.Vertices[i]; var normal = triangle.VertexNormals[i]; // Degenerate triangles are ignored. if (normal.IsNumericallyZero) continue; // Shoot a ray from outside the hull mesh to the vertex. float hitDistance; Vector3F rayOrigin = position + normal * aabbExtent; float rayLength = (position - rayOrigin).Length; var ray = new Ray(rayOrigin, -normal, rayLength); if (partition != null) { // Use AABB tree for better performance. foreach (var triangleIndex in partition.GetOverlaps(ray)) { var candidateTriangle = hullMesh.GetTriangle(triangleIndex); var hit = GeometryHelper.GetContact(ray, candidateTriangle, false, out hitDistance); if (hit) { // The concavity is the distance from the hull to the vertex. float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); break; } } } else { // No AABB tree. var hit = GeometryHelper.GetContact(hullMesh, ray, out hitDistance); if (hit) { float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); } } } } if (sampleCenters) { // Test: Also shoot from the triangle centers. var center = (triangle.Vertices[0] + triangle.Vertices[1] + triangle.Vertices[2]) / 3; var normal = triangle.Normal; // Degenerate triangles are ignored. if (normal.IsNumericallyZero) continue; // Shoot a ray from outside the hull mesh to the vertex. float hitDistance; Vector3F rayOrigin = center + normal * aabbExtent; float rayLength = (center - rayOrigin).Length; var ray = new Ray(rayOrigin, -normal, rayLength); if (partition != null) { // Use AABBTree for better performance. foreach (var triangleIndex in partition.GetOverlaps(ray)) { var candidateTriangle = hullMesh.GetTriangle(triangleIndex); var hit = GeometryHelper.GetContact(ray, candidateTriangle, false, out hitDistance); if (hit) { // The concavity is the distance from the hull to the vertex. float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); break; } } } else { // No AABBTree. var hit = GeometryHelper.GetContact(hullMesh, ray, out hitDistance); if (hit) { float concavity = rayLength - hitDistance; maxConcavity = Math.Max(maxConcavity, concavity); } } } } return maxConcavity; } catch (GeometryException) { // Ouch, the convex hull generation failed. This can happen for degenerate inputs // and numerical problems in the convex hull builder. ConvexHullBuilder = null; return 0; } }
public ConvexHullBuilder Clone() { var clone = new ConvexHullBuilder { _isPlanar = _isPlanar, _type = _type, _mesh = new DcelMesh(_mesh), }; return clone; }