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 | }
|