Esempio n. 1
0
        /// <summary>
        /// Combine coplanar triangles from the faceted body if they share the edge. From this process, polygonal faces (with or without holes) will be created
        /// </summary>
        public void SimplifyAndMergeFaces()
        {
            int noTriangle = _geom.TriangleCount;
            int noVertices = _geom.VertexCount;
            IEqualityComparer <XYZ>       normalComparer     = new vectorCompare();
            Dictionary <XYZ, List <int> > faceSortedByNormal = new Dictionary <XYZ, List <int> >(normalComparer);

            for (int ef = 0; ef < noTriangle; ++ef)
            {
                TriangleInShellComponent f = _geom.GetTriangle(ef);
                IList <int> vertIndex      = new List <int>();
                vertIndex.Add(f.VertexIndex0);
                vertIndex.Add(f.VertexIndex1);
                vertIndex.Add(f.VertexIndex2);

                IndexFace intF = new IndexFace(vertIndex);
                facesColl.Add(ef, intF);     // Keep faces in a dictionary and assigns ID
                List <int> fIDList;

                if (!faceSortedByNormal.TryGetValue(intF.normal, out fIDList))
                {
                    fIDList = new List <int>();
                    fIDList.Add(ef);
                    faceSortedByNormal.Add(intF.normal, fIDList);
                }
                else
                {
                    if (!fIDList.Contains(ef))
                    {
                        fIDList.Add(ef);
                    }
                }
            }

            foreach (KeyValuePair <XYZ, List <int> > fListDict in faceSortedByNormal)
            {
                List <int> mergedFaceList = null;
                if (fListDict.Value.Count > 1)
                {
                    TryMergeFaces(fListDict.Value, out mergedFaceList);
                    if (mergedFaceList != null && mergedFaceList.Count > 0)
                    {
                        // insert only new face indexes as the mergedlist from different vertices can be duplicated
                        foreach (int fIdx in mergedFaceList)
                        {
                            if (!_mergedFaceList.Contains(fIdx))
                            {
                                _mergedFaceList.Add(fIdx);
                            }
                        }
                    }
                }
                else if (!_mergedFaceList.Contains(fListDict.Value[0]))
                {
                    _mergedFaceList.Add(fListDict.Value[0]); // No pair face, add it into the mergedList
                }
            }
        }
Esempio n. 2
0
        bool SegmentOfFaceToDict(ref IDictionary <IndexSegment, Tuple <int, int> > segmentOfFaceDict, int indexFace)
        {
            IList <IndexSegment> entriesToRollback = new List <IndexSegment>();
            IndexFace            theFace           = m_FacesCollDict[indexFace];

            try
            {
                int idx = 0;
                foreach (KeyValuePair <int, IndexSegment> idxSeg in theFace.OuterAndInnerBoundaries)
                {
                    segmentOfFaceDict.Add(idxSeg.Value, new Tuple <int, int>(indexFace, idx++));
                    entriesToRollback.Add(idxSeg.Value);
                }
                return(true);
            }
            catch
            {
                // If exception, it is likely that there is duplicate. Remove all segments of this face first to rollback
                foreach (IndexSegment segDel in entriesToRollback)
                {
                    segmentOfFaceDict.Remove(segDel);
                }
                entriesToRollback.Clear();
            }

            theFace.Reverse();
            try
            {
                int idx = 0;
                foreach (KeyValuePair <int, IndexSegment> idxSeg in theFace.OuterAndInnerBoundaries)
                {
                    segmentOfFaceDict.Add(idxSeg.Value, new Tuple <int, int>(indexFace, idx++));
                    entriesToRollback.Add(idxSeg.Value);
                }
                return(true);
            }
            catch
            {
                // If still raises an exception (that shouldn't be). It is likely there is simple duplicate face. Cleanup, and return false;
                foreach (IndexSegment segDel in entriesToRollback)
                {
                    segmentOfFaceDict.Remove(segDel);
                }
                return(false);
            }
        }
