using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using CaeMesh; using CaeGlobals; using CaeModel; namespace FileInOut.Input { static public class VisFileReader { private static string[] spaceSplitter = new string[] { " " }; private static string[] colonSplitter = new string[] { ":" }; private static string[] lineSplitter = new string[] { "\r", "\n" }; private static string[] allSplitter = new string[] { " ", "\r", "\n" }; public static FeMesh Read(string fileName) { if (File.Exists(fileName)) { string data = File.ReadAllText(fileName); // Dictionary> surfaceIdNodeIds = new Dictionary>(); Dictionary> edgeIdNodeIds = new Dictionary>(); HashSet vertexNodeIds = new HashSet(); Dictionary nodes = new Dictionary(); Dictionary elements = new Dictionary(); Dictionary faceTypes = new Dictionary(); Dictionary edgeTypes = new Dictionary(); // BoundingBox bBox = new BoundingBox(); double epsilon = 1E-9; int offsetNodeId = 0; int offsetElementId = 0; // Read the vertices first - later the merging of nodes is done string[] vertexSplitData = data.Split(new string[] { "Number of vertices: " }, StringSplitOptions.RemoveEmptyEntries); // A compound shell contains multiple shells - vertices are printed for each of them if (vertexSplitData.Length >= 2) { string vertexData = vertexSplitData[1]; int endVertexData = vertexData.IndexOf("****"); vertexData = vertexData.Substring(0, endVertexData); offsetNodeId = ReadNodes(vertexData, offsetNodeId, nodes, ref bBox); // vertexNodeIds.UnionWith(nodes.Keys); } // string textToFind = null; ImportOptions importOptions = ImportOptions.DetectEdges; // Import solid geometry if (data.Contains("Solid number: ")) { textToFind = "Solid number: "; importOptions = ImportOptions.ImportOneCADSolidPart; } // Import shell geometry else if (data.Contains("Shell number: ")) { textToFind = "Shell number: "; importOptions = ImportOptions.ImportCADShellParts; } else if (data.Contains("Free face number: ")) { textToFind = "Free face number: "; importOptions = ImportOptions.ImportCADShellParts; } // if (textToFind != null) { string[] partData = data.Split(new string[] { textToFind }, StringSplitOptions.RemoveEmptyEntries); // for (int k = 1; k < partData.Length; k++) { string[] faceData = partData[k].Split(new string[] { "Face number: " }, StringSplitOptions.RemoveEmptyEntries); // for (int i = 1; i < faceData.Length; i++) // start with 1 to skip first line: ******** { ReadFace(faceData[i], ref offsetNodeId, nodes, ref offsetElementId, elements, surfaceIdNodeIds, faceTypes, edgeIdNodeIds, edgeTypes, ref bBox); } } // double max = bBox.GetDiagonal(); MergeNodes(nodes, elements, surfaceIdNodeIds, edgeIdNodeIds, epsilon * max, out _); MergeEdgeElements(elements); // FeMesh mesh = new FeMesh(nodes, elements, MeshRepresentation.Geometry, importOptions); // mesh.ConvertLineFeElementsToEdges(vertexNodeIds, true); // mesh.RenumberVisualizationSurfaces(surfaceIdNodeIds, faceTypes); mesh.RenumberVisualizationEdges(edgeIdNodeIds, edgeTypes); // mesh.RemoveElementsByType(); mesh.RemoveElementsByType(); // Read mass data and overwrite the one computed in new FeMesh if (mesh.Parts.Count == 1) { PartMassProperties massProperties; string[] massData = data.Split(new string[] { "Geometry properties" }, StringSplitOptions.RemoveEmptyEntries); if (massData.Length == 2) { massProperties = ReadMass(massData[1]); mesh.Parts.First().Value.MassProperties = massProperties; } } return mesh; } } // return null; } private static void ReadFace(string faceData, ref int offsetNodeId, Dictionary nodes, ref int offsetElementId, Dictionary elements, Dictionary> surfaceIdNodeIds, Dictionary faceTypes, Dictionary> edgeIdNodeIds, Dictionary edgeTypes, ref BoundingBox bBox) { int numOfNodes = 0; int numOfElements = 0; string[] data = faceData.Split(new string[] { "*", "Number of " }, StringSplitOptions.RemoveEmptyEntries); // string[] tmp = data[0].Split(allSplitter, StringSplitOptions.RemoveEmptyEntries); int surfaceId = int.Parse(tmp[0]); int orientation = int.Parse(tmp[3]); GeomFaceType faceType = (GeomFaceType)Enum.Parse(typeof(GeomFaceType), tmp[6]); bool reverse = orientation == 1; // if (!faceTypes.ContainsKey(surfaceId)) faceTypes.Add(surfaceId, faceType); // Dictionary surfaceNodes = new Dictionary(); CompareIntArray comparer = new CompareIntArray(); HashSet freeEdgeKeys = new HashSet(comparer); for (int i = 1; i < data.Length; i++) { if (data[i].StartsWith("nodes")) { numOfNodes = ReadNodes(data[i], offsetNodeId, surfaceNodes, ref bBox); nodes.AddRange(surfaceNodes); } else if (data[i].StartsWith("triangles")) { numOfElements = ReadTriangles(data[i], reverse, offsetNodeId, offsetElementId, elements, freeEdgeKeys); offsetElementId += numOfElements; } else if (data[i].StartsWith("edges")) { numOfElements = ReadEdges(data[i], offsetNodeId, offsetElementId, elements, edgeIdNodeIds, edgeTypes, freeEdgeKeys); offsetElementId += numOfElements; } } offsetNodeId += numOfNodes; // Add surface if it contains more than 1 node if (surfaceNodes.Count > 0) { HashSet surface; if (surfaceIdNodeIds.TryGetValue(surfaceId, out surface)) surface.UnionWith(surfaceNodes.Keys); else surfaceIdNodeIds.Add(surfaceId, new HashSet(surfaceNodes.Keys)); // create a copy!!! } } private static int ReadNodes(string nodeData, int offsetNodeId, Dictionary nodes, ref BoundingBox bBox) { string[] data = nodeData.Split(lineSplitter, StringSplitOptions.RemoveEmptyEntries); string[] tmp; FeNode node; // for (int i = 1; i < data.Length; i++) // skip first row: Number of nodes: 14 { tmp = data[i].Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); node = new FeNode(); node.Id = int.Parse(tmp[0]) + offsetNodeId; node.X = double.Parse(tmp[1]); node.Y = double.Parse(tmp[2]); node.Z = double.Parse(tmp[3]); // bBox.IncludeNode(node); // nodes.Add(node.Id, node); } // return data.Length - 1; // return number of read nodes } private static int ReadTriangles(string elementData, bool reverse, int offsetNodeId, int offsetElementId, Dictionary elements, HashSet freeEdgeKeys) { int id; int[] key; int[] count; int[] nodeIds; string[] data = elementData.Split(lineSplitter, StringSplitOptions.RemoveEmptyEntries); string[] tmp; LinearTriangleElement element; CompareIntArray comparer = new CompareIntArray(); Dictionary edgeKeyCount = new Dictionary(comparer); // for (int i = 1; i < data.Length; i++) // skip first row: Number of elements: 23 { tmp = data[i].Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); id = int.Parse(tmp[0]) + offsetElementId; nodeIds = new int[3]; nodeIds[0] = int.Parse(tmp[1]) + offsetNodeId; if (reverse) { nodeIds[1] = int.Parse(tmp[3]) + offsetNodeId; nodeIds[2] = int.Parse(tmp[2]) + offsetNodeId; } else { nodeIds[1] = int.Parse(tmp[2]) + offsetNodeId; nodeIds[2] = int.Parse(tmp[3]) + offsetNodeId; } element = new LinearTriangleElement(id, nodeIds); // elements.Add(element.Id, element); // key = nodeIds[0] < nodeIds[1] ? new int[] { nodeIds[0], nodeIds[1] } : new int[] { nodeIds[1], nodeIds[0] }; if (edgeKeyCount.TryGetValue(key, out count)) count[0]++; else edgeKeyCount[key] = new int[] { 1 }; // key = nodeIds[1] < nodeIds[2] ? new int[] { nodeIds[1], nodeIds[2] } : new int[] { nodeIds[2], nodeIds[1] }; if (edgeKeyCount.TryGetValue(key, out count)) count[0]++; else edgeKeyCount[key] = new int[] { 1 }; // key = nodeIds[2] < nodeIds[0] ? new int[] { nodeIds[2], nodeIds[0] } : new int[] { nodeIds[0], nodeIds[2] }; if (edgeKeyCount.TryGetValue(key, out count)) count[0]++; else edgeKeyCount[key] = new int[] { 1 }; } // foreach (var entry in edgeKeyCount) { if (entry.Value[0] == 1) freeEdgeKeys.Add(entry.Key); } return data.Length - 1; // return number of read elements } private static int ReadEdges(string elementData, int offsetNodeId, int offsetElementId, Dictionary elements, Dictionary> edgeIdNodeIds, Dictionary edgeTypes, HashSet freeEdgeKeys) { string[] data = elementData.Split(lineSplitter, StringSplitOptions.RemoveEmptyEntries); GeomCurveType edgeType; GeomCurveType prevEdgeType; string[] splitData; int id; int internalId = 1; int[] nodeIds; LinearBeamElement element; // int edgeId; int[] key; int[] edgeNodeIdsArr; HashSet edgeNodeIds = new HashSet(); HashSet edgeNodeIdsOut; // for (int i = 1; i < data.Length; i++) // skip first row: Number of edges: 4 { splitData = data[i].Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); edgeType = (GeomCurveType)Enum.Parse(typeof(GeomCurveType), splitData[0]); edgeId = int.Parse(splitData[1]); // edgeNodeIds.Clear(); for (int j = 2; j < splitData.Length - 1; j++) { nodeIds = new int[2]; nodeIds[0] = int.Parse(splitData[j]) + offsetNodeId; nodeIds[1] = int.Parse(splitData[j + 1]) + offsetNodeId; // edgeNodeIds.Add(nodeIds[0]); edgeNodeIds.Add(nodeIds[1]); // //id = internalId + offsetElementId; //element = new LinearBeamElement(id, nodeIds); //elements.Add(element.Id, element); //internalId++; } edgeNodeIdsArr = edgeNodeIds.ToArray(); for (int j = 0; j < edgeNodeIdsArr.Length; j++) { for (int k = j + 1; k < edgeNodeIdsArr.Length; k++) { key = edgeNodeIdsArr[j] < edgeNodeIdsArr[k] ? new int[] { edgeNodeIdsArr[j], edgeNodeIdsArr[k] } : new int[] { edgeNodeIdsArr[k], edgeNodeIdsArr[j] }; if (freeEdgeKeys.Contains(key)) { id = internalId + offsetElementId; element = new LinearBeamElement(id, new int[] { edgeNodeIdsArr[j] , edgeNodeIdsArr[k] }); elements.Add(element.Id, element); internalId++; } } } // if (edgeIdNodeIds.TryGetValue(edgeId, out edgeNodeIdsOut)) edgeNodeIdsOut.UnionWith(edgeNodeIds); else edgeIdNodeIds.Add(edgeId, new HashSet(edgeNodeIds)); // create a copy!!! // if (edgeTypes.TryGetValue(edgeId, out prevEdgeType)) { if (prevEdgeType != edgeType) throw new NotSupportedException(); } else edgeTypes.Add(edgeId, edgeType); } // return internalId - 1; // return number of read edges } private static PartMassProperties ReadMass(string massData) { PartMassProperties massProperties = new PartMassProperties(true); string[] data = massData.Split(lineSplitter, StringSplitOptions.RemoveEmptyEntries); if (data.Length >= 11) { string[] tmp; tmp = data[1].Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); if (tmp[0].ToUpper().StartsWith("VOL")) massProperties.Volume = double.Parse(tmp[1]); else if (tmp[0].ToUpper().StartsWith("ARE")) massProperties.Area = double.Parse(tmp[1]); tmp = data[2].Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); massProperties.CenterOfMass[0] = double.Parse(tmp[3]); massProperties.CenterOfMass[1] = double.Parse(tmp[4]); massProperties.CenterOfMass[2] = double.Parse(tmp[5]); // InertiaMatrixCG tmp = data[4].Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); massProperties.InertiaMatrixCG[0][0] = double.Parse(tmp[0]); massProperties.InertiaMatrixCG[0][1] = double.Parse(tmp[1]); massProperties.InertiaMatrixCG[0][2] = double.Parse(tmp[2]); tmp = data[5].Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); massProperties.InertiaMatrixCG[1][0] = double.Parse(tmp[0]); massProperties.InertiaMatrixCG[1][1] = double.Parse(tmp[1]); massProperties.InertiaMatrixCG[1][2] = double.Parse(tmp[2]); tmp = data[6].Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); massProperties.InertiaMatrixCG[2][0] = double.Parse(tmp[0]); massProperties.InertiaMatrixCG[2][1] = double.Parse(tmp[1]); massProperties.InertiaMatrixCG[2][2] = double.Parse(tmp[2]); // InertiaMatrixOrigin tmp = data[8].Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); massProperties.InertiaMatrixOrigin[0][0] = double.Parse(tmp[0]); massProperties.InertiaMatrixOrigin[0][1] = double.Parse(tmp[1]); massProperties.InertiaMatrixOrigin[0][2] = double.Parse(tmp[2]); tmp = data[9].Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); massProperties.InertiaMatrixOrigin[1][0] = double.Parse(tmp[0]); massProperties.InertiaMatrixOrigin[1][1] = double.Parse(tmp[1]); massProperties.InertiaMatrixOrigin[1][2] = double.Parse(tmp[2]); tmp = data[10].Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); massProperties.InertiaMatrixOrigin[2][0] = double.Parse(tmp[0]); massProperties.InertiaMatrixOrigin[2][1] = double.Parse(tmp[1]); massProperties.InertiaMatrixOrigin[2][2] = double.Parse(tmp[2]); } return massProperties; } private static void MergeNodes(Dictionary nodes, Dictionary elements, Dictionary> surfaceIdNodeIds, Dictionary> edgeIdNodeIds, double epsilon, out int[] mergedNodeIds) { int count = 0; FeNode[] sortedNodes = new FeNode[nodes.Count]; // Sort the nodes by x foreach (var entry in nodes) sortedNodes[count++] = entry.Value; // IComparer comparerByX = new CompareFeNodeByX(); Array.Sort(sortedNodes, comparerByX); // Create a map of node ids to be merged to another node id Dictionary oldIdNewIdMap = new Dictionary(); for (int i = 0; i < sortedNodes.Length - 1; i++) { if (oldIdNewIdMap.ContainsKey(sortedNodes[i].Id)) continue; // this node was merged and does not exist anymore // for (int j = i + 1; j < sortedNodes.Length; j++) { if (oldIdNewIdMap.ContainsKey(sortedNodes[j].Id)) continue; // this node was merged and does not exist anymore // if (Math.Abs(sortedNodes[i].X - sortedNodes[j].X) < epsilon) { if (Math.Abs(sortedNodes[i].Y - sortedNodes[j].Y) < epsilon) { if (Math.Abs(sortedNodes[i].Z - sortedNodes[j].Z) < epsilon) { oldIdNewIdMap.Add(sortedNodes[j].Id, sortedNodes[i].Id); } } } else { break; // since nodes are sortred by X if dX > epsilon break; } } } // Collect close nodes into bins HashSet allNodeIds; Dictionary> newNodeIdAllNodeIds = new Dictionary>(); foreach (var entry in oldIdNewIdMap) { if (newNodeIdAllNodeIds.TryGetValue(entry.Value, out allNodeIds)) allNodeIds.Add(entry.Key); else newNodeIdAllNodeIds.Add(entry.Value, new HashSet() { entry.Value, entry.Key }); } // Find the smallest node id oldIdNewIdMap.Clear(); int[] sortedNodeIds; foreach (var entry in newNodeIdAllNodeIds) { sortedNodeIds = entry.Value.ToArray(); Array.Sort(sortedNodeIds); // for (int i = 1; i < sortedNodeIds.Length; i++) oldIdNewIdMap.Add(sortedNodeIds[i], sortedNodeIds[0]); } // Remove unused nodes mergedNodeIds = oldIdNewIdMap.Keys.ToArray(); foreach (int mergedNode in mergedNodeIds) nodes.Remove(mergedNode); // Apply the map to the elements int newId; HashSet nodeIds = new HashSet(); List elementIdsToRemove = new List(); foreach (var entry in elements) { nodeIds.Clear(); for (int i = 0; i < entry.Value.NodeIds.Length; i++) { if (oldIdNewIdMap.TryGetValue(entry.Value.NodeIds[i], out newId)) entry.Value.NodeIds[i] = newId; // nodeIds.Add(entry.Value.NodeIds[i]); } if (nodeIds.Count != entry.Value.NodeIds.Length) elementIdsToRemove.Add(entry.Key); } // Remove collapsed elements foreach (var elementId in elementIdsToRemove) { elements.Remove(elementId); // Might be also necessary to remove some nodes ? } // Surface node ids HashSet newIds = new HashSet(); foreach (var entry in surfaceIdNodeIds) { newIds.Clear(); foreach (var nodeId in entry.Value) { if (oldIdNewIdMap.TryGetValue(nodeId, out newId)) newIds.Add(newId); else newIds.Add(nodeId); } entry.Value.Clear(); entry.Value.UnionWith(newIds); } // Edge node ids foreach (var entry in edgeIdNodeIds) { newIds.Clear(); foreach (var nodeId in entry.Value) { if (oldIdNewIdMap.TryGetValue(nodeId, out newId)) newIds.Add(newId); else newIds.Add(nodeId); } entry.Value.Clear(); entry.Value.UnionWith(newIds); } } private static void MergeEdgeElements(Dictionary elements) { int[] key; FeElement[] elementsToRemove; List elementsToMerge; CompareIntArray comparer = new CompareIntArray(); Dictionary> nodeIdsElements = new Dictionary>(comparer); foreach (var entry in elements) { if (entry.Value is FeElement1D edgeElement) { key = edgeElement.NodeIds; Array.Sort(key); if (nodeIdsElements.TryGetValue(key, out elementsToMerge)) elementsToMerge.Add(edgeElement); else nodeIdsElements.Add(key, new List() { edgeElement }); } } // foreach (var entry in nodeIdsElements) { if (entry.Value.Count > 1) { elementsToRemove = entry.Value.ToArray(); for (int i = 1; i < elementsToRemove.Length; i++) { elements.Remove(elementsToRemove[i].Id); } } } } } }