mirror of
https://gitgud.io/AbstractConcept/rimworld-animation-studio.git
synced 2024-08-15 00:43:27 +00:00
Initial commit
This commit is contained in:
commit
3c7cc0c973
8391 changed files with 704313 additions and 0 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 56ae7d772de344c1a9a4484e952b7d33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ca1b61f918cd44aec95c1fcde79a51f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 189b37483531447aa818511aea18b139
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6376b9aaff8504ac697cd831a610f1e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c5038e46090aa4f809c75db148ca3b5f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8586f5d059bef4957927fa6f9b5f33f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4a1337b0388344c16a0e94a38038934f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9bd4358267e2d4fe3966c374d0a5b5b7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2d813b6701ce64ba398e4a1d159ae1a3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue