svg-path-simplify 0.0.5 → 0.0.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 +193 -11
- package/dist/svg-path-simplify.esm.js +152 -131
- package/dist/svg-path-simplify.esm.min.js +1 -1
- package/dist/svg-path-simplify.js +4068 -4048
- package/dist/svg-path-simplify.min.js +1 -1
- package/dist/svg-path-simplify.node.js +151 -131
- package/dist/svg-path-simplify.node.min.js +1 -1
- package/index.html +6 -1
- package/package.json +2 -1
- package/src/detect_input.js +2 -0
- package/src/index.js +4 -4
- package/src/pathData_simplify_cubic_extrapolate.js +6 -3
- package/src/pathSimplify-main.js +118 -64
- package/src/svgii/pathData_convert.js +7 -3
- package/src/svgii/pathData_remove_collinear.js +10 -2
- package/src/svgii/pathData_remove_zerolength.js +16 -0
- package/src/svgii/pathData_split.js +4 -0
- package/src/svgii/pathData_toPolygon.js +98 -47
- package/src/svgii/poly_analyze.js +20 -16
- package/src/svgii/svg_cleanup.js +10 -1
- package/test.js +14 -0
- /package/src/svgii/{simplify_polygon.js → polygon_unite.js} +0 -0
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
// remove zero-length segments introduced by rounding
|
|
3
|
+
export function removeZeroLengthLinetos_post(pathData) {
|
|
4
|
+
let pathDataOpt = []
|
|
5
|
+
pathData.forEach((com, i) => {
|
|
6
|
+
let { type, values } = com;
|
|
7
|
+
if (type === 'l' || type === 'v' || type === 'h') {
|
|
8
|
+
let hasLength = type === 'l' ? (values.join('') !== '00') : values[0] !== 0
|
|
9
|
+
if (hasLength) pathDataOpt.push(com)
|
|
10
|
+
} else {
|
|
11
|
+
pathDataOpt.push(com)
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
return pathDataOpt
|
|
15
|
+
}
|
|
16
|
+
|
|
1
17
|
export function removeZeroLengthLinetos(pathData) {
|
|
2
18
|
|
|
3
19
|
let M = { x: pathData[0].values[0], y: pathData[0].values[1] }
|
|
@@ -280,6 +280,8 @@ export function addExtemesToCommand(p0, values, tMin=0, tMax=1) {
|
|
|
280
280
|
|
|
281
281
|
if(tArr.length){
|
|
282
282
|
let commandsSplit = splitCommandAtTValues(p0, values, tArr)
|
|
283
|
+
//console.log('commandsSplit', commandsSplit);
|
|
284
|
+
|
|
283
285
|
pathDataNew.push(...commandsSplit)
|
|
284
286
|
extremeCount += commandsSplit.length;
|
|
285
287
|
}else{
|
|
@@ -322,6 +324,8 @@ export function addExtremePoints(pathData, tMin=0, tMax=1) {
|
|
|
322
324
|
// add extremes
|
|
323
325
|
if (type === 'C' || type === 'Q') {
|
|
324
326
|
let comExt = addExtemesToCommand(p0, values, tMin, tMax).pathData;
|
|
327
|
+
let comExt2 = com;
|
|
328
|
+
//comExt2.valu
|
|
325
329
|
//console.log('comExt', comExt);
|
|
326
330
|
pathDataNew.push(...comExt )
|
|
327
331
|
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { pointAtT } from "./geometry";
|
|
1
|
+
import { getDistAv, pointAtT } from "./geometry";
|
|
2
2
|
import { getPolyBBox } from "./geometry_bbox";
|
|
3
3
|
import { addDimensionData, analyzePathData } from "./pathData_analyze";
|
|
4
4
|
import { addExtremePoints } from "./pathData_split";
|
|
5
|
+
import { pathDataToD } from "./pathData_stringify";
|
|
6
|
+
import { analyzePoly } from "./poly_analyze";
|
|
7
|
+
import { getCurvePathData } from "./poly_to_pathdata";
|
|
8
|
+
import { renderPoint } from "./visualize";
|
|
5
9
|
|
|
6
|
-
export function pathDataToPolyPlus(pathDataArr = [], addExtremes=true) {
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
export function pathDataToPolySingle(pathData, addExtremes = true) {
|
|
12
|
+
|
|
10
13
|
|
|
11
14
|
let dimMin = Infinity;
|
|
12
15
|
let dimMax = 0;
|
|
@@ -16,77 +19,125 @@ export function pathDataToPolyPlus(pathDataArr = [], addExtremes=true) {
|
|
|
16
19
|
* add extremes to beziers
|
|
17
20
|
* to reproduce the shape better
|
|
18
21
|
*/
|
|
19
|
-
if(addExtremes){
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
//pathDataArr[i] = addExtremePoints(pathData)
|
|
23
|
-
let pathDataE = addExtremePoints(pathData)
|
|
24
|
-
|
|
25
|
-
//pathDataArr[i] = analyzePathData(pathDataE).pathData
|
|
26
|
-
pathDataArr[i] = addDimensionData(pathDataE)
|
|
27
|
-
|
|
28
|
-
})
|
|
22
|
+
if (addExtremes) {
|
|
23
|
+
pathData = addExtremePoints(pathData, 0.1, 0.9)
|
|
29
24
|
}
|
|
30
25
|
|
|
26
|
+
//console.log(pathData);
|
|
27
|
+
|
|
28
|
+
let pathDataPlus = analyzePathData(pathData);
|
|
29
|
+
let { bb } = pathDataPlus;
|
|
30
|
+
let thresh = (bb.width + bb.height) / 2 / 50
|
|
31
|
+
|
|
32
|
+
pathData = pathDataPlus.pathData
|
|
33
|
+
|
|
31
34
|
|
|
32
35
|
/**
|
|
33
36
|
* approximate min and max segment sizes
|
|
34
37
|
* for segment splitting
|
|
35
38
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (dimMinL && dimMinL < dimMin) dimMin = dimMinL;
|
|
43
|
-
if (dimMaxL && dimMaxL > dimMax) dimMax = dimMaxL;
|
|
44
|
-
|
|
45
|
-
})
|
|
39
|
+
let dimArr = pathData.filter(com => com.dimA).sort((a, b) => a.dimA - b.DimA)
|
|
40
|
+
let dimMinL = dimArr[0].dimA
|
|
41
|
+
let dimMaxL = dimArr[dimArr.length - 1].dimA
|
|
42
|
+
//console.log('dimArr', dimArr, dimMaxL);
|
|
43
|
+
if (dimMinL && dimMinL < dimMin) dimMin = dimMinL;
|
|
44
|
+
if (dimMaxL && dimMaxL > dimMax) dimMax = dimMaxL;
|
|
46
45
|
|
|
47
46
|
//console.log(dimMin, dimMax);
|
|
48
47
|
|
|
49
48
|
// find split point based on smallest point distance
|
|
50
|
-
dimMin = (dimMin * 2 + dimMax) / 2
|
|
49
|
+
dimMin = (dimMin * 2 + dimMax) / 2 / 4
|
|
50
|
+
//dimMin = (bb.width + bb.height) / 2 / 8
|
|
51
51
|
|
|
52
52
|
// collect vertices
|
|
53
53
|
let polyArr = [];
|
|
54
54
|
|
|
55
|
+
let p0 = { x: pathData[0].p0.x, y: pathData[0].p0.y, extreme: pathData[0].extreme, corner: pathData[0].corner }
|
|
56
|
+
let poly = [p0];
|
|
57
|
+
|
|
58
|
+
for (let i = 1, l = pathData.length; i < l; i++) {
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
let com = pathData[i];
|
|
61
|
+
let { type, values, extreme = false, corner = false, dimA = null, p0, p, cp1 = null, cp2 = null } = com;
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
dimA = getDistAv(p0, p);
|
|
59
64
|
|
|
60
|
-
pathData.forEach(com => {
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
if(extreme){
|
|
67
|
+
//renderPoint(markers, p, 'cyan')
|
|
68
|
+
}
|
|
64
69
|
|
|
65
|
-
|
|
70
|
+
let split = (type === 'C' || type === 'Q') && dimA ? Math.ceil(dimA / dimMin) : 0;
|
|
66
71
|
|
|
67
|
-
let splitT = 1 / split;
|
|
68
72
|
|
|
69
|
-
|
|
73
|
+
//console.log(com);
|
|
74
|
+
p.extreme = extreme
|
|
75
|
+
p.corner = corner
|
|
70
76
|
|
|
71
|
-
|
|
72
|
-
let ptI = pointAtT([p0, cp1, cp2, p], t)
|
|
73
|
-
poly.push(ptI)
|
|
74
|
-
}
|
|
77
|
+
//console.log(p);
|
|
75
78
|
|
|
79
|
+
if ((type === 'C' || type === 'Q') && split) {
|
|
80
|
+
let splitT = 1 / split;
|
|
81
|
+
for (let i = 1; i < split; i++) {
|
|
82
|
+
let t = splitT * i;
|
|
83
|
+
let cpts = type === 'C' ? [cp1, cp2] : [cp1];
|
|
84
|
+
let ptI = pointAtT([p0, ...cpts, p], t)
|
|
85
|
+
poly.push(ptI)
|
|
76
86
|
}
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
}
|
|
88
|
+
poly.push(p)
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
// remove short
|
|
94
|
+
let remove = new Set([])
|
|
95
|
+
for (let i = 1, l = poly.length; i < l; i++) {
|
|
96
|
+
let p = poly[i - 1]
|
|
97
|
+
let pN = poly[i]
|
|
98
|
+
|
|
99
|
+
let dist1 = getDistAv(p, pN)
|
|
100
|
+
if (dist1 < thresh && pN.extreme) {
|
|
101
|
+
let pR = p.extreme ? pN : p
|
|
102
|
+
let idx = p.extreme ? i : i - 1
|
|
103
|
+
//console.log('remove', idx);
|
|
104
|
+
remove.add(idx)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
79
108
|
|
|
80
|
-
|
|
109
|
+
remove = Array.from(remove).reverse();
|
|
110
|
+
//console.log(remove);
|
|
81
111
|
|
|
82
|
-
|
|
83
|
-
let
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
112
|
+
for (let i = 0; i < remove.length; i++) {
|
|
113
|
+
let idx = remove[i];
|
|
114
|
+
//console.log('idx', idx);
|
|
115
|
+
poly.splice(idx, 1)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
poly.splice(poly.length-1, poly.length)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
let polyAtt = poly.map(pt => `${pt.x} ${pt.y} `).join(' ')
|
|
122
|
+
//console.log('polyAtt', polyAtt);
|
|
123
|
+
|
|
124
|
+
//markers.insertAdjacentHTML('beforeend', `<polygon points="${polyAtt}" stroke="red" fill="none"/>`)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
poly = analyzePoly(poly, false)
|
|
128
|
+
let pathDataP = getCurvePathData(poly, 0.666, true)
|
|
129
|
+
let d = pathDataToD(pathDataP)
|
|
87
130
|
|
|
88
|
-
|
|
131
|
+
console.log(d);
|
|
132
|
+
//markers.insertAdjacentHTML('beforeend', `<path d="${d}" stroke="green" fill="none" stroke-width="1%"/>`)
|
|
89
133
|
|
|
90
|
-
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
return poly
|
|
91
139
|
|
|
92
140
|
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
@@ -3,7 +3,7 @@ import { getPolygonArea } from "./geometry_area";
|
|
|
3
3
|
import { getPolyBBox } from "./geometry_bbox";
|
|
4
4
|
import { renderPoint } from "./visualize";
|
|
5
5
|
|
|
6
|
-
export function analyzePoly(pts) {
|
|
6
|
+
export function analyzePoly(pts, debug=false) {
|
|
7
7
|
|
|
8
8
|
let l = pts.length;
|
|
9
9
|
let polyArea = getPolygonArea(pts, true)
|
|
@@ -75,22 +75,26 @@ export function analyzePoly(pts) {
|
|
|
75
75
|
/*
|
|
76
76
|
*/
|
|
77
77
|
|
|
78
|
-
if
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
78
|
+
if(debug){
|
|
79
|
+
|
|
80
|
+
if ((isExtreme && isCorner)) {
|
|
81
|
+
isExtreme = false;
|
|
82
|
+
directionChange = false;
|
|
83
|
+
//isCorner = false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (isExtreme) {
|
|
87
|
+
renderPoint(markers, pt1, 'cyan', '1%');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (isCorner) {
|
|
91
|
+
renderPoint(markers, pt1, 'purple', '0.5%');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (directionChange) {
|
|
95
|
+
renderPoint(markers, pt1, 'orange', '1.5%', '0.5');
|
|
96
|
+
}
|
|
91
97
|
|
|
92
|
-
if (directionChange) {
|
|
93
|
-
renderPoint(markers, pt1, 'orange', '1.5%', '0.5');
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
|
package/src/svgii/svg_cleanup.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
export function removeEmptySVGEls(svg) {
|
|
2
|
+
let els = svg.querySelectorAll('g, defs');
|
|
3
|
+
els.forEach(el => {
|
|
4
|
+
if (!el.children.length) el.remove()
|
|
5
|
+
})
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
1
10
|
export function cleanUpSVG(svgMarkup, {
|
|
2
11
|
returnDom=false,
|
|
3
12
|
removeHidden=true,
|
|
@@ -74,7 +83,7 @@ function removeNameSpaceAtts(el) {
|
|
|
74
83
|
});
|
|
75
84
|
}
|
|
76
85
|
|
|
77
|
-
function stringifySVG(svg){
|
|
86
|
+
export function stringifySVG(svg){
|
|
78
87
|
let markup = new XMLSerializer().serializeToString(svg);
|
|
79
88
|
markup = markup
|
|
80
89
|
.replace(/\t/g, "")
|
package/test.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { svgPathSimplify } from 'svg-path-simplify';
|
|
2
|
+
|
|
3
|
+
let pathDataString =
|
|
4
|
+
`
|
|
5
|
+
M 57.13 15.5
|
|
6
|
+
c 13.28 0 24.53 8.67 28.42 20.65
|
|
7
|
+
c 0.94 2.91 1.45 6.01 1.45 9.23
|
|
8
|
+
`
|
|
9
|
+
|
|
10
|
+
// try to simplify
|
|
11
|
+
let pathDataOpt = svgPathSimplify(pathDataString);
|
|
12
|
+
|
|
13
|
+
// simplified pathData
|
|
14
|
+
console.log(pathDataOpt)
|
|
File without changes
|