dividePath.js 11 KB

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