Triangulator.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /******************************************************************************
  2. * Spine Runtimes License Agreement
  3. * Last updated January 1, 2020. Replaces all prior versions.
  4. *
  5. * Copyright (c) 2013-2020, Esoteric Software LLC
  6. *
  7. * Integration of the Spine Runtimes into software or otherwise creating
  8. * derivative works of the Spine Runtimes is permitted under the terms and
  9. * conditions of Section 2 of the Spine Editor License Agreement:
  10. * http://esotericsoftware.com/spine-editor-license
  11. *
  12. * Otherwise, it is permitted to integrate the Spine Runtimes into software
  13. * or otherwise create derivative works of the Spine Runtimes (collectively,
  14. * "Products"), provided that each user of the Products must obtain their own
  15. * Spine Editor license and redistribution of the Products in any form must
  16. * include this license and copyright notice.
  17. *
  18. * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
  19. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
  24. * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *****************************************************************************/
  29. using System;
  30. namespace Spine {
  31. public class Triangulator {
  32. private readonly ExposedList<ExposedList<float>> convexPolygons = new ExposedList<ExposedList<float>>();
  33. private readonly ExposedList<ExposedList<int>> convexPolygonsIndices = new ExposedList<ExposedList<int>>();
  34. private readonly ExposedList<int> indicesArray = new ExposedList<int>();
  35. private readonly ExposedList<bool> isConcaveArray = new ExposedList<bool>();
  36. private readonly ExposedList<int> triangles = new ExposedList<int>();
  37. private readonly Pool<ExposedList<float>> polygonPool = new Pool<ExposedList<float>>();
  38. private readonly Pool<ExposedList<int>> polygonIndicesPool = new Pool<ExposedList<int>>();
  39. public ExposedList<int> Triangulate (ExposedList<float> verticesArray) {
  40. var vertices = verticesArray.Items;
  41. int vertexCount = verticesArray.Count >> 1;
  42. var indicesArray = this.indicesArray;
  43. indicesArray.Clear();
  44. int[] indices = indicesArray.Resize(vertexCount).Items;
  45. for (int i = 0; i < vertexCount; i++)
  46. indices[i] = i;
  47. var isConcaveArray = this.isConcaveArray;
  48. bool[] isConcave = isConcaveArray.Resize(vertexCount).Items;
  49. for (int i = 0, n = vertexCount; i < n; ++i)
  50. isConcave[i] = IsConcave(i, vertexCount, vertices, indices);
  51. var triangles = this.triangles;
  52. triangles.Clear();
  53. triangles.EnsureCapacity(Math.Max(0, vertexCount - 2) << 2);
  54. while (vertexCount > 3) {
  55. // Find ear tip.
  56. int previous = vertexCount - 1, i = 0, next = 1;
  57. // outer:
  58. while (true) {
  59. if (!isConcave[i]) {
  60. int p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1;
  61. float p1x = vertices[p1], p1y = vertices[p1 + 1];
  62. float p2x = vertices[p2], p2y = vertices[p2 + 1];
  63. float p3x = vertices[p3], p3y = vertices[p3 + 1];
  64. for (int ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) {
  65. if (!isConcave[ii]) continue;
  66. int v = indices[ii] << 1;
  67. float vx = vertices[v], vy = vertices[v + 1];
  68. if (PositiveArea(p3x, p3y, p1x, p1y, vx, vy)) {
  69. if (PositiveArea(p1x, p1y, p2x, p2y, vx, vy)) {
  70. if (PositiveArea(p2x, p2y, p3x, p3y, vx, vy)) goto break_outer; // break outer;
  71. }
  72. }
  73. }
  74. break;
  75. }
  76. break_outer:
  77. if (next == 0) {
  78. do {
  79. if (!isConcave[i]) break;
  80. i--;
  81. } while (i > 0);
  82. break;
  83. }
  84. previous = i;
  85. i = next;
  86. next = (next + 1) % vertexCount;
  87. }
  88. // Cut ear tip.
  89. triangles.Add(indices[(vertexCount + i - 1) % vertexCount]);
  90. triangles.Add(indices[i]);
  91. triangles.Add(indices[(i + 1) % vertexCount]);
  92. indicesArray.RemoveAt(i);
  93. isConcaveArray.RemoveAt(i);
  94. vertexCount--;
  95. int previousIndex = (vertexCount + i - 1) % vertexCount;
  96. int nextIndex = i == vertexCount ? 0 : i;
  97. isConcave[previousIndex] = IsConcave(previousIndex, vertexCount, vertices, indices);
  98. isConcave[nextIndex] = IsConcave(nextIndex, vertexCount, vertices, indices);
  99. }
  100. if (vertexCount == 3) {
  101. triangles.Add(indices[2]);
  102. triangles.Add(indices[0]);
  103. triangles.Add(indices[1]);
  104. }
  105. return triangles;
  106. }
  107. public ExposedList<ExposedList<float>> Decompose (ExposedList<float> verticesArray, ExposedList<int> triangles) {
  108. var vertices = verticesArray.Items;
  109. var convexPolygons = this.convexPolygons;
  110. for (int i = 0, n = convexPolygons.Count; i < n; i++) {
  111. polygonPool.Free(convexPolygons.Items[i]);
  112. }
  113. convexPolygons.Clear();
  114. var convexPolygonsIndices = this.convexPolygonsIndices;
  115. for (int i = 0, n = convexPolygonsIndices.Count; i < n; i++) {
  116. polygonIndicesPool.Free(convexPolygonsIndices.Items[i]);
  117. }
  118. convexPolygonsIndices.Clear();
  119. var polygonIndices = polygonIndicesPool.Obtain();
  120. polygonIndices.Clear();
  121. var polygon = polygonPool.Obtain();
  122. polygon.Clear();
  123. // Merge subsequent triangles if they form a triangle fan.
  124. int fanBaseIndex = -1, lastWinding = 0;
  125. int[] trianglesItems = triangles.Items;
  126. for (int i = 0, n = triangles.Count; i < n; i += 3) {
  127. int t1 = trianglesItems[i] << 1, t2 = trianglesItems[i + 1] << 1, t3 = trianglesItems[i + 2] << 1;
  128. float x1 = vertices[t1], y1 = vertices[t1 + 1];
  129. float x2 = vertices[t2], y2 = vertices[t2 + 1];
  130. float x3 = vertices[t3], y3 = vertices[t3 + 1];
  131. // If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan).
  132. var merged = false;
  133. if (fanBaseIndex == t1) {
  134. int o = polygon.Count - 4;
  135. float[] p = polygon.Items;
  136. int winding1 = Winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3);
  137. int winding2 = Winding(x3, y3, p[0], p[1], p[2], p[3]);
  138. if (winding1 == lastWinding && winding2 == lastWinding) {
  139. polygon.Add(x3);
  140. polygon.Add(y3);
  141. polygonIndices.Add(t3);
  142. merged = true;
  143. }
  144. }
  145. // Otherwise make this triangle the new base.
  146. if (!merged) {
  147. if (polygon.Count > 0) {
  148. convexPolygons.Add(polygon);
  149. convexPolygonsIndices.Add(polygonIndices);
  150. } else {
  151. polygonPool.Free(polygon);
  152. polygonIndicesPool.Free(polygonIndices);
  153. }
  154. polygon = polygonPool.Obtain();
  155. polygon.Clear();
  156. polygon.Add(x1);
  157. polygon.Add(y1);
  158. polygon.Add(x2);
  159. polygon.Add(y2);
  160. polygon.Add(x3);
  161. polygon.Add(y3);
  162. polygonIndices = polygonIndicesPool.Obtain();
  163. polygonIndices.Clear();
  164. polygonIndices.Add(t1);
  165. polygonIndices.Add(t2);
  166. polygonIndices.Add(t3);
  167. lastWinding = Winding(x1, y1, x2, y2, x3, y3);
  168. fanBaseIndex = t1;
  169. }
  170. }
  171. if (polygon.Count > 0) {
  172. convexPolygons.Add(polygon);
  173. convexPolygonsIndices.Add(polygonIndices);
  174. }
  175. // Go through the list of polygons and try to merge the remaining triangles with the found triangle fans.
  176. for (int i = 0, n = convexPolygons.Count; i < n; i++) {
  177. polygonIndices = convexPolygonsIndices.Items[i];
  178. if (polygonIndices.Count == 0) continue;
  179. int firstIndex = polygonIndices.Items[0];
  180. int lastIndex = polygonIndices.Items[polygonIndices.Count - 1];
  181. polygon = convexPolygons.Items[i];
  182. int o = polygon.Count - 4;
  183. float[] p = polygon.Items;
  184. float prevPrevX = p[o], prevPrevY = p[o + 1];
  185. float prevX = p[o + 2], prevY = p[o + 3];
  186. float firstX = p[0], firstY = p[1];
  187. float secondX = p[2], secondY = p[3];
  188. int winding = Winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY);
  189. for (int ii = 0; ii < n; ii++) {
  190. if (ii == i) continue;
  191. var otherIndices = convexPolygonsIndices.Items[ii];
  192. if (otherIndices.Count != 3) continue;
  193. int otherFirstIndex = otherIndices.Items[0];
  194. int otherSecondIndex = otherIndices.Items[1];
  195. int otherLastIndex = otherIndices.Items[2];
  196. var otherPoly = convexPolygons.Items[ii];
  197. float x3 = otherPoly.Items[otherPoly.Count - 2], y3 = otherPoly.Items[otherPoly.Count - 1];
  198. if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue;
  199. int winding1 = Winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3);
  200. int winding2 = Winding(x3, y3, firstX, firstY, secondX, secondY);
  201. if (winding1 == winding && winding2 == winding) {
  202. otherPoly.Clear();
  203. otherIndices.Clear();
  204. polygon.Add(x3);
  205. polygon.Add(y3);
  206. polygonIndices.Add(otherLastIndex);
  207. prevPrevX = prevX;
  208. prevPrevY = prevY;
  209. prevX = x3;
  210. prevY = y3;
  211. ii = 0;
  212. }
  213. }
  214. }
  215. // Remove empty polygons that resulted from the merge step above.
  216. for (int i = convexPolygons.Count - 1; i >= 0; i--) {
  217. polygon = convexPolygons.Items[i];
  218. if (polygon.Count == 0) {
  219. convexPolygons.RemoveAt(i);
  220. polygonPool.Free(polygon);
  221. polygonIndices = convexPolygonsIndices.Items[i];
  222. convexPolygonsIndices.RemoveAt(i);
  223. polygonIndicesPool.Free(polygonIndices);
  224. }
  225. }
  226. return convexPolygons;
  227. }
  228. static private bool IsConcave (int index, int vertexCount, float[] vertices, int[] indices) {
  229. int previous = indices[(vertexCount + index - 1) % vertexCount] << 1;
  230. int current = indices[index] << 1;
  231. int next = indices[(index + 1) % vertexCount] << 1;
  232. return !PositiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next],
  233. vertices[next + 1]);
  234. }
  235. static private bool PositiveArea (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) {
  236. return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0;
  237. }
  238. static private int Winding (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) {
  239. float px = p2x - p1x, py = p2y - p1y;
  240. return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1;
  241. }
  242. }
  243. }