説明なし
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

PlanarGraph.cs 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. using Unity.Collections;
  2. using Unity.Mathematics;
  3. using Unity.Collections.LowLevel.Unsafe;
  4. namespace UnityEngine.Rendering.Universal.UTess
  5. {
  6. // Ensures that there are no duplicate Points, Overlapping Edges.
  7. struct PlanarGraph
  8. {
  9. private static readonly double kEpsilon = 0.00001;
  10. private static readonly int kMaxIntersectionTolerance = 4; // Maximum Intersection Tolerance per Intersection Loop Check.
  11. internal static void RemoveDuplicateEdges(ref NativeArray<int2> edges, ref int edgeCount, NativeArray<int> duplicates, int duplicateCount)
  12. {
  13. if (duplicateCount == 0)
  14. {
  15. for (var i = 0; i < edgeCount; ++i)
  16. {
  17. var e = edges[i];
  18. e.x = math.min(edges[i].x, edges[i].y);
  19. e.y = math.max(edges[i].x, edges[i].y);
  20. edges[i] = e;
  21. }
  22. }
  23. else
  24. {
  25. for (var i = 0; i < edgeCount; ++i)
  26. {
  27. var e = edges[i];
  28. var a = duplicates[e.x];
  29. var b = duplicates[e.y];
  30. e.x = math.min(a, b);
  31. e.y = math.max(a, b);
  32. edges[i] = e;
  33. }
  34. }
  35. unsafe
  36. {
  37. ModuleHandle.InsertionSort<int2, TessEdgeCompare>(
  38. NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(edges), 0, edgeCount - 1,
  39. new TessEdgeCompare());
  40. }
  41. var n = 1;
  42. for (var i = 1; i < edgeCount; ++i)
  43. {
  44. var prev = edges[i - 1];
  45. var next = edges[i];
  46. if (next.x == prev.x && next.y == prev.y)
  47. continue;
  48. if (next.x == next.y)
  49. continue;
  50. edges[n++] = next;
  51. }
  52. edgeCount = n;
  53. }
  54. internal static bool CheckCollinear(double2 a0, double2 a1, double2 b0, double2 b1)
  55. {
  56. double2 a = a0;
  57. double2 b = a1;
  58. double2 c = b0;
  59. double2 d = b1;
  60. double x = (b.y - a.y) / (b.x - a.x);
  61. double y = (c.y - a.y) / (c.x - a.x);
  62. double z = (d.y - a.y) / (d.x - a.x);
  63. return ((!math.isinf(x) || !math.isinf(y) || !math.isinf(z)) && math.abs(x - y) > kEpsilon && math.abs(x - z) > kEpsilon);
  64. }
  65. internal static bool LineLineIntersection(double2 a0, double2 a1, double2 b0, double2 b1)
  66. {
  67. var x0 = ModuleHandle.OrientFastDouble(a0, b0, b1);
  68. var y0 = ModuleHandle.OrientFastDouble(a1, b0, b1);
  69. if ((x0 > kEpsilon && y0 > kEpsilon) || (x0 < -kEpsilon && y0 < -kEpsilon))
  70. {
  71. return false;
  72. }
  73. var x1 = ModuleHandle.OrientFastDouble(b0, a0, a1);
  74. var y1 = ModuleHandle.OrientFastDouble(b1, a0, a1);
  75. if ((x1 > kEpsilon && y1 > kEpsilon) || (x1 < -kEpsilon && y1 < -kEpsilon))
  76. {
  77. return false;
  78. }
  79. // Check for degenerate collinear case
  80. if (math.abs(x0) < kEpsilon && math.abs(y0) < kEpsilon && math.abs(x1) < kEpsilon && math.abs(y1) < kEpsilon)
  81. {
  82. return CheckCollinear(a0, a1, b0, b1);
  83. }
  84. return true;
  85. }
  86. internal static bool LineLineIntersection(double2 p1, double2 p2, double2 p3, double2 p4, ref double2 result)
  87. {
  88. double bx = p2.x - p1.x;
  89. double by = p2.y - p1.y;
  90. double dx = p4.x - p3.x;
  91. double dy = p4.y - p3.y;
  92. double bDotDPerp = bx * dy - by * dx;
  93. if (math.abs(bDotDPerp) < kEpsilon)
  94. {
  95. return false;
  96. }
  97. double cx = p3.x - p1.x;
  98. double cy = p3.y - p1.y;
  99. double t = (cx * dy - cy * dx) / bDotDPerp;
  100. if ((t >= -kEpsilon) && (t <= 1.0f + kEpsilon))
  101. {
  102. result.x = p1.x + t * bx;
  103. result.y = p1.y + t * by;
  104. return true;
  105. }
  106. return false;
  107. }
  108. internal static bool CalculateEdgeIntersections(NativeArray<int2> edges, int edgeCount, NativeArray<double2> points, int pointCount, ref NativeArray<int2> results, ref NativeArray<double2> intersects, ref int resultCount)
  109. {
  110. resultCount = 0;
  111. for (int i = 0; i < edgeCount; ++i)
  112. {
  113. for (int j = i + 1; j < edgeCount; ++j)
  114. {
  115. var e = edges[i];
  116. var f = edges[j];
  117. if (e.x == f.x || e.x == f.y || e.y == f.x || e.y == f.y)
  118. continue;
  119. var a = points[e.x];
  120. var b = points[e.y];
  121. var c = points[f.x];
  122. var d = points[f.y];
  123. var g = double2.zero;
  124. if (LineLineIntersection(a, b, c, d))
  125. {
  126. if (LineLineIntersection(a, b, c, d, ref g))
  127. {
  128. // Until we ensure Outline is generated properly, we need this useless Check every correction.
  129. if (resultCount >= intersects.Length)
  130. return false;
  131. intersects[resultCount] = g;
  132. results[resultCount++] = new int2(i, j);
  133. }
  134. }
  135. }
  136. }
  137. // Basically we have self intersections all over (eg. gobo_tree_04). Better don't generate any Mesh as even though this will eventually succeed, error correction will take long time.
  138. if (resultCount > (edgeCount * kMaxIntersectionTolerance))
  139. {
  140. return false;
  141. }
  142. unsafe
  143. {
  144. var tjc = new IntersectionCompare();
  145. tjc.edges = edges;
  146. tjc.points = points;
  147. ModuleHandle.InsertionSort<int2, IntersectionCompare>(
  148. NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(results), 0, resultCount - 1, tjc);
  149. }
  150. return true;
  151. }
  152. internal static bool CalculateTJunctions(NativeArray<int2> edges, int edgeCount, NativeArray<double2> points, int pointCount, NativeArray<int2> results, ref int resultCount)
  153. {
  154. resultCount = 0;
  155. for (int i = 0; i < edgeCount; ++i)
  156. {
  157. for (int j = 0; j < pointCount; ++j)
  158. {
  159. var e = edges[i];
  160. if (e.x == j || e.y == j)
  161. continue;
  162. var a = points[e.x];
  163. var b = points[e.y];
  164. var c = points[j];
  165. var d = points[j];
  166. if (LineLineIntersection(a, b, c, d))
  167. {
  168. // Until we ensure Outline is generated properly, we need this useless Check every correction.
  169. if (resultCount >= results.Length)
  170. return false;
  171. results[resultCount++] = new int2(i, j);
  172. }
  173. }
  174. }
  175. return true;
  176. }
  177. internal static bool CutEdges(ref NativeArray<double2> points, ref int pointCount, ref NativeArray<int2> edges, ref int edgeCount, ref NativeArray<int2> tJunctions, ref int tJunctionCount, NativeArray<int2> intersections, NativeArray<double2> intersects, int intersectionCount)
  178. {
  179. for (int i = 0; i < intersectionCount; ++i)
  180. {
  181. var intr = intersections[i];
  182. var e = intr.x;
  183. var f = intr.y;
  184. int2 j1 = int2.zero;
  185. j1.x = e;
  186. j1.y = pointCount;
  187. tJunctions[tJunctionCount++] = j1;
  188. int2 j2 = int2.zero;
  189. j2.x = f;
  190. j2.y = pointCount;
  191. tJunctions[tJunctionCount++] = j2;
  192. // Until we ensure Outline is generated properly, we need this useless Check every correction.
  193. if (pointCount >= points.Length)
  194. return false;
  195. points[pointCount++] = intersects[i];
  196. }
  197. unsafe
  198. {
  199. ModuleHandle.InsertionSort<int2, TessJunctionCompare>(
  200. NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(tJunctions), 0, tJunctionCount - 1,
  201. new TessJunctionCompare());
  202. }
  203. // Split edges along junctions
  204. for (int i = tJunctionCount - 1; i >= 0; --i)
  205. {
  206. var tJunction = tJunctions[i];
  207. var e = tJunction.x;
  208. var edge = edges[e];
  209. var s = edge.x;
  210. var t = edge.y;
  211. // Check if edge is not lexicographically sorted
  212. var a = points[s];
  213. var b = points[t];
  214. if (((a.x - b.x) < 0) || (a.x == b.x && (a.y - b.y) < 0))
  215. {
  216. var tmp = s;
  217. s = t;
  218. t = tmp;
  219. }
  220. // Split leading edge
  221. edge.x = s;
  222. var last = edge.y = tJunction.y;
  223. edges[e] = edge;
  224. // If we are grouping edges by color, remember to track data
  225. // Split other edges
  226. while (i > 0 && tJunctions[i - 1].x == e)
  227. {
  228. var next = tJunctions[--i].y;
  229. int2 te = new int2();
  230. te.x = last;
  231. te.y = next;
  232. edges[edgeCount++] = te;
  233. last = next;
  234. }
  235. int2 le = new int2();
  236. le.x = last;
  237. le.y = t;
  238. edges[edgeCount++] = le;
  239. }
  240. return true;
  241. }
  242. internal static void RemoveDuplicatePoints(ref NativeArray<double2> points, ref int pointCount, ref NativeArray<int> duplicates, ref int duplicateCount, Allocator allocator)
  243. {
  244. TessLink link = TessLink.CreateLink(pointCount, allocator);
  245. for (int i = 0; i < pointCount; ++i)
  246. {
  247. for (int j = i + 1; j < pointCount; ++j)
  248. {
  249. if (math.distance(points[i], points[j]) < kEpsilon)
  250. {
  251. link.Link(i, j);
  252. }
  253. }
  254. }
  255. duplicateCount = 0;
  256. for (var i = 0; i < pointCount; ++i)
  257. {
  258. var j = link.Find(i);
  259. if (j != i)
  260. {
  261. duplicateCount++;
  262. points[j] = math.min(points[i], points[j]);
  263. }
  264. }
  265. // Find Duplicates.
  266. if (duplicateCount != 0)
  267. {
  268. var prevPointCount = pointCount;
  269. pointCount = 0;
  270. for (var i = 0; i < prevPointCount; ++i)
  271. {
  272. var j = link.Find(i);
  273. if (j == i)
  274. {
  275. duplicates[i] = pointCount;
  276. points[pointCount++] = points[i];
  277. }
  278. else
  279. {
  280. duplicates[i] = -1;
  281. }
  282. }
  283. // Update Duplicates.
  284. for (int i = 0; i < prevPointCount; ++i)
  285. {
  286. if (duplicates[i] < 0)
  287. {
  288. duplicates[i] = duplicates[link.Find(i)];
  289. }
  290. }
  291. }
  292. TessLink.DestroyLink(link);
  293. }
  294. // Validate the Input Points ane Edges.
  295. internal static bool Validate(Allocator allocator, NativeArray<float2> inputPoints, int pointCount, NativeArray<int2> inputEdges, int edgeCount, ref NativeArray<float2> outputPoints, ref int outputPointCount, ref NativeArray<int2> outputEdges, ref int outputEdgeCount)
  296. {
  297. // Outline generated inputs can have differences in the range of 0.00001f.. See TwoLayers.psb sample.
  298. // Since PlanarGraph operates on double, scaling up and down does not result in loss of data.
  299. var precisionFudge = 10000.0f;
  300. var protectLoop = edgeCount;
  301. var requiresFix = true;
  302. var validGraph = false;
  303. // Processing Arrays.
  304. NativeArray<int> duplicates = new NativeArray<int>(ModuleHandle.kMaxEdgeCount, allocator);
  305. NativeArray<int2> edges = new NativeArray<int2>(ModuleHandle.kMaxEdgeCount, allocator);
  306. NativeArray<int2> tJunctions = new NativeArray<int2>(ModuleHandle.kMaxEdgeCount, allocator);
  307. NativeArray<int2> edgeIntersections = new NativeArray<int2>(ModuleHandle.kMaxEdgeCount, allocator);
  308. NativeArray<double2> points = new NativeArray<double2>(pointCount * 8, allocator);
  309. NativeArray<double2> intersects = new NativeArray<double2>(pointCount * 8, allocator);
  310. // Initialize.
  311. for (int i = 0; i < pointCount; ++i)
  312. points[i] = inputPoints[i] * precisionFudge;
  313. ModuleHandle.Copy(inputEdges, edges, edgeCount);
  314. // Pre-clear duplicates, otherwise the following will simply fail.
  315. RemoveDuplicateEdges(ref edges, ref edgeCount, duplicates, 0);
  316. // While PSG is clean.
  317. while (requiresFix && --protectLoop > 0)
  318. {
  319. // Edge Edge Intersection.
  320. int intersectionCount = 0;
  321. validGraph = CalculateEdgeIntersections(edges, edgeCount, points, pointCount, ref edgeIntersections, ref intersects, ref intersectionCount);
  322. if (!validGraph)
  323. break;
  324. // Edge Point Intersection. T-Junction.
  325. int tJunctionCount = 0;
  326. validGraph = CalculateTJunctions(edges, edgeCount, points, pointCount, tJunctions, ref tJunctionCount);
  327. if (!validGraph)
  328. break;
  329. // Cut Overlapping Edges.
  330. validGraph = CutEdges(ref points, ref pointCount, ref edges, ref edgeCount, ref tJunctions, ref tJunctionCount, edgeIntersections, intersects, intersectionCount);
  331. if (!validGraph)
  332. break;
  333. // Remove Duplicate Points.
  334. int duplicateCount = 0;
  335. RemoveDuplicatePoints(ref points, ref pointCount, ref duplicates, ref duplicateCount, allocator);
  336. RemoveDuplicateEdges(ref edges, ref edgeCount, duplicates, duplicateCount);
  337. requiresFix = intersectionCount != 0 || tJunctionCount != 0;
  338. }
  339. if (validGraph)
  340. {
  341. // Finalize Output.
  342. outputEdgeCount = edgeCount;
  343. outputPointCount = pointCount;
  344. ModuleHandle.Copy(edges, outputEdges, edgeCount);
  345. for (int i = 0; i < pointCount; ++i)
  346. outputPoints[i] = new float2((float)(points[i].x / precisionFudge), (float)(points[i].y / precisionFudge));
  347. }
  348. edges.Dispose();
  349. points.Dispose();
  350. intersects.Dispose();
  351. duplicates.Dispose();
  352. tJunctions.Dispose();
  353. edgeIntersections.Dispose();
  354. return (validGraph && protectLoop > 0);
  355. }
  356. }
  357. }