IList <IndexSegment> setupEdges(IList <int> vertxIndices) { IList <IndexSegment> indexList = new List <IndexSegment>(); int boundLinesDictOffset = 0; if (boundaryLinesDict == null) { IEqualityComparer <IndexSegment> segCompare = new SegmentCompare(); boundaryLinesDict = new Dictionary <IndexSegment, int>(segCompare); } else { boundLinesDictOffset = boundaryLinesDict.Count(); } for (int ii = 0; ii < vertxIndices.Count; ++ii) { IndexSegment segm; if (ii == vertxIndices.Count - 1) { segm = new IndexSegment(vertxIndices[ii], vertxIndices[0]); } else { segm = new IndexSegment(vertxIndices[ii], vertxIndices[ii + 1]); } indexList.Add(segm); boundaryLinesDict.Add(segm, ii + boundLinesDictOffset); // boundaryLinesDict is a dictionary for the combined outer and inner boundaries, the values should be sequential } return(indexList); }
/// <summary> /// Go through the input face list that share the same vertex and has the same normal (coplannar). /// </summary> /// <param name="inputFaceList"></param> /// <param name="outputFaceList"></param> /// <returns>True if done successfully</returns> void TryMergeFaces(List <int> inputFaceList, out List <int> outputFaceList) { outputFaceList = new List <int>(); IndexFace firstF = facesColl[inputFaceList[0]]; int currProcFace = inputFaceList[0]; inputFaceList.RemoveAt(0); // remove the first face from the list bool merged = false; IEqualityComparer <IndexSegment> segCompare = new SegmentCompare(); IDictionary <IndexSegment, Tuple <IndexFace, int, int> > segmentOfFaceDict = new Dictionary <IndexSegment, Tuple <IndexFace, int, int> >(segCompare); IList <int> discardList = new List <int>(); for (int iFace = 0; iFace < inputFaceList.Count; ++iFace) { int fidx = inputFaceList[iFace]; IndexFace IdxFace = facesColl[fidx]; if (!segmentOfFaceToDict(ref segmentOfFaceDict, ref IdxFace, fidx)) { discardList.Add(fidx); } } // Remove bad face from the input list, if any if (discardList.Count > 0) { foreach (int fidx in discardList) { inputFaceList.Remove(fidx); } } discardList.Clear(); while (inputFaceList.Count > 0) { IndexFace mergedFace = null; for (int currEdgeIdx = 0; currEdgeIdx < firstF.outerAndInnerBoundaries.Count; currEdgeIdx++) { IndexSegment currEdge = firstF.outerAndInnerBoundaries[currEdgeIdx]; IndexSegment reversedEdge = currEdge.reverse(); IndexFace currFace = null; int currFaceIdx = -1; int idx = -1; Tuple <IndexFace, int, int> pairedFace = null; if (!segmentOfFaceDict.TryGetValue(reversedEdge, out pairedFace)) { if (!segmentOfFaceDict.TryGetValue(currEdge, out pairedFace)) { merged = false; continue; } else { currFace = pairedFace.Item1; currFaceIdx = pairedFace.Item2; // Need to reverse the face boundaries. Remove the entries in the Dict first, reverse the face, and add them back for (int cidx = 0; cidx < currFace.outerAndInnerBoundaries.Count; ++cidx) { segmentOfFaceDict.Remove(currFace.outerAndInnerBoundaries[cidx]); } currFace.Reverse(); if (!segmentOfFaceToDict(ref segmentOfFaceDict, ref currFace, currFaceIdx)) { // Something is wrong with this face (should not be here in the normal circumstance), discard it and continue inputFaceList.Remove(currFaceIdx); merged = false; continue; } if (!segmentOfFaceDict.TryGetValue(reversedEdge, out pairedFace)) { if (!segmentOfFaceDict.TryGetValue(currEdge, out pairedFace)) { // Should not be here. If somehow here, discard the face and continue inputFaceList.Remove(currFaceIdx); merged = false; continue; } } idx = pairedFace.Item3; } } else { currFace = pairedFace.Item1; currFaceIdx = pairedFace.Item2; idx = pairedFace.Item3; } // Now we need to check other edges of this face whether there is other coincide edge (this is in the case of hole(s)) List <int> fFaceIdxList = new List <int>(); List <int> currFaceIdxList = new List <int>(); for (int ci = 0; ci < currFace.outerAndInnerBoundaries.Count; ci++) { if (ci == idx) { continue; // skip already known coincide edge } int ffIdx = -1; IndexSegment reL = new IndexSegment(currFace.outerAndInnerBoundaries[ci].endPIndex, currFace.outerAndInnerBoundaries[ci].startPindex); ffIdx = firstF.findMatchedIndexSegment(reL); if (ffIdx > 0) { fFaceIdxList.Add(ffIdx); // List of edges to skip when merging currFaceIdxList.Add(ci); // List of edges to skip when merging } } // Now we will remove the paired edges and merge the faces List <IndexSegment> newFaceEdges = new List <IndexSegment>(); for (int ii = 0; ii < currEdgeIdx; ii++) { bool toSkip = false; if (fFaceIdxList.Count > 0) { toSkip = fFaceIdxList.Contains(ii); } if (!toSkip) { newFaceEdges.Add(firstF.outerAndInnerBoundaries[ii]); // add the previous edges from the firstF faces first. This will skip the currEdge } } // Add the next-in-sequence edges from the second face for (int ii = idx + 1; ii < currFace.outerAndInnerBoundaries.Count; ii++) { bool toSkip = false; if (currFaceIdxList.Count > 0) { toSkip = currFaceIdxList.Contains(ii); } if (!toSkip) { newFaceEdges.Add(currFace.outerAndInnerBoundaries[ii]); } } for (int ii = 0; ii < idx; ii++) { bool toSkip = false; if (currFaceIdxList.Count > 0) { toSkip = currFaceIdxList.Contains(ii); } if (!toSkip) { newFaceEdges.Add(currFace.outerAndInnerBoundaries[ii]); } } for (int ii = currEdgeIdx + 1; ii < firstF.outerAndInnerBoundaries.Count; ii++) { bool toSkip = false; if (fFaceIdxList.Count > 0) { toSkip = fFaceIdxList.Contains(ii); } if (!toSkip) { newFaceEdges.Add(firstF.outerAndInnerBoundaries[ii]); } } // Build a new face // Important to note that the list of edges may not be continuous if there is a hole. We need to go through the list here to identify whether there is any such // discontinuity and collect the edges into their respective loops List <List <IndexSegment> > loops = new List <List <IndexSegment> >(); List <IndexSegment> loopEdges = new List <IndexSegment>(); loops.Add(loopEdges); for (int ii = 0; ii < newFaceEdges.Count; ii++) { if (ii == 0) { loopEdges.Add(newFaceEdges[ii]); } else { if (newFaceEdges[ii].startPindex == newFaceEdges[ii - 1].endPIndex) { loopEdges.Add(newFaceEdges[ii]); } else { // Discontinuity detected loopEdges = new List <IndexSegment>(); // start new loop loops.Add(loopEdges); loopEdges.Add(newFaceEdges[ii]); } } } List <List <IndexSegment> > finalLoops = new List <List <IndexSegment> >(); { while (loops.Count > 1) { // There are more than 1 loops, need to consolidate if there are fragments to combine due to their continuity between the fragments int toDelIdx = -1; for (int ii = 1; ii < loops.Count; ii++) { if (loops[0][loops[0].Count - 1].endPIndex == loops[ii][0].startPindex) { // found continuity, merge the loops List <IndexSegment> newLoop = new List <IndexSegment>(loops[0]); newLoop.AddRange(loops[ii]); finalLoops.Add(newLoop); toDelIdx = ii; break; } } if (toDelIdx > 0) { loops.RemoveAt(toDelIdx); // !!!! Important to remove the later member first before removing the first one loops.RemoveAt(0); } else { // No continuity found, copy the first loop to the final loop List <IndexSegment> newLoop = new List <IndexSegment>(loops[0]); finalLoops.Add(newLoop); loops.RemoveAt(0); } } if (loops.Count > 0) { // Add remaining list into the final loops finalLoops.AddRange(loops); } } if (finalLoops.Count > 1) { // Find the largest loop and put it in the first position signifying the outer loop and the rest are the inner loops int largestPerimeterIdx = 0; double largestPerimeter = 0.0; for (int i = 0; i < finalLoops.Count; i++) { double loopPerimeter = 0.0; foreach (IndexSegment line in finalLoops[i]) { loopPerimeter += line.extent; } if (loopPerimeter > largestPerimeter) { largestPerimeter = loopPerimeter; largestPerimeterIdx = i; } } // We need to move the largest loop into the head if it is not if (largestPerimeterIdx > 0) { List <IndexSegment> largestLoop = new List <IndexSegment>(finalLoops[largestPerimeterIdx]); finalLoops.RemoveAt(largestPerimeterIdx); finalLoops.Insert(0, largestLoop); } } // Collect the vertices from the list of Edges into list of list of vertices starting with the outer loop (largest loop) following the finalLoop IList <IList <int> > newFaceVertsLoops = new List <IList <int> >(); foreach (List <IndexSegment> loop in finalLoops) { IList <int> newFaceVerts = new List <int>(); for (int ii = 0; ii < loop.Count; ii++) { if (ii == 0) { newFaceVerts.Add(loop[ii].startPindex); newFaceVerts.Add(loop[ii].endPIndex); } else if (ii == loop.Count - 1) // Last { // Add nothing as the last segment ends at the first vertex } else { newFaceVerts.Add(loop[ii].endPIndex); } } // close the loop with end point from the starting point (it is important to mark the end of loop and if there is any other vertex follow, they start the inner loop) if (newFaceVerts.Count > 0) { if (newFaceVerts.Count >= 3) { newFaceVertsLoops.Add(newFaceVerts); } else { // Something wrong, a face cannot have less than 3 vertices Debug.WriteLine("Something went wrong when merging faces resulting with a loop that has less than 3 vertices"); } } } mergedFace = new IndexFace(newFaceVertsLoops); inputFaceList.Remove(currFaceIdx); // Remove the merged face from segmentOfFaceDict IList <IndexSegment> rem = facesColl[currFaceIdx].outerAndInnerBoundaries; for (int cidx = 0; cidx < rem.Count; ++cidx) { segmentOfFaceDict.Remove(rem[cidx]); } merged = true; break; // Once there is an edge merged, create a new face and continue the process of merging } int lastFaceID = facesColl.Count; // The new index is always the next one in the collection was inserted based on the seq order if (mergedFace != null) { facesColl.Add(lastFaceID, mergedFace); } if (!merged) { // No edge match for this face, add the face into the output face list and move to the next face in the input list outputFaceList.Add(currProcFace); if (inputFaceList.Count > 0) { firstF = facesColl[inputFaceList[0]]; // Remove the merged face from segmentOfFaceDict IList <IndexSegment> rem = firstF.outerAndInnerBoundaries; for (int cidx = 0; cidx < rem.Count; ++cidx) { segmentOfFaceDict.Remove(rem[cidx]); } currProcFace = inputFaceList[0]; // keep the last processed item inputFaceList.RemoveAt(0); // remove the first face from the list merged = false; // If there is no more face to process, add the merged face into the output list if (inputFaceList.Count == 0) { outputFaceList.Add(currProcFace); } } } else { // If there is no more face to process, add the merged face into the output list if (inputFaceList.Count == 0) { outputFaceList.Add(lastFaceID); } if (inputFaceList.Count > 0) { // use the current face as the next face as a merge candidate firstF = mergedFace; currProcFace = lastFaceID; merged = false; } } } // Finally, there may be multiple faces left because there are multiple disconnected faces at the same normal. Collect them and return if (segmentOfFaceDict.Count > 0) { HashSet <int> indexFaces = new HashSet <int>(); foreach (KeyValuePair <IndexSegment, Tuple <IndexFace, int, int> > segmentFace in segmentOfFaceDict) { indexFaces.Add(segmentFace.Value.Item2); } foreach (int idxFace in indexFaces) { outputFaceList.Add(idxFace); } } }