rj-action-library/External/Triangle.NET/Triangle/IO/TriangleWriter.cs

460 lines
17 KiB
C#

// -----------------------------------------------------------------------
// <copyright file="TriangleWriter.cs" company="">
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.IO
{
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using TriangleNet.Geometry;
using TriangleNet.Topology;
/// <summary>
/// Helper methods for writing Triangle file formats.
/// </summary>
public class TriangleWriter
{
static NumberFormatInfo nfi = NumberFormatInfo.InvariantInfo;
/// <summary>
/// Number the vertices and write them to a .node file.
/// </summary>
/// <param name="mesh"></param>
/// <param name="filename"></param>
public void Write(Mesh mesh, string filename)
{
WritePoly(mesh, Path.ChangeExtension(filename, ".poly"));
WriteElements(mesh, Path.ChangeExtension(filename, ".ele"));
}
/// <summary>
/// Number the vertices and write them to a .node file.
/// </summary>
/// <param name="mesh"></param>
/// <param name="filename"></param>
public void WriteNodes(Mesh mesh, string filename)
{
using (var writer = new StreamWriter(filename))
{
WriteNodes(writer, mesh);
}
}
/// <summary>
/// Number the vertices and write them to a .node file.
/// </summary>
private void WriteNodes(StreamWriter writer, Mesh mesh)
{
int outvertices = mesh.vertices.Count;
int nextras = mesh.nextras;
Behavior behavior = mesh.behavior;
if (behavior.Jettison)
{
outvertices = mesh.vertices.Count - mesh.undeads;
}
if (writer != null)
{
// Number of vertices, number of dimensions, number of vertex attributes,
// and number of boundary markers (zero or one).
writer.WriteLine("{0} {1} {2} {3}", outvertices, mesh.mesh_dim, nextras,
behavior.UseBoundaryMarkers ? "1" : "0");
if (mesh.numbering == NodeNumbering.None)
{
// If the mesh isn't numbered yet, use linear node numbering.
mesh.Renumber();
}
if (mesh.numbering == NodeNumbering.Linear)
{
// If numbering is linear, just use the dictionary values.
WriteNodes(writer, mesh.vertices.Values, behavior.UseBoundaryMarkers,
nextras, behavior.Jettison);
}
else
{
// If numbering is not linear, a simple 'foreach' traversal of the dictionary
// values doesn't reflect the actual numbering. Use an array instead.
// TODO: Could use a custom sorting function on dictionary values instead.
Vertex[] nodes = new Vertex[mesh.vertices.Count];
foreach (var node in mesh.vertices.Values)
{
nodes[node.id] = node;
}
WriteNodes(writer, nodes, behavior.UseBoundaryMarkers,
nextras, behavior.Jettison);
}
}
}
/// <summary>
/// Write the vertices to a stream.
/// </summary>
/// <param name="nodes"></param>
/// <param name="writer"></param>
private void WriteNodes(StreamWriter writer, IEnumerable<Vertex> nodes, bool markers,
int attribs, bool jettison)
{
int index = 0;
foreach (var vertex in nodes)
{
if (!jettison || vertex.type != VertexType.UndeadVertex)
{
// Vertex number, x and y coordinates.
writer.Write("{0} {1} {2}", index, vertex.x.ToString(nfi), vertex.y.ToString(nfi));
#if USE_ATTRIBS
// Write attributes.
for (int j = 0; j < attribs; j++)
{
writer.Write(" {0}", vertex.attributes[j].ToString(nfi));
}
#endif
if (markers)
{
// Write the boundary marker.
writer.Write(" {0}", vertex.label);
}
writer.WriteLine();
index++;
}
}
}
/// <summary>
/// Write the triangles to an .ele file.
/// </summary>
/// <param name="mesh"></param>
/// <param name="filename"></param>
public void WriteElements(Mesh mesh, string filename)
{
Otri tri = default(Otri);
Vertex p1, p2, p3;
bool regions = mesh.behavior.useRegions;
int j = 0;
tri.orient = 0;
using (var writer = new StreamWriter(filename))
{
// Number of triangles, vertices per triangle, attributes per triangle.
writer.WriteLine("{0} 3 {1}", mesh.triangles.Count, regions ? 1 : 0);
foreach (var item in mesh.triangles)
{
tri.tri = item;
p1 = tri.Org();
p2 = tri.Dest();
p3 = tri.Apex();
// Triangle number, indices for three vertices.
writer.Write("{0} {1} {2} {3}", j, p1.id, p2.id, p3.id);
if (regions)
{
writer.Write(" {0}", tri.tri.label);
}
writer.WriteLine();
// Number elements
item.id = j++;
}
}
}
/// <summary>
/// Write the segments and holes to a .poly file.
/// </summary>
/// <param name="polygon">Data source.</param>
/// <param name="filename">File name.</param>
/// <param name="writeNodes">Write nodes into this file.</param>
/// <remarks>If the nodes should not be written into this file,
/// make sure a .node file was written before, so that the nodes
/// are numbered right.</remarks>
public void WritePoly(IPolygon polygon, string filename)
{
bool hasMarkers = polygon.HasSegmentMarkers;
using (var writer = new StreamWriter(filename))
{
// TODO: write vertex attributes
writer.WriteLine("{0} 2 0 {1}", polygon.Points.Count, polygon.HasPointMarkers ? "1" : "0");
// Write nodes to this file.
WriteNodes(writer, polygon.Points, polygon.HasPointMarkers, 0, false);
// Number of segments, number of boundary markers (zero or one).
writer.WriteLine("{0} {1}", polygon.Segments.Count, hasMarkers ? "1" : "0");
Vertex p, q;
int j = 0;
foreach (var seg in polygon.Segments)
{
p = seg.GetVertex(0);
q = seg.GetVertex(1);
// Segment number, indices of its two endpoints, and possibly a marker.
if (hasMarkers)
{
writer.WriteLine("{0} {1} {2} {3}", j, p.ID, q.ID, seg.Label);
}
else
{
writer.WriteLine("{0} {1} {2}", j, p.ID, q.ID);
}
j++;
}
// Holes
j = 0;
writer.WriteLine("{0}", polygon.Holes.Count);
foreach (var hole in polygon.Holes)
{
writer.WriteLine("{0} {1} {2}", j++, hole.X.ToString(nfi), hole.Y.ToString(nfi));
}
// Regions
if (polygon.Regions.Count > 0)
{
j = 0;
writer.WriteLine("{0}", polygon.Regions.Count);
foreach (var region in polygon.Regions)
{
writer.WriteLine("{0} {1} {2} {3}", j, region.point.X.ToString(nfi),
region.point.Y.ToString(nfi), region.id);
j++;
}
}
}
}
/// <summary>
/// Write the segments and holes to a .poly file.
/// </summary>
/// <param name="mesh"></param>
/// <param name="filename"></param>
public void WritePoly(Mesh mesh, string filename)
{
WritePoly(mesh, filename, true);
}
/// <summary>
/// Write the segments and holes to a .poly file.
/// </summary>
/// <param name="mesh">Data source.</param>
/// <param name="filename">File name.</param>
/// <param name="writeNodes">Write nodes into this file.</param>
/// <remarks>If the nodes should not be written into this file,
/// make sure a .node file was written before, so that the nodes
/// are numbered right.</remarks>
public void WritePoly(Mesh mesh, string filename, bool writeNodes)
{
Osub subseg = default(Osub);
Vertex pt1, pt2;
bool useBoundaryMarkers = mesh.behavior.UseBoundaryMarkers;
using (var writer = new StreamWriter(filename))
{
if (writeNodes)
{
// Write nodes to this file.
WriteNodes(writer, mesh);
}
else
{
// The zero indicates that the vertices are in a separate .node file.
// Followed by number of dimensions, number of vertex attributes,
// and number of boundary markers (zero or one).
writer.WriteLine("0 {0} {1} {2}", mesh.mesh_dim, mesh.nextras,
useBoundaryMarkers ? "1" : "0");
}
// Number of segments, number of boundary markers (zero or one).
writer.WriteLine("{0} {1}", mesh.subsegs.Count,
useBoundaryMarkers ? "1" : "0");
subseg.orient = 0;
int j = 0;
foreach (var item in mesh.subsegs.Values)
{
subseg.seg = item;
pt1 = subseg.Org();
pt2 = subseg.Dest();
// Segment number, indices of its two endpoints, and possibly a marker.
if (useBoundaryMarkers)
{
writer.WriteLine("{0} {1} {2} {3}", j, pt1.id, pt2.id, subseg.seg.boundary);
}
else
{
writer.WriteLine("{0} {1} {2}", j, pt1.id, pt2.id);
}
j++;
}
// Holes
j = 0;
writer.WriteLine("{0}", mesh.holes.Count);
foreach (var hole in mesh.holes)
{
writer.WriteLine("{0} {1} {2}", j++, hole.X.ToString(nfi), hole.Y.ToString(nfi));
}
// Regions
if (mesh.regions.Count > 0)
{
j = 0;
writer.WriteLine("{0}", mesh.regions.Count);
foreach (var region in mesh.regions)
{
writer.WriteLine("{0} {1} {2} {3}", j, region.point.X.ToString(nfi),
region.point.Y.ToString(nfi), region.id);
j++;
}
}
}
}
/// <summary>
/// Write the edges to an .edge file.
/// </summary>
/// <param name="mesh"></param>
/// <param name="filename"></param>
public void WriteEdges(Mesh mesh, string filename)
{
Otri tri = default(Otri), trisym = default(Otri);
Osub checkmark = default(Osub);
Vertex p1, p2;
Behavior behavior = mesh.behavior;
using (var writer = new StreamWriter(filename))
{
// Number of edges, number of boundary markers (zero or one).
writer.WriteLine("{0} {1}", mesh.NumberOfEdges, behavior.UseBoundaryMarkers ? "1" : "0");
long index = 0;
// To loop over the set of edges, loop over all triangles, and look at
// the three edges of each triangle. If there isn't another triangle
// adjacent to the edge, operate on the edge. If there is another
// adjacent triangle, operate on the edge only if the current triangle
// has a smaller pointer than its neighbor. This way, each edge is
// considered only once.
foreach (var item in mesh.triangles)
{
tri.tri = item;
for (tri.orient = 0; tri.orient < 3; tri.orient++)
{
tri.Sym(ref trisym);
if ((tri.tri.id < trisym.tri.id) || (trisym.tri.id == Mesh.DUMMY))
{
p1 = tri.Org();
p2 = tri.Dest();
if (behavior.UseBoundaryMarkers)
{
// Edge number, indices of two endpoints, and a boundary marker.
// If there's no subsegment, the boundary marker is zero.
if (behavior.useSegments)
{
tri.Pivot(ref checkmark);
if (checkmark.seg.hash == Mesh.DUMMY)
{
writer.WriteLine("{0} {1} {2} {3}", index, p1.id, p2.id, 0);
}
else
{
writer.WriteLine("{0} {1} {2} {3}", index, p1.id, p2.id,
checkmark.seg.boundary);
}
}
else
{
writer.WriteLine("{0} {1} {2} {3}", index, p1.id, p2.id,
trisym.tri.id == Mesh.DUMMY ? "1" : "0");
}
}
else
{
// Edge number, indices of two endpoints.
writer.WriteLine("{0} {1} {2}", index, p1.id, p2.id);
}
index++;
}
}
}
}
}
/// <summary>
/// Write the triangle neighbors to a .neigh file.
/// </summary>
/// <param name="mesh"></param>
/// <param name="filename"></param>
/// <remarks>WARNING: Be sure WriteElements has been called before,
/// so the elements are numbered right!</remarks>
public void WriteNeighbors(Mesh mesh, string filename)
{
Otri tri = default(Otri), trisym = default(Otri);
int n1, n2, n3;
int i = 0;
using (StreamWriter writer = new StreamWriter(filename))
{
// Number of triangles, three neighbors per triangle.
writer.WriteLine("{0} 3", mesh.triangles.Count);
foreach (var item in mesh.triangles)
{
tri.tri = item;
tri.orient = 1;
tri.Sym(ref trisym);
n1 = trisym.tri.id;
tri.orient = 2;
tri.Sym(ref trisym);
n2 = trisym.tri.id;
tri.orient = 0;
tri.Sym(ref trisym);
n3 = trisym.tri.id;
// Triangle number, neighboring triangle numbers.
writer.WriteLine("{0} {1} {2} {3}", i++, n1, n2, n3);
}
}
}
}
}