Пример #1
0
            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);
            }
Пример #2
0
        /// <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);
                }
            }
        }