Esempio n. 3
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       = 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);
                }
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Combine coplanar triangles from the faceted body if they share the edge. From this process, polygonal faces (with or without holes) will be created
        /// </summary>
        public void SimplifyAndMergeFaces(bool ignoreMerge = false)
        {
            int eulerBefore = ignoreMerge ? 0 : CalculateEulerCharacteristic();

            int noTriangle = GetTriangleCount();
            IEqualityComparer <XYZ>       normalComparer     = new VectorCompare();
            Dictionary <XYZ, List <int> > faceSortedByNormal = new Dictionary <XYZ, List <int> >(normalComparer);

            for (int ef = 0; ef < noTriangle; ++ef)
            {
                IList <int> vertIndex = new List <int>();

                if (IsMesh)
                {
                    MeshTriangle f = m_MeshGeom.get_Triangle(ef);
                    vertIndex = new List <int>(3)
                    {
                        (int)f.get_Index(0), (int)f.get_Index(1), (int)f.get_Index(2)
                    };
                }
                else
                {
                    TriangleInShellComponent f = m_Geom.GetTriangle(ef);
                    vertIndex = new List <int>(3)
                    {
                        f.VertexIndex0, f.VertexIndex1, f.VertexIndex2
                    };
                }

                IndexFace intF = new IndexFace(vertIndex, ref m_MeshVertices);
                m_FacesCollDict.Add(faceIdxOffset++, intF);     // Keep faces in a dictionary and assigns ID
                List <int> fIDList;

                if (!faceSortedByNormal.TryGetValue(intF.Normal, out fIDList))
                {
                    fIDList = new List <int>(1)
                    {
                        ef
                    };
                    faceSortedByNormal.Add(intF.Normal, fIDList);
                }
                else if (!fIDList.Contains(ef))
                {
                    fIDList.Add(ef);
                }
            }

            foreach (KeyValuePair <XYZ, List <int> > fListDict in faceSortedByNormal)
            {
                List <int> mergedFaceList = null;
                if (fListDict.Value.Count > 1)
                {
                    if (!ignoreMerge)
                    {
                        TryMergeFaces(fListDict.Value, out mergedFaceList);
                    }
                    else
                    {
                        // keep original face list
                        mergedFaceList = fListDict.Value;
                    }
                    if (mergedFaceList != null && mergedFaceList.Count > 0)
                    {
                        // insert only new face indexes as the mergedlist from different vertices can be duplicated
                        foreach (int fIdx in mergedFaceList)
                        {
                            if (!m_MergedFaceSet.Contains(fIdx))
                            {
                                m_MergedFaceSet.Add(fIdx);
                            }
                        }
                    }
                }
                else if (!m_MergedFaceSet.Contains(fListDict.Value[0]))
                {
                    m_MergedFaceSet.Add(fListDict.Value[0]); // No pair face, add it into the mergedList
                }
            }

            // Remove unused vertices
            CleanVerticesAndUpdateIndexes();

            int eulerAfter = ignoreMerge ? 0 : CalculateEulerCharacteristic();

            if (eulerBefore != eulerAfter)
            {
                throw new InvalidOperationException(); // Coplanar merge broke the mesh in some way, so we need to fall back to exporting a triangular mesh
            }
        }
