convertPath.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import { cubicSubdivide } from '../core/curve.js';
  2. import PathProxy from '../core/PathProxy.js';
  3. var CMD = PathProxy.CMD;
  4. function aroundEqual(a, b) {
  5. return Math.abs(a - b) < 1e-5;
  6. }
  7. export function pathToBezierCurves(path) {
  8. var data = path.data;
  9. var len = path.len();
  10. var bezierArrayGroups = [];
  11. var currentSubpath;
  12. var xi = 0;
  13. var yi = 0;
  14. var x0 = 0;
  15. var y0 = 0;
  16. function createNewSubpath(x, y) {
  17. if (currentSubpath && currentSubpath.length > 2) {
  18. bezierArrayGroups.push(currentSubpath);
  19. }
  20. currentSubpath = [x, y];
  21. }
  22. function addLine(x0, y0, x1, y1) {
  23. if (!(aroundEqual(x0, x1) && aroundEqual(y0, y1))) {
  24. currentSubpath.push(x0, y0, x1, y1, x1, y1);
  25. }
  26. }
  27. function addArc(startAngle, endAngle, cx, cy, rx, ry) {
  28. var delta = Math.abs(endAngle - startAngle);
  29. var len = Math.tan(delta / 4) * 4 / 3;
  30. var dir = endAngle < startAngle ? -1 : 1;
  31. var c1 = Math.cos(startAngle);
  32. var s1 = Math.sin(startAngle);
  33. var c2 = Math.cos(endAngle);
  34. var s2 = Math.sin(endAngle);
  35. var x1 = c1 * rx + cx;
  36. var y1 = s1 * ry + cy;
  37. var x4 = c2 * rx + cx;
  38. var y4 = s2 * ry + cy;
  39. var hx = rx * len * dir;
  40. var hy = ry * len * dir;
  41. currentSubpath.push(x1 - hx * s1, y1 + hy * c1, x4 + hx * s2, y4 - hy * c2, x4, y4);
  42. }
  43. var x1;
  44. var y1;
  45. var x2;
  46. var y2;
  47. for (var i = 0; i < len;) {
  48. var cmd = data[i++];
  49. var isFirst = i === 1;
  50. if (isFirst) {
  51. xi = data[i];
  52. yi = data[i + 1];
  53. x0 = xi;
  54. y0 = yi;
  55. if (cmd === CMD.L || cmd === CMD.C || cmd === CMD.Q) {
  56. currentSubpath = [x0, y0];
  57. }
  58. }
  59. switch (cmd) {
  60. case CMD.M:
  61. xi = x0 = data[i++];
  62. yi = y0 = data[i++];
  63. createNewSubpath(x0, y0);
  64. break;
  65. case CMD.L:
  66. x1 = data[i++];
  67. y1 = data[i++];
  68. addLine(xi, yi, x1, y1);
  69. xi = x1;
  70. yi = y1;
  71. break;
  72. case CMD.C:
  73. currentSubpath.push(data[i++], data[i++], data[i++], data[i++], xi = data[i++], yi = data[i++]);
  74. break;
  75. case CMD.Q:
  76. x1 = data[i++];
  77. y1 = data[i++];
  78. x2 = data[i++];
  79. y2 = data[i++];
  80. currentSubpath.push(xi + 2 / 3 * (x1 - xi), yi + 2 / 3 * (y1 - yi), x2 + 2 / 3 * (x1 - x2), y2 + 2 / 3 * (y1 - y2), x2, y2);
  81. xi = x2;
  82. yi = y2;
  83. break;
  84. case CMD.A:
  85. var cx = data[i++];
  86. var cy = data[i++];
  87. var rx = data[i++];
  88. var ry = data[i++];
  89. var startAngle = data[i++];
  90. var endAngle = data[i++] + startAngle;
  91. i += 1;
  92. var anticlockwise = !data[i++];
  93. x1 = Math.cos(startAngle) * rx + cx;
  94. y1 = Math.sin(startAngle) * ry + cy;
  95. if (isFirst) {
  96. x0 = x1;
  97. y0 = y1;
  98. createNewSubpath(x0, y0);
  99. }
  100. else {
  101. addLine(xi, yi, x1, y1);
  102. }
  103. xi = Math.cos(endAngle) * rx + cx;
  104. yi = Math.sin(endAngle) * ry + cy;
  105. var step = (anticlockwise ? -1 : 1) * Math.PI / 2;
  106. for (var angle = startAngle; anticlockwise ? angle > endAngle : angle < endAngle; angle += step) {
  107. var nextAngle = anticlockwise ? Math.max(angle + step, endAngle)
  108. : Math.min(angle + step, endAngle);
  109. addArc(angle, nextAngle, cx, cy, rx, ry);
  110. }
  111. break;
  112. case CMD.R:
  113. x0 = xi = data[i++];
  114. y0 = yi = data[i++];
  115. x1 = x0 + data[i++];
  116. y1 = y0 + data[i++];
  117. createNewSubpath(x1, y0);
  118. addLine(x1, y0, x1, y1);
  119. addLine(x1, y1, x0, y1);
  120. addLine(x0, y1, x0, y0);
  121. addLine(x0, y0, x1, y0);
  122. break;
  123. case CMD.Z:
  124. currentSubpath && addLine(xi, yi, x0, y0);
  125. xi = x0;
  126. yi = y0;
  127. break;
  128. }
  129. }
  130. if (currentSubpath && currentSubpath.length > 2) {
  131. bezierArrayGroups.push(currentSubpath);
  132. }
  133. return bezierArrayGroups;
  134. }
  135. function adpativeBezier(x0, y0, x1, y1, x2, y2, x3, y3, out, scale) {
  136. if (aroundEqual(x0, x1) && aroundEqual(y0, y1) && aroundEqual(x2, x3) && aroundEqual(y2, y3)) {
  137. out.push(x3, y3);
  138. return;
  139. }
  140. var PIXEL_DISTANCE = 2 / scale;
  141. var PIXEL_DISTANCE_SQR = PIXEL_DISTANCE * PIXEL_DISTANCE;
  142. var dx = x3 - x0;
  143. var dy = y3 - y0;
  144. var d = Math.sqrt(dx * dx + dy * dy);
  145. dx /= d;
  146. dy /= d;
  147. var dx1 = x1 - x0;
  148. var dy1 = y1 - y0;
  149. var dx2 = x2 - x3;
  150. var dy2 = y2 - y3;
  151. var cp1LenSqr = dx1 * dx1 + dy1 * dy1;
  152. var cp2LenSqr = dx2 * dx2 + dy2 * dy2;
  153. if (cp1LenSqr < PIXEL_DISTANCE_SQR && cp2LenSqr < PIXEL_DISTANCE_SQR) {
  154. out.push(x3, y3);
  155. return;
  156. }
  157. var projLen1 = dx * dx1 + dy * dy1;
  158. var projLen2 = -dx * dx2 - dy * dy2;
  159. var d1Sqr = cp1LenSqr - projLen1 * projLen1;
  160. var d2Sqr = cp2LenSqr - projLen2 * projLen2;
  161. if (d1Sqr < PIXEL_DISTANCE_SQR && projLen1 >= 0
  162. && d2Sqr < PIXEL_DISTANCE_SQR && projLen2 >= 0) {
  163. out.push(x3, y3);
  164. return;
  165. }
  166. var tmpSegX = [];
  167. var tmpSegY = [];
  168. cubicSubdivide(x0, x1, x2, x3, 0.5, tmpSegX);
  169. cubicSubdivide(y0, y1, y2, y3, 0.5, tmpSegY);
  170. adpativeBezier(tmpSegX[0], tmpSegY[0], tmpSegX[1], tmpSegY[1], tmpSegX[2], tmpSegY[2], tmpSegX[3], tmpSegY[3], out, scale);
  171. adpativeBezier(tmpSegX[4], tmpSegY[4], tmpSegX[5], tmpSegY[5], tmpSegX[6], tmpSegY[6], tmpSegX[7], tmpSegY[7], out, scale);
  172. }
  173. export function pathToPolygons(path, scale) {
  174. var bezierArrayGroups = pathToBezierCurves(path);
  175. var polygons = [];
  176. scale = scale || 1;
  177. for (var i = 0; i < bezierArrayGroups.length; i++) {
  178. var beziers = bezierArrayGroups[i];
  179. var polygon = [];
  180. var x0 = beziers[0];
  181. var y0 = beziers[1];
  182. polygon.push(x0, y0);
  183. for (var k = 2; k < beziers.length;) {
  184. var x1 = beziers[k++];
  185. var y1 = beziers[k++];
  186. var x2 = beziers[k++];
  187. var y2 = beziers[k++];
  188. var x3 = beziers[k++];
  189. var y3 = beziers[k++];
  190. adpativeBezier(x0, y0, x1, y1, x2, y2, x3, y3, polygon, scale);
  191. x0 = x3;
  192. y0 = y3;
  193. }
  194. polygons.push(polygon);
  195. }
  196. return polygons;
  197. }