Triangulator.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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. convexPolygons.Clear();
  113. var convexPolygonsIndices = this.convexPolygonsIndices;
  114. for (int i = 0, n = convexPolygonsIndices.Count; i < n; i++)
  115. polygonIndicesPool.Free(convexPolygonsIndices.Items[i]);
  116. convexPolygonsIndices.Clear();
  117. var polygonIndices = polygonIndicesPool.Obtain();
  118. polygonIndices.Clear();
  119. var polygon = polygonPool.Obtain();
  120. polygon.Clear();
  121. // Merge subsequent triangles if they form a triangle fan.
  122. int fanBaseIndex = -1, lastWinding = 0;
  123. int[] trianglesItems = triangles.Items;
  124. for (int i = 0, n = triangles.Count; i < n; i += 3) {
  125. int t1 = trianglesItems[i] << 1, t2 = trianglesItems[i + 1] << 1, t3 = trianglesItems[i + 2] << 1;
  126. float x1 = vertices[t1], y1 = vertices[t1 + 1];
  127. float x2 = vertices[t2], y2 = vertices[t2 + 1];
  128. float x3 = vertices[t3], y3 = vertices[t3 + 1];
  129. // If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan).
  130. var merged = false;
  131. if (fanBaseIndex == t1) {
  132. int o = polygon.Count - 4;
  133. float[] p = polygon.Items;
  134. int winding1 = Winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3);
  135. int winding2 = Winding(x3, y3, p[0], p[1], p[2], p[3]);
  136. if (winding1 == lastWinding && winding2 == lastWinding) {
  137. polygon.Add(x3);
  138. polygon.Add(y3);
  139. polygonIndices.Add(t3);
  140. merged = true;
  141. }
  142. }
  143. // Otherwise make this triangle the new base.
  144. if (!merged) {
  145. if (polygon.Count > 0) {
  146. convexPolygons.Add(polygon);
  147. convexPolygonsIndices.Add(polygonIndices);
  148. } else {
  149. polygonPool.Free(polygon);
  150. polygonIndicesPool.Free(polygonIndices);
  151. }
  152. polygon = polygonPool.Obtain();
  153. polygon.Clear();
  154. polygon.Add(x1);
  155. polygon.Add(y1);
  156. polygon.Add(x2);
  157. polygon.Add(y2);
  158. polygon.Add(x3);
  159. polygon.Add(y3);
  160. polygonIndices = polygonIndicesPool.Obtain();
  161. polygonIndices.Clear();
  162. polygonIndices.Add(t1);
  163. polygonIndices.Add(t2);
  164. polygonIndices.Add(t3);
  165. lastWinding = Winding(x1, y1, x2, y2, x3, y3);
  166. fanBaseIndex = t1;
  167. }
  168. }
  169. if (polygon.Count > 0) {
  170. convexPolygons.Add(polygon);
  171. convexPolygonsIndices.Add(polygonIndices);
  172. }
  173. // Go through the list of polygons and try to merge the remaining triangles with the found triangle fans.
  174. for (int i = 0, n = convexPolygons.Count; i < n; i++) {
  175. polygonIndices = convexPolygonsIndices.Items[i];
  176. if (polygonIndices.Count == 0) continue;
  177. int firstIndex = polygonIndices.Items[0];
  178. int lastIndex = polygonIndices.Items[polygonIndices.Count - 1];
  179. polygon = convexPolygons.Items[i];
  180. int o = polygon.Count - 4;
  181. float[] p = polygon.Items;
  182. float prevPrevX = p[o], prevPrevY = p[o + 1];
  183. float prevX = p[o + 2], prevY = p[o + 3];
  184. float firstX = p[0], firstY = p[1];
  185. float secondX = p[2], secondY = p[3];
  186. int winding = Winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY);
  187. for (int ii = 0; ii < n; ii++) {
  188. if (ii == i) continue;
  189. var otherIndices = convexPolygonsIndices.Items[ii];
  190. if (otherIndices.Count != 3) continue;
  191. int otherFirstIndex = otherIndices.Items[0];
  192. int otherSecondIndex = otherIndices.Items[1];
  193. int otherLastIndex = otherIndices.Items[2];
  194. var otherPoly = convexPolygons.Items[ii];
  195. float x3 = otherPoly.Items[otherPoly.Count - 2], y3 = otherPoly.Items[otherPoly.Count - 1];
  196. if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue;
  197. int winding1 = Winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3);
  198. int winding2 = Winding(x3, y3, firstX, firstY, secondX, secondY);
  199. if (winding1 == winding && winding2 == winding) {
  200. otherPoly.Clear();
  201. otherIndices.Clear();
  202. polygon.Add(x3);
  203. polygon.Add(y3);
  204. polygonIndices.Add(otherLastIndex);
  205. prevPrevX = prevX;
  206. prevPrevY = prevY;
  207. prevX = x3;
  208. prevY = y3;
  209. ii = 0;
  210. }
  211. }
  212. }
  213. // Remove empty polygons that resulted from the merge step above.
  214. for (int i = convexPolygons.Count - 1; i >= 0; i--) {
  215. polygon = convexPolygons.Items[i];
  216. if (polygon.Count == 0) {
  217. convexPolygons.RemoveAt(i);
  218. polygonPool.Free(polygon);
  219. polygonIndices = convexPolygonsIndices.Items[i];
  220. convexPolygonsIndices.RemoveAt(i);
  221. polygonIndicesPool.Free(polygonIndices);
  222. }
  223. }
  224. return convexPolygons;
  225. }
  226. static private bool IsConcave (int index, int vertexCount, float[] vertices, int[] indices) {
  227. int previous = indices[(vertexCount + index - 1) % vertexCount] << 1;
  228. int current = indices[index] << 1;
  229. int next = indices[(index + 1) % vertexCount] << 1;
  230. return !PositiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next],
  231. vertices[next + 1]);
  232. }
  233. static private bool PositiveArea (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) {
  234. return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0;
  235. }
  236. static private int Winding (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) {
  237. float px = p2x - p1x, py = p2y - p1y;
  238. return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1;
  239. }
  240. }
  241. }