IList <IndexSegment> setupEdges(IList <int> vertxIndices) { IList <IndexSegment> indexList = new List <IndexSegment>(); int boundLinesDictOffset = 0; if (boundaryLinesDict == null) { IEqualityComparer <IndexSegment> segCompare = new SegmentComparer(false /*compareBothDirections*/); 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); }
void SetupEdges(IList <int> vertxIndices, int idxOffset) { int boundLinesDictOffset = 0; if (BoundaryLinesDict == null) { IEqualityComparer <IndexSegment> segCompare = new SegmentComparer(false /*compareBothDirections*/); BoundaryLinesDict = new Dictionary <IndexSegment, int>(segCompare); } else { boundLinesDictOffset = BoundaryLinesDict.Count(); } int prevIdx = 0; int idx = 0; int vertCount = vertxIndices.Count; foreach (int vIdx in vertxIndices) { IndexSegment segm = null; if (idx > 0) { segm = new IndexSegment(prevIdx, vIdx); OuterAndInnerBoundaries.Add(idx - 1 + idxOffset, segm); BoundaryLinesDict.Add(segm, (idx - 1 + boundLinesDictOffset)); // boundaryLinesDict is a dictionary for the combined outer and inner boundaries, the values should be sequential } if (idx == vertCount - 1) // The last index. Close the loop by connecing it to the first index { segm = new IndexSegment(vIdx, vertxIndices[0]); OuterAndInnerBoundaries.Add((idx + idxOffset), segm); BoundaryLinesDict.Add(segm, (idx + boundLinesDictOffset)); // boundaryLinesDict is a dictionary for the combined outer and inner boundaries, the values should be sequential } prevIdx = vIdx; idx++; } }
/// <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 = m_FacesCollDict[inputFaceList[0]]; int currProcFace = inputFaceList[0]; inputFaceList.RemoveAt(0); // remove the first face from the list bool merged = false; IEqualityComparer <IndexSegment> segCompare = new SegmentComparer(false /*compareBothDirections*/); IDictionary <IndexSegment, Tuple <int, int> > segmentOfFaceDict = new Dictionary <IndexSegment, Tuple <int, int> >(segCompare); IList <int> discardList = new List <int>(); foreach (int fidx in inputFaceList) { if (!SegmentOfFaceToDict(ref segmentOfFaceDict, 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 <int, int> pairedFace = null; if (!segmentOfFaceDict.TryGetValue(reversedEdge, out pairedFace)) { if (!segmentOfFaceDict.TryGetValue(currEdge, out pairedFace)) { merged = false; continue; } else { currFaceIdx = pairedFace.Item1; currFace = m_FacesCollDict[currFaceIdx]; // 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, 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.Item2; } } else { currFaceIdx = pairedFace.Item1; currFace = m_FacesCollDict[currFaceIdx]; idx = pairedFace.Item2; } // 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>(); int ci = -1; foreach (KeyValuePair <int, IndexSegment> idxSeg in currFace.OuterAndInnerBoundaries) { ci++; if (ci == idx) { continue; // skip already known coincide edge } int ffIdx = -1; IndexSegment reL = new IndexSegment(idxSeg.Value.EndPIndex, idxSeg.Value.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); IndexSegment prevSegm = newFaceEdges[0]; foreach (IndexSegment idxSeg in newFaceEdges) { if (prevSegm == idxSeg) { loopEdges.Add(idxSeg); } else { if (idxSeg.StartPindex == prevSegm.EndPIndex) { loopEdges.Add(idxSeg); } else { // Discontinuity detected loopEdges = new List <IndexSegment>(); // start new loop loops.Add(loopEdges); loopEdges.Add(idxSeg); } } prevSegm = idxSeg; } 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(ref m_MeshVertices); } 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>(); foreach (IndexSegment idxSeg in loop) { newFaceVerts.Add(idxSeg.StartPindex); } 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, ref m_MeshVertices); inputFaceList.Remove(currFaceIdx); // Remove the merged face from segmentOfFaceDict foreach (KeyValuePair <int, IndexSegment> idxSeg in m_FacesCollDict[currFaceIdx].OuterAndInnerBoundaries) { segmentOfFaceDict.Remove(idxSeg.Value); } if (m_FacesCollDict.ContainsKey(currFaceIdx)) { m_FacesCollDict.Remove(currFaceIdx); } merged = true; break; // Once there is an edge merged, create a new face and continue the process of merging } int lastFaceID = faceIdxOffset++; // The new index is always the next one in the collection was inserted based on the seq order if (mergedFace != null) { m_FacesCollDict.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 = m_FacesCollDict[inputFaceList[0]]; // Remove the merged face from segmentOfFaceDict foreach (KeyValuePair <int, IndexSegment> idxSeg in firstF.OuterAndInnerBoundaries) { segmentOfFaceDict.Remove(idxSeg.Value); } 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); } // Remove merged face from the Dict if (m_FacesCollDict.ContainsKey(currProcFace)) { m_FacesCollDict.Remove(currProcFace); } 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 <int, int> > segmentFace in segmentOfFaceDict) { indexFaces.Add(segmentFace.Value.Item1); } foreach (int idxFace in indexFaces) { outputFaceList.Add(idxFace); } } }