using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using CaeMesh; using CaeGlobals; using System.Collections; namespace FileInOut.Input { static public class ObjFileReader { private static string[] spaceSplitter = 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[] lines = File.ReadAllLines(fileName); // Dictionary nodes = new Dictionary(); Dictionary elements = new Dictionary(); // BoundingBox bBox = new BoundingBox(); // List vertexLines = new List(); List faceLines = new List(); // Read vertices and lines for (int i = 0; i < lines.Length; i++) { lines[i] = lines[i].Trim().ToUpper(); if (lines[i].StartsWith("V")) vertexLines.Add(lines[i]); else if (lines[i].StartsWith("F")) faceLines.Add(lines[i]); } // Read nodes ReadNodes(vertexLines, nodes, ref bBox); // Read elements ReadFaces(faceLines, nodes, elements); // FeMesh mesh = new FeMesh(nodes, elements, MeshRepresentation.Mesh, null, null, false, ImportOptions.DetectEdges); // return mesh; } // return null; } private static void ReadNodes(List lines, Dictionary nodes, ref BoundingBox bBox) { int count = 1; string[] tmp; FeNode node; // foreach (var line in lines) { tmp = line.Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); node = new FeNode(); node.Id = count++; 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); } } private static void ReadFaces(List lines, Dictionary nodes, Dictionary elements) { int id = 1; int loopId = -1; int firstId; int maxId; int[] nodeIds; double min; double minAngle; double maxAngle; string[] tmp; string[] data; string[] splitter = new string[] { "/" }; FeElement element; LinearTriangleElement lte; List[] loopElements; // foreach (var line in lines) { data = line.Split(spaceSplitter, StringSplitOptions.RemoveEmptyEntries); // nodeIds = new int[data.Length - 1]; for (int i = 0; i < nodeIds.Length; i++) { tmp = data[i + 1].Split(splitter, StringSplitOptions.RemoveEmptyEntries); nodeIds[i] = int.Parse(tmp[0]); } // Triangles if (nodeIds.Length == 3) { element = new LinearTriangleElement(id++, nodeIds); elements.Add(element.Id, element); } // Quads else if (nodeIds.Length == 4) { element = new LinearQuadrilateralElement(id++, nodeIds); elements.Add(element.Id, element); } // Split multi-polygons to triangles else if (nodeIds.Length > 4) { maxId = -1; maxAngle = 0; loopElements = new List[nodeIds.Length]; // Find max min angle for (int i = 0; i < nodeIds.Length; i++) { loopId = id; minAngle = double.MaxValue; loopElements[i] = new List(); // for (int j = 0; j < nodeIds.Length - 2; j++) { lte = new LinearTriangleElement(loopId++, new int[] { nodeIds[0], nodeIds[j + 1], nodeIds[j + 2] }); // min = lte.GetMinAngleDeg(nodes); if (min < minAngle) minAngle = min; // loopElements[i].Add(lte); } // if (minAngle > maxAngle) { maxAngle = minAngle; maxId = i; } // Loop nodes firstId = nodeIds[0]; Array.Copy(nodeIds, 1, nodeIds, 0, nodeIds.Length - 1); nodeIds[nodeIds.Length - 1] = firstId; } // id += loopElements[maxId].Count; foreach (var loopElement in loopElements[maxId]) elements.Add(loopElement.Id, loopElement); } } } private static int ReadTriangles(string elementData, bool reverse, int offsetNodeId, int offsetElementId, Dictionary elements) { string[] data = elementData.Split(lineSplitter, StringSplitOptions.RemoveEmptyEntries); string[] tmp; int id; int[] nodeIds; LinearTriangleElement element; 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); } 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) { string[] data = elementData.Split(lineSplitter, StringSplitOptions.RemoveEmptyEntries); GeomCurveType edgeType; GeomCurveType prevEdgeType; string[] splitData; int id; int internalId = 1; int[] nodeIds; LinearBeamElement element; // int edgeId; 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++) { id = internalId + offsetElementId; 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]); // element = new LinearBeamElement(id, nodeIds); 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 void MergeNodes(Dictionary nodes, Dictionary elements, Dictionary> surfaceIdNodeIds, Dictionary> edgeIdNodeIds, double epsilon, out int[] mergedNodes) { 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; } } } // 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 mergedNodes = oldIdNewIdMap.Keys.ToArray(); foreach (int mergedNode in mergedNodes) 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); } } } } } }