Intersections2D.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. import Cartesian2 from "./Cartesian2.js";
  2. import Cartesian3 from "./Cartesian3.js";
  3. import Check from "./Check.js";
  4. import defined from "./defined.js";
  5. import DeveloperError from "./DeveloperError.js";
  6. /**
  7. * Contains functions for operating on 2D triangles.
  8. *
  9. * @namespace Intersections2D
  10. */
  11. const Intersections2D = {};
  12. /**
  13. * Splits a 2D triangle at given axis-aligned threshold value and returns the resulting
  14. * polygon on a given side of the threshold. The resulting polygon may have 0, 1, 2,
  15. * 3, or 4 vertices.
  16. *
  17. * @param {Number} threshold The threshold coordinate value at which to clip the triangle.
  18. * @param {Boolean} keepAbove true to keep the portion of the triangle above the threshold, or false
  19. * to keep the portion below.
  20. * @param {Number} u0 The coordinate of the first vertex in the triangle, in counter-clockwise order.
  21. * @param {Number} u1 The coordinate of the second vertex in the triangle, in counter-clockwise order.
  22. * @param {Number} u2 The coordinate of the third vertex in the triangle, in counter-clockwise order.
  23. * @param {Number[]} [result] The array into which to copy the result. If this parameter is not supplied,
  24. * a new array is constructed and returned.
  25. * @returns {Number[]} The polygon that results after the clip, specified as a list of
  26. * vertices. The vertices are specified in counter-clockwise order.
  27. * Each vertex is either an index from the existing list (identified as
  28. * a 0, 1, or 2) or -1 indicating a new vertex not in the original triangle.
  29. * For new vertices, the -1 is followed by three additional numbers: the
  30. * index of each of the two original vertices forming the line segment that
  31. * the new vertex lies on, and the fraction of the distance from the first
  32. * vertex to the second one.
  33. *
  34. * @example
  35. * const result = Cesium.Intersections2D.clipTriangleAtAxisAlignedThreshold(0.5, false, 0.2, 0.6, 0.4);
  36. * // result === [2, 0, -1, 1, 0, 0.25, -1, 1, 2, 0.5]
  37. */
  38. Intersections2D.clipTriangleAtAxisAlignedThreshold = function (
  39. threshold,
  40. keepAbove,
  41. u0,
  42. u1,
  43. u2,
  44. result
  45. ) {
  46. //>>includeStart('debug', pragmas.debug);
  47. if (!defined(threshold)) {
  48. throw new DeveloperError("threshold is required.");
  49. }
  50. if (!defined(keepAbove)) {
  51. throw new DeveloperError("keepAbove is required.");
  52. }
  53. if (!defined(u0)) {
  54. throw new DeveloperError("u0 is required.");
  55. }
  56. if (!defined(u1)) {
  57. throw new DeveloperError("u1 is required.");
  58. }
  59. if (!defined(u2)) {
  60. throw new DeveloperError("u2 is required.");
  61. }
  62. //>>includeEnd('debug');
  63. if (!defined(result)) {
  64. result = [];
  65. } else {
  66. result.length = 0;
  67. }
  68. let u0Behind;
  69. let u1Behind;
  70. let u2Behind;
  71. if (keepAbove) {
  72. u0Behind = u0 < threshold;
  73. u1Behind = u1 < threshold;
  74. u2Behind = u2 < threshold;
  75. } else {
  76. u0Behind = u0 > threshold;
  77. u1Behind = u1 > threshold;
  78. u2Behind = u2 > threshold;
  79. }
  80. const numBehind = u0Behind + u1Behind + u2Behind;
  81. let u01Ratio;
  82. let u02Ratio;
  83. let u12Ratio;
  84. let u10Ratio;
  85. let u20Ratio;
  86. let u21Ratio;
  87. if (numBehind === 1) {
  88. if (u0Behind) {
  89. u01Ratio = (threshold - u0) / (u1 - u0);
  90. u02Ratio = (threshold - u0) / (u2 - u0);
  91. result.push(1);
  92. result.push(2);
  93. if (u02Ratio !== 1.0) {
  94. result.push(-1);
  95. result.push(0);
  96. result.push(2);
  97. result.push(u02Ratio);
  98. }
  99. if (u01Ratio !== 1.0) {
  100. result.push(-1);
  101. result.push(0);
  102. result.push(1);
  103. result.push(u01Ratio);
  104. }
  105. } else if (u1Behind) {
  106. u12Ratio = (threshold - u1) / (u2 - u1);
  107. u10Ratio = (threshold - u1) / (u0 - u1);
  108. result.push(2);
  109. result.push(0);
  110. if (u10Ratio !== 1.0) {
  111. result.push(-1);
  112. result.push(1);
  113. result.push(0);
  114. result.push(u10Ratio);
  115. }
  116. if (u12Ratio !== 1.0) {
  117. result.push(-1);
  118. result.push(1);
  119. result.push(2);
  120. result.push(u12Ratio);
  121. }
  122. } else if (u2Behind) {
  123. u20Ratio = (threshold - u2) / (u0 - u2);
  124. u21Ratio = (threshold - u2) / (u1 - u2);
  125. result.push(0);
  126. result.push(1);
  127. if (u21Ratio !== 1.0) {
  128. result.push(-1);
  129. result.push(2);
  130. result.push(1);
  131. result.push(u21Ratio);
  132. }
  133. if (u20Ratio !== 1.0) {
  134. result.push(-1);
  135. result.push(2);
  136. result.push(0);
  137. result.push(u20Ratio);
  138. }
  139. }
  140. } else if (numBehind === 2) {
  141. if (!u0Behind && u0 !== threshold) {
  142. u10Ratio = (threshold - u1) / (u0 - u1);
  143. u20Ratio = (threshold - u2) / (u0 - u2);
  144. result.push(0);
  145. result.push(-1);
  146. result.push(1);
  147. result.push(0);
  148. result.push(u10Ratio);
  149. result.push(-1);
  150. result.push(2);
  151. result.push(0);
  152. result.push(u20Ratio);
  153. } else if (!u1Behind && u1 !== threshold) {
  154. u21Ratio = (threshold - u2) / (u1 - u2);
  155. u01Ratio = (threshold - u0) / (u1 - u0);
  156. result.push(1);
  157. result.push(-1);
  158. result.push(2);
  159. result.push(1);
  160. result.push(u21Ratio);
  161. result.push(-1);
  162. result.push(0);
  163. result.push(1);
  164. result.push(u01Ratio);
  165. } else if (!u2Behind && u2 !== threshold) {
  166. u02Ratio = (threshold - u0) / (u2 - u0);
  167. u12Ratio = (threshold - u1) / (u2 - u1);
  168. result.push(2);
  169. result.push(-1);
  170. result.push(0);
  171. result.push(2);
  172. result.push(u02Ratio);
  173. result.push(-1);
  174. result.push(1);
  175. result.push(2);
  176. result.push(u12Ratio);
  177. }
  178. } else if (numBehind !== 3) {
  179. // Completely in front of threshold
  180. result.push(0);
  181. result.push(1);
  182. result.push(2);
  183. }
  184. // else Completely behind threshold
  185. return result;
  186. };
  187. /**
  188. * Compute the barycentric coordinates of a 2D position within a 2D triangle.
  189. *
  190. * @param {Number} x The x coordinate of the position for which to find the barycentric coordinates.
  191. * @param {Number} y The y coordinate of the position for which to find the barycentric coordinates.
  192. * @param {Number} x1 The x coordinate of the triangle's first vertex.
  193. * @param {Number} y1 The y coordinate of the triangle's first vertex.
  194. * @param {Number} x2 The x coordinate of the triangle's second vertex.
  195. * @param {Number} y2 The y coordinate of the triangle's second vertex.
  196. * @param {Number} x3 The x coordinate of the triangle's third vertex.
  197. * @param {Number} y3 The y coordinate of the triangle's third vertex.
  198. * @param {Cartesian3} [result] The instance into to which to copy the result. If this parameter
  199. * is undefined, a new instance is created and returned.
  200. * @returns {Cartesian3} The barycentric coordinates of the position within the triangle.
  201. *
  202. * @example
  203. * const result = Cesium.Intersections2D.computeBarycentricCoordinates(0.0, 0.0, 0.0, 1.0, -1, -0.5, 1, -0.5);
  204. * // result === new Cesium.Cartesian3(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0);
  205. */
  206. Intersections2D.computeBarycentricCoordinates = function (
  207. x,
  208. y,
  209. x1,
  210. y1,
  211. x2,
  212. y2,
  213. x3,
  214. y3,
  215. result
  216. ) {
  217. //>>includeStart('debug', pragmas.debug);
  218. if (!defined(x)) {
  219. throw new DeveloperError("x is required.");
  220. }
  221. if (!defined(y)) {
  222. throw new DeveloperError("y is required.");
  223. }
  224. if (!defined(x1)) {
  225. throw new DeveloperError("x1 is required.");
  226. }
  227. if (!defined(y1)) {
  228. throw new DeveloperError("y1 is required.");
  229. }
  230. if (!defined(x2)) {
  231. throw new DeveloperError("x2 is required.");
  232. }
  233. if (!defined(y2)) {
  234. throw new DeveloperError("y2 is required.");
  235. }
  236. if (!defined(x3)) {
  237. throw new DeveloperError("x3 is required.");
  238. }
  239. if (!defined(y3)) {
  240. throw new DeveloperError("y3 is required.");
  241. }
  242. //>>includeEnd('debug');
  243. const x1mx3 = x1 - x3;
  244. const x3mx2 = x3 - x2;
  245. const y2my3 = y2 - y3;
  246. const y1my3 = y1 - y3;
  247. const inverseDeterminant = 1.0 / (y2my3 * x1mx3 + x3mx2 * y1my3);
  248. const ymy3 = y - y3;
  249. const xmx3 = x - x3;
  250. const l1 = (y2my3 * xmx3 + x3mx2 * ymy3) * inverseDeterminant;
  251. const l2 = (-y1my3 * xmx3 + x1mx3 * ymy3) * inverseDeterminant;
  252. const l3 = 1.0 - l1 - l2;
  253. if (defined(result)) {
  254. result.x = l1;
  255. result.y = l2;
  256. result.z = l3;
  257. return result;
  258. }
  259. return new Cartesian3(l1, l2, l3);
  260. };
  261. /**
  262. * Compute the intersection between 2 line segments
  263. *
  264. * @param {Number} x00 The x coordinate of the first line's first vertex.
  265. * @param {Number} y00 The y coordinate of the first line's first vertex.
  266. * @param {Number} x01 The x coordinate of the first line's second vertex.
  267. * @param {Number} y01 The y coordinate of the first line's second vertex.
  268. * @param {Number} x10 The x coordinate of the second line's first vertex.
  269. * @param {Number} y10 The y coordinate of the second line's first vertex.
  270. * @param {Number} x11 The x coordinate of the second line's second vertex.
  271. * @param {Number} y11 The y coordinate of the second line's second vertex.
  272. * @param {Cartesian2} [result] The instance into to which to copy the result. If this parameter
  273. * is undefined, a new instance is created and returned.
  274. * @returns {Cartesian2} The intersection point, undefined if there is no intersection point or lines are coincident.
  275. *
  276. * @example
  277. * const result = Cesium.Intersections2D.computeLineSegmentLineSegmentIntersection(0.0, 0.0, 0.0, 2.0, -1, 1, 1, 1);
  278. * // result === new Cesium.Cartesian2(0.0, 1.0);
  279. */
  280. Intersections2D.computeLineSegmentLineSegmentIntersection = function (
  281. x00,
  282. y00,
  283. x01,
  284. y01,
  285. x10,
  286. y10,
  287. x11,
  288. y11,
  289. result
  290. ) {
  291. //>>includeStart('debug', pragmas.debug);
  292. Check.typeOf.number("x00", x00);
  293. Check.typeOf.number("y00", y00);
  294. Check.typeOf.number("x01", x01);
  295. Check.typeOf.number("y01", y01);
  296. Check.typeOf.number("x10", x10);
  297. Check.typeOf.number("y10", y10);
  298. Check.typeOf.number("x11", x11);
  299. Check.typeOf.number("y11", y11);
  300. //>>includeEnd('debug');
  301. const numerator1A = (x11 - x10) * (y00 - y10) - (y11 - y10) * (x00 - x10);
  302. const numerator1B = (x01 - x00) * (y00 - y10) - (y01 - y00) * (x00 - x10);
  303. const denominator1 = (y11 - y10) * (x01 - x00) - (x11 - x10) * (y01 - y00);
  304. // If denominator = 0, then lines are parallel. If denominator = 0 and both numerators are 0, then coincident
  305. if (denominator1 === 0) {
  306. return;
  307. }
  308. const ua1 = numerator1A / denominator1;
  309. const ub1 = numerator1B / denominator1;
  310. if (ua1 >= 0 && ua1 <= 1 && ub1 >= 0 && ub1 <= 1) {
  311. if (!defined(result)) {
  312. result = new Cartesian2();
  313. }
  314. result.x = x00 + ua1 * (x01 - x00);
  315. result.y = y00 + ua1 * (y01 - y00);
  316. return result;
  317. }
  318. };
  319. export default Intersections2D;