Esempio n. 5
0
        bool segmentOfFaceToDict(ref IDictionary <IndexSegment, Tuple <IndexFace, int, int> > segmentOfFaceDict, ref IndexFace theFace, int indexFace)
        {
            IList <IndexSegment> entriesToRollback = new List <IndexSegment>();

            try
            {
                for (int idx = 0; idx < theFace.outerAndInnerBoundaries.Count; ++idx)
                {
                    segmentOfFaceDict.Add(theFace.outerAndInnerBoundaries[idx], new Tuple <IndexFace, int, int>(theFace, indexFace, idx));
                    entriesToRollback.Add(theFace.outerAndInnerBoundaries[idx]);
                }
                return(true);
            }
            catch
            {
                // If exception, it is likely that there is duplicate. Remove all segments of this face first to rollback
                foreach (IndexSegment segDel in entriesToRollback)
                {
                    segmentOfFaceDict.Remove(segDel);
                }
                entriesToRollback.Clear();
            }

            theFace.Reverse();
            try
            {
                for (int idx = 0; idx < theFace.outerAndInnerBoundaries.Count; ++idx)
                {
                    segmentOfFaceDict.Add(theFace.outerAndInnerBoundaries[idx], new Tuple <IndexFace, int, int>(theFace, indexFace, idx));
                    entriesToRollback.Add(theFace.outerAndInnerBoundaries[idx]);
                }
                return(true);
            }
            catch
            {
                // If still raises an exception (that shouldn't be). It is likely there is simple duplicate face. Cleanup, and return false;
                foreach (IndexSegment segDel in entriesToRollback)
                {
                    segmentOfFaceDict.Remove(segDel);
                }
                return(false);
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Combine coplanar triangles from the faceted body if they share the edge. From this process, polygonal faces (with or without holes) will be created
        /// </summary>
        public void SimplifyAndMergeFaces()
        {
            int eulerBefore = CalculateEulerCharacteristic();

            int noTriangle = (IsMesh) ? _meshGeom.NumTriangles : _geom.TriangleCount;
            int noVertices = (IsMesh) ? _meshGeom.Vertices.Count : _geom.VertexCount;
            IEqualityComparer <XYZ>       normalComparer     = new vectorCompare();
            Dictionary <XYZ, List <int> > faceSortedByNormal = new Dictionary <XYZ, List <int> >(normalComparer);

            for (int ef = 0; ef < noTriangle; ++ef)
            {
                IList <int> vertIndex = new List <int>();

                if (IsMesh)
                {
                    MeshTriangle f = _meshGeom.get_Triangle(ef);
                    vertIndex.Add((int)f.get_Index(0));
                    vertIndex.Add((int)f.get_Index(1));
                    vertIndex.Add((int)f.get_Index(2));
                }
                else
                {
                    TriangleInShellComponent f = _geom.GetTriangle(ef);
                    vertIndex.Add(f.VertexIndex0);
                    vertIndex.Add(f.VertexIndex1);
                    vertIndex.Add(f.VertexIndex2);
                }

                IndexFace intF = new IndexFace(vertIndex);
                facesColl.Add(ef, intF);     // Keep faces in a dictionary and assigns ID
                List <int> fIDList;

                if (!faceSortedByNormal.TryGetValue(intF.normal, out fIDList))
                {
                    fIDList = new List <int>();
                    fIDList.Add(ef);
                    faceSortedByNormal.Add(intF.normal, fIDList);
                }
                else
                {
                    if (!fIDList.Contains(ef))
                    {
                        fIDList.Add(ef);
                    }
                }
            }

            foreach (KeyValuePair <XYZ, List <int> > fListDict in faceSortedByNormal)
            {
                List <int> mergedFaceList = null;
                if (fListDict.Value.Count > 1)
                {
                    TryMergeFaces(fListDict.Value, out mergedFaceList);
                    if (mergedFaceList != null && mergedFaceList.Count > 0)
                    {
                        // insert only new face indexes as the mergedlist from different vertices can be duplicated
                        foreach (int fIdx in mergedFaceList)
                        {
                            if (!_mergedFaceList.Contains(fIdx))
                            {
                                _mergedFaceList.Add(fIdx);
                            }
                        }
                    }
                }
                else if (!_mergedFaceList.Contains(fListDict.Value[0]))
                {
                    _mergedFaceList.Add(fListDict.Value[0]); // No pair face, add it into the mergedList
                }
            }

            int eulerAfter = CalculateEulerCharacteristic();

            if (eulerBefore != eulerAfter)
            {
                throw new InvalidOperationException(); // Coplanar merge broke the mesh in some way, so we need to fall back to exporting a triangular mesh
            }
        }
Esempio n. 7
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>
        bool TryMergeFaces(List <int> inputFaceList, out List <int> outputFaceList)
        {
            outputFaceList = new List <int>();
            IndexFace firstF = facesColl[inputFaceList[0]];
            //int prevFirstFIdx = 0;
            HashSet <int> mergedFacesIdxList = new HashSet <int>();

            mergedFacesIdxList.Add(inputFaceList[0]);

            inputFaceList.RemoveAt(0); // remove the first face from the list
            int  currEdgeIdx = 0;
            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 (currEdgeIdx < firstF.outerAndInnerBoundaries.Count && inputFaceList.Count > 0)
            {
                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))
                        {
                            currEdgeIdx++;
                            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);
                                currEdgeIdx++;
                                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);
                                    currEdgeIdx++;
                                    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");
                            }
                        }
                    }

                    firstF = new IndexFace(newFaceVertsLoops);

                    currEdgeIdx  = 0;
                    reversedEdge = new IndexSegment(firstF.outerAndInnerBoundaries[currEdgeIdx].endPIndex, firstF.outerAndInnerBoundaries[currEdgeIdx].startPindex);

                    mergedFacesIdxList.Add(currFaceIdx);
                    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;
                }

                if (!merged)
                {
                    currEdgeIdx++;
                }
                if (merged || currEdgeIdx == firstF.outerAndInnerBoundaries.Count)
                {
                    int lastFaceID = facesColl.Count; // The new index is always the next one in the collection was inserted based on the seq order

                    facesColl.Add(lastFaceID, firstF);
                    //prevFirstFIdx = lastFaceID;

                    // If there is no more face to process, add the merged face into the output list
                    if (inputFaceList.Count == 0)
                    {
                        outputFaceList.Add(lastFaceID);
                    }

                    // Now loop through all the dictionary of the sortedVert and replace all merged face indexes with the new one
                    foreach (KeyValuePair <int, HashSet <int> > v in sortedFVert)
                    {
                        HashSet <int> fIndexes = v.Value;
                        bool          replaced = false;
                        foreach (int Idx in mergedFacesIdxList)
                        {
                            replaced |= fIndexes.Remove(Idx);
                            _mergedFaceList.Remove(Idx); // Remove the idx face also from _mergeFaceList as some faces might be left unmerged in the previous step(s)
                        }
                        if (replaced)
                        {
                            fIndexes.Add(lastFaceID); // replace the merged face indexes with the new merged face index
                        }
                    }

                    if (inputFaceList.Count > 0)
                    {
                        firstF = facesColl[inputFaceList[0]];
                        mergedFacesIdxList.Clear();
                        mergedFacesIdxList.Add(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]);
                        }
                        inputFaceList.RemoveAt(0); // remove the first face from the list
                        currEdgeIdx = 0;
                        merged      = false;

                        // If there is still more face to process, add the merged face into the list to be processed futher
                        // Add new face to segmentOfFaceDict
                        IndexFace newFace = facesColl[lastFaceID];
                        if (segmentOfFaceToDict(ref segmentOfFaceDict, ref newFace, lastFaceID))
                        {
                            inputFaceList.Add(lastFaceID);
                        }
                    }
                }
            }

            return(merged);
        }
