TriangleReader.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. // -----------------------------------------------------------------------
  2. // <copyright file="TriangleReader.cs" company="">
  3. // Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
  4. // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
  5. // </copyright>
  6. // -----------------------------------------------------------------------
  7. namespace UnityEngine.U2D.Animation.TriangleNet
  8. .IO
  9. {
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Globalization;
  13. using System.IO;
  14. using Animation.TriangleNet.Geometry;
  15. /// <summary>
  16. /// Helper methods for reading Triangle file formats.
  17. /// </summary>
  18. internal class TriangleReader
  19. {
  20. static NumberFormatInfo nfi = NumberFormatInfo.InvariantInfo;
  21. int startIndex = 0;
  22. #region Helper methods
  23. private bool TryReadLine(StreamReader reader, out string[] token)
  24. {
  25. token = null;
  26. if (reader.EndOfStream)
  27. {
  28. return false;
  29. }
  30. string line = reader.ReadLine().Trim();
  31. while (IsStringNullOrWhiteSpace(line) || line.StartsWith("#"))
  32. {
  33. if (reader.EndOfStream)
  34. {
  35. return false;
  36. }
  37. line = reader.ReadLine().Trim();
  38. }
  39. token = line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
  40. return true;
  41. }
  42. /// <summary>
  43. /// Read vertex information of the given line.
  44. /// </summary>
  45. /// <param name="data">The input geometry.</param>
  46. /// <param name="index">The current vertex index.</param>
  47. /// <param name="line">The current line.</param>
  48. /// <param name="attributes">Number of point attributes</param>
  49. /// <param name="marks">Number of point markers (0 or 1)</param>
  50. private void ReadVertex(List<Vertex> data, int index, string[] line, int attributes, int marks)
  51. {
  52. double x = double.Parse(line[1], nfi);
  53. double y = double.Parse(line[2], nfi);
  54. var v = new Vertex(x, y);
  55. // Read a vertex marker.
  56. if (marks > 0 && line.Length > 3 + attributes)
  57. {
  58. v.Label = int.Parse(line[3 + attributes]);
  59. }
  60. if (attributes > 0)
  61. {
  62. #if USE_ATTRIBS
  63. var attribs = new double[attributes];
  64. // Read the vertex attributes.
  65. for (int j = 0; j < attributes; j++)
  66. {
  67. if (line.Length > 3 + j)
  68. {
  69. attribs[j] = double.Parse(line[3 + j], nfi);
  70. }
  71. }
  72. v.attributes = attribs;
  73. #endif
  74. }
  75. data.Add(v);
  76. }
  77. #endregion
  78. #region Main I/O methods
  79. /// <summary>
  80. /// Reads geometry information from .node or .poly files.
  81. /// </summary>
  82. public void Read(string filename, out Polygon polygon)
  83. {
  84. polygon = null;
  85. string path = Path.ChangeExtension(filename, ".poly");
  86. if (File.Exists(path))
  87. {
  88. polygon = ReadPolyFile(path);
  89. }
  90. else
  91. {
  92. path = Path.ChangeExtension(filename, ".node");
  93. polygon = ReadNodeFile(path);
  94. }
  95. }
  96. /// <summary>
  97. /// Reads a mesh from .node, .poly or .ele files.
  98. /// </summary>
  99. public void Read(string filename, out Polygon geometry, out List<ITriangle> triangles)
  100. {
  101. triangles = null;
  102. Read(filename, out geometry);
  103. string path = Path.ChangeExtension(filename, ".ele");
  104. if (File.Exists(path) && geometry != null)
  105. {
  106. triangles = ReadEleFile(path);
  107. }
  108. }
  109. /// <summary>
  110. /// Reads geometry information from .node or .poly files.
  111. /// </summary>
  112. public IPolygon Read(string filename)
  113. {
  114. Polygon geometry = null;
  115. Read(filename, out geometry);
  116. return geometry;
  117. }
  118. #endregion
  119. /// <summary>
  120. /// Read the vertices from a file, which may be a .node or .poly file.
  121. /// </summary>
  122. /// <param name="nodefilename"></param>
  123. /// <remarks>Will NOT read associated .ele by default.</remarks>
  124. public Polygon ReadNodeFile(string nodefilename)
  125. {
  126. return ReadNodeFile(nodefilename, false);
  127. }
  128. /// <summary>
  129. /// Read the vertices from a file, which may be a .node or .poly file.
  130. /// </summary>
  131. /// <param name="nodefilename"></param>
  132. /// <param name="readElements"></param>
  133. public Polygon ReadNodeFile(string nodefilename, bool readElements)
  134. {
  135. Polygon data;
  136. startIndex = 0;
  137. string[] line;
  138. int invertices = 0, attributes = 0, nodemarkers = 0;
  139. using (var reader = new StreamReader(nodefilename))
  140. {
  141. if (!TryReadLine(reader, out line))
  142. {
  143. throw new Exception("Can't read input file.");
  144. }
  145. // Read number of vertices, number of dimensions, number of vertex
  146. // attributes, and number of boundary markers.
  147. invertices = int.Parse(line[0]);
  148. if (invertices < 3)
  149. {
  150. throw new Exception("Input must have at least three input vertices.");
  151. }
  152. if (line.Length > 1)
  153. {
  154. if (int.Parse(line[1]) != 2)
  155. {
  156. throw new Exception("Triangle only works with two-dimensional meshes.");
  157. }
  158. }
  159. if (line.Length > 2)
  160. {
  161. attributes = int.Parse(line[2]);
  162. }
  163. if (line.Length > 3)
  164. {
  165. nodemarkers = int.Parse(line[3]);
  166. }
  167. data = new Polygon(invertices);
  168. // Read the vertices.
  169. if (invertices > 0)
  170. {
  171. for (int i = 0; i < invertices; i++)
  172. {
  173. if (!TryReadLine(reader, out line))
  174. {
  175. throw new Exception("Can't read input file (vertices).");
  176. }
  177. if (line.Length < 3)
  178. {
  179. throw new Exception("Invalid vertex.");
  180. }
  181. if (i == 0)
  182. {
  183. startIndex = int.Parse(line[0], nfi);
  184. }
  185. ReadVertex(data.Points, i, line, attributes, nodemarkers);
  186. }
  187. }
  188. }
  189. if (readElements)
  190. {
  191. // Read area file
  192. string elefile = Path.ChangeExtension(nodefilename, ".ele");
  193. if (File.Exists(elefile))
  194. {
  195. ReadEleFile(elefile, true);
  196. }
  197. }
  198. return data;
  199. }
  200. /// <summary>
  201. /// Read the vertices and segments from a .poly file.
  202. /// </summary>
  203. /// <param name="polyfilename"></param>
  204. /// <remarks>Will NOT read associated .ele by default.</remarks>
  205. public Polygon ReadPolyFile(string polyfilename)
  206. {
  207. return ReadPolyFile(polyfilename, false, false);
  208. }
  209. /// <summary>
  210. /// Read the vertices and segments from a .poly file.
  211. /// </summary>
  212. /// <param name="polyfilename"></param>
  213. /// <param name="readElements">If true, look for an associated .ele file.</param>
  214. /// <remarks>Will NOT read associated .area by default.</remarks>
  215. public Polygon ReadPolyFile(string polyfilename, bool readElements)
  216. {
  217. return ReadPolyFile(polyfilename, readElements, false);
  218. }
  219. /// <summary>
  220. /// Read the vertices and segments from a .poly file.
  221. /// </summary>
  222. /// <param name="polyfilename"></param>
  223. /// <param name="readElements">If true, look for an associated .ele file.</param>
  224. /// <param name="readElements">If true, look for an associated .area file.</param>
  225. public Polygon ReadPolyFile(string polyfilename, bool readElements, bool readArea)
  226. {
  227. // Read poly file
  228. Polygon data;
  229. startIndex = 0;
  230. string[] line;
  231. int invertices = 0, attributes = 0, nodemarkers = 0;
  232. using (var reader = new StreamReader(polyfilename))
  233. {
  234. if (!TryReadLine(reader, out line))
  235. {
  236. throw new Exception("Can't read input file.");
  237. }
  238. // Read number of vertices, number of dimensions, number of vertex
  239. // attributes, and number of boundary markers.
  240. invertices = int.Parse(line[0]);
  241. if (line.Length > 1)
  242. {
  243. if (int.Parse(line[1]) != 2)
  244. {
  245. throw new Exception("Triangle only works with two-dimensional meshes.");
  246. }
  247. }
  248. if (line.Length > 2)
  249. {
  250. attributes = int.Parse(line[2]);
  251. }
  252. if (line.Length > 3)
  253. {
  254. nodemarkers = int.Parse(line[3]);
  255. }
  256. // Read the vertices.
  257. if (invertices > 0)
  258. {
  259. data = new Polygon(invertices);
  260. for (int i = 0; i < invertices; i++)
  261. {
  262. if (!TryReadLine(reader, out line))
  263. {
  264. throw new Exception("Can't read input file (vertices).");
  265. }
  266. if (line.Length < 3)
  267. {
  268. throw new Exception("Invalid vertex.");
  269. }
  270. if (i == 0)
  271. {
  272. // Set the start index!
  273. startIndex = int.Parse(line[0], nfi);
  274. }
  275. ReadVertex(data.Points, i, line, attributes, nodemarkers);
  276. }
  277. }
  278. else
  279. {
  280. // If the .poly file claims there are zero vertices, that means that
  281. // the vertices should be read from a separate .node file.
  282. data = ReadNodeFile(Path.ChangeExtension(polyfilename, ".node"));
  283. invertices = data.Points.Count;
  284. }
  285. var points = data.Points;
  286. if (points.Count == 0)
  287. {
  288. throw new Exception("No nodes available.");
  289. }
  290. // Read the segments from a .poly file.
  291. // Read number of segments and number of boundary markers.
  292. if (!TryReadLine(reader, out line))
  293. {
  294. throw new Exception("Can't read input file (segments).");
  295. }
  296. int insegments = int.Parse(line[0]);
  297. int segmentmarkers = 0;
  298. if (line.Length > 1)
  299. {
  300. segmentmarkers = int.Parse(line[1]);
  301. }
  302. int end1, end2, mark;
  303. // Read and insert the segments.
  304. for (int i = 0; i < insegments; i++)
  305. {
  306. if (!TryReadLine(reader, out line))
  307. {
  308. throw new Exception("Can't read input file (segments).");
  309. }
  310. if (line.Length < 3)
  311. {
  312. throw new Exception("Segment has no endpoints.");
  313. }
  314. // TODO: startIndex ok?
  315. end1 = int.Parse(line[1]) - startIndex;
  316. end2 = int.Parse(line[2]) - startIndex;
  317. mark = 0;
  318. if (segmentmarkers > 0 && line.Length > 3)
  319. {
  320. mark = int.Parse(line[3]);
  321. }
  322. if ((end1 < 0) || (end1 >= invertices))
  323. {
  324. if (Log.Verbose)
  325. {
  326. Log.Instance.Warning("Invalid first endpoint of segment.",
  327. "MeshReader.ReadPolyfile()");
  328. }
  329. }
  330. else if ((end2 < 0) || (end2 >= invertices))
  331. {
  332. if (Log.Verbose)
  333. {
  334. Log.Instance.Warning("Invalid second endpoint of segment.",
  335. "MeshReader.ReadPolyfile()");
  336. }
  337. }
  338. else
  339. {
  340. data.Add(new Segment(points[end1], points[end2], mark));
  341. }
  342. }
  343. // Read holes from a .poly file.
  344. // Read the holes.
  345. if (!TryReadLine(reader, out line))
  346. {
  347. throw new Exception("Can't read input file (holes).");
  348. }
  349. int holes = int.Parse(line[0]);
  350. if (holes > 0)
  351. {
  352. for (int i = 0; i < holes; i++)
  353. {
  354. if (!TryReadLine(reader, out line))
  355. {
  356. throw new Exception("Can't read input file (holes).");
  357. }
  358. if (line.Length < 3)
  359. {
  360. throw new Exception("Invalid hole.");
  361. }
  362. data.Holes.Add(new Point(double.Parse(line[1], nfi),
  363. double.Parse(line[2], nfi)));
  364. }
  365. }
  366. // Read area constraints (optional).
  367. if (TryReadLine(reader, out line))
  368. {
  369. int id, regions = int.Parse(line[0]);
  370. if (regions > 0)
  371. {
  372. for (int i = 0; i < regions; i++)
  373. {
  374. if (!TryReadLine(reader, out line))
  375. {
  376. throw new Exception("Can't read input file (region).");
  377. }
  378. if (line.Length < 4)
  379. {
  380. throw new Exception("Invalid region attributes.");
  381. }
  382. if (!int.TryParse(line[3], out id))
  383. {
  384. id = i;
  385. }
  386. double area = 0.0;
  387. if (line.Length > 4)
  388. {
  389. double.TryParse(line[4], NumberStyles.Number, nfi, out area);
  390. }
  391. // Triangle's .poly file format allows region definitions with
  392. // either 4 or 5 parameters, and different interpretations for
  393. // them depending on the number of parameters.
  394. //
  395. // See http://www.cs.cmu.edu/~quake/triangle.poly.html
  396. //
  397. // The .NET version will interpret the fourth parameter always
  398. // as an integer region id and the optional fifth parameter as
  399. // an area constraint.
  400. data.Regions.Add(new RegionPointer(
  401. double.Parse(line[1], nfi), // Region x
  402. double.Parse(line[2], nfi), // Region y
  403. id, area));
  404. }
  405. }
  406. }
  407. }
  408. // Read ele file
  409. if (readElements)
  410. {
  411. string elefile = Path.ChangeExtension(polyfilename, ".ele");
  412. if (File.Exists(elefile))
  413. {
  414. ReadEleFile(elefile, readArea);
  415. }
  416. }
  417. return data;
  418. }
  419. /// <summary>
  420. /// Read elements from an .ele file.
  421. /// </summary>
  422. /// <param name="elefilename">The file name.</param>
  423. /// <returns>A list of triangles.</returns>
  424. public List<ITriangle> ReadEleFile(string elefilename)
  425. {
  426. return ReadEleFile(elefilename, false);
  427. }
  428. /// <summary>
  429. /// Read the elements from an .ele file.
  430. /// </summary>
  431. /// <param name="elefilename"></param>
  432. /// <param name="data"></param>
  433. /// <param name="readArea"></param>
  434. private List<ITriangle> ReadEleFile(string elefilename, bool readArea)
  435. {
  436. int intriangles = 0, attributes = 0;
  437. List<ITriangle> triangles;
  438. using (var reader = new StreamReader(elefilename))
  439. {
  440. // Read number of elements and number of attributes.
  441. string[] line;
  442. bool validRegion = false;
  443. if (!TryReadLine(reader, out line))
  444. {
  445. throw new Exception("Can't read input file (elements).");
  446. }
  447. intriangles = int.Parse(line[0]);
  448. // We irgnore index 1 (number of nodes per triangle)
  449. attributes = 0;
  450. if (line.Length > 2)
  451. {
  452. attributes = int.Parse(line[2]);
  453. validRegion = true;
  454. }
  455. if (attributes > 1)
  456. {
  457. Log.Instance.Warning("Triangle attributes not supported.", "FileReader.Read");
  458. }
  459. triangles = new List<ITriangle>(intriangles);
  460. InputTriangle tri;
  461. // Read triangles.
  462. for (int i = 0; i < intriangles; i++)
  463. {
  464. if (!TryReadLine(reader, out line))
  465. {
  466. throw new Exception("Can't read input file (elements).");
  467. }
  468. if (line.Length < 4)
  469. {
  470. throw new Exception("Triangle has no nodes.");
  471. }
  472. // TODO: startIndex ok?
  473. tri = new InputTriangle(
  474. int.Parse(line[1]) - startIndex,
  475. int.Parse(line[2]) - startIndex,
  476. int.Parse(line[3]) - startIndex);
  477. // Read triangle region
  478. if (attributes > 0 && validRegion)
  479. {
  480. int region = 0;
  481. validRegion = int.TryParse(line[4], out region);
  482. tri.label = region;
  483. }
  484. triangles.Add(tri);
  485. }
  486. }
  487. // Read area file
  488. if (readArea)
  489. {
  490. string areafile = Path.ChangeExtension(elefilename, ".area");
  491. if (File.Exists(areafile))
  492. {
  493. ReadAreaFile(areafile, intriangles);
  494. }
  495. }
  496. return triangles;
  497. }
  498. /// <summary>
  499. /// Read the area constraints from an .area file.
  500. /// </summary>
  501. /// <param name="areafilename"></param>
  502. /// <param name="intriangles"></param>
  503. /// <param name="data"></param>
  504. private double[] ReadAreaFile(string areafilename, int intriangles)
  505. {
  506. double[] data = null;
  507. using (var reader = new StreamReader(areafilename))
  508. {
  509. string[] line;
  510. if (!TryReadLine(reader, out line))
  511. {
  512. throw new Exception("Can't read input file (area).");
  513. }
  514. if (int.Parse(line[0]) != intriangles)
  515. {
  516. Log.Instance.Warning("Number of area constraints doesn't match number of triangles.",
  517. "ReadAreaFile()");
  518. return null;
  519. }
  520. data = new double[intriangles];
  521. // Read area constraints.
  522. for (int i = 0; i < intriangles; i++)
  523. {
  524. if (!TryReadLine(reader, out line))
  525. {
  526. throw new Exception("Can't read input file (area).");
  527. }
  528. if (line.Length != 2)
  529. {
  530. throw new Exception("Triangle has no nodes.");
  531. }
  532. data[i] = double.Parse(line[1], nfi);
  533. }
  534. }
  535. return data;
  536. }
  537. /// <summary>
  538. /// Read an .edge file.
  539. /// </summary>
  540. /// <param name="edgeFile">The file name.</param>
  541. /// <param name="invertices">The number of input vertices (read from a .node or .poly file).</param>
  542. /// <returns>A List of edges.</returns>
  543. public List<Edge> ReadEdgeFile(string edgeFile, int invertices)
  544. {
  545. // Read poly file
  546. List<Edge> data = null;
  547. startIndex = 0;
  548. string[] line;
  549. using (var reader = new StreamReader(edgeFile))
  550. {
  551. // Read the edges from a .edge file.
  552. // Read number of segments and number of boundary markers.
  553. if (!TryReadLine(reader, out line))
  554. {
  555. throw new Exception("Can't read input file (segments).");
  556. }
  557. int inedges = int.Parse(line[0]);
  558. int edgemarkers = 0;
  559. if (line.Length > 1)
  560. {
  561. edgemarkers = int.Parse(line[1]);
  562. }
  563. if (inedges > 0)
  564. {
  565. data = new List<Edge>(inedges);
  566. }
  567. int end1, end2, mark;
  568. // Read and insert the segments.
  569. for (int i = 0; i < inedges; i++)
  570. {
  571. if (!TryReadLine(reader, out line))
  572. {
  573. throw new Exception("Can't read input file (segments).");
  574. }
  575. if (line.Length < 3)
  576. {
  577. throw new Exception("Segment has no endpoints.");
  578. }
  579. // TODO: startIndex ok?
  580. end1 = int.Parse(line[1]) - startIndex;
  581. end2 = int.Parse(line[2]) - startIndex;
  582. mark = 0;
  583. if (edgemarkers > 0 && line.Length > 3)
  584. {
  585. mark = int.Parse(line[3]);
  586. }
  587. if ((end1 < 0) || (end1 >= invertices))
  588. {
  589. if (Log.Verbose)
  590. {
  591. Log.Instance.Warning("Invalid first endpoint of segment.",
  592. "MeshReader.ReadPolyfile()");
  593. }
  594. }
  595. else if ((end2 < 0) || (end2 >= invertices))
  596. {
  597. if (Log.Verbose)
  598. {
  599. Log.Instance.Warning("Invalid second endpoint of segment.",
  600. "MeshReader.ReadPolyfile()");
  601. }
  602. }
  603. else
  604. {
  605. data.Add(new Edge(end1, end2, mark));
  606. }
  607. }
  608. }
  609. return data;
  610. }
  611. bool IsStringNullOrWhiteSpace(string value)
  612. {
  613. if (value != null)
  614. {
  615. for (int i = 0; i < value.Length; i++)
  616. {
  617. if (!char.IsWhiteSpace(value[i]))
  618. {
  619. return false;
  620. }
  621. }
  622. }
  623. return true;
  624. }
  625. }
  626. }