using System.Collections.Generic;
public class MeshOutlineExtractor
public Edge(int v1, int v2)
V1 = Mathf.Min(v1, v2); // Ensure consistent ordering
public override bool Equals(object obj)
return V1 == other.V1 && V2 == other.V2;
public override int GetHashCode()
/// Extracts the boundary edges of the mesh and orders them into a continuous outline.
private List<Vector3> GetOrderedMeshOutline(Mesh mesh)
var edgeCount = new Dictionary<Edge, int>();
var edgeAdjacency = new Dictionary<int, List<int>>();
var orderedOutline = new List<Vector3>();
var triangles = mesh.triangles;
var vertices = mesh.vertices;
// Step 1: Find boundary edges
for (int i = 0; i < triangles.Length; i += 3)
new Edge(triangles[i], triangles[i + 1]),
new Edge(triangles[i + 1], triangles[i + 2]),
new Edge(triangles[i + 2], triangles[i])
foreach (var edge in edges)
if (edgeCount.ContainsKey(edge))
// Step 2: Identify boundary edges
var boundaryEdges = new List<Edge>();
foreach (var kvp in edgeCount)
if (kvp.Value == 1) // Boundary edges appear only once
boundaryEdges.Add(kvp.Key);
// Track adjacency (for ordering)
if (!edgeAdjacency.ContainsKey(kvp.Key.V1))
edgeAdjacency[kvp.Key.V1] = new List<int>();
if (!edgeAdjacency.ContainsKey(kvp.Key.V2))
edgeAdjacency[kvp.Key.V2] = new List<int>();
edgeAdjacency[kvp.Key.V1].Add(kvp.Key.V2);
edgeAdjacency[kvp.Key.V2].Add(kvp.Key.V1);
// Step 3: Order edges into a continuous path
if (boundaryEdges.Count > 0)
var startVertex = boundaryEdges[0].V1;
var currentVertex = startVertex;
orderedOutline.Add(vertices[currentVertex]);
if (!edgeAdjacency.ContainsKey(currentVertex) || edgeAdjacency[currentVertex].Count == 0)
var nextVertex = edgeAdjacency[currentVertex].FirstOrDefault(v => v != previousVertex);
previousVertex = currentVertex;
currentVertex = nextVertex;
edgeAdjacency[previousVertex].Remove(currentVertex);
edgeAdjacency[currentVertex].Remove(previousVertex);
} while (currentVertex != startVertex && currentVertex != -1);