You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

293 lines
11 KiB

3 years ago
  1. // ******* Region MANAGER ******** //
  2. $axure.internal(function($ax) {
  3. var _geometry = $ax.geometry = {};
  4. var regionMap = {};
  5. var regionList = [];
  6. var _unregister = function(label) {
  7. var regionIndex = regionList.indexOf(label);
  8. if(regionIndex != -1) {
  9. var end = $ax.splice(regionList, regionIndex + 1);
  10. $ax.splice(regionList, regionIndex, regionList.length - regionIndex);
  11. regionList = regionList.concat(end);
  12. }
  13. delete regionMap[label];
  14. };
  15. _geometry.unregister = _unregister;
  16. var clear = function() {
  17. regionMap = {};
  18. regionList = [];
  19. };
  20. var _polygonRegistered = function(label) {
  21. return Boolean(regionMap[label]);
  22. };
  23. _geometry.polygonRegistered = _polygonRegistered;
  24. // Must be counterclockwise, or enter/exit will be wrong
  25. var _registerPolygon = function(label, points, callback, info) {
  26. var regionIndex = regionList.indexOf(label);
  27. if(regionIndex == -1) regionList.push(label);
  28. regionMap[label] = { points: points, callback: callback, info: info };
  29. };
  30. _geometry.registerPolygon = _registerPolygon;
  31. var _getPolygonInfo = function(label) {
  32. if(!_polygonRegistered(label)) return undefined;
  33. return regionMap[label].info;
  34. };
  35. _geometry.getPolygonInfo = _getPolygonInfo;
  36. var _genRect = function(info, roundHalfPixel) {
  37. var x = info.pagex();
  38. var y = info.pagey();
  39. var w = info.width();
  40. var h = info.height();
  41. if(roundHalfPixel) {
  42. if(x % 1 != 0) {
  43. x = Math.floor(x);
  44. w++;
  45. }
  46. if(y % 1 != 0) {
  47. y = Math.floor(y);
  48. h++;
  49. }
  50. }
  51. var r = x + w;
  52. var b = y + h;
  53. var rect = {
  54. X: function() { return x; },
  55. Y: function() { return y; },
  56. Wigth: function() { return w; },
  57. Height: function() { return h; },
  58. Left: function() { return x; },
  59. Right: function() { return r; },
  60. Top: function() { return y; },
  61. Bottom: function() { return b; }
  62. };
  63. return rect;
  64. };
  65. _geometry.genRect = _genRect;
  66. var _genPoint = function(x, y) {
  67. return { x: x, y: y };
  68. };
  69. _geometry.genPoint = _genPoint;
  70. var oldPoint = _genPoint(0, 0);
  71. _geometry.tick = function(x, y, end) {
  72. var lastPoint = oldPoint;
  73. var nextPoint = oldPoint = _genPoint(x, y);
  74. var line = { p1: lastPoint, p2: nextPoint };
  75. if(!regionList.length) return;
  76. for(var i = 0; i < regionList.length; i++) {
  77. var region = regionMap[regionList[i]];
  78. var points = region.points;
  79. for(var j = 0; j < points.length; j++) {
  80. var startSegment = points[j];
  81. var endSegment = points[(j + 1) % points.length];
  82. var intersectInfo = linesIntersect(line, { p1: startSegment, p2: endSegment });
  83. if(intersectInfo) {
  84. region.callback(intersectInfo);
  85. break;
  86. }
  87. }
  88. }
  89. if(end) clear();
  90. };
  91. // Info if the one line touches the other (even barely), false otherwise
  92. // Info includes point, if l1 is entering or exiting l2, and any ties that happened, or parallel info
  93. var linesIntersect = function(l1, l2) {
  94. var retval = {};
  95. var ties = {};
  96. var l1p1 = l1.p1.x < l1.p2.x || (l1.p1.x == l1.p2.x && l1.p1.y < l1.p2.y) ? l1.p1 : l1.p2;
  97. var l1p2 = l1.p1.x < l1.p2.x || (l1.p1.x == l1.p2.x && l1.p1.y < l1.p2.y) ? l1.p2 : l1.p1;
  98. var m1 = (l1p2.y - l1p1.y) / (l1p2.x - l1p1.x);
  99. var l2p1 = l2.p1.x < l2.p2.x || (l2.p1.x == l2.p2.x && l2.p1.y < l2.p2.y) ? l2.p1 : l2.p2;
  100. var l2p2 = l2.p1.x < l2.p2.x || (l2.p1.x == l2.p2.x && l2.p1.y < l2.p2.y) ? l2.p2 : l2.p1;
  101. var m2 = (l2p2.y - l2p1.y) / (l2p2.x - l2p1.x);
  102. var l1Vert = l1.p1.x == l1.p2.x;
  103. var l2Vert = l2.p1.x == l2.p2.x;
  104. if(l1Vert || l2Vert) {
  105. if(l1Vert && l2Vert) {
  106. // If the lines don't follow the same path, return
  107. if(l1p1.x != l2p1.x) return false;
  108. // if they never meet, return
  109. if(l1p2.y < l2p1.y || l1p1.y > l2p2.y) return false;
  110. var firstVert = l1p1.y >= l2p1.y ? l1p1 : l2p1;
  111. var secondVert = l1p2.y <= l2p2.y ? l1p2 : l2p2;
  112. // First is from the perspective of l1
  113. retval.parallel = {
  114. first: l1p1 == l1.p1 ? firstVert : secondVert,
  115. second: l1p2 == l1.p2 ? secondVert : firstVert,
  116. sameDirection: (l1p1 == l1.p1) == (l2p1 == l2.p1)
  117. };
  118. return retval;
  119. }
  120. var x1 = l2Vert ? l1p1.x : l2p1.x;
  121. var x2 = l2Vert ? l1p2.x : l2p2.x;
  122. var xVert = l2Vert ? l2p1.x : l1p1.x;
  123. var y = l2Vert ? l1p1.y + (xVert - x1) * m1 : l2p1.y + (xVert - x1) * m2;
  124. var y1 = l2Vert ? l2p1.y : l1p1.y;
  125. var y2 = l2Vert ? l2p2.y : l1p2.y;
  126. if(xVert >= x1 && xVert <= x2 && y >= y1 && y <= y2) {
  127. retval.point = { x: xVert, y: y };
  128. retval.exiting = l2Vert == (y1 == (l2Vert ? l2.p1.y : l1.p1.y)) == (x1 == (l2Vert ? l1.p1.x : l2.p1.x));
  129. retval.entering = !retval.exiting;
  130. // Calculate ties
  131. if(x1 == xVert) {
  132. ties[l2Vert ? 'l1' : 'l2'] = (x1 == (l2Vert ? l1.p1.x : l2.p1.x)) ? 'start' : 'end';
  133. retval.ties = ties;
  134. } else if(x2 == xVert) {
  135. ties[l2Vert ? 'l1' : 'l2'] = (x2 == (l2Vert ? l1.p2.x : l2.p2.x)) ? 'end' : 'start';
  136. retval.ties = ties;
  137. }
  138. if(y1 == y) {
  139. ties[l2Vert ? 'l2' : 'l1'] = (y1 == (l2Vert ? l2.p1.y : l1.p1.y)) ? 'start' : 'end';
  140. retval.ties = ties;
  141. } else if(y2 == y) {
  142. ties[l2Vert ? 'l2' : 'l1'] = (y2 == (l2Vert ? l2.p2.y : l1.p2.y)) ? 'end' : 'start';
  143. retval.ties = ties;
  144. }
  145. return retval;
  146. }
  147. return false;
  148. }
  149. // If here, no vertical lines
  150. if(m1 == m2) {
  151. // If the lines don't follow the same path, return
  152. if(l1p1.y != (l2p1.y + (l1p1.x - l2p1.x) * m1)) return false;
  153. // if they never meet, return
  154. if(l1p2.x < l2p1.x || l1p1.x > l2p2.x) return false;
  155. var first = l1p1.x >= l2p1.x ? l1p1 : l2p1;
  156. var second = l1p2.x <= l2p2.x ? l1p2 : l2p2;
  157. // First is from the perspective of l1
  158. retval.parallel = {
  159. first: l1p1 == l1.p1 ? first : second,
  160. second: l1p2 == l1.p2 ? second : first,
  161. sameDirection: (l1p1 == l1.p1) == (l2p1 == l2.p1)
  162. };
  163. return retval;
  164. }
  165. var x = (l2p1.y - l2p1.x * m2 - l1p1.y + l1p1.x * m1) / (m1 - m2);
  166. // Check if x is out of bounds
  167. if(x >= l1p1.x && x <= l1p2.x && x >= l2p1.x && x <= l2p2.x) {
  168. var y = l1p1.y + (x - l1p1.x) * m1;
  169. retval.point = { x: x, y: y };
  170. retval.entering = m1 > m2 == (l1p1 == l1.p1) == (l2p1 == l2.p1);
  171. retval.exiting = !retval.entering;
  172. // Calculate ties
  173. if(l1.p1.x == x) {
  174. ties.l1 = 'start';
  175. retval.ties = ties;
  176. } else if(l1.p2.x == x) {
  177. ties.l1 = 'end';
  178. retval.ties = ties;
  179. }
  180. if(l2.p1.x == x) {
  181. ties.l2 = 'start';
  182. retval.ties = ties;
  183. } else if(l2.p2.x == x) {
  184. ties.l2 = 'end';
  185. retval.ties = ties;
  186. }
  187. return retval;
  188. }
  189. return false;
  190. };
  191. var _checkInsideRegion = function(label, point) {
  192. if(!_polygonRegistered(label)) return false;
  193. return _checkInside(regionMap[label].points, point || $ax.mouseLocation);
  194. };
  195. _geometry.checkInsideRegion = _checkInsideRegion;
  196. // Returns true if point is inside the polygon, including ties
  197. var _checkInside = function(polygon, point) {
  198. // Make horizontal line wider than the polygon, with the y of point to test location
  199. var firstX = polygon[0].x;
  200. var secondX = firstX;
  201. var i;
  202. for(i = 1; i < polygon.length; i++) {
  203. var polyX = polygon[i].x;
  204. firstX = Math.min(firstX, polyX);
  205. secondX = Math.max(secondX, polyX);
  206. }
  207. var line = {
  208. p1: _genPoint(--firstX, point.y),
  209. p2: _genPoint(++secondX, point.y)
  210. };
  211. // If entered true, with closest intersection says you are inside the polygon.
  212. var entered = false;
  213. // Closest is the closest intersection to the left of the point
  214. var closest = line.p1.x;
  215. // This is for if intersections hit the same point, to find out which is correct
  216. var cos = -2;
  217. var getCos = function(line) {
  218. var x = line.p2.x - line.p1.x;
  219. var y = line.p2.y - line.p1.y;
  220. return x / Math.sqrt(x * x + y * y);
  221. };
  222. for(i = 0; i < polygon.length; i++) {
  223. var polyLine = { p1: polygon[i], p2: polygon[(i + 1) % polygon.length] };
  224. var intersectInfo = linesIntersect(line, polyLine);
  225. if(!intersectInfo) continue;
  226. if(intersectInfo.parallel) {
  227. // Only really care about this if it actually touches the point
  228. if(intersectInfo.parallel.first.x <= point.x && intersectInfo.parallel.second.x >= point.x) return true;
  229. continue;
  230. }
  231. var intersectionX = intersectInfo.point.x;
  232. if(intersectionX > point.x || intersectionX < closest) continue;
  233. if(intersectionX == point.x) return true;
  234. // If closer than last time, reset cosine.
  235. if(intersectionX != closest) cos = -2;
  236. // For getting cosine, need to possibly reverse the direction of polyLine.
  237. if(intersectInfo.ties) {
  238. // Tie must be on l2, if the ties is end, reverse so cosine indicates how close the angle is to that of 'point' from here.
  239. if(intersectInfo.ties.l2 == 'end') polyLine = { p1: polyLine.p2, p2: polyLine.p1 };
  240. } else {
  241. // It is on both side, so you can take the larger one
  242. if(polyLine.p1.x > polyLine.p2.x) polyLine = { p1: polyLine.p2, p2: polyLine.p1 };
  243. }
  244. var currCos = getCos(polyLine);
  245. if(currCos > cos) {
  246. cos = currCos;
  247. closest = intersectionX;
  248. entered = intersectInfo.entering;
  249. }
  250. }
  251. return entered;
  252. };
  253. _geometry.checkInside = _checkInside;
  254. });