svg-path-simplify 0.2.4 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/svg-path-simplify.esm.js +465 -390
- package/dist/svg-path-simplify.esm.min.js +6 -6
- package/dist/svg-path-simplify.js +465 -390
- package/dist/svg-path-simplify.min.js +6 -6
- package/dist/svg-path-simplify.min.js.gz +0 -0
- package/dist/svg-path-simplify.poly.cjs +10 -1
- package/dist/svg-path-simplify.worker.js +62 -0
- package/dist/svg-path-simplify.worker.polyfills.js +32 -0
- package/index.html +90 -17
- package/package.json +3 -2
- package/src/index-poly.js +9 -1
- package/src/index-worker.js +17 -0
- package/src/index.js +4 -1
- package/src/pathData_simplify_revertToquadratics.js +2 -2
- package/src/pathSimplify-main.js +53 -71
- package/src/svgii/geometry.js +48 -4
- package/src/svgii/pathData_analyze.js +8 -6
- package/src/svgii/pathData_convert.js +1 -1
- package/src/svgii/pathData_fix_directions.js +83 -0
- package/src/svgii/pathData_line_to_cubic.js +21 -0
- package/src/svgii/pathData_parse_els.js +7 -2
- package/src/svgii/pathData_remove_zerolength.js +0 -1
- package/src/svgii/pathData_reverse.js +0 -1
- package/src/svgii/pathData_toPolygon.js +61 -12
- package/src/svgii/rounding.js +2 -0
- package/src/svgii/svg-styles-to-attributes-const.js +2 -0
- package/src/svgii/svg_cleanup.js +92 -15
- package/tests/testSVG.js +1 -0
- package/tests/testSVG2.js +55 -0
|
@@ -159,7 +159,7 @@ function getDeltaAngle(centerPoint, startPoint, endPoint, largeArc = false) {
|
|
|
159
159
|
* http://jsfiddle.net/justin_c_rounds/Gd2S2/light/
|
|
160
160
|
*/
|
|
161
161
|
|
|
162
|
-
function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact = true, debug = false) {
|
|
162
|
+
function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact = true, respectDirection = false, debug = false) {
|
|
163
163
|
// if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
|
|
164
164
|
let denominator, a, b, numerator1, numerator2;
|
|
165
165
|
let intersectionPoint = {};
|
|
@@ -171,7 +171,9 @@ function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact
|
|
|
171
171
|
|
|
172
172
|
try {
|
|
173
173
|
denominator = ((p4.y - p3.y) * (p2.x - p1.x)) - ((p4.x - p3.x) * (p2.y - p1.y));
|
|
174
|
-
|
|
174
|
+
|
|
175
|
+
// parallel or colinear
|
|
176
|
+
if (denominator === 0) {
|
|
175
177
|
return false;
|
|
176
178
|
}
|
|
177
179
|
} catch {
|
|
@@ -200,8 +202,13 @@ function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact
|
|
|
200
202
|
|
|
201
203
|
}
|
|
202
204
|
|
|
203
|
-
|
|
205
|
+
// direction
|
|
206
|
+
if (!exact && respectDirection && ((a > 0 && b < 0) || (a < 0 && b > 0))) {
|
|
207
|
+
intersection = false;
|
|
208
|
+
return false
|
|
209
|
+
}
|
|
204
210
|
|
|
211
|
+
if (exact && !intersection) {
|
|
205
212
|
return false;
|
|
206
213
|
}
|
|
207
214
|
|
|
@@ -210,6 +217,44 @@ function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact
|
|
|
210
217
|
return intersectionPoint;
|
|
211
218
|
}
|
|
212
219
|
|
|
220
|
+
/** Get relationship between a point and a polygon using ray-casting algorithm
|
|
221
|
+
* based on timepp's answer
|
|
222
|
+
* https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon#63436180
|
|
223
|
+
*/
|
|
224
|
+
function isPointInPolygon(pt, polygon, bb, skipBB = false) {
|
|
225
|
+
const between = (p, a, b) => (p >= a && p <= b) || (p <= a && p >= b);
|
|
226
|
+
let inside = false;
|
|
227
|
+
|
|
228
|
+
// not in bbox - quit || no bbox defined
|
|
229
|
+
if (!skipBB || !bb.bottom) {
|
|
230
|
+
if (bb.left > pt.x || bb.top > pt.y || bb.bottom < pt.y || bb.right < pt.x) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let l=polygon.length;
|
|
236
|
+
for (let i = l - 1, j = 0; j < l; i = j, j++) {
|
|
237
|
+
const A = polygon[i];
|
|
238
|
+
const B = polygon[j];
|
|
239
|
+
// corner cases
|
|
240
|
+
if ((pt.x == A.x && pt.y == A.y) || (pt.x == B.x && pt.y == B.y))
|
|
241
|
+
return true;
|
|
242
|
+
if (A.y == B.y && pt.y == A.y && between(pt.x, A.x, B.x)) return true;
|
|
243
|
+
if (between(pt.y, A.y, B.y)) {
|
|
244
|
+
/**
|
|
245
|
+
* if pt inside the vertical range filter out "ray pass vertex" problem
|
|
246
|
+
* by treating the line a little lower
|
|
247
|
+
*/
|
|
248
|
+
if ((pt.y == A.y && B.y >= A.y) || (pt.y == B.y && A.y >= B.y)) continue;
|
|
249
|
+
// calc cross product `ptA X ptB`, pt lays on left side of AB if c > 0
|
|
250
|
+
const c = (A.x - pt.x) * (B.y - pt.y) - (B.x - pt.x) * (A.y - pt.y);
|
|
251
|
+
if (c == 0) return true;
|
|
252
|
+
if (A.y < B.y == c > 0) inside = !inside;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return inside ? true : false;
|
|
256
|
+
}
|
|
257
|
+
|
|
213
258
|
/**
|
|
214
259
|
* Linear interpolation (LERP) helper
|
|
215
260
|
*/
|
|
@@ -2369,6 +2414,7 @@ function detectAccuracy(pathData) {
|
|
|
2369
2414
|
}
|
|
2370
2415
|
|
|
2371
2416
|
let dim_min = dims.sort();
|
|
2417
|
+
|
|
2372
2418
|
let sliceIdx = Math.ceil(dim_min.length / 8);
|
|
2373
2419
|
dim_min = dim_min.slice(0, sliceIdx);
|
|
2374
2420
|
let minVal = dim_min.reduce((a, b) => a + b, 0) / sliceIdx;
|
|
@@ -2472,6 +2518,7 @@ function analyzePathData(pathData = [], {
|
|
|
2472
2518
|
(type === 'C' ? [p0, cp1, cp2, p] : [p0, cp1, p]) :
|
|
2473
2519
|
([p0, p]);
|
|
2474
2520
|
let thresholdLength = dimA * 0.1;
|
|
2521
|
+
let threshold = thresholdLength*0.01;
|
|
2475
2522
|
|
|
2476
2523
|
// bezier types
|
|
2477
2524
|
let isBezier = type === 'Q' || type === 'C';
|
|
@@ -2488,10 +2535,12 @@ function analyzePathData(pathData = [], {
|
|
|
2488
2535
|
let dx = type === 'C' ? Math.abs(com.cp2.x - com.p.x) : Math.abs(com.cp1.x - com.p.x);
|
|
2489
2536
|
let dy = type === 'C' ? Math.abs(com.cp2.y - com.p.y) : Math.abs(com.cp1.y - com.p.y);
|
|
2490
2537
|
|
|
2491
|
-
let horizontal = dy === 0 && dx > 0;
|
|
2492
|
-
let vertical = dx === 0 && dy > 0;
|
|
2538
|
+
let horizontal = (dy === 0 || dy<threshold ) && dx > 0;
|
|
2539
|
+
let vertical = (dx === 0 || dx<threshold ) && dy > 0;
|
|
2493
2540
|
|
|
2494
|
-
if (horizontal || vertical)
|
|
2541
|
+
if (horizontal || vertical) {
|
|
2542
|
+
hasExtremes = true;
|
|
2543
|
+
}
|
|
2495
2544
|
|
|
2496
2545
|
// is extreme relative to bounding box
|
|
2497
2546
|
if ((p.x === left || p.y === top || p.x === right || p.y === bottom)) {
|
|
@@ -2503,7 +2552,7 @@ function analyzePathData(pathData = [], {
|
|
|
2503
2552
|
let couldHaveExtremes = bezierhasExtreme(null, commandPts);
|
|
2504
2553
|
if (couldHaveExtremes) {
|
|
2505
2554
|
let tArr = getTatAngles(commandPts);
|
|
2506
|
-
if (tArr.length && (tArr[0] > 0.
|
|
2555
|
+
if (tArr.length && (tArr[0] > 0.2)) {
|
|
2507
2556
|
hasExtremes = true;
|
|
2508
2557
|
}
|
|
2509
2558
|
}
|
|
@@ -3107,7 +3156,7 @@ function pathDataToShorthands(pathData, decimals = -1, test = false) {
|
|
|
3107
3156
|
let dx1 = (p0.x - cpPrev.x);
|
|
3108
3157
|
let dy1 = (p0.y - cpPrev.y);
|
|
3109
3158
|
|
|
3110
|
-
maxDist = getDistManhattan(cpPrev, cpFirst) * 0.
|
|
3159
|
+
maxDist = getDistManhattan(cpPrev, cpFirst) * 0.025;
|
|
3111
3160
|
|
|
3112
3161
|
// reflected cp
|
|
3113
3162
|
let cpR = { x: cpPrev.x + dx1 * 2, y: cpPrev.y + dy1 * 2 };
|
|
@@ -3895,12 +3944,14 @@ function shapeElToPath(el) {
|
|
|
3895
3944
|
let attributes = [...el.attributes].map(att => att.name);
|
|
3896
3945
|
|
|
3897
3946
|
let pathN = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
3947
|
+
|
|
3898
3948
|
pathN.setAttribute('d', d);
|
|
3899
3949
|
|
|
3900
|
-
let exclude = ['x', 'y', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points'];
|
|
3950
|
+
let exclude = ['x', 'y', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points'];
|
|
3901
3951
|
|
|
3902
3952
|
attributes.forEach(att => {
|
|
3903
3953
|
if (!exclude.includes(att)) {
|
|
3954
|
+
|
|
3904
3955
|
let val = el.getAttribute(att);
|
|
3905
3956
|
pathN.setAttribute(att, val);
|
|
3906
3957
|
}
|
|
@@ -3914,7 +3965,7 @@ function shapeElToPath(el) {
|
|
|
3914
3965
|
function getPathDataFromEl(el, stringify = false) {
|
|
3915
3966
|
|
|
3916
3967
|
let pathData = [];
|
|
3917
|
-
let type = el.nodeName;
|
|
3968
|
+
let type = el.nodeName.toLowerCase();
|
|
3918
3969
|
let atts, attNames, d, x, y, width, height, r, rx, ry, cx, cy, x1, x2, y1, y2;
|
|
3919
3970
|
|
|
3920
3971
|
// convert relative or absolute units
|
|
@@ -4537,6 +4588,125 @@ function addClosePathLineto(pathData) {
|
|
|
4537
4588
|
return pathData;
|
|
4538
4589
|
}
|
|
4539
4590
|
|
|
4591
|
+
/**
|
|
4592
|
+
* reverse pathdata
|
|
4593
|
+
* make sure all command coordinates are absolute and
|
|
4594
|
+
* shorthands are converted to long notation
|
|
4595
|
+
*/
|
|
4596
|
+
function reversePathData(pathData, {
|
|
4597
|
+
arcToCubic = false,
|
|
4598
|
+
quadraticToCubic = false,
|
|
4599
|
+
toClockwise = false,
|
|
4600
|
+
returnD = false
|
|
4601
|
+
} = {}) {
|
|
4602
|
+
|
|
4603
|
+
/**
|
|
4604
|
+
* Add closing lineto:
|
|
4605
|
+
* needed for path reversing or adding points
|
|
4606
|
+
*/
|
|
4607
|
+
const addClosePathLineto = (pathData) => {
|
|
4608
|
+
let closed = pathData[pathData.length - 1].type.toLowerCase() === "z";
|
|
4609
|
+
let M = pathData[0];
|
|
4610
|
+
let [x0, y0] = [M.values[0], M.values[1]];
|
|
4611
|
+
let lastCom = closed ? pathData[pathData.length - 2] : pathData[pathData.length - 1];
|
|
4612
|
+
let [xE, yE] = [lastCom.values[lastCom.values.length - 2], lastCom.values[lastCom.values.length - 1]];
|
|
4613
|
+
|
|
4614
|
+
if (closed && (x0 != xE || y0 != yE)) {
|
|
4615
|
+
|
|
4616
|
+
pathData.pop();
|
|
4617
|
+
pathData.push(
|
|
4618
|
+
{
|
|
4619
|
+
type: "L",
|
|
4620
|
+
values: [x0, y0]
|
|
4621
|
+
},
|
|
4622
|
+
{
|
|
4623
|
+
type: "Z",
|
|
4624
|
+
values: []
|
|
4625
|
+
}
|
|
4626
|
+
);
|
|
4627
|
+
}
|
|
4628
|
+
return pathData;
|
|
4629
|
+
};
|
|
4630
|
+
|
|
4631
|
+
// helper to rearrange control points for all command types
|
|
4632
|
+
const reverseControlPoints = (type, values) => {
|
|
4633
|
+
let controlPoints = [];
|
|
4634
|
+
let endPoints = [];
|
|
4635
|
+
if (type !== "A") {
|
|
4636
|
+
for (let p = 0; p < values.length; p += 2) {
|
|
4637
|
+
controlPoints.push([values[p], values[p + 1]]);
|
|
4638
|
+
}
|
|
4639
|
+
endPoints = controlPoints.pop();
|
|
4640
|
+
controlPoints.reverse();
|
|
4641
|
+
}
|
|
4642
|
+
// is arc
|
|
4643
|
+
else {
|
|
4644
|
+
|
|
4645
|
+
let sweep = values[4] == 0 ? 1 : 0;
|
|
4646
|
+
controlPoints = [values[0], values[1], values[2], values[3], sweep];
|
|
4647
|
+
endPoints = [values[5], values[6]];
|
|
4648
|
+
}
|
|
4649
|
+
return { controlPoints, endPoints };
|
|
4650
|
+
};
|
|
4651
|
+
|
|
4652
|
+
// start compiling new path data
|
|
4653
|
+
let pathDataNew = [];
|
|
4654
|
+
|
|
4655
|
+
let closed =
|
|
4656
|
+
pathData[pathData.length - 1].type.toLowerCase() === "z" ? true : false;
|
|
4657
|
+
if (closed) {
|
|
4658
|
+
// add lineto closing space between Z and M
|
|
4659
|
+
pathData = addClosePathLineto(pathData);
|
|
4660
|
+
// remove Z closepath
|
|
4661
|
+
pathData.pop();
|
|
4662
|
+
}
|
|
4663
|
+
|
|
4664
|
+
// define last point as new M if path isn't closed
|
|
4665
|
+
let valuesLast = pathData[pathData.length - 1].values;
|
|
4666
|
+
let valuesLastL = valuesLast.length;
|
|
4667
|
+
let M = closed
|
|
4668
|
+
? pathData[0]
|
|
4669
|
+
: {
|
|
4670
|
+
type: "M",
|
|
4671
|
+
values: [valuesLast[valuesLastL - 2], valuesLast[valuesLastL - 1]]
|
|
4672
|
+
};
|
|
4673
|
+
// starting M stays the same – unless the path is not closed
|
|
4674
|
+
pathDataNew.push(M);
|
|
4675
|
+
|
|
4676
|
+
// reverse path data command order for processing
|
|
4677
|
+
pathData.reverse();
|
|
4678
|
+
for (let i = 1; i < pathData.length; i++) {
|
|
4679
|
+
let com = pathData[i];
|
|
4680
|
+
let type = com.type;
|
|
4681
|
+
let values = com.values;
|
|
4682
|
+
let comPrev = pathData[i - 1];
|
|
4683
|
+
let typePrev = comPrev.type;
|
|
4684
|
+
let valuesPrev = comPrev.values;
|
|
4685
|
+
|
|
4686
|
+
// get reversed control points and new end coordinates
|
|
4687
|
+
let controlPointsPrev = reverseControlPoints(typePrev, valuesPrev).controlPoints;
|
|
4688
|
+
let endPoints = reverseControlPoints(type, values).endPoints;
|
|
4689
|
+
|
|
4690
|
+
// create new path data
|
|
4691
|
+
let newValues = [];
|
|
4692
|
+
newValues = [controlPointsPrev, endPoints].flat();
|
|
4693
|
+
pathDataNew.push({
|
|
4694
|
+
type: typePrev,
|
|
4695
|
+
values: newValues.flat()
|
|
4696
|
+
});
|
|
4697
|
+
}
|
|
4698
|
+
|
|
4699
|
+
// add previously removed Z close path
|
|
4700
|
+
if (closed) {
|
|
4701
|
+
pathDataNew.push({
|
|
4702
|
+
type: "z",
|
|
4703
|
+
values: []
|
|
4704
|
+
});
|
|
4705
|
+
}
|
|
4706
|
+
|
|
4707
|
+
return pathDataNew;
|
|
4708
|
+
}
|
|
4709
|
+
|
|
4540
4710
|
function simplifyRDP(pts, {
|
|
4541
4711
|
quality = 0.9,
|
|
4542
4712
|
width = 0,
|
|
@@ -6287,6 +6457,8 @@ const attLookup = {
|
|
|
6287
6457
|
...textEls,
|
|
6288
6458
|
],
|
|
6289
6459
|
|
|
6460
|
+
"fill-rule": ["svg", "g", "path", "polygon", "text", "textPath"],
|
|
6461
|
+
|
|
6290
6462
|
opacity: [
|
|
6291
6463
|
"svg",
|
|
6292
6464
|
"g",
|
|
@@ -6651,26 +6823,30 @@ function cleanUpSVG(svgMarkup, {
|
|
|
6651
6823
|
removeUnused = true,
|
|
6652
6824
|
stylesToAttributes = true,
|
|
6653
6825
|
removePrologue = true,
|
|
6826
|
+
removeIds = false,
|
|
6827
|
+
removeClassNames = false,
|
|
6828
|
+
removeDimensions = false,
|
|
6654
6829
|
fixHref = true,
|
|
6655
6830
|
mergePaths = false,
|
|
6656
6831
|
cleanupSVGAtts = true,
|
|
6657
6832
|
removeNameSpaced = true,
|
|
6658
6833
|
attributesToGroup = true,
|
|
6834
|
+
shapesToPaths = false,
|
|
6659
6835
|
decimals = -1,
|
|
6660
6836
|
excludedEls = [],
|
|
6661
6837
|
} = {}) {
|
|
6662
6838
|
|
|
6663
|
-
|
|
6839
|
+
attributesToGroup = cleanupSVGAtts ? true : false;
|
|
6664
6840
|
|
|
6665
6841
|
// replace namespaced refs
|
|
6666
6842
|
if (fixHref) svgMarkup = svgMarkup.replaceAll("xlink:href=", "href=");
|
|
6667
6843
|
|
|
6668
6844
|
let svg = new DOMParser()
|
|
6669
|
-
|
|
6670
6845
|
.parseFromString(svgMarkup, "text/html")
|
|
6671
6846
|
.querySelector("svg");
|
|
6672
6847
|
|
|
6673
6848
|
if (cleanupSVGAtts) {
|
|
6849
|
+
|
|
6674
6850
|
let allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin'];
|
|
6675
6851
|
removeExcludedAttribues(svg, allowed);
|
|
6676
6852
|
|
|
@@ -6682,11 +6858,22 @@ function cleanUpSVG(svgMarkup, {
|
|
|
6682
6858
|
let els = svg.querySelectorAll('*');
|
|
6683
6859
|
let elProps = [];
|
|
6684
6860
|
|
|
6861
|
+
let geometryElements = ['polygon', 'polyline', 'line', 'rect', 'circle', 'ellipse'];
|
|
6862
|
+
|
|
6685
6863
|
for (let i = 0; i < els.length; i++) {
|
|
6686
6864
|
let el = els[i];
|
|
6687
6865
|
|
|
6688
6866
|
let name = el.nodeName.toLowerCase();
|
|
6689
6867
|
|
|
6868
|
+
// convert shapes
|
|
6869
|
+
if (shapesToPaths && name !== 'path' && geometryElements.includes(name)) {
|
|
6870
|
+
let path = shapeElToPath(el);
|
|
6871
|
+
el.replaceWith(path);
|
|
6872
|
+
name = 'path';
|
|
6873
|
+
el = path;
|
|
6874
|
+
|
|
6875
|
+
}
|
|
6876
|
+
|
|
6690
6877
|
// remove hidden elements
|
|
6691
6878
|
let style = el.getAttribute('style') || '';
|
|
6692
6879
|
let isHiddenByStyle = style ? style.trim().includes('display:none') : false;
|
|
@@ -6706,11 +6893,25 @@ function cleanUpSVG(svgMarkup, {
|
|
|
6706
6893
|
}
|
|
6707
6894
|
|
|
6708
6895
|
// group styles
|
|
6709
|
-
|
|
6710
6896
|
if (attributesToGroup || mergePaths) {
|
|
6711
6897
|
moveAttributesToGroup(elProps, mergePaths);
|
|
6712
6898
|
}
|
|
6713
6899
|
|
|
6900
|
+
if (removeDimensions) {
|
|
6901
|
+
svg.removeAttribute('width');
|
|
6902
|
+
svg.removeAttribute('height');
|
|
6903
|
+
}
|
|
6904
|
+
|
|
6905
|
+
if (removeClassNames || removeIds) {
|
|
6906
|
+
let att = removeClassNames ? 'class' : 'id';
|
|
6907
|
+
let selector = `[${att}]`;
|
|
6908
|
+
let els = svg.querySelectorAll(selector);
|
|
6909
|
+
svg.removeAttribute(att);
|
|
6910
|
+
els.forEach(el => {
|
|
6911
|
+
el.removeAttribute(att);
|
|
6912
|
+
});
|
|
6913
|
+
}
|
|
6914
|
+
|
|
6714
6915
|
if (returnDom) return svg
|
|
6715
6916
|
let markup = stringifySVG(svg);
|
|
6716
6917
|
|
|
@@ -6722,11 +6923,39 @@ function moveAttributesToGroup(elProps = [], mergePaths = true) {
|
|
|
6722
6923
|
let combine = [[elProps[0]]];
|
|
6723
6924
|
let idx = 0;
|
|
6724
6925
|
let lastProps = '';
|
|
6725
|
-
|
|
6926
|
+
let l = elProps.length;
|
|
6927
|
+
let itemsWithProps = elProps.filter(item => item.propstr);
|
|
6928
|
+
let path0;
|
|
6929
|
+
|
|
6930
|
+
// merge paths without properties
|
|
6931
|
+
if (!itemsWithProps.length && mergePaths) {
|
|
6932
|
+
let item0 = elProps[0];
|
|
6933
|
+
path0 = item0.el;
|
|
6934
|
+
let dCombined = item0.propsFiltered.d;
|
|
6935
|
+
|
|
6936
|
+
for (let i = 1; i < l; i++) {
|
|
6937
|
+
let item = elProps[i];
|
|
6938
|
+
let path = item.el;
|
|
6939
|
+
|
|
6940
|
+
let d = item.propsFiltered.d;
|
|
6941
|
+
let isAbs = d.startsWith('M');
|
|
6942
|
+
let dAbs = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ');
|
|
6943
|
+
|
|
6944
|
+
dCombined += dAbs;
|
|
6945
|
+
|
|
6946
|
+
// delete path el
|
|
6947
|
+
path.remove();
|
|
6948
|
+
}
|
|
6949
|
+
|
|
6950
|
+
path0.setAttribute('d', dCombined);
|
|
6951
|
+
return
|
|
6952
|
+
}
|
|
6953
|
+
|
|
6954
|
+
// add to combine chunks
|
|
6955
|
+
for (let i = 0; i < l; i++) {
|
|
6726
6956
|
let item = elProps[i];
|
|
6727
6957
|
let props = item.propsFiltered;
|
|
6728
6958
|
let propstr = [];
|
|
6729
|
-
|
|
6730
6959
|
for (let prop in props) {
|
|
6731
6960
|
if (prop !== 'd' && prop !== 'id') {
|
|
6732
6961
|
propstr.push(`${prop}:${props[prop]}`);
|
|
@@ -6735,16 +6964,15 @@ function moveAttributesToGroup(elProps = [], mergePaths = true) {
|
|
|
6735
6964
|
propstr = propstr.join('_');
|
|
6736
6965
|
item.propstr = propstr;
|
|
6737
6966
|
|
|
6738
|
-
if (propstr === lastProps) {
|
|
6967
|
+
if (l > 1 && propstr === lastProps) {
|
|
6739
6968
|
combine[idx].push(item);
|
|
6740
6969
|
} else {
|
|
6741
|
-
if (combine[idx].length) {
|
|
6970
|
+
if (l > 1 && combine[idx].length) {
|
|
6742
6971
|
combine.push([]);
|
|
6743
6972
|
idx++;
|
|
6744
6973
|
}
|
|
6745
6974
|
}
|
|
6746
6975
|
lastProps = propstr;
|
|
6747
|
-
|
|
6748
6976
|
}
|
|
6749
6977
|
|
|
6750
6978
|
// add att groups
|
|
@@ -6782,10 +7010,15 @@ function moveAttributesToGroup(elProps = [], mergePaths = true) {
|
|
|
6782
7010
|
}
|
|
6783
7011
|
|
|
6784
7012
|
if (mergePaths) {
|
|
6785
|
-
|
|
7013
|
+
group = group.filter(Boolean);
|
|
7014
|
+
let l = group.length;
|
|
7015
|
+
// nothing to merge
|
|
7016
|
+
if (l === 1) return group[0].el;
|
|
7017
|
+
|
|
7018
|
+
path0 = group[0].el;
|
|
6786
7019
|
let dCombined = group[0].propsFiltered.d;
|
|
6787
7020
|
|
|
6788
|
-
for (let i = 1; i <
|
|
7021
|
+
for (let i = 1; i < l; i++) {
|
|
6789
7022
|
let item = group[i];
|
|
6790
7023
|
let path = item.el;
|
|
6791
7024
|
let d = item.propsFiltered.d;
|
|
@@ -6810,20 +7043,6 @@ function moveAttributesToGroup(elProps = [], mergePaths = true) {
|
|
|
6810
7043
|
|
|
6811
7044
|
}
|
|
6812
7045
|
|
|
6813
|
-
function cleanSvgPrologue(svgString) {
|
|
6814
|
-
return (
|
|
6815
|
-
svgString
|
|
6816
|
-
// Remove XML prologues like <?xml ... ?>
|
|
6817
|
-
.replace(/<\?xml[\s\S]*?\?>/gi, "")
|
|
6818
|
-
// Remove DOCTYPE declarations
|
|
6819
|
-
.replace(/<!DOCTYPE[\s\S]*?>/gi, "")
|
|
6820
|
-
// Remove comments <!-- ... -->
|
|
6821
|
-
.replace(/<!--[\s\S]*?-->/g, "")
|
|
6822
|
-
// Trim extra whitespace
|
|
6823
|
-
.trim()
|
|
6824
|
-
);
|
|
6825
|
-
}
|
|
6826
|
-
|
|
6827
7046
|
function removeExcludedAttribues(el, allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class']) {
|
|
6828
7047
|
let atts = [...el.attributes].map((att) => att.name);
|
|
6829
7048
|
atts.forEach((att) => {
|
|
@@ -6833,13 +7052,22 @@ function removeExcludedAttribues(el, allowed = ['viewBox', 'xmlns', 'width', 'he
|
|
|
6833
7052
|
});
|
|
6834
7053
|
}
|
|
6835
7054
|
|
|
6836
|
-
function stringifySVG(svg) {
|
|
7055
|
+
function stringifySVG(svg, omitNamespace = false) {
|
|
6837
7056
|
let markup = new XMLSerializer().serializeToString(svg);
|
|
7057
|
+
|
|
7058
|
+
if (omitNamespace) {
|
|
7059
|
+
markup = markup.replaceAll('xmlns="http://www.w3.org/2000/svg"', '');
|
|
7060
|
+
}
|
|
7061
|
+
|
|
6838
7062
|
markup = markup
|
|
6839
7063
|
.replace(/\t/g, "")
|
|
6840
7064
|
.replace(/[\n\r|]/g, "\n")
|
|
6841
7065
|
.replace(/\n\s*\n/g, '\n')
|
|
6842
|
-
.replace(/ +/g, ' ')
|
|
7066
|
+
.replace(/ +/g, ' ')
|
|
7067
|
+
|
|
7068
|
+
.replace(/> </g, '><')
|
|
7069
|
+
.trim();
|
|
7070
|
+
|
|
6843
7071
|
|
|
6844
7072
|
return markup
|
|
6845
7073
|
}
|
|
@@ -7406,296 +7634,14 @@ function getViewBox(svg = null, decimals = -1) {
|
|
|
7406
7634
|
return viewBox
|
|
7407
7635
|
}
|
|
7408
7636
|
|
|
7409
|
-
function
|
|
7410
|
-
tolerance = 1
|
|
7411
|
-
|
|
7412
|
-
} = {}) {
|
|
7413
|
-
let chunks = [];
|
|
7414
|
-
let chunk = [];
|
|
7415
|
-
|
|
7416
|
-
let l = pathData.length;
|
|
7417
|
-
|
|
7418
|
-
for (let i = 1; i < l; i++) {
|
|
7419
|
-
let com = pathData[i];
|
|
7420
|
-
let { type, values, p0, cp1 = null, cp2 = null, p, extreme = null, semiExtreme = null, corner = null, directionChange } = com;
|
|
7421
|
-
|
|
7422
|
-
let comN = pathData[i + 1] || null;
|
|
7423
|
-
|
|
7424
|
-
/*
|
|
7425
|
-
if (extreme || corner || semiExtreme || directionChange) {
|
|
7426
|
-
|
|
7427
|
-
if (extreme) renderPoint(markers, com.p, 'cyan', '1%', '0.5')
|
|
7428
|
-
|
|
7429
|
-
if (semiExtreme) renderPoint(markers, com.p, 'orange', '1%', '0.5')
|
|
7430
|
-
if (corner) renderPoint(markers, com.p, 'magenta', '1.75%', '0.5')
|
|
7431
|
-
}
|
|
7432
|
-
*/
|
|
7433
|
-
|
|
7434
|
-
if (extreme || corner || (comN && comN.type !== type)) {
|
|
7435
|
-
chunk.push(com);
|
|
7436
|
-
chunks.push(chunk);
|
|
7437
|
-
chunk = [];
|
|
7438
|
-
continue
|
|
7439
|
-
}
|
|
7440
|
-
|
|
7441
|
-
chunk.push(com);
|
|
7442
|
-
|
|
7443
|
-
}
|
|
7444
|
-
|
|
7445
|
-
console.log('!!!chunks', chunks);
|
|
7446
|
-
|
|
7447
|
-
renderChunks(chunks);
|
|
7448
|
-
|
|
7449
|
-
// cleanup chunks
|
|
7450
|
-
|
|
7451
|
-
let chunksLen = chunks.length;
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
for (let c = 0; c < chunksLen; c++) {
|
|
7456
|
-
let chunk = chunks[c];
|
|
7457
|
-
let chunkN = chunks[c + 1] || null;
|
|
7458
|
-
|
|
7459
|
-
let chunkLen = chunk.length;
|
|
7460
|
-
|
|
7461
|
-
if (chunkLen === 1 && chunkN && chunkN[0].type === 'C') ;
|
|
7462
|
-
|
|
7463
|
-
}
|
|
7464
|
-
|
|
7465
|
-
chunks = chunks.filter(Boolean);
|
|
7466
|
-
|
|
7467
|
-
// test render
|
|
7468
|
-
|
|
7469
|
-
let pathDataC = [pathData[0]];
|
|
7470
|
-
|
|
7471
|
-
/**
|
|
7472
|
-
* combine chunk based
|
|
7473
|
-
*/
|
|
7474
|
-
for (let c = 0; c < chunks.length; c++) {
|
|
7475
|
-
let chunk = chunks[c];
|
|
7476
|
-
let chunkLen = chunk.length;
|
|
7477
|
-
let comChunk0 = chunk[0];
|
|
7478
|
-
let comChunk1 = chunk[chunkLen - 1];
|
|
7479
|
-
let thresh = getDistManhattan(comChunk0.p0, comChunk1.p) * 0.05;
|
|
7480
|
-
|
|
7481
|
-
// commands in chunk
|
|
7482
|
-
for (let i = 0, l = chunkLen; i < l; i++) {
|
|
7483
|
-
let com = chunk[i];
|
|
7484
|
-
chunk[i + 1];
|
|
7485
|
-
chunk[l - 1];
|
|
7486
|
-
|
|
7487
|
-
let isBezier = comChunk0.type === 'C' && comChunk1.type === 'C';
|
|
7488
|
-
|
|
7489
|
-
let { type, values, p0, cp1 = null, cp2 = null, p = null, extreme, semiExtreme = null, corner = null } = com;
|
|
7490
|
-
|
|
7491
|
-
let pI1 = null, pI2 = null;
|
|
7492
|
-
let cp1_S = null, cp2_S = null;
|
|
7493
|
-
let cp2_M = null;
|
|
7494
|
-
let cp1_M = null;
|
|
7495
|
-
let pathDataS = [];
|
|
7496
|
-
let tSplit = 0.666;
|
|
7497
|
-
let comMid = null;
|
|
7498
|
-
|
|
7499
|
-
// 0. adjust Extreme cpts
|
|
7500
|
-
if (isBezier) {
|
|
7501
|
-
let dx1 = Math.abs(comChunk0.p0.x - comChunk0.cp1.x);
|
|
7502
|
-
let dy1 = Math.abs(comChunk0.p0.y - comChunk0.cp1.y);
|
|
7503
|
-
let dx2 = Math.abs(comChunk1.p.x - comChunk1.cp2.x);
|
|
7504
|
-
let dy2 = Math.abs(comChunk1.p.y - comChunk1.cp2.y);
|
|
7505
|
-
|
|
7506
|
-
let vertical1 = dx1 < thresh && dx1 < dy1;
|
|
7507
|
-
let horizontal1 = dy1 < thresh && dx1 > dy1;
|
|
7508
|
-
|
|
7509
|
-
let vertical2 = dx2 < thresh && dx2 < dy2;
|
|
7510
|
-
let horizontal2 = dy2 < thresh && dx2 > dy2;
|
|
7511
|
-
|
|
7512
|
-
if (horizontal1) comChunk0.cp1.y = comChunk0.p0.y;
|
|
7513
|
-
if (horizontal2) comChunk1.cp2.y = comChunk1.p.y;
|
|
7514
|
-
if (vertical1) comChunk0.cp1.x = comChunk0.p0.x;
|
|
7515
|
-
if (vertical2) comChunk1.cp2.x = comChunk1.p.x;
|
|
7516
|
-
}
|
|
7517
|
-
|
|
7518
|
-
// test render - original pathdata
|
|
7519
|
-
let pathDataChunk = [
|
|
7520
|
-
{ type: 'M', values: [com.p0.x, com.p0.y] },
|
|
7521
|
-
{ type, values },
|
|
7522
|
-
];
|
|
7523
|
-
|
|
7524
|
-
pathDataToD(pathDataChunk);
|
|
7525
|
-
// renderPath(markers, d, stroke, '1%', '0.5')
|
|
7526
|
-
// continue
|
|
7527
|
-
/*
|
|
7528
|
-
|
|
7529
|
-
*/
|
|
7530
|
-
|
|
7531
|
-
// 1. only one command in chunk - nothing to simplify
|
|
7532
|
-
if (chunkLen === 1 || type !== 'C') {
|
|
7533
|
-
pathDataC.push(com);
|
|
7534
|
-
}
|
|
7535
|
-
|
|
7536
|
-
// 2. could be simplified
|
|
7537
|
-
else {
|
|
7538
|
-
// 2.1 has semi extreme - extrapolate
|
|
7539
|
-
// 2.2 has sdirection change
|
|
7540
|
-
let semiExtremes = chunk.filter(ch => ch.semiExtreme);
|
|
7541
|
-
let comsDirectionChange = chunk.filter(ch => ch.directionChange);
|
|
7542
|
-
|
|
7543
|
-
if (semiExtremes.length || comsDirectionChange.length) {
|
|
7544
|
-
|
|
7545
|
-
// semiExtreme command
|
|
7546
|
-
comMid = semiExtremes.length ? semiExtremes[0] : comsDirectionChange[0];
|
|
7547
|
-
|
|
7548
|
-
// zero length cpt vectors
|
|
7549
|
-
if (comChunk0.p0.x === comChunk0.cp1.x && comChunk0.p0.y === comChunk0.cp1.y) {
|
|
7550
|
-
comChunk0.cp1 = pointAtT([comChunk0.p0, comChunk0.cp1, comChunk0.cp2, comChunk0.p], 0.5);
|
|
7551
|
-
}
|
|
7552
|
-
else if (comChunk1.p.x === comChunk1.cp2.x && comChunk1.p.y === comChunk1.cp2.y) {
|
|
7553
|
-
comChunk1.cp2 = pointAtT([comChunk1.p0, comChunk1.cp1, comChunk1.cp2, comChunk1.p], 0.5);
|
|
7554
|
-
}
|
|
7555
|
-
|
|
7556
|
-
pI1 = checkLineIntersection(comMid.p, comMid.cp2, comChunk0.p0, comChunk0.cp1, false);
|
|
7557
|
-
pI2 = checkLineIntersection(comMid.p, comMid.cp2, comChunk1.p, comChunk1.cp2, false);
|
|
7558
|
-
|
|
7559
|
-
// intersections try to extrapolate cpts
|
|
7560
|
-
if (pI1 && pI2) {
|
|
7561
|
-
|
|
7562
|
-
cp1_S = pointAtT([comChunk0.p0, pI1], tSplit);
|
|
7563
|
-
cp2_S = pointAtT([comChunk1.p, pI2], tSplit);
|
|
7564
|
-
|
|
7565
|
-
cp2_M = pointAtT([comMid.p, pI1], tSplit);
|
|
7566
|
-
cp1_M = pointAtT([comMid.p, pI2], tSplit);
|
|
7567
|
-
|
|
7568
|
-
/*
|
|
7569
|
-
renderPoint(markers, cp1_S, 'magenta', '1%', '1' )
|
|
7570
|
-
*/
|
|
7571
|
-
|
|
7572
|
-
pathDataS = [
|
|
7573
|
-
{ type: 'M', values: [comChunk0.p0.x, comChunk0.p0.y] },
|
|
7574
|
-
{
|
|
7575
|
-
type: 'C', values: [
|
|
7576
|
-
cp1_S.x, cp1_S.y,
|
|
7577
|
-
cp2_M.x, cp2_M.y,
|
|
7578
|
-
comMid.p.x,
|
|
7579
|
-
comMid.p.y
|
|
7580
|
-
]
|
|
7581
|
-
},
|
|
7582
|
-
{
|
|
7583
|
-
type: 'C', values: [
|
|
7584
|
-
cp1_M.x, cp1_M.y,
|
|
7585
|
-
cp2_S.x, cp2_S.y,
|
|
7586
|
-
comChunk1.p.x,
|
|
7587
|
-
comChunk1.p.y,
|
|
7588
|
-
]
|
|
7589
|
-
},
|
|
7590
|
-
];
|
|
7591
|
-
pathDataToD(pathDataS);
|
|
7592
|
-
|
|
7593
|
-
pathDataC.push(
|
|
7594
|
-
{
|
|
7595
|
-
type: 'C', values: [
|
|
7596
|
-
cp1_S.x, cp1_S.y,
|
|
7597
|
-
cp2_M.x, cp2_M.y,
|
|
7598
|
-
comMid.p.x,
|
|
7599
|
-
comMid.p.y
|
|
7600
|
-
],
|
|
7601
|
-
p0: comChunk0.p0,
|
|
7602
|
-
cp1: cp1_S,
|
|
7603
|
-
cp2: cp2_M,
|
|
7604
|
-
p: comMid.p,
|
|
7605
|
-
dimA: getDistManhattan(comChunk0.p0, comMid.p)
|
|
7606
|
-
},
|
|
7607
|
-
|
|
7608
|
-
{
|
|
7609
|
-
type: 'C', values: [
|
|
7610
|
-
cp1_M.x, cp1_M.y,
|
|
7611
|
-
cp2_S.x, cp2_S.y,
|
|
7612
|
-
comChunk1.p.x,
|
|
7613
|
-
comChunk1.p.y,
|
|
7614
|
-
],
|
|
7615
|
-
p0: comMid.p,
|
|
7616
|
-
cp1: cp1_M,
|
|
7617
|
-
cp2: cp2_S,
|
|
7618
|
-
p: comChunk1.p,
|
|
7619
|
-
extreme: true,
|
|
7620
|
-
dimA: getDistManhattan(comMid.p, comChunk1.p)
|
|
7621
|
-
|
|
7622
|
-
}
|
|
7623
|
-
);
|
|
7624
|
-
break
|
|
7625
|
-
|
|
7626
|
-
}
|
|
7627
|
-
} else {
|
|
7628
|
-
pathDataC.push(com);
|
|
7629
|
-
}
|
|
7630
|
-
|
|
7631
|
-
}
|
|
7632
|
-
|
|
7633
|
-
}
|
|
7634
|
-
|
|
7635
|
-
}
|
|
7636
|
-
|
|
7637
|
-
/*
|
|
7638
|
-
// render
|
|
7639
|
-
let d = pathDataToD(pathDataC)
|
|
7640
|
-
console.log(d);
|
|
7641
|
-
renderPath(markers, d, 'red', '1%', '0.5')
|
|
7642
|
-
*/
|
|
7643
|
-
|
|
7644
|
-
return pathDataC
|
|
7645
|
-
|
|
7646
|
-
}
|
|
7647
|
-
|
|
7648
|
-
function renderChunks(chunks) {
|
|
7649
|
-
|
|
7650
|
-
console.log('chunks', chunks);
|
|
7651
|
-
|
|
7652
|
-
let stroke = 'green';
|
|
7653
|
-
|
|
7654
|
-
/**
|
|
7655
|
-
* combine chunk based
|
|
7656
|
-
*/
|
|
7657
|
-
for (let c = 0; c < chunks.length; c++) {
|
|
7658
|
-
let chunk = chunks[c];
|
|
7659
|
-
let chunkLen = chunk.length;
|
|
7660
|
-
|
|
7661
|
-
stroke = c % 2 === 0 ? 'orange' : 'green';
|
|
7662
|
-
let comChunk0 = chunk[0];
|
|
7663
|
-
let comChunk1 = chunk[chunkLen - 1];
|
|
7664
|
-
|
|
7665
|
-
let pathDataChunk = [
|
|
7666
|
-
{ type: 'M', values: [comChunk0.p0.x, comChunk0.p0.y] }
|
|
7667
|
-
];
|
|
7668
|
-
|
|
7669
|
-
// commands in chunk
|
|
7670
|
-
for (let i = 0, l = chunkLen; i < l; i++) {
|
|
7671
|
-
let com = chunk[i];
|
|
7672
|
-
chunk[i + 1];
|
|
7673
|
-
chunk[l - 1];
|
|
7674
|
-
comChunk0.type === 'C' && comChunk1.type === 'C';
|
|
7675
|
-
|
|
7676
|
-
let { type, values, p0, cp1 = null, cp2 = null, p = null, extreme, semiExtreme = null, corner = null } = com;
|
|
7677
|
-
|
|
7678
|
-
// test render - original pathdata
|
|
7679
|
-
pathDataChunk.push(
|
|
7680
|
-
{ type, values },
|
|
7681
|
-
);
|
|
7682
|
-
|
|
7683
|
-
}
|
|
7684
|
-
|
|
7685
|
-
let d = pathDataToD(pathDataChunk);
|
|
7686
|
-
renderPath(markers, d, stroke, '1%', '0.5');
|
|
7687
|
-
|
|
7688
|
-
}
|
|
7689
|
-
}
|
|
7690
|
-
|
|
7691
|
-
function pathDataRevertCubicToQuadratic(pathData) {
|
|
7637
|
+
function pathDataRevertCubicToQuadratic(pathData, tolerance=1) {
|
|
7692
7638
|
|
|
7693
7639
|
for (let c = 1, l = pathData.length; c < l; c++) {
|
|
7694
7640
|
let com = pathData[c];
|
|
7695
7641
|
let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
|
|
7696
7642
|
if (type === 'C') {
|
|
7697
7643
|
|
|
7698
|
-
let comQ = revertCubicQuadratic(p0, cp1, cp2, p);
|
|
7644
|
+
let comQ = revertCubicQuadratic(p0, cp1, cp2, p, tolerance);
|
|
7699
7645
|
if (comQ.type === 'Q') {
|
|
7700
7646
|
comQ.extreme = com.extreme;
|
|
7701
7647
|
comQ.corner = com.corner;
|
|
@@ -8210,12 +8156,59 @@ export function combineCubicsToArcs(pathData = [], {
|
|
|
8210
8156
|
}
|
|
8211
8157
|
*/
|
|
8212
8158
|
|
|
8159
|
+
function getPathDataPolyPrecise(pathData = []) {
|
|
8160
|
+
|
|
8161
|
+
let poly = [];
|
|
8162
|
+
for (let i = 0; i < pathData.length; i++) {
|
|
8163
|
+
let com = pathData[i];
|
|
8164
|
+
let prev = i > 0 ? pathData[i - 1] : pathData[i];
|
|
8165
|
+
let { type, values } = com;
|
|
8166
|
+
let p0 = { x: prev.values[prev.values.length - 2], y: prev.values[prev.values.length - 1] };
|
|
8167
|
+
let p = values.length ? { x: values[values.length - 2], y: values[values.length - 1] } : '';
|
|
8168
|
+
let cp1 = values.length ? { x: values[0], y: values[1] } : '';
|
|
8169
|
+
|
|
8170
|
+
switch (type) {
|
|
8171
|
+
|
|
8172
|
+
// convert to cubic to get polygon
|
|
8173
|
+
case 'A':
|
|
8174
|
+
if (typeof arcToBezier$1 !== 'function') {
|
|
8175
|
+
|
|
8176
|
+
break;
|
|
8177
|
+
}
|
|
8178
|
+
let cubic = arcToBezier$1(p0, values);
|
|
8179
|
+
cubic.forEach(com => {
|
|
8180
|
+
let vals = com.values;
|
|
8181
|
+
let cp1 = { x: vals[0], y: vals[1] };
|
|
8182
|
+
let cp2 = { x: vals[2], y: vals[3] };
|
|
8183
|
+
let p = { x: vals[4], y: vals[5] };
|
|
8184
|
+
poly.push(cp1, cp2, p);
|
|
8185
|
+
});
|
|
8186
|
+
break;
|
|
8187
|
+
|
|
8188
|
+
case 'C':
|
|
8189
|
+
let cp2 = { x: values[2], y: values[3] };
|
|
8190
|
+
poly.push(cp1, cp2);
|
|
8191
|
+
break;
|
|
8192
|
+
case 'Q':
|
|
8193
|
+
poly.push(cp1);
|
|
8194
|
+
break;
|
|
8195
|
+
}
|
|
8196
|
+
|
|
8197
|
+
// M and L commands
|
|
8198
|
+
if (type.toLowerCase() !== 'z') {
|
|
8199
|
+
poly.push(p);
|
|
8200
|
+
}
|
|
8201
|
+
}
|
|
8202
|
+
|
|
8203
|
+
return poly;
|
|
8204
|
+
}
|
|
8205
|
+
|
|
8213
8206
|
function pathDataToPolygon(pathData, {
|
|
8214
8207
|
angles = [],
|
|
8215
8208
|
split = 0,
|
|
8216
8209
|
getPathData = true,
|
|
8217
|
-
width=0,
|
|
8218
|
-
height=0
|
|
8210
|
+
width = 0,
|
|
8211
|
+
height = 0
|
|
8219
8212
|
} = {}) {
|
|
8220
8213
|
|
|
8221
8214
|
let l = pathData.length;
|
|
@@ -8231,9 +8224,9 @@ function pathDataToPolygon(pathData, {
|
|
|
8231
8224
|
|
|
8232
8225
|
split = !split ? 1 : split;
|
|
8233
8226
|
|
|
8234
|
-
if(width && height){
|
|
8235
|
-
minLength = (width+height) * 0.025 / split;
|
|
8236
|
-
}else {
|
|
8227
|
+
if (width && height) {
|
|
8228
|
+
minLength = (width + height) * 0.025 / split;
|
|
8229
|
+
} else {
|
|
8237
8230
|
|
|
8238
8231
|
let lengths = pathData.map(com => com.dimA || 0).filter(Boolean).sort();
|
|
8239
8232
|
minLength = lengths[0];
|
|
@@ -8259,8 +8252,8 @@ function pathDataToPolygon(pathData, {
|
|
|
8259
8252
|
split = Math.ceil(length / minLength);
|
|
8260
8253
|
|
|
8261
8254
|
let tArr = [];
|
|
8262
|
-
for(let i=1; i<split; i++){
|
|
8263
|
-
tArr.push(1/split*i);
|
|
8255
|
+
for (let i = 1; i < split; i++) {
|
|
8256
|
+
tArr.push(1 / split * i);
|
|
8264
8257
|
}
|
|
8265
8258
|
|
|
8266
8259
|
tArr.forEach(t => {
|
|
@@ -8274,10 +8267,10 @@ function pathDataToPolygon(pathData, {
|
|
|
8274
8267
|
M = p;
|
|
8275
8268
|
}
|
|
8276
8269
|
|
|
8277
|
-
p.area = com.cptArea|| 0;
|
|
8278
|
-
p.isExtreme = com.extreme|| false;
|
|
8279
|
-
p.isCorner = com.corner|| false;
|
|
8280
|
-
p.isDirChange = com.directionChange || false
|
|
8270
|
+
p.area = com.cptArea || 0;
|
|
8271
|
+
p.isExtreme = com.extreme || false;
|
|
8272
|
+
p.isCorner = com.corner || false;
|
|
8273
|
+
p.isDirChange = com.directionChange || false;
|
|
8281
8274
|
|
|
8282
8275
|
// segment end point
|
|
8283
8276
|
pts.push(p);
|
|
@@ -8291,7 +8284,7 @@ function pathDataToPolygon(pathData, {
|
|
|
8291
8284
|
|
|
8292
8285
|
// reduce poly vertices
|
|
8293
8286
|
|
|
8294
|
-
pts = simplifyRD(pts, {quality:0.5, width, height});
|
|
8287
|
+
pts = simplifyRD(pts, { quality: 0.5, width, height });
|
|
8295
8288
|
|
|
8296
8289
|
/*
|
|
8297
8290
|
pts.forEach(pt => {
|
|
@@ -8304,6 +8297,103 @@ function pathDataToPolygon(pathData, {
|
|
|
8304
8297
|
return getPathData ? pathDataPoly : pts;
|
|
8305
8298
|
}
|
|
8306
8299
|
|
|
8300
|
+
function pathDataLineToCubic(pathData) {
|
|
8301
|
+
|
|
8302
|
+
for (let c = 1, l = pathData.length; c < l; c++) {
|
|
8303
|
+
let com = pathData[c];
|
|
8304
|
+
let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
|
|
8305
|
+
if (type === 'L') {
|
|
8306
|
+
|
|
8307
|
+
let cp1 = interpolate(p0, p, 0.333);
|
|
8308
|
+
let cp2 = interpolate(p, p0, 0.333);
|
|
8309
|
+
|
|
8310
|
+
pathData[c].type = 'C';
|
|
8311
|
+
pathData[c].values = [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y];
|
|
8312
|
+
pathData[c].cp1 = cp1;
|
|
8313
|
+
pathData[c].cp2 = cp2;
|
|
8314
|
+
|
|
8315
|
+
}
|
|
8316
|
+
}
|
|
8317
|
+
return pathData
|
|
8318
|
+
}
|
|
8319
|
+
|
|
8320
|
+
/**
|
|
8321
|
+
* fix sub path directions
|
|
8322
|
+
* pathdata must be be normalized to
|
|
8323
|
+
* absolute and longhand commands
|
|
8324
|
+
* toClockwise = force default direction
|
|
8325
|
+
*/
|
|
8326
|
+
|
|
8327
|
+
function fixPathDataDirections(pathDataArr = [], toClockwise = false) {
|
|
8328
|
+
|
|
8329
|
+
let polys = [];
|
|
8330
|
+
|
|
8331
|
+
pathDataArr.forEach((sub, i) => {
|
|
8332
|
+
let pathData = sub.pathData;
|
|
8333
|
+
|
|
8334
|
+
let vertices = getPathDataPolyPrecise(pathData);
|
|
8335
|
+
let area = getPolygonArea(vertices);
|
|
8336
|
+
let isClockwise = area >= 0;
|
|
8337
|
+
polys.push({ pts: vertices, bb: getPolyBBox(vertices), cw: isClockwise, index: i, inter: 0, includes: [], includedIn: [] });
|
|
8338
|
+
});
|
|
8339
|
+
|
|
8340
|
+
// check poly intersections
|
|
8341
|
+
let l = polys.length;
|
|
8342
|
+
for (let i = 0; i < l; i++) {
|
|
8343
|
+
let prev = polys[i];
|
|
8344
|
+
let bb0 = prev.bb;
|
|
8345
|
+
|
|
8346
|
+
for (let j = 0; j < l; j++) {
|
|
8347
|
+
|
|
8348
|
+
let poly = polys[j];
|
|
8349
|
+
let bb = poly.bb;
|
|
8350
|
+
|
|
8351
|
+
// skip if the same poly or parent
|
|
8352
|
+
if (i === j || poly.includes.includes(i)) continue
|
|
8353
|
+
|
|
8354
|
+
// if mid point is in previous polygon
|
|
8355
|
+
let ptMid = { x: bb.left + bb.width / 2, y: bb.top + bb.height / 2 };
|
|
8356
|
+
let inPoly = isPointInPolygon(ptMid, prev.pts, bb0);
|
|
8357
|
+
|
|
8358
|
+
if (inPoly) {
|
|
8359
|
+
polys[j].inter += 1;
|
|
8360
|
+
poly.includedIn.push(i);
|
|
8361
|
+
prev.includes.push(j);
|
|
8362
|
+
}
|
|
8363
|
+
}
|
|
8364
|
+
}
|
|
8365
|
+
|
|
8366
|
+
// reverse paths
|
|
8367
|
+
for (let i = 0; i < l; i++) {
|
|
8368
|
+
|
|
8369
|
+
let poly = polys[i];
|
|
8370
|
+
let { cw, includedIn, includes } = poly;
|
|
8371
|
+
|
|
8372
|
+
// outer path direction to counter clockwise
|
|
8373
|
+
if (!includedIn.length && cw && !toClockwise
|
|
8374
|
+
|| !includedIn.length && !cw && toClockwise
|
|
8375
|
+
) {
|
|
8376
|
+
pathDataArr[i].pathData = reversePathData(pathDataArr[i].pathData);
|
|
8377
|
+
polys[i].cw = polys[i].cw ? false : true;
|
|
8378
|
+
cw = polys[i].cw;
|
|
8379
|
+
}
|
|
8380
|
+
|
|
8381
|
+
// reverse inner sub paths
|
|
8382
|
+
for (let j = 0; j < includes.length; j++) {
|
|
8383
|
+
let ind = includes[j];
|
|
8384
|
+
let child = polys[ind];
|
|
8385
|
+
|
|
8386
|
+
if (child.cw === cw) {
|
|
8387
|
+
pathDataArr[ind].pathData = reversePathData(pathDataArr[ind].pathData);
|
|
8388
|
+
polys[ind].cw = polys[ind].cw ? false : true;
|
|
8389
|
+
}
|
|
8390
|
+
}
|
|
8391
|
+
}
|
|
8392
|
+
|
|
8393
|
+
return pathDataArr
|
|
8394
|
+
|
|
8395
|
+
}
|
|
8396
|
+
|
|
8307
8397
|
function svgPathSimplify(input = '', {
|
|
8308
8398
|
|
|
8309
8399
|
// return svg markup or object
|
|
@@ -8331,6 +8421,12 @@ function svgPathSimplify(input = '', {
|
|
|
8331
8421
|
|
|
8332
8422
|
refineExtremes = true,
|
|
8333
8423
|
simplifyCorners = false,
|
|
8424
|
+
removeDimensions = false,
|
|
8425
|
+
removeIds = false,
|
|
8426
|
+
removeClassNames = false,
|
|
8427
|
+
omitNamespace = false,
|
|
8428
|
+
|
|
8429
|
+
fixDirections = false,
|
|
8334
8430
|
|
|
8335
8431
|
keepExtremes = true,
|
|
8336
8432
|
keepCorners = true,
|
|
@@ -8344,7 +8440,6 @@ function svgPathSimplify(input = '', {
|
|
|
8344
8440
|
toPolygon = false,
|
|
8345
8441
|
|
|
8346
8442
|
removeOrphanSubpaths = false,
|
|
8347
|
-
|
|
8348
8443
|
simplifyRound = false,
|
|
8349
8444
|
|
|
8350
8445
|
scale = 1,
|
|
@@ -8352,32 +8447,28 @@ function svgPathSimplify(input = '', {
|
|
|
8352
8447
|
crop = false,
|
|
8353
8448
|
alignToOrigin = false,
|
|
8354
8449
|
|
|
8355
|
-
// svg path optimizations
|
|
8356
8450
|
decimals = 3,
|
|
8357
8451
|
autoAccuracy = true,
|
|
8358
8452
|
|
|
8359
8453
|
minifyD = 0,
|
|
8360
8454
|
tolerance = 1,
|
|
8361
|
-
|
|
8455
|
+
reversePath = false,
|
|
8362
8456
|
|
|
8363
|
-
|
|
8364
|
-
cleanupSVGAtts=true,
|
|
8457
|
+
cleanupSVGAtts = true,
|
|
8365
8458
|
removePrologue = true,
|
|
8366
8459
|
stylesToAttributes = true,
|
|
8367
8460
|
fixHref = true,
|
|
8368
|
-
removeNameSpaced=true,
|
|
8369
|
-
attributesToGroup=false,
|
|
8461
|
+
removeNameSpaced = true,
|
|
8462
|
+
attributesToGroup = false,
|
|
8370
8463
|
mergePaths = false,
|
|
8371
8464
|
removeHidden = true,
|
|
8372
8465
|
removeUnused = true,
|
|
8373
|
-
shapesToPaths =
|
|
8466
|
+
shapesToPaths = false,
|
|
8467
|
+
lineToCubic = false,
|
|
8374
8468
|
|
|
8375
8469
|
tMin = 0,
|
|
8376
8470
|
tMax = 1,
|
|
8377
8471
|
|
|
8378
|
-
// redraw - for messed up paths
|
|
8379
|
-
redraw = false,
|
|
8380
|
-
|
|
8381
8472
|
} = {}) {
|
|
8382
8473
|
|
|
8383
8474
|
// clamp tolerance and scale
|
|
@@ -8435,14 +8526,16 @@ function svgPathSimplify(input = '', {
|
|
|
8435
8526
|
else {
|
|
8436
8527
|
|
|
8437
8528
|
let returnDom = true;
|
|
8438
|
-
svg = cleanUpSVG(input, { cleanupSVGAtts, returnDom, removeHidden, removeUnused, removeNameSpaced,
|
|
8529
|
+
svg = cleanUpSVG(input, { removeIds, removeClassNames, removeDimensions, cleanupSVGAtts, returnDom, removeHidden, removeUnused, removeNameSpaced, stylesToAttributes, removePrologue, fixHref, mergePaths, shapesToPaths }
|
|
8439
8530
|
);
|
|
8440
8531
|
|
|
8441
8532
|
if (shapesToPaths) {
|
|
8442
8533
|
let shapes = svg.querySelectorAll('polygon, polyline, line, rect, circle, ellipse');
|
|
8443
8534
|
shapes.forEach(shape => {
|
|
8444
8535
|
let path = shapeElToPath(shape);
|
|
8536
|
+
|
|
8445
8537
|
shape.replaceWith(path);
|
|
8538
|
+
|
|
8446
8539
|
});
|
|
8447
8540
|
}
|
|
8448
8541
|
|
|
@@ -8595,23 +8688,6 @@ function svgPathSimplify(input = '', {
|
|
|
8595
8688
|
// remove zero length linetos
|
|
8596
8689
|
if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub);
|
|
8597
8690
|
|
|
8598
|
-
/**
|
|
8599
|
-
* try to redraw messed up paths
|
|
8600
|
-
* based on significant points suchas
|
|
8601
|
-
* extremes, semi-extremes and corners
|
|
8602
|
-
*/
|
|
8603
|
-
if (redraw) {
|
|
8604
|
-
addExtremes = true;
|
|
8605
|
-
addSemiExtremes = true;
|
|
8606
|
-
simplifyCorners = false;
|
|
8607
|
-
keepCorners = true;
|
|
8608
|
-
keepExtremes = true;
|
|
8609
|
-
optimizeOrder = true;
|
|
8610
|
-
|
|
8611
|
-
tMin = 0;
|
|
8612
|
-
tMax = 0;
|
|
8613
|
-
}
|
|
8614
|
-
|
|
8615
8691
|
// sort to top left
|
|
8616
8692
|
if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
|
|
8617
8693
|
|
|
@@ -8621,6 +8697,11 @@ function svgPathSimplify(input = '', {
|
|
|
8621
8697
|
if (addExtremes || addSemiExtremes) pathDataSub = addExtremePoints(pathDataSub,
|
|
8622
8698
|
{ tMin, tMax, addExtremes, addSemiExtremes, angles: [30] });
|
|
8623
8699
|
|
|
8700
|
+
// reverse
|
|
8701
|
+
if(reversePath) {
|
|
8702
|
+
pathDataSub = reversePathData(pathDataSub);
|
|
8703
|
+
}
|
|
8704
|
+
|
|
8624
8705
|
// analyze pathdata to add info about signicant properties such as extremes, corners
|
|
8625
8706
|
let pathDataPlus = analyzePathData(pathDataSub, {
|
|
8626
8707
|
detectSemiExtremes: addSemiExtremes,
|
|
@@ -8633,7 +8714,7 @@ function svgPathSimplify(input = '', {
|
|
|
8633
8714
|
|
|
8634
8715
|
if (refineClosing) pathData = refineClosingCommand(pathData, { threshold: dimA * 0.001 });
|
|
8635
8716
|
|
|
8636
|
-
pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance
|
|
8717
|
+
pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance }) : pathData;
|
|
8637
8718
|
|
|
8638
8719
|
// refine extremes
|
|
8639
8720
|
if (refineExtremes) {
|
|
@@ -8642,29 +8723,6 @@ function svgPathSimplify(input = '', {
|
|
|
8642
8723
|
pathData = refineAdjacentExtremes(pathData, { threshold: thresholdEx, tolerance });
|
|
8643
8724
|
}
|
|
8644
8725
|
|
|
8645
|
-
/**
|
|
8646
|
-
* try redrawing
|
|
8647
|
-
*/
|
|
8648
|
-
|
|
8649
|
-
if (redraw) {
|
|
8650
|
-
|
|
8651
|
-
/*
|
|
8652
|
-
pathData = addExtremePoints(pathData,
|
|
8653
|
-
{ tMin: 0, tMax: 1, addExtremes: true, addSemiExtremes: true })
|
|
8654
|
-
|
|
8655
|
-
pathData = analyzePathData(pathDataSub, {
|
|
8656
|
-
detectSemiExtremes: true,
|
|
8657
|
-
detectExtremes: true,
|
|
8658
|
-
}).pathData;
|
|
8659
|
-
|
|
8660
|
-
*/
|
|
8661
|
-
|
|
8662
|
-
(bb.width + bb.height) * 0.1;
|
|
8663
|
-
|
|
8664
|
-
pathData = redrawPathData(pathData, { tolerance, threshold: dimA * 0.001 });
|
|
8665
|
-
|
|
8666
|
-
}
|
|
8667
|
-
|
|
8668
8726
|
// cubic to arcs
|
|
8669
8727
|
if (cubicToArc) pathData = pathDataCubicsToArc(pathData, { areaThreshold: 2.5 });
|
|
8670
8728
|
|
|
@@ -8684,13 +8742,14 @@ function svgPathSimplify(input = '', {
|
|
|
8684
8742
|
if (simplifyRound) pathData = refineRoundSegments(pathData);
|
|
8685
8743
|
|
|
8686
8744
|
// simplify to quadratics
|
|
8687
|
-
if (revertToQuadratics) pathData = pathDataRevertCubicToQuadratic(pathData);
|
|
8745
|
+
if (revertToQuadratics) pathData = pathDataRevertCubicToQuadratic(pathData, tolerance);
|
|
8746
|
+
|
|
8747
|
+
if (lineToCubic) pathData = pathDataLineToCubic(pathData);
|
|
8688
8748
|
|
|
8689
8749
|
// optimize close path
|
|
8690
8750
|
if (optimizeOrder) pathData = optimizeClosePath(pathData, { autoClose });
|
|
8691
8751
|
|
|
8692
8752
|
// update
|
|
8693
|
-
|
|
8694
8753
|
pathDataPlusArr.push({ pathData, bb });
|
|
8695
8754
|
|
|
8696
8755
|
} // end sup paths
|
|
@@ -8709,6 +8768,11 @@ function svgPathSimplify(input = '', {
|
|
|
8709
8768
|
pathDataPlusArr = isPortrait ? pathDataPlusArr.sort((a, b) => a.bb.y - b.bb.y || a.bb.x - b.bb.x) : pathDataPlusArr.sort((a, b) => a.bb.x - b.bb.x || a.bb.y - b.bb.y);
|
|
8710
8769
|
}
|
|
8711
8770
|
|
|
8771
|
+
// fix path directions
|
|
8772
|
+
if (fixDirections) {
|
|
8773
|
+
pathDataPlusArr = fixPathDataDirections(pathDataPlusArr);
|
|
8774
|
+
}
|
|
8775
|
+
|
|
8712
8776
|
// flatten compound paths
|
|
8713
8777
|
pathData = [];
|
|
8714
8778
|
pathDataPlusArr.forEach(sub => {
|
|
@@ -8718,11 +8782,10 @@ function svgPathSimplify(input = '', {
|
|
|
8718
8782
|
if (autoAccuracy) {
|
|
8719
8783
|
decimals = detectAccuracy(pathData);
|
|
8720
8784
|
pathOptions.decimals = decimals;
|
|
8721
|
-
|
|
8722
8785
|
}
|
|
8723
8786
|
|
|
8724
8787
|
// collect for merged svg paths
|
|
8725
|
-
mergePaths= false;
|
|
8788
|
+
mergePaths = false;
|
|
8726
8789
|
if (el && mergePaths) {
|
|
8727
8790
|
pathData_merged.push(...pathData);
|
|
8728
8791
|
}
|
|
@@ -8825,7 +8888,16 @@ function svgPathSimplify(input = '', {
|
|
|
8825
8888
|
}
|
|
8826
8889
|
}
|
|
8827
8890
|
|
|
8828
|
-
|
|
8891
|
+
// remove fill rules
|
|
8892
|
+
if (fixDirections) {
|
|
8893
|
+
let elsFill = svg.querySelectorAll('path[fill-rule], path[clip-rule]');
|
|
8894
|
+
elsFill.forEach(el => {
|
|
8895
|
+
el.removeAttribute('fill-rule');
|
|
8896
|
+
el.removeAttribute('clip-rule');
|
|
8897
|
+
});
|
|
8898
|
+
}
|
|
8899
|
+
|
|
8900
|
+
svg = stringifySVG(svg, omitNamespace);
|
|
8829
8901
|
|
|
8830
8902
|
svgSizeOpt = svg.length;
|
|
8831
8903
|
|
|
@@ -8869,6 +8941,9 @@ export {fitCurveN as fitCurveN}
|
|
|
8869
8941
|
|
|
8870
8942
|
import {parsePathDataString_plus} from './svgii/pathData_parse2';
|
|
8871
8943
|
export {parsePathDataString_plus as parsePathDataString_plus}
|
|
8944
|
+
|
|
8945
|
+
import {getPathDataFromEl} from './svgii/pathData_parse_els';
|
|
8946
|
+
export{getPathDataFromEl as getPathDataFromEl};
|
|
8872
8947
|
*/
|
|
8873
8948
|
|
|
8874
8949
|
// IIFE
|