Esempio n. 8
0
        /// <summary>
        /// Combine coplanar triangles from the faceted body if they share the edge. From this process, polygonal faces (with or without holes) will be created
        /// </summary>
        public void SimplifyAndMergeFaces()
        {
            int noTriangle = _geom.TriangleCount;
            int noVertices = _geom.VertexCount;

            for (int ef = 0; ef < noTriangle; ++ef)
            {
                TriangleInShellComponent f = _geom.GetTriangle(ef);
                IList <int> vertIndex      = new List <int>();
                vertIndex.Add(f.VertexIndex0);
                vertIndex.Add(f.VertexIndex1);
                vertIndex.Add(f.VertexIndex2);

                IndexFace intF = new IndexFace(vertIndex);
                facesColl.Add(ef, intF);     // Keep faces in a dictionary and assigns ID
                SortVertAndFaces(f.VertexIndex0, ef);
                SortVertAndFaces(f.VertexIndex1, ef);
                SortVertAndFaces(f.VertexIndex2, ef);
            }

            // After the above, we have a sorted polyhedron vertices that contains hashset of faces it belongs to
            // Loop through the dictionary to merge faces that have the same normal (on the same plane)
            foreach (KeyValuePair <int, HashSet <int> > dictItem in sortedFVert)
            {
                IEqualityComparer <XYZ>       normalComparer     = new vectorCompare();
                Dictionary <XYZ, List <int> > faceSortedByNormal = new Dictionary <XYZ, List <int> >(normalComparer);
                List <int> fIDList;

                foreach (int fID in dictItem.Value)
                {
                    IndexFace f = facesColl[fID];

                    if (!faceSortedByNormal.TryGetValue(f.normal, out fIDList))
                    {
                        fIDList = new List <int>();
                        fIDList.Add(fID);
                        faceSortedByNormal.Add(f.normal, fIDList);
                    }
                    else
                    {
                        if (!fIDList.Contains(fID))
                        {
                            fIDList.Add(fID);
                        }
                    }
                }

                foreach (KeyValuePair <XYZ, List <int> > fListDict in faceSortedByNormal)
                {
                    List <int> mergedFaceList = null;
                    if (fListDict.Value.Count > 1)
                    {
                        TryMergeFaces(fListDict.Value, out mergedFaceList);
                        if (mergedFaceList != null && mergedFaceList.Count > 0)
                        {
                            // insert only new face indexes as the mergedlist from different vertices can be duplicated
                            foreach (int fIdx in mergedFaceList)
                            {
                                if (!_mergedFaceList.Contains(fIdx))
                                {
                                    _mergedFaceList.Add(fIdx);
                                }
                            }
                        }
                    }
                    else
                    if (!_mergedFaceList.Contains(fListDict.Value[0]))
                    {
                        _mergedFaceList.Add(fListDict.Value[0]); // No pair face, add it into the mergedList
                    }
                }
            }
        }