path.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. import PathProxy from '../core/PathProxy.js';
  2. import * as line from './line.js';
  3. import * as cubic from './cubic.js';
  4. import * as quadratic from './quadratic.js';
  5. import * as arc from './arc.js';
  6. import * as curve from '../core/curve.js';
  7. import windingLine from './windingLine.js';
  8. var CMD = PathProxy.CMD;
  9. var PI2 = Math.PI * 2;
  10. var EPSILON = 1e-4;
  11. function isAroundEqual(a, b) {
  12. return Math.abs(a - b) < EPSILON;
  13. }
  14. var roots = [-1, -1, -1];
  15. var extrema = [-1, -1];
  16. function swapExtrema() {
  17. var tmp = extrema[0];
  18. extrema[0] = extrema[1];
  19. extrema[1] = tmp;
  20. }
  21. function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {
  22. if ((y > y0 && y > y1 && y > y2 && y > y3)
  23. || (y < y0 && y < y1 && y < y2 && y < y3)) {
  24. return 0;
  25. }
  26. var nRoots = curve.cubicRootAt(y0, y1, y2, y3, y, roots);
  27. if (nRoots === 0) {
  28. return 0;
  29. }
  30. else {
  31. var w = 0;
  32. var nExtrema = -1;
  33. var y0_ = void 0;
  34. var y1_ = void 0;
  35. for (var i = 0; i < nRoots; i++) {
  36. var t = roots[i];
  37. var unit = (t === 0 || t === 1) ? 0.5 : 1;
  38. var x_ = curve.cubicAt(x0, x1, x2, x3, t);
  39. if (x_ < x) {
  40. continue;
  41. }
  42. if (nExtrema < 0) {
  43. nExtrema = curve.cubicExtrema(y0, y1, y2, y3, extrema);
  44. if (extrema[1] < extrema[0] && nExtrema > 1) {
  45. swapExtrema();
  46. }
  47. y0_ = curve.cubicAt(y0, y1, y2, y3, extrema[0]);
  48. if (nExtrema > 1) {
  49. y1_ = curve.cubicAt(y0, y1, y2, y3, extrema[1]);
  50. }
  51. }
  52. if (nExtrema === 2) {
  53. if (t < extrema[0]) {
  54. w += y0_ < y0 ? unit : -unit;
  55. }
  56. else if (t < extrema[1]) {
  57. w += y1_ < y0_ ? unit : -unit;
  58. }
  59. else {
  60. w += y3 < y1_ ? unit : -unit;
  61. }
  62. }
  63. else {
  64. if (t < extrema[0]) {
  65. w += y0_ < y0 ? unit : -unit;
  66. }
  67. else {
  68. w += y3 < y0_ ? unit : -unit;
  69. }
  70. }
  71. }
  72. return w;
  73. }
  74. }
  75. function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {
  76. if ((y > y0 && y > y1 && y > y2)
  77. || (y < y0 && y < y1 && y < y2)) {
  78. return 0;
  79. }
  80. var nRoots = curve.quadraticRootAt(y0, y1, y2, y, roots);
  81. if (nRoots === 0) {
  82. return 0;
  83. }
  84. else {
  85. var t = curve.quadraticExtremum(y0, y1, y2);
  86. if (t >= 0 && t <= 1) {
  87. var w = 0;
  88. var y_ = curve.quadraticAt(y0, y1, y2, t);
  89. for (var i = 0; i < nRoots; i++) {
  90. var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;
  91. var x_ = curve.quadraticAt(x0, x1, x2, roots[i]);
  92. if (x_ < x) {
  93. continue;
  94. }
  95. if (roots[i] < t) {
  96. w += y_ < y0 ? unit : -unit;
  97. }
  98. else {
  99. w += y2 < y_ ? unit : -unit;
  100. }
  101. }
  102. return w;
  103. }
  104. else {
  105. var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;
  106. var x_ = curve.quadraticAt(x0, x1, x2, roots[0]);
  107. if (x_ < x) {
  108. return 0;
  109. }
  110. return y2 < y0 ? unit : -unit;
  111. }
  112. }
  113. }
  114. function windingArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y) {
  115. y -= cy;
  116. if (y > r || y < -r) {
  117. return 0;
  118. }
  119. var tmp = Math.sqrt(r * r - y * y);
  120. roots[0] = -tmp;
  121. roots[1] = tmp;
  122. var dTheta = Math.abs(startAngle - endAngle);
  123. if (dTheta < 1e-4) {
  124. return 0;
  125. }
  126. if (dTheta >= PI2 - 1e-4) {
  127. startAngle = 0;
  128. endAngle = PI2;
  129. var dir = anticlockwise ? 1 : -1;
  130. if (x >= roots[0] + cx && x <= roots[1] + cx) {
  131. return dir;
  132. }
  133. else {
  134. return 0;
  135. }
  136. }
  137. if (startAngle > endAngle) {
  138. var tmp_1 = startAngle;
  139. startAngle = endAngle;
  140. endAngle = tmp_1;
  141. }
  142. if (startAngle < 0) {
  143. startAngle += PI2;
  144. endAngle += PI2;
  145. }
  146. var w = 0;
  147. for (var i = 0; i < 2; i++) {
  148. var x_ = roots[i];
  149. if (x_ + cx > x) {
  150. var angle = Math.atan2(y, x_);
  151. var dir = anticlockwise ? 1 : -1;
  152. if (angle < 0) {
  153. angle = PI2 + angle;
  154. }
  155. if ((angle >= startAngle && angle <= endAngle)
  156. || (angle + PI2 >= startAngle && angle + PI2 <= endAngle)) {
  157. if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {
  158. dir = -dir;
  159. }
  160. w += dir;
  161. }
  162. }
  163. }
  164. return w;
  165. }
  166. function containPath(path, lineWidth, isStroke, x, y) {
  167. var data = path.data;
  168. var len = path.len();
  169. var w = 0;
  170. var xi = 0;
  171. var yi = 0;
  172. var x0 = 0;
  173. var y0 = 0;
  174. var x1;
  175. var y1;
  176. for (var i = 0; i < len;) {
  177. var cmd = data[i++];
  178. var isFirst = i === 1;
  179. if (cmd === CMD.M && i > 1) {
  180. if (!isStroke) {
  181. w += windingLine(xi, yi, x0, y0, x, y);
  182. }
  183. }
  184. if (isFirst) {
  185. xi = data[i];
  186. yi = data[i + 1];
  187. x0 = xi;
  188. y0 = yi;
  189. }
  190. switch (cmd) {
  191. case CMD.M:
  192. x0 = data[i++];
  193. y0 = data[i++];
  194. xi = x0;
  195. yi = y0;
  196. break;
  197. case CMD.L:
  198. if (isStroke) {
  199. if (line.containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {
  200. return true;
  201. }
  202. }
  203. else {
  204. w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;
  205. }
  206. xi = data[i++];
  207. yi = data[i++];
  208. break;
  209. case CMD.C:
  210. if (isStroke) {
  211. if (cubic.containStroke(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {
  212. return true;
  213. }
  214. }
  215. else {
  216. w += windingCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y) || 0;
  217. }
  218. xi = data[i++];
  219. yi = data[i++];
  220. break;
  221. case CMD.Q:
  222. if (isStroke) {
  223. if (quadratic.containStroke(xi, yi, data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {
  224. return true;
  225. }
  226. }
  227. else {
  228. w += windingQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y) || 0;
  229. }
  230. xi = data[i++];
  231. yi = data[i++];
  232. break;
  233. case CMD.A:
  234. var cx = data[i++];
  235. var cy = data[i++];
  236. var rx = data[i++];
  237. var ry = data[i++];
  238. var theta = data[i++];
  239. var dTheta = data[i++];
  240. i += 1;
  241. var anticlockwise = !!(1 - data[i++]);
  242. x1 = Math.cos(theta) * rx + cx;
  243. y1 = Math.sin(theta) * ry + cy;
  244. if (!isFirst) {
  245. w += windingLine(xi, yi, x1, y1, x, y);
  246. }
  247. else {
  248. x0 = x1;
  249. y0 = y1;
  250. }
  251. var _x = (x - cx) * ry / rx + cx;
  252. if (isStroke) {
  253. if (arc.containStroke(cx, cy, ry, theta, theta + dTheta, anticlockwise, lineWidth, _x, y)) {
  254. return true;
  255. }
  256. }
  257. else {
  258. w += windingArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y);
  259. }
  260. xi = Math.cos(theta + dTheta) * rx + cx;
  261. yi = Math.sin(theta + dTheta) * ry + cy;
  262. break;
  263. case CMD.R:
  264. x0 = xi = data[i++];
  265. y0 = yi = data[i++];
  266. var width = data[i++];
  267. var height = data[i++];
  268. x1 = x0 + width;
  269. y1 = y0 + height;
  270. if (isStroke) {
  271. if (line.containStroke(x0, y0, x1, y0, lineWidth, x, y)
  272. || line.containStroke(x1, y0, x1, y1, lineWidth, x, y)
  273. || line.containStroke(x1, y1, x0, y1, lineWidth, x, y)
  274. || line.containStroke(x0, y1, x0, y0, lineWidth, x, y)) {
  275. return true;
  276. }
  277. }
  278. else {
  279. w += windingLine(x1, y0, x1, y1, x, y);
  280. w += windingLine(x0, y1, x0, y0, x, y);
  281. }
  282. break;
  283. case CMD.Z:
  284. if (isStroke) {
  285. if (line.containStroke(xi, yi, x0, y0, lineWidth, x, y)) {
  286. return true;
  287. }
  288. }
  289. else {
  290. w += windingLine(xi, yi, x0, y0, x, y);
  291. }
  292. xi = x0;
  293. yi = y0;
  294. break;
  295. }
  296. }
  297. if (!isStroke && !isAroundEqual(yi, y0)) {
  298. w += windingLine(xi, yi, x0, y0, x, y) || 0;
  299. }
  300. return w !== 0;
  301. }
  302. export function contain(pathProxy, x, y) {
  303. return containPath(pathProxy, 0, false, x, y);
  304. }
  305. export function containStroke(pathProxy, lineWidth, x, y) {
  306. return containPath(pathProxy, lineWidth, true, x, y);
  307. }