Statistic.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. // -----------------------------------------------------------------------
  2. // <copyright file="Statistic.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. .Tools
  9. {
  10. using System;
  11. using Animation.TriangleNet.Topology;
  12. using Animation.TriangleNet.Geometry;
  13. /// <summary>
  14. /// Gather mesh statistics.
  15. /// </summary>
  16. internal class Statistic
  17. {
  18. #region Static members
  19. /// <summary>
  20. /// Number of incircle tests performed.
  21. /// </summary>
  22. internal static long InCircleCount = 0;
  23. internal static long InCircleAdaptCount = 0;
  24. /// <summary>
  25. /// Number of counterclockwise tests performed.
  26. /// </summary>
  27. internal static long CounterClockwiseCount = 0;
  28. internal static long CounterClockwiseAdaptCount = 0;
  29. /// <summary>
  30. /// Number of 3D orientation tests performed.
  31. /// </summary>
  32. internal static long Orient3dCount = 0;
  33. /// <summary>
  34. /// Number of right-of-hyperbola tests performed.
  35. /// </summary>
  36. internal static long HyperbolaCount = 0;
  37. /// <summary>
  38. /// // Number of circumcenter calculations performed.
  39. /// </summary>
  40. internal static long CircumcenterCount = 0;
  41. /// <summary>
  42. /// Number of circle top calculations performed.
  43. /// </summary>
  44. internal static long CircleTopCount = 0;
  45. /// <summary>
  46. /// Number of vertex relocations.
  47. /// </summary>
  48. internal static long RelocationCount = 0;
  49. #endregion
  50. #region Properties
  51. double minEdge = 0;
  52. /// <summary>
  53. /// Gets the shortest edge.
  54. /// </summary>
  55. public double ShortestEdge { get { return minEdge; } }
  56. double maxEdge = 0;
  57. /// <summary>
  58. /// Gets the longest edge.
  59. /// </summary>
  60. public double LongestEdge { get { return maxEdge; } }
  61. //
  62. double minAspect = 0;
  63. /// <summary>
  64. /// Gets the shortest altitude.
  65. /// </summary>
  66. public double ShortestAltitude { get { return minAspect; } }
  67. double maxAspect = 0;
  68. /// <summary>
  69. /// Gets the largest aspect ratio.
  70. /// </summary>
  71. public double LargestAspectRatio { get { return maxAspect; } }
  72. double minArea = 0;
  73. /// <summary>
  74. /// Gets the smallest area.
  75. /// </summary>
  76. public double SmallestArea { get { return minArea; } }
  77. double maxArea = 0;
  78. /// <summary>
  79. /// Gets the largest area.
  80. /// </summary>
  81. public double LargestArea { get { return maxArea; } }
  82. double minAngle = 0;
  83. /// <summary>
  84. /// Gets the smallest angle.
  85. /// </summary>
  86. public double SmallestAngle { get { return minAngle; } }
  87. double maxAngle = 0;
  88. /// <summary>
  89. /// Gets the largest angle.
  90. /// </summary>
  91. public double LargestAngle { get { return maxAngle; } }
  92. int[] angleTable;
  93. /// <summary>
  94. /// Gets the angle histogram.
  95. /// </summary>
  96. public int[] AngleHistogram { get { return angleTable; } }
  97. int[] minAngles;
  98. /// <summary>
  99. /// Gets the min angles histogram.
  100. /// </summary>
  101. public int[] MinAngleHistogram { get { return minAngles; } }
  102. int[] maxAngles;
  103. /// <summary>
  104. /// Gets the max angles histogram.
  105. /// </summary>
  106. public int[] MaxAngleHistogram { get { return maxAngles; } }
  107. double meshArea = 0;
  108. /// <summary>
  109. /// Gets the total mesh area.
  110. /// </summary>
  111. public double MeshArea { get { return meshArea; } }
  112. #endregion
  113. #region Private methods
  114. private void GetAspectHistogram(Mesh mesh)
  115. {
  116. int[] aspecttable;
  117. double[] ratiotable;
  118. aspecttable = new int[16];
  119. ratiotable = new double[]
  120. {
  121. 1.5, 2.0, 2.5, 3.0, 4.0, 6.0, 10.0, 15.0, 25.0, 50.0,
  122. 100.0, 300.0, 1000.0, 10000.0, 100000.0, 0.0
  123. };
  124. Otri tri = default(Otri);
  125. Vertex[] p = new Vertex[3];
  126. double[] dx = new double[3], dy = new double[3];
  127. double[] edgelength = new double[3];
  128. double triarea;
  129. double trilongest2;
  130. double triminaltitude2;
  131. double triaspect2;
  132. int aspectindex;
  133. int i, j, k;
  134. tri.orient = 0;
  135. foreach (var t in mesh.triangles)
  136. {
  137. tri.tri = t;
  138. p[0] = tri.Org();
  139. p[1] = tri.Dest();
  140. p[2] = tri.Apex();
  141. trilongest2 = 0.0;
  142. for (i = 0; i < 3; i++)
  143. {
  144. j = plus1Mod3[i];
  145. k = minus1Mod3[i];
  146. dx[i] = p[j].x - p[k].x;
  147. dy[i] = p[j].y - p[k].y;
  148. edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i];
  149. if (edgelength[i] > trilongest2)
  150. {
  151. trilongest2 = edgelength[i];
  152. }
  153. }
  154. //triarea = Primitives.CounterClockwise(p[0], p[1], p[2]);
  155. triarea = Math.Abs((p[2].x - p[0].x) * (p[1].y - p[0].y) -
  156. (p[1].x - p[0].x) * (p[2].y - p[0].y)) / 2.0;
  157. triminaltitude2 = triarea * triarea / trilongest2;
  158. triaspect2 = trilongest2 / triminaltitude2;
  159. aspectindex = 0;
  160. while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex]) && (aspectindex < 15))
  161. {
  162. aspectindex++;
  163. }
  164. aspecttable[aspectindex]++;
  165. }
  166. }
  167. #endregion
  168. static readonly int[] plus1Mod3 = { 1, 2, 0 };
  169. static readonly int[] minus1Mod3 = { 2, 0, 1 };
  170. /// <summary>
  171. /// Update statistics about the quality of the mesh.
  172. /// </summary>
  173. /// <param name="mesh"></param>
  174. public void Update(Mesh mesh, int sampleDegrees)
  175. {
  176. Point[] p = new Point[3];
  177. int k1, k2;
  178. int degreeStep;
  179. //sampleDegrees = 36; // sample every 5 degrees
  180. //sampleDegrees = 45; // sample every 4 degrees
  181. sampleDegrees = 60; // sample every 3 degrees
  182. double[] cosSquareTable = new double[sampleDegrees / 2 - 1];
  183. double[] dx = new double[3];
  184. double[] dy = new double[3];
  185. double[] edgeLength = new double[3];
  186. double dotProduct;
  187. double cosSquare;
  188. double triArea;
  189. double triLongest2;
  190. double triMinAltitude2;
  191. double triAspect2;
  192. double radconst = Math.PI / sampleDegrees;
  193. double degconst = 180.0 / Math.PI;
  194. // New angle table
  195. angleTable = new int[sampleDegrees];
  196. minAngles = new int[sampleDegrees];
  197. maxAngles = new int[sampleDegrees];
  198. for (int i = 0; i < sampleDegrees / 2 - 1; i++)
  199. {
  200. cosSquareTable[i] = Math.Cos(radconst * (i + 1));
  201. cosSquareTable[i] = cosSquareTable[i] * cosSquareTable[i];
  202. }
  203. for (int i = 0; i < sampleDegrees; i++)
  204. {
  205. angleTable[i] = 0;
  206. }
  207. minAspect = mesh.bounds.Width + mesh.bounds.Height;
  208. minAspect = minAspect * minAspect;
  209. maxAspect = 0.0;
  210. minEdge = minAspect;
  211. maxEdge = 0.0;
  212. minArea = minAspect;
  213. maxArea = 0.0;
  214. minAngle = 0.0;
  215. maxAngle = 2.0;
  216. meshArea = 0.0;
  217. bool acuteBiggest = true;
  218. bool acuteBiggestTri = true;
  219. double triMinAngle, triMaxAngle = 1;
  220. foreach (var tri in mesh.triangles)
  221. {
  222. triMinAngle = 0; // Min angle: 0 < a < 60 degress
  223. triMaxAngle = 1; // Max angle: 60 < a < 180 degress
  224. p[0] = tri.vertices[0];
  225. p[1] = tri.vertices[1];
  226. p[2] = tri.vertices[2];
  227. triLongest2 = 0.0;
  228. for (int i = 0; i < 3; i++)
  229. {
  230. k1 = plus1Mod3[i];
  231. k2 = minus1Mod3[i];
  232. dx[i] = p[k1].x - p[k2].x;
  233. dy[i] = p[k1].y - p[k2].y;
  234. edgeLength[i] = dx[i] * dx[i] + dy[i] * dy[i];
  235. if (edgeLength[i] > triLongest2)
  236. {
  237. triLongest2 = edgeLength[i];
  238. }
  239. if (edgeLength[i] > maxEdge)
  240. {
  241. maxEdge = edgeLength[i];
  242. }
  243. if (edgeLength[i] < minEdge)
  244. {
  245. minEdge = edgeLength[i];
  246. }
  247. }
  248. //triarea = Primitives.CounterClockwise(p[0], p[1], p[2]);
  249. triArea = Math.Abs((p[2].x - p[0].x) * (p[1].y - p[0].y) -
  250. (p[1].x - p[0].x) * (p[2].y - p[0].y));
  251. meshArea += triArea;
  252. if (triArea < minArea)
  253. {
  254. minArea = triArea;
  255. }
  256. if (triArea > maxArea)
  257. {
  258. maxArea = triArea;
  259. }
  260. triMinAltitude2 = triArea * triArea / triLongest2;
  261. if (triMinAltitude2 < minAspect)
  262. {
  263. minAspect = triMinAltitude2;
  264. }
  265. triAspect2 = triLongest2 / triMinAltitude2;
  266. if (triAspect2 > maxAspect)
  267. {
  268. maxAspect = triAspect2;
  269. }
  270. for (int i = 0; i < 3; i++)
  271. {
  272. k1 = plus1Mod3[i];
  273. k2 = minus1Mod3[i];
  274. dotProduct = dx[k1] * dx[k2] + dy[k1] * dy[k2];
  275. cosSquare = dotProduct * dotProduct / (edgeLength[k1] * edgeLength[k2]);
  276. degreeStep = sampleDegrees / 2 - 1;
  277. for (int j = degreeStep - 1; j >= 0; j--)
  278. {
  279. if (cosSquare > cosSquareTable[j])
  280. {
  281. degreeStep = j;
  282. }
  283. }
  284. if (dotProduct <= 0.0)
  285. {
  286. angleTable[degreeStep]++;
  287. if (cosSquare > minAngle)
  288. {
  289. minAngle = cosSquare;
  290. }
  291. if (acuteBiggest && (cosSquare < maxAngle))
  292. {
  293. maxAngle = cosSquare;
  294. }
  295. // Update min/max angle per triangle
  296. if (cosSquare > triMinAngle)
  297. {
  298. triMinAngle = cosSquare;
  299. }
  300. if (acuteBiggestTri && (cosSquare < triMaxAngle))
  301. {
  302. triMaxAngle = cosSquare;
  303. }
  304. }
  305. else
  306. {
  307. angleTable[sampleDegrees - degreeStep - 1]++;
  308. if (acuteBiggest || (cosSquare > maxAngle))
  309. {
  310. maxAngle = cosSquare;
  311. acuteBiggest = false;
  312. }
  313. // Update max angle for (possibly non-acute) triangle
  314. if (acuteBiggestTri || (cosSquare > triMaxAngle))
  315. {
  316. triMaxAngle = cosSquare;
  317. acuteBiggestTri = false;
  318. }
  319. }
  320. }
  321. // Update min angle histogram
  322. degreeStep = sampleDegrees / 2 - 1;
  323. for (int j = degreeStep - 1; j >= 0; j--)
  324. {
  325. if (triMinAngle > cosSquareTable[j])
  326. {
  327. degreeStep = j;
  328. }
  329. }
  330. minAngles[degreeStep]++;
  331. // Update max angle histogram
  332. degreeStep = sampleDegrees / 2 - 1;
  333. for (int j = degreeStep - 1; j >= 0; j--)
  334. {
  335. if (triMaxAngle > cosSquareTable[j])
  336. {
  337. degreeStep = j;
  338. }
  339. }
  340. if (acuteBiggestTri)
  341. {
  342. maxAngles[degreeStep]++;
  343. }
  344. else
  345. {
  346. maxAngles[sampleDegrees - degreeStep - 1]++;
  347. }
  348. acuteBiggestTri = true;
  349. }
  350. minEdge = Math.Sqrt(minEdge);
  351. maxEdge = Math.Sqrt(maxEdge);
  352. minAspect = Math.Sqrt(minAspect);
  353. maxAspect = Math.Sqrt(maxAspect);
  354. minArea *= 0.5;
  355. maxArea *= 0.5;
  356. if (minAngle >= 1.0)
  357. {
  358. minAngle = 0.0;
  359. }
  360. else
  361. {
  362. minAngle = degconst * Math.Acos(Math.Sqrt(minAngle));
  363. }
  364. if (maxAngle >= 1.0)
  365. {
  366. maxAngle = 180.0;
  367. }
  368. else
  369. {
  370. if (acuteBiggest)
  371. {
  372. maxAngle = degconst * Math.Acos(Math.Sqrt(maxAngle));
  373. }
  374. else
  375. {
  376. maxAngle = 180.0 - degconst * Math.Acos(Math.Sqrt(maxAngle));
  377. }
  378. }
  379. }
  380. /// <summary>
  381. /// Compute angle information for given triangle.
  382. /// </summary>
  383. /// <param name="triangle">The triangle to check.</param>
  384. /// <param name="data">Array of doubles (length 6).</param>
  385. /// <remarks>
  386. /// On return, the squared cosines of the minimum and maximum angle will
  387. /// be stored at position data[0] and data[1] respectively.
  388. /// If the triangle was obtuse, data[2] will be set to -1 and maximum angle
  389. /// is computed as (pi - acos(sqrt(data[1]))).
  390. /// </remarks>
  391. internal static void ComputeAngles(ITriangle triangle, double[] data)
  392. {
  393. double min = 0.0;
  394. double max = 1.0;
  395. var va = triangle.GetVertex(0);
  396. var vb = triangle.GetVertex(1);
  397. var vc = triangle.GetVertex(2);
  398. double dxa = vb.x - vc.x;
  399. double dya = vb.y - vc.y;
  400. double lena = dxa * dxa + dya * dya;
  401. double dxb = vc.x - va.x;
  402. double dyb = vc.y - va.y;
  403. double lenb = dxb * dxb + dyb * dyb;
  404. double dxc = va.x - vb.x;
  405. double dyc = va.y - vb.y;
  406. double lenc = dxc * dxc + dyc * dyc;
  407. // Dot products.
  408. double dota = data[0] = dxb * dxc + dyb * dyc;
  409. double dotb = data[1] = dxc * dxa + dyc * dya;
  410. double dotc = data[2] = dxa * dxb + dya * dyb;
  411. // Squared cosines.
  412. data[3] = (dota * dota) / (lenb * lenc);
  413. data[4] = (dotb * dotb) / (lenc * lena);
  414. data[5] = (dotc * dotc) / (lena * lenb);
  415. // The sign of the dot product will tell us, if the angle is
  416. // acute (value < 0) or obtuse (value > 0).
  417. bool acute = true;
  418. double cos, dot;
  419. for (int i = 0; i < 3; i++)
  420. {
  421. dot = data[i];
  422. cos = data[3 + i];
  423. if (dot <= 0.0)
  424. {
  425. if (cos > min)
  426. {
  427. min = cos;
  428. }
  429. if (acute && (cos < max))
  430. {
  431. max = cos;
  432. }
  433. }
  434. else
  435. {
  436. // Update max angle for (possibly non-acute) triangle
  437. if (acute || (cos > max))
  438. {
  439. max = cos;
  440. acute = false;
  441. }
  442. }
  443. }
  444. data[0] = min;
  445. data[1] = max;
  446. data[2] = acute ? 1.0 : -1.0;
  447. }
  448. }
  449. }