QualityMeasure.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. // -----------------------------------------------------------------------
  2. // <copyright file="QualityMeasure.cs" company="">
  3. // Original Matlab code by John Burkardt, Florida State University
  4. // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
  5. // </copyright>
  6. // -----------------------------------------------------------------------
  7. namespace UnityEngine.U2D.Animation.TriangleNet
  8. .Tools
  9. {
  10. using System;
  11. using Animation.TriangleNet.Geometry;
  12. /// <summary>
  13. /// Provides mesh quality information.
  14. /// </summary>
  15. /// <remarks>
  16. /// Given a triangle abc with points A (ax, ay), B (bx, by), C (cx, cy).
  17. ///
  18. /// The side lengths are given as
  19. /// a = sqrt((cx - bx)^2 + (cy - by)^2) -- side BC opposite of A
  20. /// b = sqrt((cx - ax)^2 + (cy - ay)^2) -- side CA opposite of B
  21. /// c = sqrt((ax - bx)^2 + (ay - by)^2) -- side AB opposite of C
  22. ///
  23. /// The angles are given as
  24. /// ang_a = acos((b^2 + c^2 - a^2) / (2 * b * c)) -- angle at A
  25. /// ang_b = acos((c^2 + a^2 - b^2) / (2 * c * a)) -- angle at B
  26. /// ang_c = acos((a^2 + b^2 - c^2) / (2 * a * b)) -- angle at C
  27. ///
  28. /// The semiperimeter is given as
  29. /// s = (a + b + c) / 2
  30. ///
  31. /// The area is given as
  32. /// D = abs(ax * (by - cy) + bx * (cy - ay) + cx * (ay - by)) / 2
  33. /// = sqrt(s * (s - a) * (s - b) * (s - c))
  34. ///
  35. /// The inradius is given as
  36. /// r = D / s
  37. ///
  38. /// The circumradius is given as
  39. /// R = a * b * c / (4 * D)
  40. ///
  41. /// The altitudes are given as
  42. /// alt_a = 2 * D / a -- altitude above side a
  43. /// alt_b = 2 * D / b -- altitude above side b
  44. /// alt_c = 2 * D / c -- altitude above side c
  45. ///
  46. /// The aspect ratio may be given as the ratio of the longest to the
  47. /// shortest edge or, more commonly as the ratio of the circumradius
  48. /// to twice the inradius
  49. /// ar = R / (2 * r)
  50. /// = a * b * c / (8 * (s - a) * (s - b) * (s - c))
  51. /// = a * b * c / ((b + c - a) * (c + a - b) * (a + b - c))
  52. /// </remarks>
  53. internal class QualityMeasure
  54. {
  55. AreaMeasure areaMeasure;
  56. AlphaMeasure alphaMeasure;
  57. Q_Measure qMeasure;
  58. Mesh mesh;
  59. public QualityMeasure()
  60. {
  61. areaMeasure = new AreaMeasure();
  62. alphaMeasure = new AlphaMeasure();
  63. qMeasure = new Q_Measure();
  64. }
  65. #region Public properties
  66. /// <summary>
  67. /// Minimum triangle area.
  68. /// </summary>
  69. public double AreaMinimum
  70. {
  71. get { return areaMeasure.area_min; }
  72. }
  73. /// <summary>
  74. /// Maximum triangle area.
  75. /// </summary>
  76. public double AreaMaximum
  77. {
  78. get { return areaMeasure.area_max; }
  79. }
  80. /// <summary>
  81. /// Ratio of maximum and minimum triangle area.
  82. /// </summary>
  83. public double AreaRatio
  84. {
  85. get { return areaMeasure.area_max / areaMeasure.area_min; }
  86. }
  87. /// <summary>
  88. /// Smallest angle.
  89. /// </summary>
  90. public double AlphaMinimum
  91. {
  92. get { return alphaMeasure.alpha_min; }
  93. }
  94. /// <summary>
  95. /// Maximum smallest angle.
  96. /// </summary>
  97. public double AlphaMaximum
  98. {
  99. get { return alphaMeasure.alpha_max; }
  100. }
  101. /// <summary>
  102. /// Average angle.
  103. /// </summary>
  104. public double AlphaAverage
  105. {
  106. get { return alphaMeasure.alpha_ave; }
  107. }
  108. /// <summary>
  109. /// Average angle weighted by area.
  110. /// </summary>
  111. public double AlphaArea
  112. {
  113. get { return alphaMeasure.alpha_area; }
  114. }
  115. /// <summary>
  116. /// Smallest aspect ratio.
  117. /// </summary>
  118. public double Q_Minimum
  119. {
  120. get { return qMeasure.q_min; }
  121. }
  122. /// <summary>
  123. /// Largest aspect ratio.
  124. /// </summary>
  125. public double Q_Maximum
  126. {
  127. get { return qMeasure.q_max; }
  128. }
  129. /// <summary>
  130. /// Average aspect ratio.
  131. /// </summary>
  132. public double Q_Average
  133. {
  134. get { return qMeasure.q_ave; }
  135. }
  136. /// <summary>
  137. /// Average aspect ratio weighted by area.
  138. /// </summary>
  139. public double Q_Area
  140. {
  141. get { return qMeasure.q_area; }
  142. }
  143. #endregion
  144. public void Update(Mesh mesh)
  145. {
  146. this.mesh = mesh;
  147. // Reset all measures.
  148. areaMeasure.Reset();
  149. alphaMeasure.Reset();
  150. qMeasure.Reset();
  151. Compute();
  152. }
  153. private void Compute()
  154. {
  155. Point a, b, c;
  156. double ab, bc, ca;
  157. double lx, ly;
  158. double area;
  159. int n = 0;
  160. foreach (var tri in mesh.triangles)
  161. {
  162. n++;
  163. a = tri.vertices[0];
  164. b = tri.vertices[1];
  165. c = tri.vertices[2];
  166. lx = a.x - b.x;
  167. ly = a.y - b.y;
  168. ab = Math.Sqrt(lx * lx + ly * ly);
  169. lx = b.x - c.x;
  170. ly = b.y - c.y;
  171. bc = Math.Sqrt(lx * lx + ly * ly);
  172. lx = c.x - a.x;
  173. ly = c.y - a.y;
  174. ca = Math.Sqrt(lx * lx + ly * ly);
  175. area = areaMeasure.Measure(a, b, c);
  176. alphaMeasure.Measure(ab, bc, ca, area);
  177. qMeasure.Measure(ab, bc, ca, area);
  178. }
  179. // Normalize measures
  180. alphaMeasure.Normalize(n, areaMeasure.area_total);
  181. qMeasure.Normalize(n, areaMeasure.area_total);
  182. }
  183. /// <summary>
  184. /// Determines the bandwidth of the coefficient matrix.
  185. /// </summary>
  186. /// <returns>Bandwidth of the coefficient matrix.</returns>
  187. /// <remarks>
  188. /// The quantity computed here is the "geometric" bandwidth determined
  189. /// by the finite element mesh alone.
  190. ///
  191. /// If a single finite element variable is associated with each node
  192. /// of the mesh, and if the nodes and variables are numbered in the
  193. /// same way, then the geometric bandwidth is the same as the bandwidth
  194. /// of a typical finite element matrix.
  195. ///
  196. /// The bandwidth M is defined in terms of the lower and upper bandwidths:
  197. ///
  198. /// M = ML + 1 + MU
  199. ///
  200. /// where
  201. ///
  202. /// ML = maximum distance from any diagonal label to a nonzero
  203. /// label in the same row, but earlier column,
  204. ///
  205. /// MU = maximum distance from any diagonal label to a nonzero
  206. /// label in the same row, but later column.
  207. ///
  208. /// Because the finite element node adjacency relationship is symmetric,
  209. /// we are guaranteed that ML = MU.
  210. /// </remarks>
  211. public int Bandwidth()
  212. {
  213. if (mesh == null) return 0;
  214. // Lower and upper bandwidth of the matrix
  215. int ml = 0, mu = 0;
  216. int gi, gj;
  217. foreach (var tri in mesh.triangles)
  218. {
  219. for (int j = 0; j < 3; j++)
  220. {
  221. gi = tri.GetVertex(j).id;
  222. for (int k = 0; k < 3; k++)
  223. {
  224. gj = tri.GetVertex(k).id;
  225. mu = Math.Max(mu, gj - gi);
  226. ml = Math.Max(ml, gi - gj);
  227. }
  228. }
  229. }
  230. return ml + 1 + mu;
  231. }
  232. class AreaMeasure
  233. {
  234. // Minimum area
  235. public double area_min = double.MaxValue;
  236. // Maximum area
  237. public double area_max = -double.MaxValue;
  238. // Total area of geometry
  239. public double area_total = 0;
  240. // Nmber of triangles with zero area
  241. public int area_zero = 0;
  242. /// <summary>
  243. /// Reset all values.
  244. /// </summary>
  245. public void Reset()
  246. {
  247. area_min = double.MaxValue;
  248. area_max = -double.MaxValue;
  249. area_total = 0;
  250. area_zero = 0;
  251. }
  252. /// <summary>
  253. /// Compute the area of given triangle.
  254. /// </summary>
  255. /// <param name="a">Triangle corner a.</param>
  256. /// <param name="b">Triangle corner b.</param>
  257. /// <param name="c">Triangle corner c.</param>
  258. /// <returns>Triangle area.</returns>
  259. public double Measure(Point a, Point b, Point c)
  260. {
  261. double area = 0.5 * Math.Abs(a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y));
  262. area_min = Math.Min(area_min, area);
  263. area_max = Math.Max(area_max, area);
  264. area_total += area;
  265. if (area == 0.0)
  266. {
  267. area_zero = area_zero + 1;
  268. }
  269. return area;
  270. }
  271. }
  272. /// <summary>
  273. /// The alpha measure determines the triangulated pointset quality.
  274. /// </summary>
  275. /// <remarks>
  276. /// The alpha measure evaluates the uniformity of the shapes of the triangles
  277. /// defined by a triangulated pointset.
  278. ///
  279. /// We compute the minimum angle among all the triangles in the triangulated
  280. /// dataset and divide by the maximum possible value (which, in degrees,
  281. /// is 60). The best possible value is 1, and the worst 0. A good
  282. /// triangulation should have an alpha score close to 1.
  283. /// </remarks>
  284. class AlphaMeasure
  285. {
  286. // Minimum value over all triangles
  287. public double alpha_min;
  288. // Maximum value over all triangles
  289. public double alpha_max;
  290. // Value averaged over all triangles
  291. public double alpha_ave;
  292. // Value averaged over all triangles and weighted by area
  293. public double alpha_area;
  294. /// <summary>
  295. /// Reset all values.
  296. /// </summary>
  297. public void Reset()
  298. {
  299. alpha_min = double.MaxValue;
  300. alpha_max = -double.MaxValue;
  301. alpha_ave = 0;
  302. alpha_area = 0;
  303. }
  304. double acos(double c)
  305. {
  306. if (c <= -1.0)
  307. {
  308. return Math.PI;
  309. }
  310. else if (1.0 <= c)
  311. {
  312. return 0.0;
  313. }
  314. else
  315. {
  316. return Math.Acos(c);
  317. }
  318. }
  319. /// <summary>
  320. /// Compute q value of given triangle.
  321. /// </summary>
  322. /// <param name="ab">Side length ab.</param>
  323. /// <param name="bc">Side length bc.</param>
  324. /// <param name="ca">Side length ca.</param>
  325. /// <param name="area">Triangle area.</param>
  326. /// <returns></returns>
  327. public double Measure(double ab, double bc, double ca, double area)
  328. {
  329. double alpha = double.MaxValue;
  330. double ab2 = ab * ab;
  331. double bc2 = bc * bc;
  332. double ca2 = ca * ca;
  333. double a_angle;
  334. double b_angle;
  335. double c_angle;
  336. // Take care of a ridiculous special case.
  337. if (ab == 0.0 && bc == 0.0 && ca == 0.0)
  338. {
  339. a_angle = 2.0 * Math.PI / 3.0;
  340. b_angle = 2.0 * Math.PI / 3.0;
  341. c_angle = 2.0 * Math.PI / 3.0;
  342. }
  343. else
  344. {
  345. if (ca == 0.0 || ab == 0.0)
  346. {
  347. a_angle = Math.PI;
  348. }
  349. else
  350. {
  351. a_angle = acos((ca2 + ab2 - bc2) / (2.0 * ca * ab));
  352. }
  353. if (ab == 0.0 || bc == 0.0)
  354. {
  355. b_angle = Math.PI;
  356. }
  357. else
  358. {
  359. b_angle = acos((ab2 + bc2 - ca2) / (2.0 * ab * bc));
  360. }
  361. if (bc == 0.0 || ca == 0.0)
  362. {
  363. c_angle = Math.PI;
  364. }
  365. else
  366. {
  367. c_angle = acos((bc2 + ca2 - ab2) / (2.0 * bc * ca));
  368. }
  369. }
  370. alpha = Math.Min(alpha, a_angle);
  371. alpha = Math.Min(alpha, b_angle);
  372. alpha = Math.Min(alpha, c_angle);
  373. // Normalize angle from [0,pi/3] radians into qualities in [0,1].
  374. alpha = alpha * 3.0 / Math.PI;
  375. alpha_ave += alpha;
  376. alpha_area += area * alpha;
  377. alpha_min = Math.Min(alpha, alpha_min);
  378. alpha_max = Math.Max(alpha, alpha_max);
  379. return alpha;
  380. }
  381. /// <summary>
  382. /// Normalize values.
  383. /// </summary>
  384. public void Normalize(int n, double area_total)
  385. {
  386. if (n > 0)
  387. {
  388. alpha_ave /= n;
  389. }
  390. else
  391. {
  392. alpha_ave = 0.0;
  393. }
  394. if (0.0 < area_total)
  395. {
  396. alpha_area /= area_total;
  397. }
  398. else
  399. {
  400. alpha_area = 0.0;
  401. }
  402. }
  403. }
  404. /// <summary>
  405. /// The Q measure determines the triangulated pointset quality.
  406. /// </summary>
  407. /// <remarks>
  408. /// The Q measure evaluates the uniformity of the shapes of the triangles
  409. /// defined by a triangulated pointset. It uses the aspect ratio
  410. ///
  411. /// 2 * (incircle radius) / (circumcircle radius)
  412. ///
  413. /// In an ideally regular mesh, all triangles would have the same
  414. /// equilateral shape, for which Q = 1. A good mesh would have
  415. /// 0.5 &lt; Q.
  416. /// </remarks>
  417. class Q_Measure
  418. {
  419. // Minimum value over all triangles
  420. public double q_min;
  421. // Maximum value over all triangles
  422. public double q_max;
  423. // Average value
  424. public double q_ave;
  425. // Average value weighted by the area of each triangle
  426. public double q_area;
  427. /// <summary>
  428. /// Reset all values.
  429. /// </summary>
  430. public void Reset()
  431. {
  432. q_min = double.MaxValue;
  433. q_max = -double.MaxValue;
  434. q_ave = 0;
  435. q_area = 0;
  436. }
  437. /// <summary>
  438. /// Compute q value of given triangle.
  439. /// </summary>
  440. /// <param name="ab">Side length ab.</param>
  441. /// <param name="bc">Side length bc.</param>
  442. /// <param name="ca">Side length ca.</param>
  443. /// <param name="area">Triangle area.</param>
  444. /// <returns></returns>
  445. public double Measure(double ab, double bc, double ca, double area)
  446. {
  447. double q = (bc + ca - ab) * (ca + ab - bc) * (ab + bc - ca) / (ab * bc * ca);
  448. q_min = Math.Min(q_min, q);
  449. q_max = Math.Max(q_max, q);
  450. q_ave += q;
  451. q_area += q * area;
  452. return q;
  453. }
  454. /// <summary>
  455. /// Normalize values.
  456. /// </summary>
  457. public void Normalize(int n, double area_total)
  458. {
  459. if (n > 0)
  460. {
  461. q_ave /= n;
  462. }
  463. else
  464. {
  465. q_ave = 0.0;
  466. }
  467. if (area_total > 0.0)
  468. {
  469. q_area /= area_total;
  470. }
  471. else
  472. {
  473. q_area = 0.0;
  474. }
  475. }
  476. }
  477. }
  478. }