QualityMesher.cs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
  1. // -----------------------------------------------------------------------
  2. // <copyright file="QualityMesher.cs">
  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. .Meshing
  9. {
  10. using System;
  11. using System.Collections.Generic;
  12. using Animation.TriangleNet.Geometry;
  13. using Animation.TriangleNet.Logging;
  14. using Animation.TriangleNet.Meshing.Data;
  15. using Animation.TriangleNet.Tools;
  16. using Animation.TriangleNet.Topology;
  17. /// <summary>
  18. /// Provides methods for mesh quality enforcement and testing.
  19. /// </summary>
  20. class QualityMesher
  21. {
  22. IPredicates predicates;
  23. Queue<BadSubseg> badsubsegs;
  24. BadTriQueue queue;
  25. Mesh mesh;
  26. Behavior behavior;
  27. NewLocation newLocation;
  28. ILog<LogItem> logger;
  29. // Stores the vertices of the triangle that contains newvertex
  30. // in SplitTriangle method.
  31. Triangle newvertex_tri;
  32. public QualityMesher(Mesh mesh, Configuration config)
  33. {
  34. logger = Log.Instance;
  35. badsubsegs = new Queue<BadSubseg>();
  36. queue = new BadTriQueue();
  37. this.mesh = mesh;
  38. this.predicates = config.Predicates();
  39. this.behavior = mesh.behavior;
  40. newLocation = new NewLocation(mesh, predicates);
  41. newvertex_tri = new Triangle();
  42. }
  43. /// <summary>
  44. /// Apply quality constraints to a mesh.
  45. /// </summary>
  46. /// <param name="quality">The quality constraints.</param>
  47. /// <param name="delaunay">A value indicating, if the refined mesh should be Conforming Delaunay.</param>
  48. public void Apply(QualityOptions quality, bool delaunay = false)
  49. {
  50. // Copy quality options
  51. if (quality != null)
  52. {
  53. behavior.Quality = true;
  54. behavior.MinAngle = quality.MinimumAngle;
  55. behavior.MaxAngle = quality.MaximumAngle;
  56. behavior.MaxArea = quality.MaximumArea;
  57. behavior.UserTest = quality.UserTest;
  58. behavior.VarArea = quality.VariableArea;
  59. behavior.ConformingDelaunay = behavior.ConformingDelaunay || delaunay;
  60. mesh.steinerleft = quality.SteinerPoints == 0 ? -1 : quality.SteinerPoints;
  61. }
  62. // TODO: remove
  63. if (!behavior.Poly)
  64. {
  65. // Be careful not to allocate space for element area constraints that
  66. // will never be assigned any value (other than the default -1.0).
  67. behavior.VarArea = false;
  68. }
  69. // Ensure that no vertex can be mistaken for a triangular bounding
  70. // box vertex in insertvertex().
  71. mesh.infvertex1 = null;
  72. mesh.infvertex2 = null;
  73. mesh.infvertex3 = null;
  74. if (behavior.useSegments)
  75. {
  76. mesh.checksegments = true;
  77. }
  78. if (behavior.Quality && mesh.triangles.Count > 0)
  79. {
  80. // Enforce angle and area constraints.
  81. EnforceQuality();
  82. }
  83. }
  84. /// <summary>
  85. /// Add a bad subsegment to the queue.
  86. /// </summary>
  87. /// <param name="badseg">Bad subsegment.</param>
  88. public void AddBadSubseg(BadSubseg badseg)
  89. {
  90. badsubsegs.Enqueue(badseg);
  91. }
  92. #region Check
  93. /// <summary>
  94. /// Check a subsegment to see if it is encroached; add it to the list if it is.
  95. /// </summary>
  96. /// <param name="testsubseg">The subsegment to check.</param>
  97. /// <returns>Returns a nonzero value if the subsegment is encroached.</returns>
  98. /// <remarks>
  99. /// A subsegment is encroached if there is a vertex in its diametral lens.
  100. /// For Ruppert's algorithm (-D switch), the "diametral lens" is the
  101. /// diametral circle. For Chew's algorithm (default), the diametral lens is
  102. /// just big enough to enclose two isosceles triangles whose bases are the
  103. /// subsegment. Each of the two isosceles triangles has two angles equal
  104. /// to 'b.minangle'.
  105. ///
  106. /// Chew's algorithm does not require diametral lenses at all--but they save
  107. /// time. Any vertex inside a subsegment's diametral lens implies that the
  108. /// triangle adjoining the subsegment will be too skinny, so it's only a
  109. /// matter of time before the encroaching vertex is deleted by Chew's
  110. /// algorithm. It's faster to simply not insert the doomed vertex in the
  111. /// first place, which is why I use diametral lenses with Chew's algorithm.
  112. /// </remarks>
  113. public int CheckSeg4Encroach(ref Osub testsubseg)
  114. {
  115. Otri neighbortri = default(Otri);
  116. Osub testsym = default(Osub);
  117. BadSubseg encroachedseg;
  118. double dotproduct;
  119. int encroached;
  120. int sides;
  121. Vertex eorg, edest, eapex;
  122. encroached = 0;
  123. sides = 0;
  124. eorg = testsubseg.Org();
  125. edest = testsubseg.Dest();
  126. // Check one neighbor of the subsegment.
  127. testsubseg.Pivot(ref neighbortri);
  128. // Does the neighbor exist, or is this a boundary edge?
  129. if (neighbortri.tri.id != Mesh.DUMMY)
  130. {
  131. sides++;
  132. // Find a vertex opposite this subsegment.
  133. eapex = neighbortri.Apex();
  134. // Check whether the apex is in the diametral lens of the subsegment
  135. // (the diametral circle if 'conformdel' is set). A dot product
  136. // of two sides of the triangle is used to check whether the angle
  137. // at the apex is greater than (180 - 2 'minangle') degrees (for
  138. // lenses; 90 degrees for diametral circles).
  139. dotproduct = (eorg.x - eapex.x) * (edest.x - eapex.x) +
  140. (eorg.y - eapex.y) * (edest.y - eapex.y);
  141. if (dotproduct < 0.0)
  142. {
  143. if (behavior.ConformingDelaunay ||
  144. (dotproduct * dotproduct >=
  145. (2.0 * behavior.goodAngle - 1.0) * (2.0 * behavior.goodAngle - 1.0) *
  146. ((eorg.x - eapex.x) * (eorg.x - eapex.x) +
  147. (eorg.y - eapex.y) * (eorg.y - eapex.y)) *
  148. ((edest.x - eapex.x) * (edest.x - eapex.x) +
  149. (edest.y - eapex.y) * (edest.y - eapex.y))))
  150. {
  151. encroached = 1;
  152. }
  153. }
  154. }
  155. // Check the other neighbor of the subsegment.
  156. testsubseg.Sym(ref testsym);
  157. testsym.Pivot(ref neighbortri);
  158. // Does the neighbor exist, or is this a boundary edge?
  159. if (neighbortri.tri.id != Mesh.DUMMY)
  160. {
  161. sides++;
  162. // Find the other vertex opposite this subsegment.
  163. eapex = neighbortri.Apex();
  164. // Check whether the apex is in the diametral lens of the subsegment
  165. // (or the diametral circle, if 'conformdel' is set).
  166. dotproduct = (eorg.x - eapex.x) * (edest.x - eapex.x) +
  167. (eorg.y - eapex.y) * (edest.y - eapex.y);
  168. if (dotproduct < 0.0)
  169. {
  170. if (behavior.ConformingDelaunay ||
  171. (dotproduct * dotproduct >=
  172. (2.0 * behavior.goodAngle - 1.0) * (2.0 * behavior.goodAngle - 1.0) *
  173. ((eorg.x - eapex.x) * (eorg.x - eapex.x) +
  174. (eorg.y - eapex.y) * (eorg.y - eapex.y)) *
  175. ((edest.x - eapex.x) * (edest.x - eapex.x) +
  176. (edest.y - eapex.y) * (edest.y - eapex.y))))
  177. {
  178. encroached += 2;
  179. }
  180. }
  181. }
  182. if (encroached > 0 && (behavior.NoBisect == 0 || ((behavior.NoBisect == 1) && (sides == 2))))
  183. {
  184. // Add the subsegment to the list of encroached subsegments.
  185. // Be sure to get the orientation right.
  186. encroachedseg = new BadSubseg();
  187. if (encroached == 1)
  188. {
  189. encroachedseg.subseg = testsubseg;
  190. encroachedseg.org = eorg;
  191. encroachedseg.dest = edest;
  192. }
  193. else
  194. {
  195. encroachedseg.subseg = testsym;
  196. encroachedseg.org = edest;
  197. encroachedseg.dest = eorg;
  198. }
  199. badsubsegs.Enqueue(encroachedseg);
  200. }
  201. return encroached;
  202. }
  203. /// <summary>
  204. /// Test a triangle for quality and size.
  205. /// </summary>
  206. /// <param name="testtri">Triangle to check.</param>
  207. /// <remarks>
  208. /// Tests a triangle to see if it satisfies the minimum angle condition and
  209. /// the maximum area condition. Triangles that aren't up to spec are added
  210. /// to the bad triangle queue.
  211. /// </remarks>
  212. public void TestTriangle(ref Otri testtri)
  213. {
  214. Otri tri1 = default(Otri), tri2 = default(Otri);
  215. Osub testsub = default(Osub);
  216. Vertex torg, tdest, tapex;
  217. Vertex base1, base2;
  218. Vertex org1, dest1, org2, dest2;
  219. Vertex joinvertex;
  220. double dxod, dyod, dxda, dyda, dxao, dyao;
  221. double dxod2, dyod2, dxda2, dyda2, dxao2, dyao2;
  222. double apexlen, orglen, destlen, minedge;
  223. double angle;
  224. double area;
  225. double dist1, dist2;
  226. double maxangle;
  227. torg = testtri.Org();
  228. tdest = testtri.Dest();
  229. tapex = testtri.Apex();
  230. dxod = torg.x - tdest.x;
  231. dyod = torg.y - tdest.y;
  232. dxda = tdest.x - tapex.x;
  233. dyda = tdest.y - tapex.y;
  234. dxao = tapex.x - torg.x;
  235. dyao = tapex.y - torg.y;
  236. dxod2 = dxod * dxod;
  237. dyod2 = dyod * dyod;
  238. dxda2 = dxda * dxda;
  239. dyda2 = dyda * dyda;
  240. dxao2 = dxao * dxao;
  241. dyao2 = dyao * dyao;
  242. // Find the lengths of the triangle's three edges.
  243. apexlen = dxod2 + dyod2;
  244. orglen = dxda2 + dyda2;
  245. destlen = dxao2 + dyao2;
  246. if ((apexlen < orglen) && (apexlen < destlen))
  247. {
  248. // The edge opposite the apex is shortest.
  249. minedge = apexlen;
  250. // Find the square of the cosine of the angle at the apex.
  251. angle = dxda * dxao + dyda * dyao;
  252. angle = angle * angle / (orglen * destlen);
  253. base1 = torg;
  254. base2 = tdest;
  255. testtri.Copy(ref tri1);
  256. }
  257. else if (orglen < destlen)
  258. {
  259. // The edge opposite the origin is shortest.
  260. minedge = orglen;
  261. // Find the square of the cosine of the angle at the origin.
  262. angle = dxod * dxao + dyod * dyao;
  263. angle = angle * angle / (apexlen * destlen);
  264. base1 = tdest;
  265. base2 = tapex;
  266. testtri.Lnext(ref tri1);
  267. }
  268. else
  269. {
  270. // The edge opposite the destination is shortest.
  271. minedge = destlen;
  272. // Find the square of the cosine of the angle at the destination.
  273. angle = dxod * dxda + dyod * dyda;
  274. angle = angle * angle / (apexlen * orglen);
  275. base1 = tapex;
  276. base2 = torg;
  277. testtri.Lprev(ref tri1);
  278. }
  279. if (behavior.VarArea || behavior.fixedArea || (behavior.UserTest != null))
  280. {
  281. // Check whether the area is larger than permitted.
  282. area = 0.5 * (dxod * dyda - dyod * dxda);
  283. if (behavior.fixedArea && (area > behavior.MaxArea))
  284. {
  285. // Add this triangle to the list of bad triangles.
  286. queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
  287. return;
  288. }
  289. // Nonpositive area constraints are treated as unconstrained.
  290. if ((behavior.VarArea) && (area > testtri.tri.area) && (testtri.tri.area > 0.0))
  291. {
  292. // Add this triangle to the list of bad triangles.
  293. queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
  294. return;
  295. }
  296. // Check whether the user thinks this triangle is too large.
  297. if ((behavior.UserTest != null) && behavior.UserTest(testtri.tri, area))
  298. {
  299. queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
  300. return;
  301. }
  302. }
  303. // find the maximum edge and accordingly the pqr orientation
  304. if ((apexlen > orglen) && (apexlen > destlen))
  305. {
  306. // The edge opposite the apex is longest.
  307. // maxedge = apexlen;
  308. // Find the cosine of the angle at the apex.
  309. maxangle = (orglen + destlen - apexlen) / (2 * Math.Sqrt(orglen * destlen));
  310. }
  311. else if (orglen > destlen)
  312. {
  313. // The edge opposite the origin is longest.
  314. // maxedge = orglen;
  315. // Find the cosine of the angle at the origin.
  316. maxangle = (apexlen + destlen - orglen) / (2 * Math.Sqrt(apexlen * destlen));
  317. }
  318. else
  319. {
  320. // The edge opposite the destination is longest.
  321. // maxedge = destlen;
  322. // Find the cosine of the angle at the destination.
  323. maxangle = (apexlen + orglen - destlen) / (2 * Math.Sqrt(apexlen * orglen));
  324. }
  325. // Check whether the angle is smaller than permitted.
  326. if ((angle > behavior.goodAngle) || (maxangle < behavior.maxGoodAngle && behavior.MaxAngle != 0.0))
  327. {
  328. // Use the rules of Miller, Pav, and Walkington to decide that certain
  329. // triangles should not be split, even if they have bad angles.
  330. // A skinny triangle is not split if its shortest edge subtends a
  331. // small input angle, and both endpoints of the edge lie on a
  332. // concentric circular shell. For convenience, I make a small
  333. // adjustment to that rule: I check if the endpoints of the edge
  334. // both lie in segment interiors, equidistant from the apex where
  335. // the two segments meet.
  336. // First, check if both points lie in segment interiors.
  337. if ((base1.type == VertexType.SegmentVertex) &&
  338. (base2.type == VertexType.SegmentVertex))
  339. {
  340. // Check if both points lie in a common segment. If they do, the
  341. // skinny triangle is enqueued to be split as usual.
  342. tri1.Pivot(ref testsub);
  343. if (testsub.seg.hash == Mesh.DUMMY)
  344. {
  345. // No common segment. Find a subsegment that contains 'torg'.
  346. tri1.Copy(ref tri2);
  347. do
  348. {
  349. tri1.Oprev();
  350. tri1.Pivot(ref testsub);
  351. }
  352. while (testsub.seg.hash == Mesh.DUMMY);
  353. // Find the endpoints of the containing segment.
  354. org1 = testsub.SegOrg();
  355. dest1 = testsub.SegDest();
  356. // Find a subsegment that contains 'tdest'.
  357. do
  358. {
  359. tri2.Dnext();
  360. tri2.Pivot(ref testsub);
  361. }
  362. while (testsub.seg.hash == Mesh.DUMMY);
  363. // Find the endpoints of the containing segment.
  364. org2 = testsub.SegOrg();
  365. dest2 = testsub.SegDest();
  366. // Check if the two containing segments have an endpoint in common.
  367. joinvertex = null;
  368. if ((dest1.x == org2.x) && (dest1.y == org2.y))
  369. {
  370. joinvertex = dest1;
  371. }
  372. else if ((org1.x == dest2.x) && (org1.y == dest2.y))
  373. {
  374. joinvertex = org1;
  375. }
  376. if (joinvertex != null)
  377. {
  378. // Compute the distance from the common endpoint (of the two
  379. // segments) to each of the endpoints of the shortest edge.
  380. dist1 = ((base1.x - joinvertex.x) * (base1.x - joinvertex.x) +
  381. (base1.y - joinvertex.y) * (base1.y - joinvertex.y));
  382. dist2 = ((base2.x - joinvertex.x) * (base2.x - joinvertex.x) +
  383. (base2.y - joinvertex.y) * (base2.y - joinvertex.y));
  384. // If the two distances are equal, don't split the triangle.
  385. if ((dist1 < 1.001 * dist2) && (dist1 > 0.999 * dist2))
  386. {
  387. // Return now to avoid enqueueing the bad triangle.
  388. return;
  389. }
  390. }
  391. }
  392. }
  393. // Add this triangle to the list of bad triangles.
  394. queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
  395. }
  396. }
  397. #endregion
  398. #region Maintanance
  399. /// <summary>
  400. /// Traverse the entire list of subsegments, and check each to see if it
  401. /// is encroached. If so, add it to the list.
  402. /// </summary>
  403. private void TallyEncs()
  404. {
  405. Osub subsegloop = default(Osub);
  406. subsegloop.orient = 0;
  407. foreach (var seg in mesh.subsegs.Values)
  408. {
  409. subsegloop.seg = seg;
  410. // If the segment is encroached, add it to the list.
  411. CheckSeg4Encroach(ref subsegloop);
  412. }
  413. }
  414. /// <summary>
  415. /// Split all the encroached subsegments.
  416. /// </summary>
  417. /// <param name="triflaws">A flag that specifies whether one should take
  418. /// note of new bad triangles that result from inserting vertices to repair
  419. /// encroached subsegments.</param>
  420. /// <remarks>
  421. /// Each encroached subsegment is repaired by splitting it - inserting a
  422. /// vertex at or near its midpoint. Newly inserted vertices may encroach
  423. /// upon other subsegments; these are also repaired.
  424. /// </remarks>
  425. private void SplitEncSegs(bool triflaws)
  426. {
  427. Otri enctri = default(Otri);
  428. Otri testtri = default(Otri);
  429. Osub testsh = default(Osub);
  430. Osub currentenc = default(Osub);
  431. BadSubseg seg;
  432. Vertex eorg, edest, eapex;
  433. Vertex newvertex;
  434. InsertVertexResult success;
  435. double segmentlength, nearestpoweroftwo;
  436. double split;
  437. double multiplier, divisor;
  438. bool acuteorg, acuteorg2, acutedest, acutedest2;
  439. // Note that steinerleft == -1 if an unlimited number
  440. // of Steiner points is allowed.
  441. while (badsubsegs.Count > 0)
  442. {
  443. if (mesh.steinerleft == 0)
  444. {
  445. break;
  446. }
  447. seg = badsubsegs.Dequeue();
  448. currentenc = seg.subseg;
  449. eorg = currentenc.Org();
  450. edest = currentenc.Dest();
  451. // Make sure that this segment is still the same segment it was
  452. // when it was determined to be encroached. If the segment was
  453. // enqueued multiple times (because several newly inserted
  454. // vertices encroached it), it may have already been split.
  455. if (!Osub.IsDead(currentenc.seg) && (eorg == seg.org) && (edest == seg.dest))
  456. {
  457. // To decide where to split a segment, we need to know if the
  458. // segment shares an endpoint with an adjacent segment.
  459. // The concern is that, if we simply split every encroached
  460. // segment in its center, two adjacent segments with a small
  461. // angle between them might lead to an infinite loop; each
  462. // vertex added to split one segment will encroach upon the
  463. // other segment, which must then be split with a vertex that
  464. // will encroach upon the first segment, and so on forever.
  465. // To avoid this, imagine a set of concentric circles, whose
  466. // radii are powers of two, about each segment endpoint.
  467. // These concentric circles determine where the segment is
  468. // split. (If both endpoints are shared with adjacent
  469. // segments, split the segment in the middle, and apply the
  470. // concentric circles for later splittings.)
  471. // Is the origin shared with another segment?
  472. currentenc.Pivot(ref enctri);
  473. enctri.Lnext(ref testtri);
  474. testtri.Pivot(ref testsh);
  475. acuteorg = testsh.seg.hash != Mesh.DUMMY;
  476. // Is the destination shared with another segment?
  477. testtri.Lnext();
  478. testtri.Pivot(ref testsh);
  479. acutedest = testsh.seg.hash != Mesh.DUMMY;
  480. // If we're using Chew's algorithm (rather than Ruppert's)
  481. // to define encroachment, delete free vertices from the
  482. // subsegment's diametral circle.
  483. if (!behavior.ConformingDelaunay && !acuteorg && !acutedest)
  484. {
  485. eapex = enctri.Apex();
  486. while ((eapex.type == VertexType.FreeVertex) &&
  487. ((eorg.x - eapex.x) * (edest.x - eapex.x) +
  488. (eorg.y - eapex.y) * (edest.y - eapex.y) < 0.0))
  489. {
  490. mesh.DeleteVertex(ref testtri);
  491. currentenc.Pivot(ref enctri);
  492. eapex = enctri.Apex();
  493. enctri.Lprev(ref testtri);
  494. }
  495. }
  496. // Now, check the other side of the segment, if there's a triangle there.
  497. enctri.Sym(ref testtri);
  498. if (testtri.tri.id != Mesh.DUMMY)
  499. {
  500. // Is the destination shared with another segment?
  501. testtri.Lnext();
  502. testtri.Pivot(ref testsh);
  503. acutedest2 = testsh.seg.hash != Mesh.DUMMY;
  504. acutedest = acutedest || acutedest2;
  505. // Is the origin shared with another segment?
  506. testtri.Lnext();
  507. testtri.Pivot(ref testsh);
  508. acuteorg2 = testsh.seg.hash != Mesh.DUMMY;
  509. acuteorg = acuteorg || acuteorg2;
  510. // Delete free vertices from the subsegment's diametral circle.
  511. if (!behavior.ConformingDelaunay && !acuteorg2 && !acutedest2)
  512. {
  513. eapex = testtri.Org();
  514. while ((eapex.type == VertexType.FreeVertex) &&
  515. ((eorg.x - eapex.x) * (edest.x - eapex.x) +
  516. (eorg.y - eapex.y) * (edest.y - eapex.y) < 0.0))
  517. {
  518. mesh.DeleteVertex(ref testtri);
  519. enctri.Sym(ref testtri);
  520. eapex = testtri.Apex();
  521. testtri.Lprev();
  522. }
  523. }
  524. }
  525. // Use the concentric circles if exactly one endpoint is shared
  526. // with another adjacent segment.
  527. if (acuteorg || acutedest)
  528. {
  529. segmentlength = Math.Sqrt((edest.x - eorg.x) * (edest.x - eorg.x) +
  530. (edest.y - eorg.y) * (edest.y - eorg.y));
  531. // Find the power of two that most evenly splits the segment.
  532. // The worst case is a 2:1 ratio between subsegment lengths.
  533. nearestpoweroftwo = 1.0;
  534. while (segmentlength > 3.0 * nearestpoweroftwo)
  535. {
  536. nearestpoweroftwo *= 2.0;
  537. }
  538. while (segmentlength < 1.5 * nearestpoweroftwo)
  539. {
  540. nearestpoweroftwo *= 0.5;
  541. }
  542. // Where do we split the segment?
  543. split = nearestpoweroftwo / segmentlength;
  544. if (acutedest)
  545. {
  546. split = 1.0 - split;
  547. }
  548. }
  549. else
  550. {
  551. // If we're not worried about adjacent segments, split
  552. // this segment in the middle.
  553. split = 0.5;
  554. }
  555. // Create the new vertex (interpolate coordinates).
  556. newvertex = new Vertex(
  557. eorg.x + split * (edest.x - eorg.x),
  558. eorg.y + split * (edest.y - eorg.y),
  559. currentenc.seg.boundary
  560. #if USE_ATTRIBS
  561. , mesh.nextras
  562. #endif
  563. );
  564. newvertex.type = VertexType.SegmentVertex;
  565. newvertex.hash = mesh.hash_vtx++;
  566. newvertex.id = newvertex.hash;
  567. mesh.vertices.Add(newvertex.hash, newvertex);
  568. #if USE_ATTRIBS
  569. // Interpolate attributes.
  570. for (int i = 0; i < mesh.nextras; i++)
  571. {
  572. newvertex.attributes[i] = eorg.attributes[i]
  573. + split * (edest.attributes[i] - eorg.attributes[i]);
  574. }
  575. #endif
  576. #if USE_Z
  577. newvertex.z = eorg.z + split * (edest.z - eorg.z);
  578. #endif
  579. if (!Behavior.NoExact)
  580. {
  581. // Roundoff in the above calculation may yield a 'newvertex'
  582. // that is not precisely collinear with 'eorg' and 'edest'.
  583. // Improve collinearity by one step of iterative refinement.
  584. multiplier = predicates.CounterClockwise(eorg, edest, newvertex);
  585. divisor = ((eorg.x - edest.x) * (eorg.x - edest.x) +
  586. (eorg.y - edest.y) * (eorg.y - edest.y));
  587. if ((multiplier != 0.0) && (divisor != 0.0))
  588. {
  589. multiplier = multiplier / divisor;
  590. // Watch out for NANs.
  591. if (!double.IsNaN(multiplier))
  592. {
  593. newvertex.x += multiplier * (edest.y - eorg.y);
  594. newvertex.y += multiplier * (eorg.x - edest.x);
  595. }
  596. }
  597. }
  598. // Check whether the new vertex lies on an endpoint.
  599. if (((newvertex.x == eorg.x) && (newvertex.y == eorg.y)) ||
  600. ((newvertex.x == edest.x) && (newvertex.y == edest.y)))
  601. {
  602. logger.Error("Ran out of precision: I attempted to split a"
  603. + " segment to a smaller size than can be accommodated by"
  604. + " the finite precision of floating point arithmetic.",
  605. "Quality.SplitEncSegs()");
  606. throw new Exception("Ran out of precision");
  607. }
  608. // Insert the splitting vertex. This should always succeed.
  609. success = mesh.InsertVertex(newvertex, ref enctri, ref currentenc, true, triflaws);
  610. if ((success != InsertVertexResult.Successful) && (success != InsertVertexResult.Encroaching))
  611. {
  612. logger.Error("Failure to split a segment.", "Quality.SplitEncSegs()");
  613. throw new Exception("Failure to split a segment.");
  614. }
  615. if (mesh.steinerleft > 0)
  616. {
  617. mesh.steinerleft--;
  618. }
  619. // Check the two new subsegments to see if they're encroached.
  620. CheckSeg4Encroach(ref currentenc);
  621. currentenc.Next();
  622. CheckSeg4Encroach(ref currentenc);
  623. }
  624. // Set subsegment's origin to NULL. This makes it possible to detect dead
  625. // badsubsegs when traversing the list of all badsubsegs.
  626. seg.org = null;
  627. }
  628. }
  629. /// <summary>
  630. /// Test every triangle in the mesh for quality measures.
  631. /// </summary>
  632. private void TallyFaces()
  633. {
  634. Otri triangleloop = default(Otri);
  635. triangleloop.orient = 0;
  636. foreach (var tri in mesh.triangles)
  637. {
  638. triangleloop.tri = tri;
  639. // If the triangle is bad, enqueue it.
  640. TestTriangle(ref triangleloop);
  641. }
  642. }
  643. /// <summary>
  644. /// Inserts a vertex at the circumcenter of a triangle. Deletes
  645. /// the newly inserted vertex if it encroaches upon a segment.
  646. /// </summary>
  647. /// <param name="badtri"></param>
  648. private void SplitTriangle(BadTriangle badtri)
  649. {
  650. Otri badotri = default(Otri);
  651. Vertex borg, bdest, bapex;
  652. Point newloc; // Location of the new vertex
  653. double xi = 0, eta = 0;
  654. InsertVertexResult success;
  655. bool errorflag;
  656. badotri = badtri.poortri;
  657. borg = badotri.Org();
  658. bdest = badotri.Dest();
  659. bapex = badotri.Apex();
  660. // Make sure that this triangle is still the same triangle it was
  661. // when it was tested and determined to be of bad quality.
  662. // Subsequent transformations may have made it a different triangle.
  663. if (!Otri.IsDead(badotri.tri) && (borg == badtri.org) &&
  664. (bdest == badtri.dest) && (bapex == badtri.apex))
  665. {
  666. errorflag = false;
  667. // Create a new vertex at the triangle's circumcenter.
  668. // Using the original (simpler) Steiner point location method
  669. // for mesh refinement.
  670. // TODO: NewLocation doesn't work for refinement. Why? Maybe
  671. // reset VertexType?
  672. if (behavior.fixedArea || behavior.VarArea)
  673. {
  674. newloc = predicates.FindCircumcenter(borg, bdest, bapex, ref xi, ref eta, behavior.offconstant);
  675. }
  676. else
  677. {
  678. newloc = newLocation.FindLocation(borg, bdest, bapex, ref xi, ref eta, true, badotri);
  679. }
  680. // Check whether the new vertex lies on a triangle vertex.
  681. if (((newloc.x == borg.x) && (newloc.y == borg.y)) ||
  682. ((newloc.x == bdest.x) && (newloc.y == bdest.y)) ||
  683. ((newloc.x == bapex.x) && (newloc.y == bapex.y)))
  684. {
  685. if (Log.Verbose)
  686. {
  687. logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()");
  688. errorflag = true;
  689. }
  690. }
  691. else
  692. {
  693. // The new vertex must be in the interior, and therefore is a
  694. // free vertex with a marker of zero.
  695. Vertex newvertex = new Vertex(newloc.x, newloc.y, 0
  696. #if USE_ATTRIBS
  697. , mesh.nextras
  698. #endif
  699. );
  700. newvertex.type = VertexType.FreeVertex;
  701. // Ensure that the handle 'badotri' does not represent the longest
  702. // edge of the triangle. This ensures that the circumcenter must
  703. // fall to the left of this edge, so point location will work.
  704. // (If the angle org-apex-dest exceeds 90 degrees, then the
  705. // circumcenter lies outside the org-dest edge, and eta is
  706. // negative. Roundoff error might prevent eta from being
  707. // negative when it should be, so I test eta against xi.)
  708. if (eta < xi)
  709. {
  710. badotri.Lprev();
  711. }
  712. // Assign triangle for attributes interpolation.
  713. newvertex.tri.tri = newvertex_tri;
  714. // Insert the circumcenter, searching from the edge of the triangle,
  715. // and maintain the Delaunay property of the triangulation.
  716. Osub tmp = default(Osub);
  717. success = mesh.InsertVertex(newvertex, ref badotri, ref tmp, true, true);
  718. if (success == InsertVertexResult.Successful)
  719. {
  720. newvertex.hash = mesh.hash_vtx++;
  721. newvertex.id = newvertex.hash;
  722. #if USE_ATTRIBS
  723. if (mesh.nextras > 0)
  724. {
  725. Interpolation.InterpolateAttributes(newvertex, newvertex.tri.tri, mesh.nextras);
  726. }
  727. #endif
  728. #if USE_Z
  729. Interpolation.InterpolateZ(newvertex, newvertex.tri.tri);
  730. #endif
  731. mesh.vertices.Add(newvertex.hash, newvertex);
  732. if (mesh.steinerleft > 0)
  733. {
  734. mesh.steinerleft--;
  735. }
  736. }
  737. else if (success == InsertVertexResult.Encroaching)
  738. {
  739. // If the newly inserted vertex encroaches upon a subsegment,
  740. // delete the new vertex.
  741. mesh.UndoVertex();
  742. }
  743. else if (success == InsertVertexResult.Violating)
  744. {
  745. // Failed to insert the new vertex, but some subsegment was
  746. // marked as being encroached.
  747. }
  748. else
  749. { // success == DUPLICATEVERTEX
  750. // Couldn't insert the new vertex because a vertex is already there.
  751. if (Log.Verbose)
  752. {
  753. logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()");
  754. errorflag = true;
  755. }
  756. }
  757. }
  758. if (errorflag)
  759. {
  760. logger.Error("The new vertex is at the circumcenter of triangle: This probably "
  761. + "means that I am trying to refine triangles to a smaller size than can be "
  762. + "accommodated by the finite precision of floating point arithmetic.",
  763. "Quality.SplitTriangle()");
  764. throw new Exception("The new vertex is at the circumcenter of triangle.");
  765. }
  766. }
  767. }
  768. /// <summary>
  769. /// Remove all the encroached subsegments and bad triangles from the triangulation.
  770. /// </summary>
  771. private void EnforceQuality()
  772. {
  773. BadTriangle badtri;
  774. // Test all segments to see if they're encroached.
  775. TallyEncs();
  776. // Fix encroached subsegments without noting bad triangles.
  777. SplitEncSegs(false);
  778. // At this point, if we haven't run out of Steiner points, the
  779. // triangulation should be (conforming) Delaunay.
  780. // Next, we worry about enforcing triangle quality.
  781. if ((behavior.MinAngle > 0.0) || behavior.VarArea || behavior.fixedArea || behavior.UserTest != null)
  782. {
  783. // TODO: Reset queue? (Or is it always empty at this point)
  784. // Test all triangles to see if they're bad.
  785. TallyFaces();
  786. mesh.checkquality = true;
  787. while ((queue.Count > 0) && (mesh.steinerleft != 0))
  788. {
  789. // Fix one bad triangle by inserting a vertex at its circumcenter.
  790. badtri = queue.Dequeue();
  791. SplitTriangle(badtri);
  792. if (badsubsegs.Count > 0)
  793. {
  794. // Put bad triangle back in queue for another try later.
  795. queue.Enqueue(badtri);
  796. // Fix any encroached subsegments that resulted.
  797. // Record any new bad triangles that result.
  798. SplitEncSegs(true);
  799. }
  800. }
  801. }
  802. // At this point, if the "-D" switch was selected and we haven't run out
  803. // of Steiner points, the triangulation should be (conforming) Delaunay
  804. // and have no low-quality triangles.
  805. // Might we have run out of Steiner points too soon?
  806. if (Log.Verbose && behavior.ConformingDelaunay && (badsubsegs.Count > 0) && (mesh.steinerleft == 0))
  807. {
  808. logger.Warning("I ran out of Steiner points, but the mesh has encroached subsegments, "
  809. + "and therefore might not be truly Delaunay. If the Delaunay property is important "
  810. + "to you, try increasing the number of Steiner points.",
  811. "Quality.EnforceQuality()");
  812. }
  813. }
  814. #endregion
  815. }
  816. }