// ----------------------------------------------------------------------- // // Christian Woltering, Triangle.NET, http://triangle.codeplex.com/ // // ----------------------------------------------------------------------- namespace MeshExplorer.IO.Formats { using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using TriangleNet; using TriangleNet.Topology; using TriangleNet.Geometry; using TriangleNet.IO; using TriangleNet.Meshing; /// /// Read and write JSON files. /// /// /// The JSON format: /// { /// "config": { /// "ver": 1, /// "type": "mesh" | "poly" | "points", /// "dim": 2 /// }, /// "points": { /// "data": [ p0.x, p0.y ... pn.x, pn.y ], /// "markers": [ ... ], /// "attributes": [ ... ] /// }, /// "segments": { /// "data": [ s0(1), s0(2) ... sn(1), sn(2) ], /// "markers": [ ... ] /// }, /// "holes": [ h0.x, h0.y ... hn.x, hn.y ], /// "triangles": { /// "data": [ t0(1), t0(2), t0(3) ... tn(1), tn(2), tn(3) ], /// "neighbors": [ t0.n1, t0.n2, t0.n3 ... tn.n1, tn.n2, tn.n3 ], /// "attributes": [ ... ] /// } /// } /// public class JsonFile : IMeshFile { string file; Dictionary json; /// /// Gets the supported file extensions. /// public string[] Extensions { get { return new string[] { ".json" }; } } public bool ContainsMeshData(string filename) { ParseJson(filename); if (this.json.ContainsKey("config")) { var config = this.json["config"] as Dictionary; if (config != null && config.ContainsKey("type")) { return config["type"].ToString() == "mesh"; } } return false; } public bool IsSupported(string file) { throw new NotImplementedException(); } public IMesh Import(string filename) { var geometry = (Polygon)this.Read(filename); List triangles = null; if (this.json.ContainsKey("triangles")) { var tri = this.json["triangles"] as Dictionary; if (tri != null) { triangles = ReadTriangles(tri, geometry.Points.Count); } } return Converter.ToMesh(geometry, triangles); } public void Write(IMesh mesh, string filename) { using (StreamWriter writer = new StreamWriter(filename)) { int nv = mesh.Vertices.Count; int ns = mesh.Segments.Count; int nh = mesh.Holes.Count; int ne = mesh.Triangles.Count; writer.Write("{"); // Config header writer.Write("\"config\":{"); writer.Write("\"ver\":1,"); writer.Write("\"type\":\"{0}\",", ne > 0 ? "mesh" : (ns > 0 ? "poly" : "points")); writer.Write("\"dim\":2"); writer.Write("}"); if (((Mesh)mesh).CurrentNumbering == NodeNumbering.None) { ((Mesh)mesh).Renumber(NodeNumbering.Linear); } // Write the coordinates if (nv > 0) { writer.Write(","); WritePoints((Mesh)mesh, writer, nv); } // Write the segments if (ns > 0) { writer.Write(","); WriteSegments(mesh.Segments, writer, ns); } // Write the holes if (nh > 0) { writer.Write(","); WriteHoles(mesh.Holes, writer, nh); } // Write the elements if (ne > 0) { writer.Write(","); WriteTriangles(mesh.Triangles, writer, ne); } writer.Write("}"); } } public void Write(IMesh mesh, Stream stream) { throw new NotImplementedException(); } /// /// /// /// /// public IPolygon Read(string filename) { ParseJson(filename); var data = new Polygon(); if (json == null) { // TODO: Exception? return data; } if (json.ContainsKey("config")) { } int count = 0; if (json.ContainsKey("points")) { var points = json["points"] as Dictionary; if (points != null) { ReadPoints(data, points, ref count); } else { // TODO: Exception? return data; } } if (json.ContainsKey("segments")) { var segments = json["segments"] as Dictionary; if (segments != null) { ReadSegments(data, segments, count); } } if (json.ContainsKey("holes")) { var holes = json["holes"] as ArrayList; if (holes != null) { ReadHoles(data, holes); } } return data; } public void Write(IPolygon polygon, string filename) { throw new NotImplementedException(); } public void Write(IPolygon polygon, Stream stream) { throw new NotImplementedException(); } private void ParseJson(string filename) { if (this.json == null || this.file != filename) { this.file = filename; string content = File.ReadAllText(filename); JsonParser parser = new JsonParser(content); this.json = parser.Decode() as Dictionary; } } #region Read helpers private void ReadPoints(Polygon geometry, Dictionary points, ref int count) { ArrayList data = points["data"] as ArrayList; ArrayList markers = null; ArrayList attributes = null; if (points.ContainsKey("markers")) { markers = points["markers"] as ArrayList; } if (points.ContainsKey("attributes")) { attributes = points["attributes"] as ArrayList; } if (data != null) { int mark, n = data.Count; if (n % 2 != 0) { throw new Exception("JSON format error (points)."); } // Number of points count = n / 2; for (int i = 0; i < n; i += 2) { mark = 0; if (markers != null && markers.Count == count) { mark = int.Parse(markers[i / 2].ToString()); } geometry.Add(new Vertex( double.Parse(data[i].ToString(), Util.Nfi), double.Parse(data[i + 1].ToString(), Util.Nfi), mark )); } } } private void ReadSegments(Polygon geometry, Dictionary segments, int count) { ArrayList data = segments["data"] as ArrayList; ArrayList markers = null; if (segments.ContainsKey("markers")) { markers = segments["markers"] as ArrayList; } if (data != null) { int mark, n = data.Count; if (n % 2 != 0) { throw new Exception("JSON format error (segments)."); } int p0, p1; throw new NotImplementedException(); // TODO: Fix JSON format for (int i = 0; i < n; i += 2) { mark = 0; if (markers != null && markers.Count == n) { mark = int.Parse(markers[i / 2].ToString()); } p0 = int.Parse(data[i].ToString()); p1 = int.Parse(data[i + 1].ToString()); if (p0 < 0 || p0 >= count || p1 < 0 || p1 >= count) { throw new Exception("JSON format error (segment index)."); } //geometry.Add(new Edge(p0, p1, mark)); } } } private void ReadHoles(Polygon geometry, ArrayList holes) { int n = holes.Count; if (n % 2 != 0) { throw new Exception("JSON format error (holes)."); } for (int i = 0; i < n; i += 2) { geometry.Holes.Add(new Point( double.Parse(holes[i].ToString(), Util.Nfi), double.Parse(holes[i + 1].ToString(), Util.Nfi) )); } } private List ReadTriangles(Dictionary triangles, int points) { ArrayList data = triangles["data"] as ArrayList; ArrayList neighbors = null; ArrayList attributes = null; if (triangles.ContainsKey("neighbors")) { neighbors = triangles["neighbors"] as ArrayList; } if (triangles.ContainsKey("attributes")) { attributes = triangles["attributes"] as ArrayList; } List output = null; if (data != null) { int n = data.Count; if (n % 3 != 0) { throw new Exception("JSON format error (triangles)."); } output = new List(n / 3); int p0, p1, p2, n0, n1, n2; for (int i = 0; i < n; i += 3) { p0 = int.Parse(data[i].ToString()); p1 = int.Parse(data[i + 1].ToString()); p2 = int.Parse(data[i + 2].ToString()); n0 = n1 = n2 = -1; if (p0 < 0 || p0 >= points || p1 < 0 || p1 >= points || p2 < 0 || p2 >= points) { throw new Exception("JSON format error (triangle index)."); } if (neighbors.Count == n) { n0 = int.Parse(neighbors[i].ToString()); n1 = int.Parse(neighbors[i + 1].ToString()); n2 = int.Parse(neighbors[i + 2].ToString()); } // TODO: Set neighbors output.Add(new InputTriangle(p0, p1, p2)); } } return output; } #endregion #region Write helpers private void WritePoints(Mesh mesh, StreamWriter writer, int nv) { bool useMarkers = false; StringBuilder markers; writer.Write("\"points\":{\"data\":["); if (mesh.CurrentNumbering == NodeNumbering.Linear) { markers = WritePoints(mesh.Vertices, writer, nv, useMarkers); } else { Vertex[] nodes = new Vertex[mesh.Vertices.Count]; foreach (var node in mesh.Vertices) { nodes[node.ID] = node; } markers = WritePoints(nodes, writer, nv, useMarkers); } writer.Write("]"); if (useMarkers) { writer.Write(",\"markers\":[" + markers.ToString() + "]"); } // TODO: writer.Write(",\"attributes\":[]"); writer.Write("}"); } private static StringBuilder WritePoints(IEnumerable data, StreamWriter writer, int nv, bool useMarkers) { StringBuilder markers = new StringBuilder(); int i = 0; string seperator; foreach (var item in data) { seperator = (i == nv - 1) ? String.Empty : ", "; writer.Write("{0},{1}{2}", item.X.ToString(Util.Nfi), item.Y.ToString(Util.Nfi), seperator); if (item.Label > 0) { useMarkers = true; } markers.AppendFormat("{0}{1}", item.Label, seperator); i++; } return markers; } private void WriteHoles(IEnumerable data, StreamWriter writer, int nh) { int i = 0; writer.Write("\"holes\":["); foreach (var item in data) { writer.Write("{0},{1}{2}", item.X.ToString(Util.Nfi), item.Y.ToString(Util.Nfi), (i == nh - 1) ? String.Empty : ", "); i++; } writer.Write("]"); } private void WriteSegments(IEnumerable data, StreamWriter writer, int ns) { int i = 0; StringBuilder markers = new StringBuilder(); bool useMarkers = false; string seperator; writer.Write("\"segments\":{\"data\":["); foreach (var item in data) { seperator = (i == ns - 1) ? String.Empty : ", "; writer.Write("{0},{1}{2}", item.P0, item.P1, seperator); if (item.Label > 0) { useMarkers = true; } markers.AppendFormat("{0}{1}", item.Label, seperator); i++; } writer.Write("]"); if (useMarkers) { writer.Write(",\"markers\":[" + markers.ToString() + "]"); } writer.Write("}"); } private void WriteTriangles(IEnumerable data, StreamWriter writer, int ne) { int i = 0; StringBuilder neighbors = new StringBuilder(); string seperator; writer.Write("\"triangles\":{\"data\":["); foreach (var item in data) { seperator = (i == ne - 1) ? String.Empty : ", "; writer.Write("{0},{1},{2}{3}", item.GetVertexID(0), item.GetVertexID(1), item.GetVertexID(2), seperator); neighbors.AppendFormat("{0},{1},{2}{3}", item.GetNeighborID(0), item.GetNeighborID(1), item.GetNeighborID(2), seperator); i++; } writer.Write("]"); writer.Write(",\"neighbors\":[" + neighbors.ToString() + "]"); writer.Write("}"); } #endregion } }