Esempio n. 1
0
        /// <summary>
        /// Build boundary polygon. Returns either a <see cref="Polygon"/> or <see cref="MultiPolygon"/>
        /// depending on whether the mesh is fully connected or consist of independent parts.
        /// <para>
        /// To always return a <see cref="MultiPolygon"/>, set the <paramref name="alwaysMultiPolygon"/> to true.
        /// </para>
        /// </summary>
        private static Geometry BuildBoundaryGeometry(CMeshData mesh, List <CMeshFace> boundaryFaces, bool alwaysMultiPolygon)
        {
            // There will be one polygon for each connected sub mesh, in case there is more than one.

            //System.Diagnostics.Stopwatch timer = MeshExtensions.StartTimer();
            // Find all connected sub meshes.
            SubMeshes subMeshes = mesh.FindConnectedSubMeshes();
            //timer.ReportAndRestart("FindConnectedSubMeshes " + subMeshes.NumberOfSubMeshes);

            BoundarySegmentsBuilder bsb = new BoundarySegmentsBuilder(mesh);

            if (subMeshes.NumberOfSubMeshes == 1)
            {
                Polygon boundaryPoly = BuildSubMeshBoundaryGeometry(mesh, bsb, boundaryFaces);
                if (!alwaysMultiPolygon)
                {
                    return(boundaryPoly);
                }
                MultiPolygon multiPolygon = new MultiPolygon(new Polygon[] { boundaryPoly });
                return(multiPolygon);
            }

            else // More than one sub-mesh - make a Polygon for each sub mesh
            {
                // Find boundary faces for each sub mesh
                List <List <CMeshFace> > subMeshesBoundaryFaces = new List <List <CMeshFace> >(subMeshes.NumberOfSubMeshes);
                for (int i = 0; i < subMeshes.NumberOfSubMeshes; i++)
                {
                    subMeshesBoundaryFaces.Add(new List <CMeshFace>());
                }
                for (int i = 0; i < boundaryFaces.Count; i++)
                {
                    CMeshFace bcFace    = boundaryFaces[i];
                    int       subMeshId = subMeshes.ElmtSubMesh[bcFace.LeftElement.Index];
                    // subMeshId's starts from 1
                    subMeshesBoundaryFaces[subMeshId - 1].Add(bcFace);
                }

                // Make boundary polygon for each sub mesh - ordered as in SubMeshInfos to get largest parts first
                List <Polygon> polygons = new List <Polygon>(subMeshes.NumberOfSubMeshes);
                foreach (SubMeshInfo subMeshInfo in subMeshes.SubMeshInfos)
                {
                    List <CMeshFace> subMeshBoundaryFaces = subMeshesBoundaryFaces[subMeshInfo.SubMeshId - 1];
                    Polygon          subMeshBoundaryPoly  = BuildSubMeshBoundaryGeometry(mesh, bsb, subMeshBoundaryFaces);
                    polygons.Add(subMeshBoundaryPoly);
                }

                MultiPolygon multiPoly = new MultiPolygon(polygons.ToArray());
                return(multiPoly);
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Provided a list of all faces in the mesh, extract faces on the boundary - unsorted
        /// </summary>
        public static List <CMeshFace> ExtractBoundaryFaces(List <CMeshFace> meshFaces)
        {
            List <CMeshFace> bcs = new List <CMeshFace>();

            for (int i = 0; i < meshFaces.Count; i++)
            {
                CMeshFace meshFace = meshFaces[i];
                if (meshFace.IsBoundaryFace())
                {
                    bcs.Add(meshFace);
                }
            }
            return(bcs);
        }
Esempio n. 3
0
            /// <summary>
            /// Depth First Search visit method
            /// </summary>
            /// <param name="elementIndex">Start element</param>
            /// <param name="subMeshId">Sub mesh ID</param>
            /// <returns>Number of elements in sub mesh</returns>
            private int BFSVisit(int elementIndex, int subMeshId)
            {
                int numElmtsInSubMesh = 0;

                // Element has been discovered
                _stack.Push(elementIndex);
                ElmtSubMesh[elementIndex] = subMeshId;
                numElmtsInSubMesh++;

                while (_stack.Count > 0)
                {
                    int elmt = _stack.Pop();

                    List <CMeshFace> elmtFaces = _meshData.Elements[elmt].Faces;
                    for (int i = 0; i < elmtFaces.Count; i++)
                    {
                        // Find element on the other side of the face
                        CMeshFace elmtFace = elmtFaces[i];

                        // Boundary faces never has an element on the other side
                        if (elmtFace.IsBoundaryFace())
                        {
                            continue;
                        }

                        int otherElmt;
                        if (elmtFace.LeftElement.Index == elmt)
                        {
                            otherElmt = elmtFace.RightElement.Index;
                        }
                        else
                        {
                            otherElmt = elmtFace.LeftElement.Index;
                        }

                        // Check if we have already visited otherElmt
                        if (ElmtSubMesh[otherElmt] == 0)
                        {
                            // Element has been discovered
                            _stack.Push(otherElmt);
                            ElmtSubMesh[otherElmt] = subMeshId;
                            numElmtsInSubMesh++;
                        }
                    }
                }

                return(numElmtsInSubMesh);
            }
Esempio n. 4
0
        /// <summary>
        /// Return all boundary faces. Unsorted
        /// </summary>
        /// <param name="meshData">MeshData object to get boundary faces for</param>
        /// <param name="checkAllFaces">In case boundary codes are set incorrectly, this will check all faces</param>
        public static List <CMeshFace> GetBoundaryFaces(this CMeshData meshData, bool checkAllFaces)
        {
            //System.Diagnostics.Stopwatch timer = MeshExtensions.StartTimer();

            List <CMeshFace>[] facesFromNode = new List <CMeshFace> [meshData.NumberOfNodes];

            // Preallocate list of face on all nodes - used in next loop
            for (int i = 0; i < meshData.NumberOfNodes; i++)
            {
                facesFromNode[i] = new List <CMeshFace>();
            }

            // Create all potential boundary faces - those having
            // boundary code on both to-node and from-node
            // (not all those need to be boundary faces).
            for (int ielmt = 0; ielmt < meshData.NumberOfElements; ielmt++)
            {
                CreateAddElementFaces(meshData, facesFromNode, ielmt, checkAllFaces);
            }

            // Figure out boundary code and store all boundary faces
            List <CMeshFace> boundaryFaces = new List <CMeshFace>();

            for (int i = 0; i < facesFromNode.Length; i++)
            {
                List <CMeshFace> facesFromThisNode = facesFromNode[i];
                for (int j = 0; j < facesFromThisNode.Count; j++)
                {
                    CMeshFace face = facesFromThisNode[j];
                    // Only take those with fromNode matching this node -
                    // otherwise they are taken twice.
                    //if (face.FromNode.Index == i)
                    {
                        face.SetBoundaryCode();
                        if (face.IsBoundaryFace())
                        {
                            boundaryFaces.Add(face);
                        }
                    }
                }
            }

            //timer.Report("GetBoundaryFaces");
            return(boundaryFaces);
        }
Esempio n. 5
0
        /// <summary>
        /// Create and add a face.
        /// <para>
        /// A face is only "added once", i.e. when two elements share the face, it is found twice,
        /// once defined as "toNode"-"fromNode" and once as "fromNode"-"toNode". The second time,
        /// the existing face is being reused, and the element is added as the <see cref="CMeshFace.RightElement"/>
        /// </para>
        /// <para>
        /// The <see cref="CMeshFace"/> is added to the global list of faces, and also to tne nodes list of faces.
        /// </para>
        /// </summary>
        private void AddFace(MeshElement element, MeshNode fromNode, MeshNode toNode)
        {
            List <CMeshFace> fromNodeFaces = fromNode.Faces;
            List <CMeshFace> toNodeFaces   = toNode.Faces;

            // Try find "reverse face" going from to-node to from-node.
            // The FindIndex with delegate is 10+ times slower than the tight loop below.
            //int reverseFaceIndex = toNodeFaces.FindIndex(mf => mf.ToNode == fromNode);
            int reverseToNodeFaceIndex = -1;

            // Look in all faces starting from toNode
            for (int i = 0; i < toNodeFaces.Count; i++)
            {
                // Check if the face goes to fromNode
                if (toNodeFaces[i].ToNode == fromNode)
                {
                    reverseToNodeFaceIndex = i;
                    break;
                }
            }

            if (reverseToNodeFaceIndex >= 0)
            {
                // Found reverse face, reuse it and add the element as the RightElement
                CMeshFace reverseFace = toNodeFaces[reverseToNodeFaceIndex];
                reverseFace.RightElement = element;
            }
            else
            {
                // Found new face, set element as LeftElement and add it to both from-node and to-node
                CMeshFace meshFace = new CMeshFace(fromNode, toNode)
                {
                    LeftElement = element,
                };

                Faces.Add(meshFace);
                fromNodeFaces.Add(meshFace);
                // Adding to toNodeFaces is not required for the algorithm to work,
                // however, it is required in order to get NodesFaces lists right
                toNodeFaces.Add(meshFace);
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Create and add a face - special version for <see cref="MeshBoundaryExtensions.GetBoundaryFaces(MeshData)"/>
        /// <para>
        /// A face is only "added once", i.e. when two elements share the face, it is found twice,
        /// once defined as "toNode"-"fromNode" and once as "fromNode"-"toNode". The second time,
        /// the existing face is being reused, and the element is added as the <see cref="CMeshFace.RightElement"/>
        /// </para>
        /// <para>
        /// The <see cref="CMeshFace"/> is added to the global list of faces, and also to tne nodes list of faces.
        /// </para>
        /// </summary>
        internal static void AddFace(MeshElement element, MeshNode fromNode, MeshNode toNode, List <CMeshFace>[] nodeFaces)
        {
            List <CMeshFace> fromNodeFaces = nodeFaces[fromNode.Index];
            List <CMeshFace> toNodeFaces   = nodeFaces[toNode.Index];

            // Try find "reverse face" going from from-node to to-node.
            // The FindIndex with delegate is 10+ times slower than the tight loop below.
            //int reverseFaceIndex = toNodeFaces.FindIndex(mf => mf.ToNode == fromNode);
            int reverseFaceIndex = -1;

            for (int i = 0; i < toNodeFaces.Count; i++)
            {
                if (toNodeFaces[i].ToNode == fromNode)
                {
                    reverseFaceIndex = i;
                    break;
                }
            }

            if (reverseFaceIndex >= 0)
            {
                // Found reverse face, reuse it and add the element as the RightElement
                CMeshFace reverseFace = toNodeFaces[reverseFaceIndex];
                reverseFace.RightElement = element;
            }
            else
            {
                // Found new face, set element as LeftElement and add it to both from-node and to-node
                CMeshFace meshFace = new CMeshFace(fromNode, toNode)
                {
                    LeftElement = element,
                };

                fromNodeFaces.Add(meshFace);
            }
        }
Esempio n. 7
0
            /// <summary>
            /// Build boundary segments, i.e. connect the faces to lines when possible.
            /// If provided all mesh faces, this will provide the outer boundary as
            /// one segment and all holes in the mesh etc. as individual section.
            /// There is no guarantee that the first one is the outer boundary.
            /// </summary>
            public List <LinkedList <int> > BuildBoundarySegments(List <CMeshFace> faces)
            {
                if (faces.Count == 0)
                {
                    return(null);
                }

                for (int iface = 0; iface < faces.Count; iface++)
                {
                    int fromNode = faces[iface].FromNode.Index;
                    if (_fromNodeFace[fromNode] == -1)
                    {
                        // The first time the fromNode is a from-node
                        _fromNodeFace[fromNode] = iface;
                    }
                    else if (_fromNodeFace[fromNode] == -2)
                    {
                        // The third or even more time the fromNode is a from-node
                        _fromNodeFaceDict[fromNode].Add(iface);
                    }
                    else
                    {
                        // The second time the fromNode is a from-node
                        List <int> list = new List <int>();
                        list.Add(_fromNodeFace[fromNode]);
                        list.Add(iface);
                        _fromNodeFaceDict.Add(fromNode, list);
                        _fromNodeFace[fromNode] = -2;
                    }
                }

                // Array telling which boundary segment a given face belongs to
                int[] faceSegmentIndex = new int[faces.Count];
                for (int ii = 0; ii < faceSegmentIndex.Length; ii++)
                {
                    faceSegmentIndex[ii] = -1;
                }

                // All segments build by the list of faces
                List <LinkedList <int> > segments = new List <LinkedList <int> >();

                // Make sure to visit all faces
                for (int i = 0; i < faces.Count; i++)
                {
                    // Check if this face has already been visited.
                    if (faceSegmentIndex[i] >= 0)
                    {
                        continue;
                    }

                    // Start new boundary segment with face i
                    int currentSegmentIndex         = segments.Count;
                    int currentFaceIndex            = i;
                    LinkedList <int> currentSegment = new LinkedList <int>();
                    // Add current face to segment
                    currentSegment.AddLast(currentFaceIndex);
                    faceSegmentIndex[currentFaceIndex] = currentSegmentIndex;

                    while (true)
                    {
                        // Try find next face, which is the face with fromNode matching currentFace.ToNode
                        CMeshFace currentFace   = faces[currentFaceIndex];
                        int       nextFaceIndex = NextFaceFromNode(currentFace.ToNode.Index, _fromNodeFace, _fromNodeFaceDict);

                        if (nextFaceIndex < 0)
                        {
                            // No to-node, we are done with this segment
                            segments.Add(currentSegment);
                            break;
                        }

                        // Check if the next face is already part of a segment
                        if (faceSegmentIndex[nextFaceIndex] >= 0)
                        {
                            if (faceSegmentIndex[nextFaceIndex] == currentSegmentIndex)
                            {
                                // Circular boundary - we are done with this segment
                                segments.Add(currentSegment);
                                break;
                            }

                            // Now: nextSegment is not the same as the currentSection,
                            // but they should be - move entire current segment to the
                            // start of the nextFace segment

                            int nextFaceSegmentIndex     = faceSegmentIndex[nextFaceIndex];
                            LinkedList <int> nextSegment = segments[nextFaceSegmentIndex];
                            // Move all faces from currentSegment to nextFaceSegment
                            LinkedListNode <int> thisSegmentListNode = currentSegment.Last;
                            while (thisSegmentListNode != null)
                            {
                                int faceToMoveIndex = thisSegmentListNode.Value;
                                nextSegment.AddFirst(faceToMoveIndex);
                                faceSegmentIndex[faceToMoveIndex] = nextFaceSegmentIndex;
                                thisSegmentListNode = thisSegmentListNode.Previous;
                            }

                            break; // Break out of while (true) loop
                        }

                        // Next face is not already part of a segment, add it to the end of this segment
                        // Make nextFace to currentFace - add it to the list of current segments.
                        currentFaceIndex = nextFaceIndex;
                        currentSegment.AddLast(currentFaceIndex);
                        faceSegmentIndex[currentFaceIndex] = currentSegmentIndex;
                    }
                }


                // Reset fromNodeFace array to initial value - in case of more than one sub mesh
                for (int iface = 0; iface < faces.Count; iface++)
                {
                    int fromNode = faces[iface].FromNode.Index;
                    _fromNodeFace[fromNode] = -1;
                }
                _fromNodeFaceDict.Clear();

                return(segments);
            }
Esempio n. 8
0
        private static Polygon BuildSubMeshBoundaryGeometry(CMeshData mesh, BoundarySegmentsBuilder bsb, List <CMeshFace> boundaryFaces)
        {
            // Find connected segments
            List <LinkedList <int> > segments = bsb.BuildBoundarySegments(boundaryFaces);

            if (segments == null)
            {
                return(null);
            }

            LinearRing        shell = null;
            List <LinearRing> holes = new List <LinearRing>();

            // Create mesh boundary with segments
            for (int isegment = 0; isegment < segments.Count; isegment++)
            {
                LinkedList <int> segment = segments[isegment];
                CMeshFace        first   = boundaryFaces[segment.First.Value];
                CMeshFace        last    = boundaryFaces[segment.Last.Value];

                if (!NodeEquals(first.FromNode, last.ToNode))
                {
                    Console.Out.WriteLine("Skipping: {0,4} {1,8} {2,8} {3,8}", isegment, segment.Count, first.FromNode, last.ToNode);
                    continue;
                }

                Coordinate[] coords = new Coordinate[segment.Count + 1];

                int i = 0;
                foreach (int iFace in segment)
                {
                    CMeshFace face = boundaryFaces[iFace];
                    coords[i++] = new Coordinate(face.FromNode.X, face.FromNode.Y);
                }

                coords[segment.Count] = new Coordinate(last.ToNode.X, last.ToNode.Y);
                LinearRing ring = new LinearRing(coords);
                if (ring.IsCCW)
                {
                    if (shell != null)
                    {
                        throw new Exception("Finding two shells of a connected sub-mesh");
                    }
                    shell = ring;
                }
                else
                {
                    holes.Add(ring);
                }
            }
            Polygon p;

            if (holes.Count > 0)
            {
                p = new Polygon(shell, holes.ToArray());
            }
            else
            {
                p = new Polygon(shell);
            }
            return(p);
        }
Esempio n. 9
0
        /// <summary>
        /// Build list of <see cref="CMeshBoundary"/>, one for each boundary code, based on the <paramref name="meshFaces"/>
        /// <para>
        /// The <paramref name="meshFaces"/> need only contain boundary faces. Internal faces are ignored.
        /// </para>
        /// </summary>
        private static List <CMeshBoundary> BuildBoundaryList(CMeshData mesh, List <CMeshFace> meshFaces)
        {
            // Sort all faces on boundary code, assuming code numbers does not grow very big.
            List <List <CMeshFace> > bcs = new List <List <CMeshFace> >();

            for (int i = 0; i < meshFaces.Count; i++)
            {
                CMeshFace meshFace = meshFaces[i];
                if (meshFace.IsBoundaryFace())
                {
                    while (meshFace.Code + 1 > bcs.Count)
                    {
                        List <CMeshFace> boundaryFaces = new List <CMeshFace>();
                        bcs.Add(boundaryFaces);
                    }
                    bcs[meshFace.Code].Add(meshFace);
                }
            }

            List <CMeshBoundary> boundaries = new List <CMeshBoundary>();

            BoundarySegmentsBuilder bsb = new BoundarySegmentsBuilder(mesh);

            // For each boundary code, find segments
            for (int ic = 0; ic < bcs.Count; ic++)
            {
                int code = ic;
                List <CMeshFace> faces = bcs[ic];

                List <LinkedList <int> > segments = bsb.BuildBoundarySegments(faces);
                if (segments == null)
                {
                    continue;
                }

                // Create mesh boundary with segments
                CMeshBoundary meshBoundary = new CMeshBoundary()
                {
                    Code = code
                };
                foreach (LinkedList <int> segment in segments)
                {
                    if (segment == null)
                    {
                        continue;
                    }
                    List <CMeshFace> segmentFaces = new List <CMeshFace>(segment.Count);
                    foreach (int currentFace in segment)
                    {
                        segmentFaces.Add(faces[currentFace]);
                    }

                    meshBoundary.Segments.Add(segmentFaces);
                }

                boundaries.Add(meshBoundary);
            }

            //// Sort on boundary codes - well, they are created in code-order
            //boundaries.Sort((mb1, mb2) => mb1.Code.CompareTo(mb2.Code));
            return(boundaries);
        }
Esempio n. 10
0
        /// <summary>
        /// Build up the list of <see cref="Faces"/>
        /// </summary>
        /// <param name="elmtFaces">Also build up <see cref="MeshElement.Faces"/></param>
        public List <string> BuildFaces(bool elmtFaces = false)
        {
            List <string> errors = new List <string>();

            int numberOfNodes    = NumberOfNodes;
            int numberOfElements = NumberOfElements;

            bool hasElementFaces = Elements[0].Faces != null;

            //System.Diagnostics.Stopwatch timer = MeshExtensions.StartTimer();

            if (Faces == null)
            {
                // Build up face lists
                // The exact number of faces is: NumberOfElements+NumberOfNodes + numberOfSubMeshes - numberOfHoles
                Faces = new List <CMeshFace>((int)((numberOfElements + numberOfNodes) * 1.01));

                // Preallocate list of face on all nodes - used in next loop
                for (int i = 0; i < Nodes.Count; i++)
                {
                    Nodes[i].Faces = new List <CMeshFace>();
                }
                //timer.ReportAndRestart("Prealloc nodeface");

                //watch.Start();
                // Create all faces.
                for (int ielmt = 0; ielmt < Elements.Count; ielmt++)
                {
                    MeshElement     element   = Elements[ielmt];
                    List <MeshNode> elmtNodes = element.Nodes;
                    if (elmtFaces)
                    {
                        element.Faces = new List <CMeshFace>(elmtNodes.Count);
                    }
                    for (int j = 0; j < elmtNodes.Count; j++)
                    {
                        MeshNode fromNode = elmtNodes[j];
                        MeshNode toNode   = elmtNodes[(j + 1) % elmtNodes.Count];
                        AddFace(element, fromNode, toNode);
                    }
                }
                //timer.ReportAndRestart("Create faces "+Faces.Count);

                // Figure out boundary code
                for (int i = 0; i < Faces.Count; i++)
                {
                    CMeshFace face = Faces[i];
                    face.SetBoundaryCode(errors);
                }
                //timer.ReportAndRestart("Set Boundary Code");
            }

            if (elmtFaces && !hasElementFaces)
            {
                // If not already created, create the lists
                if (Elements[0].Faces == null)
                {
                    for (int ielmt = 0; ielmt < numberOfElements; ielmt++)
                    {
                        Elements[ielmt].Faces = new List <CMeshFace>();
                    }
                }

                // Add face to the elements list of faces
                for (int i = 0; i < Faces.Count; i++)
                {
                    CMeshFace face = Faces[i];
                    face.LeftElement.Faces.Add(face);
                    if (face.RightElement != null)
                    {
                        face.RightElement.Faces.Add(face);
                    }
                }
            }
            //timer.ReportAndRestart("Create element faces");

            return(errors);
        }