/// <summary> /// Build a full topology data structure from the minimal data described by a face neighbor indexer. /// </summary> /// <param name="indexer">A minimal description of the faces and their neighbors constituting a topology.</param> /// <returns>A fully constructed topology matching the description of the provided face neighbor indexer.</returns> /// <remarks> /// <para>For the edge wrap data returned by the face neighbor indexer, only the vertex-to-edge, edge-to-vertex, /// and face-to-edge data needs to be supplied. If there are no external faces, then the edge-to-vertex data is /// also unnecessary.</para> /// </remarks> public static Topology BuildTopology(IFaceNeighborIndexer indexer) { var vertexNeighborCounts = new ushort[indexer.vertexCount]; var vertexFirstEdgeIndices = new int[indexer.vertexCount]; var edgeData = new Topology.EdgeData[indexer.edgeCount]; var faceNeighborCounts = new ushort[indexer.faceCount]; var faceFirstEdgeIndices = new int[indexer.faceCount]; // Initialize the face roots and neighbor counts, and the next vertex, fNext, vNext, and wrap // fields of the internal edges. The vNext fields will result in linked lists that contain the // correct members (aside from external edges), but in an unspecified order. int edgeIndex = 0; for (int faceIndex = 0; faceIndex < indexer.internalFaceCount; ++faceIndex) { var neighborCount = indexer.GetNeighborCount(faceIndex); faceNeighborCounts[faceIndex] = neighborCount; faceFirstEdgeIndices[faceIndex] = edgeIndex; var priorVertexIndex = indexer.GetNeighborVertexIndex(faceIndex, neighborCount - 1); for (int neighborIndex = 0; neighborIndex < neighborCount; ++neighborIndex) { var vertexIndex = indexer.GetNeighborVertexIndex(faceIndex, neighborIndex); edgeData[edgeIndex].vertex = vertexIndex; edgeData[edgeIndex].fNext = edgeIndex + 1; edgeData[edgeIndex].face = -1; edgeData[edgeIndex].twin = -1; edgeData[edgeIndex].wrap = indexer.GetEdgeWrap(faceIndex, neighborIndex); AddEdgeToVertexUnordered(edgeIndex, priorVertexIndex, vertexNeighborCounts, vertexFirstEdgeIndices, edgeData); priorVertexIndex = vertexIndex; ++edgeIndex; } // Correct the face's last edge to refer back to the face's first edge. edgeData[edgeIndex - 1].fNext = edgeIndex - neighborCount; } var internalEdgeCount = edgeIndex; // Use the partial information constructed in the prior loop to determine edge twins, and set // the vertex, fNext, vNext, and wrap fields for external edges too. edgeIndex = 0; var externalEdgeIndex = internalEdgeCount; for (int faceIndex = 0; faceIndex < indexer.internalFaceCount; ++faceIndex) { var neighborCount = indexer.GetNeighborCount(faceIndex); var priorVertexIndex = indexer.GetNeighborVertexIndex(faceIndex, neighborCount - 1); for (int neighborIndex = 0; neighborIndex < neighborCount; ++neighborIndex) { var vertexIndex = indexer.GetNeighborVertexIndex(faceIndex, neighborIndex); if (edgeData[edgeIndex].twin == -1) { // The current edge is pointing at the current vertex index. Its twin will be one // of the edges pointing out from the current vertex, and will be pointing at the // prior vertex. Search for this edge. var vertexFirstEdgeIndex = vertexFirstEdgeIndices[vertexIndex]; var vertexEdgeIndex = vertexFirstEdgeIndex; while (edgeData[vertexEdgeIndex].vertex != priorVertexIndex) { vertexEdgeIndex = edgeData[vertexEdgeIndex].vNext; if (vertexEdgeIndex == vertexFirstEdgeIndex) { // This edge's twin is an external edge which needs to be initialized. edgeData[externalEdgeIndex].vertex = priorVertexIndex; edgeData[externalEdgeIndex].fNext = -1; // Cannot be determined until the vNext linked lists are in the correct order. edgeData[externalEdgeIndex].face = -1; edgeData[externalEdgeIndex].wrap = EdgeWrapUtility.Invert(edgeData[edgeIndex].wrap); AddEdgeToVertexUnordered(externalEdgeIndex, vertexIndex, vertexNeighborCounts, vertexFirstEdgeIndices, edgeData); vertexEdgeIndex = externalEdgeIndex; ++externalEdgeIndex; break; } } edgeData[vertexEdgeIndex].twin = edgeIndex; edgeData[edgeIndex].twin = vertexEdgeIndex; } ++edgeIndex; priorVertexIndex = vertexIndex; } } // Correct the order of the vNext linked lists, and set the face indices of the twins of all internal edges. edgeIndex = 0; for (int faceIndex = 0; faceIndex < indexer.internalFaceCount; ++faceIndex) { var neighborCount = indexer.GetNeighborCount(faceIndex); var priorEdgeIndex = edgeIndex + neighborCount - 1; for (int neighborIndex = 0; neighborIndex < neighborCount; ++neighborIndex) { var twinEdgeIndex = edgeData[priorEdgeIndex].twin; PutEdgesInSequence(edgeIndex, twinEdgeIndex, edgeData); edgeData[twinEdgeIndex].face = faceIndex; priorEdgeIndex = edgeIndex; ++edgeIndex; } } if (indexer.externalFaceCount > 0) { // Now that all vertex and edge relationships are established, locate all edges that don't have a // face relationship specified (those which are pointing out toward external faces) and spin around // each face to establish those edge -> face relationships and count external face neighbors. var faceIndex = indexer.internalFaceCount; for (edgeIndex = 0; edgeIndex < indexer.edgeCount; ++edgeIndex) { if (edgeData[edgeIndex].face == -1) { // Starting with the current edge, follow the edge links appropriately to wind around the // implicit face counter-clockwise and link the edges to the face. var neighborCount = 0; var twinEdgeIndex = edgeIndex; var faceEdgeIndex = edgeData[edgeIndex].twin; var faceFirstEdgeIndex = faceEdgeIndex; do { edgeData[twinEdgeIndex].face = faceIndex; twinEdgeIndex = edgeData[faceEdgeIndex].vNext; var nextFaceEdgeIndex = edgeData[twinEdgeIndex].twin; edgeData[nextFaceEdgeIndex].fNext = faceEdgeIndex; faceEdgeIndex = nextFaceEdgeIndex; ++neighborCount; if (neighborCount > vertexFirstEdgeIndices.Length) { throw new InvalidOperationException("Face neighbors were specified such that an external face was misconfigured."); } } while (twinEdgeIndex != edgeIndex); faceNeighborCounts[faceIndex] = (ushort)neighborCount; faceFirstEdgeIndices[faceIndex] = faceFirstEdgeIndex; ++faceIndex; } } } // Fill out the remainder of the edge wrap data, based on the possibly limited info supplied. for (edgeIndex = 0; edgeIndex < indexer.edgeCount; ++edgeIndex) { var twinIndex = edgeData[edgeIndex].twin; if (edgeIndex < twinIndex) { EdgeWrapUtility.CrossMergeTwins(ref edgeData[edgeIndex].wrap, ref edgeData[twinIndex].wrap); } } return(Topology.Create(vertexNeighborCounts, vertexFirstEdgeIndices, edgeData, faceNeighborCounts, faceFirstEdgeIndices, indexer.internalFaceCount)); }