Initial commit

This commit is contained in:
AbstractConcept 2022-09-13 00:36:34 -05:00
commit 3c7cc0c973
8391 changed files with 704313 additions and 0 deletions

View file

@ -0,0 +1,264 @@
// -----------------------------------------------------------------------
// <copyright file="DebugWriter.cs" company="">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace UnityEngine.U2D.Animation.TriangleNet
.IO
{
using System;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Text;
using Animation.TriangleNet.Topology;
using Animation.TriangleNet.Geometry;
/// <summary>
/// Writes a the current mesh into a text file.
/// </summary>
/// <remarks>
/// File format:
///
/// num_nodes
/// id_1 nx ny mark
/// ...
/// id_n nx ny mark
///
/// num_segs
/// id_1 p1 p2 mark
/// ...
/// id_n p1 p2 mark
///
/// num_tris
/// id_1 p1 p2 p3 n1 n2 n3
/// ...
/// id_n p1 p2 p3 n1 n2 n3
/// </remarks>
class DebugWriter
{
static NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat;
int iteration;
string session;
StreamWriter stream;
string tmpFile;
int[] vertices;
int triangles;
#region Singleton pattern
private static readonly DebugWriter instance = new DebugWriter();
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static DebugWriter() {}
private DebugWriter() {}
internal static DebugWriter Session
{
get
{
return instance;
}
}
#endregion
/// <summary>
/// Start a new session with given name.
/// </summary>
/// <param name="name">Name of the session (and output files).</param>
public void Start(string session)
{
this.iteration = 0;
this.session = session;
if (this.stream != null)
{
throw new Exception("A session is active. Finish before starting a new.");
}
this.tmpFile = Path.GetTempFileName();
this.stream = new StreamWriter(tmpFile);
}
/// <summary>
/// Write complete mesh to file.
/// </summary>
public void Write(Mesh mesh, bool skip = false)
{
this.WriteMesh(mesh, skip);
this.triangles = mesh.Triangles.Count;
}
/// <summary>
/// Finish this session.
/// </summary>
public void Finish()
{
this.Finish(session + ".mshx");
}
private void Finish(string path)
{
if (stream != null)
{
stream.Flush();
stream.Dispose();
stream = null;
string header = "#!N" + this.iteration + Environment.NewLine;
using (var gzFile = new FileStream(path, FileMode.Create))
{
using (var gzStream = new GZipStream(gzFile, CompressionMode.Compress, false))
{
byte[] bytes = Encoding.UTF8.GetBytes(header);
gzStream.Write(bytes, 0, bytes.Length);
// TODO: read with stream
bytes = File.ReadAllBytes(tmpFile);
gzStream.Write(bytes, 0, bytes.Length);
}
}
File.Delete(this.tmpFile);
}
}
private void WriteGeometry(IPolygon geometry)
{
stream.WriteLine("#!G{0}", this.iteration++);
}
private void WriteMesh(Mesh mesh, bool skip)
{
// Mesh may have changed, but we choose to skip
if (triangles == mesh.triangles.Count && skip)
{
return;
}
// Header line
stream.WriteLine("#!M{0}", this.iteration++);
Vertex p1, p2, p3;
if (VerticesChanged(mesh))
{
HashVertices(mesh);
// Number of vertices.
stream.WriteLine("{0}", mesh.vertices.Count);
foreach (var v in mesh.vertices.Values)
{
// Vertex number, x and y coordinates and marker.
stream.WriteLine("{0} {1} {2} {3}", v.id, v.x.ToString(nfi), v.y.ToString(nfi), v.label);
}
}
else
{
stream.WriteLine("0");
}
// Number of segments.
stream.WriteLine("{0}", mesh.subsegs.Count);
Osub subseg = default(Osub);
subseg.orient = 0;
foreach (var item in mesh.subsegs.Values)
{
if (item.hash <= 0)
{
continue;
}
subseg.seg = item;
p1 = subseg.Org();
p2 = subseg.Dest();
// Segment number, indices of its two endpoints, and marker.
stream.WriteLine("{0} {1} {2} {3}", subseg.seg.hash, p1.id, p2.id, subseg.seg.boundary);
}
Otri tri = default(Otri), trisym = default(Otri);
tri.orient = 0;
int n1, n2, n3, h1, h2, h3;
// Number of triangles.
stream.WriteLine("{0}", mesh.triangles.Count);
foreach (var item in mesh.triangles)
{
tri.tri = item;
p1 = tri.Org();
p2 = tri.Dest();
p3 = tri.Apex();
h1 = (p1 == null) ? -1 : p1.id;
h2 = (p2 == null) ? -1 : p2.id;
h3 = (p3 == null) ? -1 : p3.id;
// Triangle number, indices for three vertices.
stream.Write("{0} {1} {2} {3}", tri.tri.hash, h1, h2, h3);
tri.orient = 1;
tri.Sym(ref trisym);
n1 = trisym.tri.hash;
tri.orient = 2;
tri.Sym(ref trisym);
n2 = trisym.tri.hash;
tri.orient = 0;
tri.Sym(ref trisym);
n3 = trisym.tri.hash;
// Neighboring triangle numbers.
stream.WriteLine(" {0} {1} {2}", n1, n2, n3);
}
}
private bool VerticesChanged(Mesh mesh)
{
if (vertices == null || mesh.Vertices.Count != vertices.Length)
{
return true;
}
int i = 0;
foreach (var v in mesh.Vertices)
{
if (v.id != vertices[i++])
{
return true;
}
}
return false;
}
private void HashVertices(Mesh mesh)
{
if (vertices == null || mesh.Vertices.Count != vertices.Length)
{
vertices = new int[mesh.Vertices.Count];
}
int i = 0;
foreach (var v in mesh.Vertices)
{
vertices[i++] = v.id;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 56ae7d772de344c1a9a4484e952b7d33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,127 @@
// -----------------------------------------------------------------------
// <copyright file="FileProcessor.cs" company="">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace UnityEngine.U2D.Animation.TriangleNet
.IO
{
using System;
using System.Collections.Generic;
using Animation.TriangleNet.Geometry;
using Animation.TriangleNet.Meshing;
internal static class FileProcessor
{
static List<IFileFormat> formats;
static FileProcessor()
{
formats = new List<IFileFormat>();
// Add Triangle file format as default.
formats.Add(new TriangleFormat());
}
internal static void Add(IFileFormat format)
{
formats.Add(format);
}
internal static bool IsSupported(string file)
{
foreach (var format in formats)
{
if (format.IsSupported(file))
{
return true;
}
}
return false;
}
#region Polygon read/write
/// <summary>
/// Read a file containing polygon geometry.
/// </summary>
/// <param name="filename">The path of the file to read.</param>
/// <returns>An instance of the <see cref="IPolygon" /> class.</returns>
internal static IPolygon Read(string filename)
{
foreach (IPolygonFormat format in formats)
{
if (format != null && format.IsSupported(filename))
{
return format.Read(filename);
}
}
throw new Exception("File format not supported.");
}
/// <summary>
/// Save a polygon geometry to disk.
/// </summary>
/// <param name="mesh">An instance of the <see cref="IPolygon" /> class.</param>
/// <param name="filename">The path of the file to save.</param>
internal static void Write(IPolygon polygon, string filename)
{
foreach (IPolygonFormat format in formats)
{
if (format != null && format.IsSupported(filename))
{
format.Write(polygon, filename);
return;
}
}
throw new Exception("File format not supported.");
}
#endregion
#region Mesh read/write
/// <summary>
/// Read a file containing a mesh.
/// </summary>
/// <param name="filename">The path of the file to read.</param>
/// <returns>An instance of the <see cref="IMesh" /> interface.</returns>
internal static IMesh Import(string filename)
{
foreach (IMeshFormat format in formats)
{
if (format != null && format.IsSupported(filename))
{
return format.Import(filename);
}
}
throw new Exception("File format not supported.");
}
/// <summary>
/// Save a mesh to disk.
/// </summary>
/// <param name="mesh">An instance of the <see cref="IMesh" /> interface.</param>
/// <param name="filename">The path of the file to save.</param>
internal static void Write(IMesh mesh, string filename)
{
foreach (IMeshFormat format in formats)
{
if (format != null && format.IsSupported(filename))
{
format.Write(mesh, filename);
return;
}
}
throw new Exception("File format not supported.");
}
#endregion
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ca1b61f918cd44aec95c1fcde79a51f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,14 @@
// -----------------------------------------------------------------------
// <copyright file="IFileFormat.cs" company="">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace UnityEngine.U2D.Animation.TriangleNet
.IO
{
internal interface IFileFormat
{
bool IsSupported(string file);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 189b37483531447aa818511aea18b139
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,39 @@
// -----------------------------------------------------------------------
// <copyright file="IMeshFormat.cs" company="">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace UnityEngine.U2D.Animation.TriangleNet
.IO
{
using System.IO;
using Animation.TriangleNet.Meshing;
/// <summary>
/// Interface for mesh I/O.
/// </summary>
internal interface IMeshFormat : IFileFormat
{
/// <summary>
/// Read a file containing a mesh.
/// </summary>
/// <param name="filename">The path of the file to read.</param>
/// <returns>An instance of the <see cref="IMesh" /> interface.</returns>
IMesh Import(string filename);
/// <summary>
/// Save a mesh to disk.
/// </summary>
/// <param name="mesh">An instance of the <see cref="IMesh" /> interface.</param>
/// <param name="filename">The path of the file to save.</param>
void Write(IMesh mesh, string filename);
/// <summary>
/// Save a mesh to a <see cref="Stream" />.
/// </summary>
/// <param name="mesh">An instance of the <see cref="IMesh" /> interface.</param>
/// <param name="stream">The stream to save to.</param>
void Write(IMesh mesh, Stream stream);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6376b9aaff8504ac697cd831a610f1e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,39 @@
// -----------------------------------------------------------------------
// <copyright file="IPolygonFormat.cs" company="">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace UnityEngine.U2D.Animation.TriangleNet
.IO
{
using System.IO;
using Animation.TriangleNet.Geometry;
/// <summary>
/// Interface for geometry input.
/// </summary>
internal interface IPolygonFormat : IFileFormat
{
/// <summary>
/// Read a file containing polygon geometry.
/// </summary>
/// <param name="filename">The path of the file to read.</param>
/// <returns>An instance of the <see cref="IPolygon" /> class.</returns>
IPolygon Read(string filename);
/// <summary>
/// Save a polygon geometry to disk.
/// </summary>
/// <param name="polygon">An instance of the <see cref="IPolygon" /> class.</param>
/// <param name="filename">The path of the file to save.</param>
void Write(IPolygon polygon, string filename);
/// <summary>
/// Save a polygon geometry to a <see cref="Stream" />.
/// </summary>
/// <param name="polygon">An instance of the <see cref="IPolygon" /> class.</param>
/// <param name="stream">The stream to save to.</param>
void Write(IPolygon polygon, Stream stream);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c5038e46090aa4f809c75db148ca3b5f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,86 @@
// -----------------------------------------------------------------------
// <copyright file="InputTriangle.cs" company="">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace UnityEngine.U2D.Animation.TriangleNet
.IO
{
using Animation.TriangleNet.Topology;
using Animation.TriangleNet.Geometry;
/// <summary>
/// Simple triangle class for input.
/// </summary>
internal class InputTriangle : ITriangle
{
internal int[] vertices;
internal int label;
internal double area;
public InputTriangle(int p0, int p1, int p2)
{
this.vertices = new int[] { p0, p1, p2 };
}
#region Public properties
/// <summary>
/// Gets the triangle id.
/// </summary>
public int ID
{
get { return 0; }
set {}
}
/// <summary>
/// Region ID the triangle belongs to.
/// </summary>
public int Label
{
get { return label; }
set { label = value; }
}
/// <summary>
/// Gets the triangle area constraint.
/// </summary>
public double Area
{
get { return area; }
set { area = value; }
}
/// <summary>
/// Gets the specified corners vertex.
/// </summary>
public Vertex GetVertex(int index)
{
return null; // TODO: throw NotSupportedException?
}
public int GetVertexID(int index)
{
return vertices[index];
}
public ITriangle GetNeighbor(int index)
{
return null;
}
public int GetNeighborID(int index)
{
return -1;
}
public ISegment GetSegment(int index)
{
return null;
}
#endregion
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8586f5d059bef4957927fa6f9b5f33f2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,92 @@
// -----------------------------------------------------------------------
// <copyright file="TriangleFormat.cs" company="">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace UnityEngine.U2D.Animation.TriangleNet
.IO
{
using System;
using System.Collections.Generic;
using System.IO;
using Animation.TriangleNet.Geometry;
using Animation.TriangleNet.Meshing;
/// <summary>
/// Implements geometry and mesh file formats of the the original Triangle code.
/// </summary>
internal class TriangleFormat : IPolygonFormat, IMeshFormat
{
public bool IsSupported(string file)
{
string ext = Path.GetExtension(file).ToLower();
if (ext == ".node" || ext == ".poly" || ext == ".ele")
{
return true;
}
return false;
}
public IMesh Import(string filename)
{
string ext = Path.GetExtension(filename);
if (ext == ".node" || ext == ".poly" || ext == ".ele")
{
List<ITriangle> triangles;
Polygon geometry;
(new TriangleReader()).Read(filename, out geometry, out triangles);
if (geometry != null && triangles != null)
{
return Converter.ToMesh(geometry, triangles.ToArray());
}
}
throw new NotSupportedException("Could not load '" + filename + "' file.");
}
public void Write(IMesh mesh, string filename)
{
var writer = new TriangleWriter();
writer.WritePoly((Mesh)mesh, Path.ChangeExtension(filename, ".poly"));
writer.WriteElements((Mesh)mesh, Path.ChangeExtension(filename, ".ele"));
}
public void Write(IMesh mesh, Stream stream)
{
throw new NotImplementedException();
}
public IPolygon Read(string filename)
{
string ext = Path.GetExtension(filename);
if (ext == ".node")
{
return (new TriangleReader()).ReadNodeFile(filename);
}
else if (ext == ".poly")
{
return (new TriangleReader()).ReadPolyFile(filename);
}
throw new NotSupportedException("File format '" + ext + "' not supported.");
}
public void Write(IPolygon polygon, string filename)
{
(new TriangleWriter()).WritePoly(polygon, filename);
}
public void Write(IPolygon polygon, Stream stream)
{
throw new NotImplementedException();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4a1337b0388344c16a0e94a38038934f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,757 @@
// -----------------------------------------------------------------------
// <copyright file="TriangleReader.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 UnityEngine.U2D.Animation.TriangleNet
.IO
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Animation.TriangleNet.Geometry;
/// <summary>
/// Helper methods for reading Triangle file formats.
/// </summary>
internal class TriangleReader
{
static NumberFormatInfo nfi = NumberFormatInfo.InvariantInfo;
int startIndex = 0;
#region Helper methods
private bool TryReadLine(StreamReader reader, out string[] token)
{
token = null;
if (reader.EndOfStream)
{
return false;
}
string line = reader.ReadLine().Trim();
while (IsStringNullOrWhiteSpace(line) || line.StartsWith("#"))
{
if (reader.EndOfStream)
{
return false;
}
line = reader.ReadLine().Trim();
}
token = line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
return true;
}
/// <summary>
/// Read vertex information of the given line.
/// </summary>
/// <param name="data">The input geometry.</param>
/// <param name="index">The current vertex index.</param>
/// <param name="line">The current line.</param>
/// <param name="attributes">Number of point attributes</param>
/// <param name="marks">Number of point markers (0 or 1)</param>
private void ReadVertex(List<Vertex> data, int index, string[] line, int attributes, int marks)
{
double x = double.Parse(line[1], nfi);
double y = double.Parse(line[2], nfi);
var v = new Vertex(x, y);
// Read a vertex marker.
if (marks > 0 && line.Length > 3 + attributes)
{
v.Label = int.Parse(line[3 + attributes]);
}
if (attributes > 0)
{
#if USE_ATTRIBS
var attribs = new double[attributes];
// Read the vertex attributes.
for (int j = 0; j < attributes; j++)
{
if (line.Length > 3 + j)
{
attribs[j] = double.Parse(line[3 + j], nfi);
}
}
v.attributes = attribs;
#endif
}
data.Add(v);
}
#endregion
#region Main I/O methods
/// <summary>
/// Reads geometry information from .node or .poly files.
/// </summary>
public void Read(string filename, out Polygon polygon)
{
polygon = null;
string path = Path.ChangeExtension(filename, ".poly");
if (File.Exists(path))
{
polygon = ReadPolyFile(path);
}
else
{
path = Path.ChangeExtension(filename, ".node");
polygon = ReadNodeFile(path);
}
}
/// <summary>
/// Reads a mesh from .node, .poly or .ele files.
/// </summary>
public void Read(string filename, out Polygon geometry, out List<ITriangle> triangles)
{
triangles = null;
Read(filename, out geometry);
string path = Path.ChangeExtension(filename, ".ele");
if (File.Exists(path) && geometry != null)
{
triangles = ReadEleFile(path);
}
}
/// <summary>
/// Reads geometry information from .node or .poly files.
/// </summary>
public IPolygon Read(string filename)
{
Polygon geometry = null;
Read(filename, out geometry);
return geometry;
}
#endregion
/// <summary>
/// Read the vertices from a file, which may be a .node or .poly file.
/// </summary>
/// <param name="nodefilename"></param>
/// <remarks>Will NOT read associated .ele by default.</remarks>
public Polygon ReadNodeFile(string nodefilename)
{
return ReadNodeFile(nodefilename, false);
}
/// <summary>
/// Read the vertices from a file, which may be a .node or .poly file.
/// </summary>
/// <param name="nodefilename"></param>
/// <param name="readElements"></param>
public Polygon ReadNodeFile(string nodefilename, bool readElements)
{
Polygon data;
startIndex = 0;
string[] line;
int invertices = 0, attributes = 0, nodemarkers = 0;
using (var reader = new StreamReader(nodefilename))
{
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file.");
}
// Read number of vertices, number of dimensions, number of vertex
// attributes, and number of boundary markers.
invertices = int.Parse(line[0]);
if (invertices < 3)
{
throw new Exception("Input must have at least three input vertices.");
}
if (line.Length > 1)
{
if (int.Parse(line[1]) != 2)
{
throw new Exception("Triangle only works with two-dimensional meshes.");
}
}
if (line.Length > 2)
{
attributes = int.Parse(line[2]);
}
if (line.Length > 3)
{
nodemarkers = int.Parse(line[3]);
}
data = new Polygon(invertices);
// Read the vertices.
if (invertices > 0)
{
for (int i = 0; i < invertices; i++)
{
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (vertices).");
}
if (line.Length < 3)
{
throw new Exception("Invalid vertex.");
}
if (i == 0)
{
startIndex = int.Parse(line[0], nfi);
}
ReadVertex(data.Points, i, line, attributes, nodemarkers);
}
}
}
if (readElements)
{
// Read area file
string elefile = Path.ChangeExtension(nodefilename, ".ele");
if (File.Exists(elefile))
{
ReadEleFile(elefile, true);
}
}
return data;
}
/// <summary>
/// Read the vertices and segments from a .poly file.
/// </summary>
/// <param name="polyfilename"></param>
/// <remarks>Will NOT read associated .ele by default.</remarks>
public Polygon ReadPolyFile(string polyfilename)
{
return ReadPolyFile(polyfilename, false, false);
}
/// <summary>
/// Read the vertices and segments from a .poly file.
/// </summary>
/// <param name="polyfilename"></param>
/// <param name="readElements">If true, look for an associated .ele file.</param>
/// <remarks>Will NOT read associated .area by default.</remarks>
public Polygon ReadPolyFile(string polyfilename, bool readElements)
{
return ReadPolyFile(polyfilename, readElements, false);
}
/// <summary>
/// Read the vertices and segments from a .poly file.
/// </summary>
/// <param name="polyfilename"></param>
/// <param name="readElements">If true, look for an associated .ele file.</param>
/// <param name="readElements">If true, look for an associated .area file.</param>
public Polygon ReadPolyFile(string polyfilename, bool readElements, bool readArea)
{
// Read poly file
Polygon data;
startIndex = 0;
string[] line;
int invertices = 0, attributes = 0, nodemarkers = 0;
using (var reader = new StreamReader(polyfilename))
{
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file.");
}
// Read number of vertices, number of dimensions, number of vertex
// attributes, and number of boundary markers.
invertices = int.Parse(line[0]);
if (line.Length > 1)
{
if (int.Parse(line[1]) != 2)
{
throw new Exception("Triangle only works with two-dimensional meshes.");
}
}
if (line.Length > 2)
{
attributes = int.Parse(line[2]);
}
if (line.Length > 3)
{
nodemarkers = int.Parse(line[3]);
}
// Read the vertices.
if (invertices > 0)
{
data = new Polygon(invertices);
for (int i = 0; i < invertices; i++)
{
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (vertices).");
}
if (line.Length < 3)
{
throw new Exception("Invalid vertex.");
}
if (i == 0)
{
// Set the start index!
startIndex = int.Parse(line[0], nfi);
}
ReadVertex(data.Points, i, line, attributes, nodemarkers);
}
}
else
{
// If the .poly file claims there are zero vertices, that means that
// the vertices should be read from a separate .node file.
data = ReadNodeFile(Path.ChangeExtension(polyfilename, ".node"));
invertices = data.Points.Count;
}
var points = data.Points;
if (points.Count == 0)
{
throw new Exception("No nodes available.");
}
// Read the segments from a .poly file.
// Read number of segments and number of boundary markers.
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (segments).");
}
int insegments = int.Parse(line[0]);
int segmentmarkers = 0;
if (line.Length > 1)
{
segmentmarkers = int.Parse(line[1]);
}
int end1, end2, mark;
// Read and insert the segments.
for (int i = 0; i < insegments; i++)
{
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (segments).");
}
if (line.Length < 3)
{
throw new Exception("Segment has no endpoints.");
}
// TODO: startIndex ok?
end1 = int.Parse(line[1]) - startIndex;
end2 = int.Parse(line[2]) - startIndex;
mark = 0;
if (segmentmarkers > 0 && line.Length > 3)
{
mark = int.Parse(line[3]);
}
if ((end1 < 0) || (end1 >= invertices))
{
if (Log.Verbose)
{
Log.Instance.Warning("Invalid first endpoint of segment.",
"MeshReader.ReadPolyfile()");
}
}
else if ((end2 < 0) || (end2 >= invertices))
{
if (Log.Verbose)
{
Log.Instance.Warning("Invalid second endpoint of segment.",
"MeshReader.ReadPolyfile()");
}
}
else
{
data.Add(new Segment(points[end1], points[end2], mark));
}
}
// Read holes from a .poly file.
// Read the holes.
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (holes).");
}
int holes = int.Parse(line[0]);
if (holes > 0)
{
for (int i = 0; i < holes; i++)
{
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (holes).");
}
if (line.Length < 3)
{
throw new Exception("Invalid hole.");
}
data.Holes.Add(new Point(double.Parse(line[1], nfi),
double.Parse(line[2], nfi)));
}
}
// Read area constraints (optional).
if (TryReadLine(reader, out line))
{
int id, regions = int.Parse(line[0]);
if (regions > 0)
{
for (int i = 0; i < regions; i++)
{
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (region).");
}
if (line.Length < 4)
{
throw new Exception("Invalid region attributes.");
}
if (!int.TryParse(line[3], out id))
{
id = i;
}
double area = 0.0;
if (line.Length > 4)
{
double.TryParse(line[4], NumberStyles.Number, nfi, out area);
}
// Triangle's .poly file format allows region definitions with
// either 4 or 5 parameters, and different interpretations for
// them depending on the number of parameters.
//
// See http://www.cs.cmu.edu/~quake/triangle.poly.html
//
// The .NET version will interpret the fourth parameter always
// as an integer region id and the optional fifth parameter as
// an area constraint.
data.Regions.Add(new RegionPointer(
double.Parse(line[1], nfi), // Region x
double.Parse(line[2], nfi), // Region y
id, area));
}
}
}
}
// Read ele file
if (readElements)
{
string elefile = Path.ChangeExtension(polyfilename, ".ele");
if (File.Exists(elefile))
{
ReadEleFile(elefile, readArea);
}
}
return data;
}
/// <summary>
/// Read elements from an .ele file.
/// </summary>
/// <param name="elefilename">The file name.</param>
/// <returns>A list of triangles.</returns>
public List<ITriangle> ReadEleFile(string elefilename)
{
return ReadEleFile(elefilename, false);
}
/// <summary>
/// Read the elements from an .ele file.
/// </summary>
/// <param name="elefilename"></param>
/// <param name="data"></param>
/// <param name="readArea"></param>
private List<ITriangle> ReadEleFile(string elefilename, bool readArea)
{
int intriangles = 0, attributes = 0;
List<ITriangle> triangles;
using (var reader = new StreamReader(elefilename))
{
// Read number of elements and number of attributes.
string[] line;
bool validRegion = false;
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (elements).");
}
intriangles = int.Parse(line[0]);
// We irgnore index 1 (number of nodes per triangle)
attributes = 0;
if (line.Length > 2)
{
attributes = int.Parse(line[2]);
validRegion = true;
}
if (attributes > 1)
{
Log.Instance.Warning("Triangle attributes not supported.", "FileReader.Read");
}
triangles = new List<ITriangle>(intriangles);
InputTriangle tri;
// Read triangles.
for (int i = 0; i < intriangles; i++)
{
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (elements).");
}
if (line.Length < 4)
{
throw new Exception("Triangle has no nodes.");
}
// TODO: startIndex ok?
tri = new InputTriangle(
int.Parse(line[1]) - startIndex,
int.Parse(line[2]) - startIndex,
int.Parse(line[3]) - startIndex);
// Read triangle region
if (attributes > 0 && validRegion)
{
int region = 0;
validRegion = int.TryParse(line[4], out region);
tri.label = region;
}
triangles.Add(tri);
}
}
// Read area file
if (readArea)
{
string areafile = Path.ChangeExtension(elefilename, ".area");
if (File.Exists(areafile))
{
ReadAreaFile(areafile, intriangles);
}
}
return triangles;
}
/// <summary>
/// Read the area constraints from an .area file.
/// </summary>
/// <param name="areafilename"></param>
/// <param name="intriangles"></param>
/// <param name="data"></param>
private double[] ReadAreaFile(string areafilename, int intriangles)
{
double[] data = null;
using (var reader = new StreamReader(areafilename))
{
string[] line;
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (area).");
}
if (int.Parse(line[0]) != intriangles)
{
Log.Instance.Warning("Number of area constraints doesn't match number of triangles.",
"ReadAreaFile()");
return null;
}
data = new double[intriangles];
// Read area constraints.
for (int i = 0; i < intriangles; i++)
{
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (area).");
}
if (line.Length != 2)
{
throw new Exception("Triangle has no nodes.");
}
data[i] = double.Parse(line[1], nfi);
}
}
return data;
}
/// <summary>
/// Read an .edge file.
/// </summary>
/// <param name="edgeFile">The file name.</param>
/// <param name="invertices">The number of input vertices (read from a .node or .poly file).</param>
/// <returns>A List of edges.</returns>
public List<Edge> ReadEdgeFile(string edgeFile, int invertices)
{
// Read poly file
List<Edge> data = null;
startIndex = 0;
string[] line;
using (var reader = new StreamReader(edgeFile))
{
// Read the edges from a .edge file.
// Read number of segments and number of boundary markers.
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (segments).");
}
int inedges = int.Parse(line[0]);
int edgemarkers = 0;
if (line.Length > 1)
{
edgemarkers = int.Parse(line[1]);
}
if (inedges > 0)
{
data = new List<Edge>(inedges);
}
int end1, end2, mark;
// Read and insert the segments.
for (int i = 0; i < inedges; i++)
{
if (!TryReadLine(reader, out line))
{
throw new Exception("Can't read input file (segments).");
}
if (line.Length < 3)
{
throw new Exception("Segment has no endpoints.");
}
// TODO: startIndex ok?
end1 = int.Parse(line[1]) - startIndex;
end2 = int.Parse(line[2]) - startIndex;
mark = 0;
if (edgemarkers > 0 && line.Length > 3)
{
mark = int.Parse(line[3]);
}
if ((end1 < 0) || (end1 >= invertices))
{
if (Log.Verbose)
{
Log.Instance.Warning("Invalid first endpoint of segment.",
"MeshReader.ReadPolyfile()");
}
}
else if ((end2 < 0) || (end2 >= invertices))
{
if (Log.Verbose)
{
Log.Instance.Warning("Invalid second endpoint of segment.",
"MeshReader.ReadPolyfile()");
}
}
else
{
data.Add(new Edge(end1, end2, mark));
}
}
}
return data;
}
bool IsStringNullOrWhiteSpace(string value)
{
if (value != null)
{
for (int i = 0; i < value.Length; i++)
{
if (!char.IsWhiteSpace(value[i]))
{
return false;
}
}
}
return true;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9bd4358267e2d4fe3966c374d0a5b5b7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,460 @@
// -----------------------------------------------------------------------
// <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 UnityEngine.U2D.Animation.TriangleNet
.IO
{
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Animation.TriangleNet.Geometry;
using Animation.TriangleNet.Topology;
/// <summary>
/// Helper methods for writing Triangle file formats.
/// </summary>
internal 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);
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2d813b6701ce64ba398e4a1d159ae1a3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: