/// <summary> /// Get the other node of a <see cref="MeshFace"/>, when /// looking from <paramref name="fromNode"/>. /// </summary> public static MeshNode ToNode(this MeshFace face, MeshNode fromNode) { if (ReferenceEquals(face.FromNode, fromNode)) { return(face.ToNode); } return(face.FromNode); }
/// <summary> /// Get the other element of a <see cref="MeshFace"/>, when /// looking from <paramref name="element"/>. /// <para> /// For boundary faces, null is returned. /// </para> /// </summary> public static MeshElement OtherElement(this MeshFace face, MeshElement element) { if (ReferenceEquals(face.LeftElement, element)) { return(face.RightElement); } if (ReferenceEquals(face.RightElement, element)) { return(face.LeftElement); } throw new Exception("element is not part of face"); }
/// <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="MeshFace.RightElement"/> /// </para> /// <para> /// The <see cref="MeshFace"/> 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 <MeshFace> fromNodeFaces = fromNode.Faces; List <MeshFace> toNodeFaces = toNode.Faces; if (fromNodeFaces.FindIndex(mf => mf.ToNode == toNode) >= 0) { throw new Exception(string.Format("Invalid mesh: Double face, from node {0} to node {1}. " + "Hint: Probably too many nodes was merged into one of the two face nodes." + "Try decrease node merge tolerance value", fromNode.Index + 1, toNode.Index + 1)); } // Try find "reverse face" going from from-node to to-node. int reverseFaceIndex = toNodeFaces.FindIndex(mf => mf.ToNode == fromNode); if (reverseFaceIndex >= 0) { // Found reverse face, reuse it and add the elment as the RightElement MeshFace reverseFace = toNodeFaces[reverseFaceIndex]; reverseFace.RightElement = element; } else { // Found new face, set element as LeftElement and add it to both from-node and to-node MeshFace meshFace = new MeshFace() { FromNode = fromNode, ToNode = toNode, LeftElement = element, }; Faces.Add(meshFace); fromNodeFaces.Add(meshFace); toNodeFaces.Add(meshFace); } }
/// <summary> /// Build derived mesh data, especially the <see cref="MeshFace"/> lists. /// </summary> public void BuildDerivedData() { Faces = new List <MeshFace>(); // Preallocate list of face on all nodes - used in next loop for (int i = 0; i < Nodes.Count; i++) { Nodes[i].Faces = new List <MeshFace>(); } // Create all faces. for (int ielmt = 0; ielmt < Elements.Count; ielmt++) { MeshElement element = Elements[ielmt]; element.Faces = new List <MeshFace>(); List <MeshNode> elmtNodes = element.Nodes; for (int j = 0; j < elmtNodes.Count; j++) { MeshNode fromNode = elmtNodes[j]; MeshNode toNode = elmtNodes[(j + 1) % elmtNodes.Count]; AddFace(element, fromNode, toNode); } } // Figure out boundary code for (int i = 0; i < Faces.Count; i++) { MeshFace face = Faces[i]; // If the RightElement exists, this face is an internal face if (face.RightElement != null) { continue; } // RightElement does not exist, so it is a boundary face. int fromCode = face.FromNode.Code; int toCode = face.ToNode.Code; // True if "invalid" boundary face, then set it as internal face. bool internalFace = false; if (fromCode == 0) { internalFace = true; throw new Exception(string.Format("Invalid mesh: Boundary face, from node {0} to node {1} is missing a boundary code on node {0}. " + "Hint: Modify boundary code for node {0}", face.FromNode.Index + 1, face.ToNode.Index + 1)); } if (toCode == 0) { internalFace = true; throw new Exception(string.Format("Invalid mesh: Boundary face, from node {0} to node {1} is missing a boundary code on node {1}. " + "Hint: Modify boundary code for node {1}", face.FromNode.Index + 1, face.ToNode.Index + 1)); } int faceCode; // Find face code: // 1) In case any of the nodes is a land node (code value 1) then the // boundary face is a land face, given boundary code value 1. // 2) For boundary faces (if both fromNode and toNode have code values larger than 1), // the face code is the boundary code value of toNode. if (fromCode == 1 || toCode == 1) { faceCode = 1; } else { faceCode = toCode; } if (!internalFace) { face.Code = faceCode; } } // Add face to the elements list of faces for (int i = 0; i < Faces.Count; i++) { MeshFace face = Faces[i]; face.LeftElement.Faces.Add(face); face.RightElement?.Faces.Add(face); } }
/// <summary> /// Build list of face boundaries of <paramref name="mesh"/> /// </summary> public static List <MeshBoundary> BuildBoundaryList(MeshData mesh) { if (mesh.Faces == null) { mesh.BuildDerivedData(); } // Sort all faces on boundary code Dictionary <int, List <MeshFace> > bcs = new Dictionary <int, List <MeshFace> >(); for (int i = 0; i < mesh.Faces.Count; i++) { MeshFace meshFace = mesh.Faces[i]; if (meshFace.IsBoundaryFace()) { List <MeshFace> boundaryFaces; if (!bcs.TryGetValue(meshFace.Code, out boundaryFaces)) { boundaryFaces = new List <MeshFace>(); bcs.Add(meshFace.Code, boundaryFaces); } boundaryFaces.Add(meshFace); } } List <MeshBoundary> boundaries = new List <MeshBoundary>(); // For each boundary code, find segments foreach (KeyValuePair <int, List <MeshFace> > bfkvp in bcs) { int code = bfkvp.Key; List <MeshFace> faces = bfkvp.Value; // Sort faces on FromNode index faces.Sort((f1, f2) => f1.FromNode.Index.CompareTo(f2.FromNode.Index)); // Create searchable array of FromNode indices int[] fromNodes = faces.Select(f => f.FromNode.Index).ToArray(); // Matching 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 with this boundary code 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 MeshFace currentFace = faces[currentFaceIndex]; int nextFaceIndex = Array.BinarySearch(fromNodes, currentFace.ToNode.Index); 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 nextFaceSegment = faceSegmentIndex[nextFaceIndex]; // Move all from current segment to LinkedListNode <int> thisSegmentListNode = currentSegment.Last; while (thisSegmentListNode != null) { int faceToMoveIndex = thisSegmentListNode.Value; segments[nextFaceSegment].AddFirst(faceToMoveIndex); faceSegmentIndex[faceToMoveIndex] = nextFaceSegment; thisSegmentListNode = thisSegmentListNode.Previous; } break; // Break out of while (true) loop } // Make nextFace to currentFace - add it to the list of current segments. currentFaceIndex = nextFaceIndex; currentSegment.AddLast(currentFaceIndex); faceSegmentIndex[currentFaceIndex] = currentSegmentIndex; } } MeshBoundary meshBoundary = new MeshBoundary() { Code = code }; foreach (LinkedList <int> segment in segments) { if (segment == null) { continue; } List <MeshFace> segmentFaces = new List <MeshFace>(segment.Count); foreach (int currentFace in segment) { segmentFaces.Add(faces[currentFace]); } meshBoundary.Segments.Add(segmentFaces); } boundaries.Add(meshBoundary); } boundaries.Sort((mb1, mb2) => mb1.Code.CompareTo(mb2.Code)); return(boundaries); }
/// <summary> /// Add a target, by specifying its (x,y) coordinate. /// </summary> public void AddTarget(double x, double y) { if (_targets == null) { _targets = new List <InterPData>(); } if (_searcher == null) { _searcher = new MeshSearcher(_mesh); _searcher.SetupElementSearch(); } InterPData interpData = new InterPData(); // Setting "out-of-bounds" index interpData.Element1Index = -1; // Find element that includes the (x,y) coordinate MeshElement element = _searcher.FindElement(x, y); // Check if element has been found, i.e. includes the (x,y) point if (element != null) { bool found = false; interpData.Element1Index = element.Index; // Check which face the point belongs to, and which "side" of the face bool isQuad = element.IsQuadrilateral(); int numFaces = isQuad ? 4 : 3; for (int j = 0; j < numFaces; j++) { MeshFace elementFace = element.Faces[j]; // From the element (x,y), looking towards the face, // figure out wich node is right and which is left. MeshNode rightNode, leftNode; if (elementFace.LeftElement == element) { rightNode = elementFace.FromNode; leftNode = elementFace.ToNode; } else { rightNode = elementFace.ToNode; leftNode = elementFace.FromNode; } // Find also the element on the other side of the face double otherElementX, otherElementY; MeshElement otherElement = elementFace.OtherElement(element); if (otherElement != null) { otherElementX = otherElement.XCenter; otherElementY = otherElement.YCenter; interpData.Element2Index = otherElement.Index; } else { // No other element - boundary face, use center of face. otherElementX = 0.5 * (rightNode.X + leftNode.X); otherElementY = 0.5 * (rightNode.Y + leftNode.Y); // Use "itself" as element-2 interpData.Element2Index = element.Index; } // Check if point is on the right side of the line between element and other-element if (MeshExtensions.IsPointInsideLines(x, y, element.XCenter, element.YCenter, rightNode.X, rightNode.Y, otherElementX, otherElementY)) { (double w1, double w2, double w3) = MeshExtensions.InterpolationWeights(x, y, element.XCenter, element.YCenter, rightNode.X, rightNode.Y, otherElementX, otherElementY); interpData.NodeIndex = rightNode.Index; interpData.Element1Weight = w1; interpData.NodeWeight = w2; interpData.Element2Weight = w3; found = true; break; } // Check if point is on the left side of the line between element and other-element if (MeshExtensions.IsPointInsideLines(x, y, element.XCenter, element.YCenter, otherElementX, otherElementY, leftNode.X, leftNode.Y)) { (double w1, double w2, double w3) = MeshExtensions.InterpolationWeights(x, y, element.XCenter, element.YCenter, otherElementX, otherElementY, leftNode.X, leftNode.Y); interpData.NodeIndex = leftNode.Index; interpData.Element1Weight = w1; interpData.Element2Weight = w2; interpData.NodeWeight = w3; found = true; break; } } if (!found) // Should never happen, but just in case { interpData.Element1Weight = 1; interpData.Element2Weight = 0; interpData.NodeWeight = 0; interpData.Element2Index = element.Index; interpData.NodeIndex = element.Nodes[0].Index; } } _targets.Add(interpData); }
/// <summary> /// Returns true if the face is a boundary face. /// </summary> public static bool IsBoundaryFace(this MeshFace face) { return(face.RightElement == null); }