1 | import { fromPoints } from '../core/bbox.js';
|
2 | import BoundingRect from '../core/BoundingRect.js';
|
3 | import Point from '../core/Point.js';
|
4 | import { map } from '../core/util.js';
|
5 | import Polygon from '../graphic/shape/Polygon.js';
|
6 | import Rect from '../graphic/shape/Rect.js';
|
7 | import Sector from '../graphic/shape/Sector.js';
|
8 | import { pathToPolygons } from './convertPath.js';
|
9 | import { clonePath } from './path.js';
|
10 | function getDividingGrids(dimSize, rowDim, count) {
|
11 | var rowSize = dimSize[rowDim];
|
12 | var columnSize = dimSize[1 - rowDim];
|
13 | var ratio = Math.abs(rowSize / columnSize);
|
14 | var rowCount = Math.ceil(Math.sqrt(ratio * count));
|
15 | var columnCount = Math.floor(count / rowCount);
|
16 | if (columnCount === 0) {
|
17 | columnCount = 1;
|
18 | rowCount = count;
|
19 | }
|
20 | var grids = [];
|
21 | for (var i = 0; i < rowCount; i++) {
|
22 | grids.push(columnCount);
|
23 | }
|
24 | var currentCount = rowCount * columnCount;
|
25 | var remained = count - currentCount;
|
26 | if (remained > 0) {
|
27 | for (var i = 0; i < remained; i++) {
|
28 | grids[i % rowCount] += 1;
|
29 | }
|
30 | }
|
31 | return grids;
|
32 | }
|
33 | function divideSector(sectorShape, count, outShapes) {
|
34 | var r0 = sectorShape.r0;
|
35 | var r = sectorShape.r;
|
36 | var startAngle = sectorShape.startAngle;
|
37 | var endAngle = sectorShape.endAngle;
|
38 | var angle = Math.abs(endAngle - startAngle);
|
39 | var arcLen = angle * r;
|
40 | var deltaR = r - r0;
|
41 | var isAngleRow = arcLen > Math.abs(deltaR);
|
42 | var grids = getDividingGrids([arcLen, deltaR], isAngleRow ? 0 : 1, count);
|
43 | var rowSize = (isAngleRow ? angle : deltaR) / grids.length;
|
44 | for (var row = 0; row < grids.length; row++) {
|
45 | var columnSize = (isAngleRow ? deltaR : angle) / grids[row];
|
46 | for (var column = 0; column < grids[row]; column++) {
|
47 | var newShape = {};
|
48 | if (isAngleRow) {
|
49 | newShape.startAngle = startAngle + rowSize * row;
|
50 | newShape.endAngle = startAngle + rowSize * (row + 1);
|
51 | newShape.r0 = r0 + columnSize * column;
|
52 | newShape.r = r0 + columnSize * (column + 1);
|
53 | }
|
54 | else {
|
55 | newShape.startAngle = startAngle + columnSize * column;
|
56 | newShape.endAngle = startAngle + columnSize * (column + 1);
|
57 | newShape.r0 = r0 + rowSize * row;
|
58 | newShape.r = r0 + rowSize * (row + 1);
|
59 | }
|
60 | newShape.clockwise = sectorShape.clockwise;
|
61 | newShape.cx = sectorShape.cx;
|
62 | newShape.cy = sectorShape.cy;
|
63 | outShapes.push(newShape);
|
64 | }
|
65 | }
|
66 | }
|
67 | function divideRect(rectShape, count, outShapes) {
|
68 | var width = rectShape.width;
|
69 | var height = rectShape.height;
|
70 | var isHorizontalRow = width > height;
|
71 | var grids = getDividingGrids([width, height], isHorizontalRow ? 0 : 1, count);
|
72 | var rowSizeDim = isHorizontalRow ? 'width' : 'height';
|
73 | var columnSizeDim = isHorizontalRow ? 'height' : 'width';
|
74 | var rowDim = isHorizontalRow ? 'x' : 'y';
|
75 | var columnDim = isHorizontalRow ? 'y' : 'x';
|
76 | var rowSize = rectShape[rowSizeDim] / grids.length;
|
77 | for (var row = 0; row < grids.length; row++) {
|
78 | var columnSize = rectShape[columnSizeDim] / grids[row];
|
79 | for (var column = 0; column < grids[row]; column++) {
|
80 | var newShape = {};
|
81 | newShape[rowDim] = row * rowSize;
|
82 | newShape[columnDim] = column * columnSize;
|
83 | newShape[rowSizeDim] = rowSize;
|
84 | newShape[columnSizeDim] = columnSize;
|
85 | newShape.x += rectShape.x;
|
86 | newShape.y += rectShape.y;
|
87 | outShapes.push(newShape);
|
88 | }
|
89 | }
|
90 | }
|
91 | function crossProduct2d(x1, y1, x2, y2) {
|
92 | return x1 * y2 - x2 * y1;
|
93 | }
|
94 | function lineLineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) {
|
95 | var mx = a2x - a1x;
|
96 | var my = a2y - a1y;
|
97 | var nx = b2x - b1x;
|
98 | var ny = b2y - b1y;
|
99 | var nmCrossProduct = crossProduct2d(nx, ny, mx, my);
|
100 | if (Math.abs(nmCrossProduct) < 1e-6) {
|
101 | return null;
|
102 | }
|
103 | var b1a1x = a1x - b1x;
|
104 | var b1a1y = a1y - b1y;
|
105 | var p = crossProduct2d(b1a1x, b1a1y, nx, ny) / nmCrossProduct;
|
106 | if (p < 0 || p > 1) {
|
107 | return null;
|
108 | }
|
109 | return new Point(p * mx + a1x, p * my + a1y);
|
110 | }
|
111 | function projPtOnLine(pt, lineA, lineB) {
|
112 | var dir = new Point();
|
113 | Point.sub(dir, lineB, lineA);
|
114 | dir.normalize();
|
115 | var dir2 = new Point();
|
116 | Point.sub(dir2, pt, lineA);
|
117 | var len = dir2.dot(dir);
|
118 | return len;
|
119 | }
|
120 | function addToPoly(poly, pt) {
|
121 | var last = poly[poly.length - 1];
|
122 | if (last && last[0] === pt[0] && last[1] === pt[1]) {
|
123 | return;
|
124 | }
|
125 | poly.push(pt);
|
126 | }
|
127 | function splitPolygonByLine(points, lineA, lineB) {
|
128 | var len = points.length;
|
129 | var intersections = [];
|
130 | for (var i = 0; i < len; i++) {
|
131 | var p0 = points[i];
|
132 | var p1 = points[(i + 1) % len];
|
133 | var intersectionPt = lineLineIntersect(p0[0], p0[1], p1[0], p1[1], lineA.x, lineA.y, lineB.x, lineB.y);
|
134 | if (intersectionPt) {
|
135 | intersections.push({
|
136 | projPt: projPtOnLine(intersectionPt, lineA, lineB),
|
137 | pt: intersectionPt,
|
138 | idx: i
|
139 | });
|
140 | }
|
141 | }
|
142 | if (intersections.length < 2) {
|
143 | return [{ points: points }, { points: points }];
|
144 | }
|
145 | intersections.sort(function (a, b) {
|
146 | return a.projPt - b.projPt;
|
147 | });
|
148 | var splitPt0 = intersections[0];
|
149 | var splitPt1 = intersections[intersections.length - 1];
|
150 | if (splitPt1.idx < splitPt0.idx) {
|
151 | var tmp = splitPt0;
|
152 | splitPt0 = splitPt1;
|
153 | splitPt1 = tmp;
|
154 | }
|
155 | var splitPt0Arr = [splitPt0.pt.x, splitPt0.pt.y];
|
156 | var splitPt1Arr = [splitPt1.pt.x, splitPt1.pt.y];
|
157 | var newPolyA = [splitPt0Arr];
|
158 | var newPolyB = [splitPt1Arr];
|
159 | for (var i = splitPt0.idx + 1; i <= splitPt1.idx; i++) {
|
160 | addToPoly(newPolyA, points[i].slice());
|
161 | }
|
162 | addToPoly(newPolyA, splitPt1Arr);
|
163 | addToPoly(newPolyA, splitPt0Arr);
|
164 | for (var i = splitPt1.idx + 1; i <= splitPt0.idx + len; i++) {
|
165 | addToPoly(newPolyB, points[i % len].slice());
|
166 | }
|
167 | addToPoly(newPolyB, splitPt0Arr);
|
168 | addToPoly(newPolyB, splitPt1Arr);
|
169 | return [{
|
170 | points: newPolyA
|
171 | }, {
|
172 | points: newPolyB
|
173 | }];
|
174 | }
|
175 | function binaryDividePolygon(polygonShape) {
|
176 | var points = polygonShape.points;
|
177 | var min = [];
|
178 | var max = [];
|
179 | fromPoints(points, min, max);
|
180 | var boundingRect = new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);
|
181 | var width = boundingRect.width;
|
182 | var height = boundingRect.height;
|
183 | var x = boundingRect.x;
|
184 | var y = boundingRect.y;
|
185 | var pt0 = new Point();
|
186 | var pt1 = new Point();
|
187 | if (width > height) {
|
188 | pt0.x = pt1.x = x + width / 2;
|
189 | pt0.y = y;
|
190 | pt1.y = y + height;
|
191 | }
|
192 | else {
|
193 | pt0.y = pt1.y = y + height / 2;
|
194 | pt0.x = x;
|
195 | pt1.x = x + width;
|
196 | }
|
197 | return splitPolygonByLine(points, pt0, pt1);
|
198 | }
|
199 | function binaryDivideRecursive(divider, shape, count, out) {
|
200 | if (count === 1) {
|
201 | out.push(shape);
|
202 | }
|
203 | else {
|
204 | var mid = Math.floor(count / 2);
|
205 | var sub = divider(shape);
|
206 | binaryDivideRecursive(divider, sub[0], mid, out);
|
207 | binaryDivideRecursive(divider, sub[1], count - mid, out);
|
208 | }
|
209 | return out;
|
210 | }
|
211 | export function clone(path, count) {
|
212 | var paths = [];
|
213 | for (var i = 0; i < count; i++) {
|
214 | paths.push(clonePath(path));
|
215 | }
|
216 | return paths;
|
217 | }
|
218 | function copyPathProps(source, target) {
|
219 | target.setStyle(source.style);
|
220 | target.z = source.z;
|
221 | target.z2 = source.z2;
|
222 | target.zlevel = source.zlevel;
|
223 | }
|
224 | function polygonConvert(points) {
|
225 | var out = [];
|
226 | for (var i = 0; i < points.length;) {
|
227 | out.push([points[i++], points[i++]]);
|
228 | }
|
229 | return out;
|
230 | }
|
231 | export function split(path, count) {
|
232 | var outShapes = [];
|
233 | var shape = path.shape;
|
234 | var OutShapeCtor;
|
235 | switch (path.type) {
|
236 | case 'rect':
|
237 | divideRect(shape, count, outShapes);
|
238 | OutShapeCtor = Rect;
|
239 | break;
|
240 | case 'sector':
|
241 | divideSector(shape, count, outShapes);
|
242 | OutShapeCtor = Sector;
|
243 | break;
|
244 | case 'circle':
|
245 | divideSector({
|
246 | r0: 0, r: shape.r, startAngle: 0, endAngle: Math.PI * 2,
|
247 | cx: shape.cx, cy: shape.cy
|
248 | }, count, outShapes);
|
249 | OutShapeCtor = Sector;
|
250 | break;
|
251 | default:
|
252 | var m = path.getComputedTransform();
|
253 | var scale = m ? Math.sqrt(Math.max(m[0] * m[0] + m[1] * m[1], m[2] * m[2] + m[3] * m[3])) : 1;
|
254 | var polygons = map(pathToPolygons(path.getUpdatedPathProxy(), scale), function (poly) { return polygonConvert(poly); });
|
255 | var polygonCount = polygons.length;
|
256 | if (polygonCount === 0) {
|
257 | binaryDivideRecursive(binaryDividePolygon, {
|
258 | points: polygons[0]
|
259 | }, count, outShapes);
|
260 | }
|
261 | else if (polygonCount === count) {
|
262 | for (var i = 0; i < polygonCount; i++) {
|
263 | outShapes.push({
|
264 | points: polygons[i]
|
265 | });
|
266 | }
|
267 | }
|
268 | else {
|
269 | var totalArea_1 = 0;
|
270 | var items = map(polygons, function (poly) {
|
271 | var min = [];
|
272 | var max = [];
|
273 | fromPoints(poly, min, max);
|
274 | var area = (max[1] - min[1]) * (max[0] - min[0]);
|
275 | totalArea_1 += area;
|
276 | return { poly: poly, area: area };
|
277 | });
|
278 | items.sort(function (a, b) { return b.area - a.area; });
|
279 | var left = count;
|
280 | for (var i = 0; i < polygonCount; i++) {
|
281 | var item = items[i];
|
282 | if (left <= 0) {
|
283 | break;
|
284 | }
|
285 | var selfCount = i === polygonCount - 1
|
286 | ? left
|
287 | : Math.ceil(item.area / totalArea_1 * count);
|
288 | if (selfCount < 0) {
|
289 | continue;
|
290 | }
|
291 | binaryDivideRecursive(binaryDividePolygon, {
|
292 | points: item.poly
|
293 | }, selfCount, outShapes);
|
294 | left -= selfCount;
|
295 | }
|
296 | ;
|
297 | }
|
298 | OutShapeCtor = Polygon;
|
299 | break;
|
300 | }
|
301 | if (!OutShapeCtor) {
|
302 | return clone(path, count);
|
303 | }
|
304 | var out = [];
|
305 | for (var i = 0; i < outShapes.length; i++) {
|
306 | var subPath = new OutShapeCtor();
|
307 | subPath.setShape(outShapes[i]);
|
308 | copyPathProps(path, subPath);
|
309 | out.push(subPath);
|
310 | }
|
311 | return out;
|
312 | }
|