svg-path-simplify 0.4.2 → 0.4.4

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +7 -4
  3. package/dist/svg-path-simplify.esm.js +3593 -1279
  4. package/dist/svg-path-simplify.esm.min.js +2 -2
  5. package/dist/svg-path-simplify.js +3594 -1278
  6. package/dist/svg-path-simplify.min.js +2 -2
  7. package/dist/svg-path-simplify.pathdata.esm.js +1017 -538
  8. package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
  9. package/dist/svg-path-simplify.poly.cjs +9 -8
  10. package/docs/privacy-webapp.md +24 -0
  11. package/index.html +331 -152
  12. package/package.json +1 -1
  13. package/src/constants.js +4 -0
  14. package/src/css_parse.js +317 -0
  15. package/src/detect_input.js +76 -28
  16. package/src/index.js +8 -0
  17. package/src/pathData_simplify_cubic.js +26 -16
  18. package/src/pathData_simplify_harmonize_cpts.js +77 -1
  19. package/src/pathData_simplify_revertToquadratics.js +0 -1
  20. package/src/pathSimplify-main.js +304 -276
  21. package/src/pathSimplify-only-pathdata.js +7 -2
  22. package/src/pathSimplify-presets.js +254 -0
  23. package/src/poly-fit-curve-schneider.js +14 -7
  24. package/src/simplify_poly_RC.js +102 -0
  25. package/src/simplify_poly_RDP.js +109 -1
  26. package/src/simplify_poly_radial_distance.js +3 -3
  27. package/src/string_helpers.js +130 -4
  28. package/src/svg-getAttributes.js +4 -2
  29. package/src/svgii/convert_units.js +1 -1
  30. package/src/svgii/geometry.js +322 -5
  31. package/src/svgii/geometry_bbox_element.js +1 -1
  32. package/src/svgii/geometry_deduceRadius.js +116 -27
  33. package/src/svgii/geometry_length.js +253 -0
  34. package/src/svgii/pathData_analyze.js +18 -0
  35. package/src/svgii/pathData_convert.js +193 -89
  36. package/src/svgii/pathData_fix_directions.js +12 -14
  37. package/src/svgii/pathData_fromPoly.js +3 -3
  38. package/src/svgii/pathData_getLength.js +86 -0
  39. package/src/svgii/pathData_parse.js +2 -0
  40. package/src/svgii/pathData_parse_els.js +66 -68
  41. package/src/svgii/pathData_reorder.js +122 -16
  42. package/src/svgii/pathData_simplify_refineCorners.js +130 -35
  43. package/src/svgii/pathData_simplify_refine_round.js +420 -0
  44. package/src/svgii/pathData_split_to_groups.js +168 -0
  45. package/src/svgii/pathData_stringify.js +26 -64
  46. package/src/svgii/pathData_toPolygon.js +3 -4
  47. package/src/svgii/poly_analyze.js +61 -0
  48. package/src/svgii/poly_normalize.js +11 -2
  49. package/src/svgii/poly_to_pathdata.js +85 -24
  50. package/src/svgii/rounding.js +80 -78
  51. package/src/svgii/svg_cleanup.js +421 -619
  52. package/src/svgii/svg_cleanup_convertPathLength.js +39 -0
  53. package/src/svgii/svg_cleanup_general_svg_atts.js +97 -0
  54. package/src/svgii/svg_cleanup_normalize_transforms.js +83 -0
  55. package/src/svgii/svg_cleanup_remove_els_and_atts.js +77 -0
  56. package/src/svgii/svg_cleanup_ungroup.js +36 -0
  57. package/src/svgii/svg_el_parse_style_props.js +72 -47
  58. package/src/svgii/svg_getElementLength.js +67 -0
  59. package/src/svgii/svg_validate.js +220 -0
  60. package/tests/testSVG.js +14 -1
  61. package/src/svgii/pathData_refine_round.js +0 -222
@@ -13,10 +13,11 @@ import { pathDataToD } from './svgii/pathData_stringify';
13
13
  import { detectAccuracy } from './svgii/rounding';
14
14
  import { refineAdjacentExtremes } from './svgii/pathData_simplify_refineExtremes';
15
15
  import { refineRoundedCorners } from './svgii/pathData_simplify_refineCorners';
16
- import { refineRoundSegments } from './svgii/pathData_refine_round';
16
+ //import { refineRoundSegments } from './svgii/pathData_refine_round';
17
17
  import { refineClosingCommand } from './svgii/pathData_remove_short';
18
18
  import { pathDataRevertCubicToQuadratic } from './pathData_simplify_revertToquadratics';
19
19
  import { pathDataLineToCubic } from './svgii/pathData_line_to_cubic';
20
+ import { refineRoundSegments } from './svgii/pathData_simplify_refine_round';
20
21
 
21
22
  //import { installDOMPolyfills } from './dom-polyfill';
22
23
 
@@ -97,7 +98,11 @@ export function simplifyPathData(input = '', {
97
98
  let yArr = []
98
99
 
99
100
  // mode:0 – single path
100
- let inputType = detectInputType(input)
101
+ //let inputType = detectInputType(input)
102
+ let inputDetection = detectInputType(input);
103
+ let {inputType, log} = inputDetection
104
+
105
+
101
106
  if (inputType === 'pathDataString') {
102
107
  d = input
103
108
  } else if (inputType === 'polyString') {
@@ -0,0 +1,254 @@
1
+ export let settingsDefaults = {
2
+
3
+ // SVG elements
4
+ removeComments: true,
5
+ removeOffCanvas: false,
6
+
7
+ // attributes
8
+ removeDimensions: false,
9
+ removeIds: false,
10
+ removeClassNames: false,
11
+ omitNamespace: false,
12
+ cleanUpStrokes: true,
13
+ addViewBox: true,
14
+ addDimensions: false,
15
+ removePrologue: true,
16
+ removeHidden: true,
17
+ removeUnused: true,
18
+ cleanupDefs: true,
19
+ cleanupClip: true,
20
+ cleanupSVGAtts: true,
21
+ removeNameSpaced: true,
22
+ removeNameSpacedAtts: true,
23
+ attributesToGroup: false,
24
+ minifyRgbColors: true,
25
+ stylesToAttributes: false,
26
+ fixHref: false,
27
+ legacyHref: false,
28
+ allowMeta: false,
29
+ allowDataAtts: true,
30
+ allowAriaAtts: true,
31
+ //pathlength conversion
32
+ convertPathLength: false,
33
+ toAbsoluteUnits: false,
34
+
35
+ // custom removal
36
+ removeElements: [],
37
+ removeSVGAttributes: [],
38
+ removeElAttributes: [],
39
+
40
+ // merging/splitting
41
+ unGroup: false,
42
+ mergePaths: false,
43
+ splitCompound: false,
44
+
45
+
46
+
47
+ // shape conversions
48
+ shapesToPaths: false,
49
+ shapeConvert: 0,
50
+ convertShapes: ['rect', 'ellipse', 'circle', 'line', 'polygon', 'polyline'],
51
+
52
+
53
+ // simplify
54
+ keepSmaller: true,
55
+ simplifyBezier: true,
56
+ optimizeOrder: true,
57
+ autoClose: false,
58
+ removeZeroLength: true,
59
+ refineClosing: true,
60
+ removeColinear: true,
61
+ flatBezierToLinetos: true,
62
+ revertToQuadratics: true,
63
+ refineExtremes: false,
64
+ simplifyCorners: false,
65
+ simplifyQuadraticCorners: false,
66
+ keepExtremes: true,
67
+ keepCorners: true,
68
+ keepInflections: false,
69
+ addExtremes: false,
70
+
71
+ // draw direction
72
+ fixDirections: false,
73
+ reversePath: false,
74
+
75
+
76
+ // pathdata
77
+ toAbsolute: false,
78
+ toRelative: true,
79
+ toMixed: false,
80
+ toShorthands: true,
81
+ toLonghands: false,
82
+ quadraticToCubic: true,
83
+ arcToCubic: false,
84
+ cubicToArc: false,
85
+ lineToCubic: false,
86
+
87
+ // minification
88
+ decimals: 3,
89
+ autoAccuracy: true,
90
+ minifyD: 0,
91
+ tolerance: 1,
92
+
93
+
94
+ // polygon
95
+ toPolygon: false,
96
+ smoothPoly: false,
97
+ polyFormat: 'object',
98
+ precisionPoly: 1,
99
+ simplifyRD: 0,
100
+ simplifyRDP: 0,
101
+ harmonizeCpts: false,
102
+ removeOrphanSubpaths: false,
103
+ simplifyRound: false,
104
+
105
+ //svg scaling
106
+ scale: 1,
107
+ scaleTo: 0,
108
+ crop: false,
109
+ alignToOrigin: false,
110
+
111
+ // flatten transforms
112
+ convertTransforms: false,
113
+
114
+
115
+ }
116
+
117
+ const settingsNull = {}
118
+
119
+ for (let prop in settingsDefaults) {
120
+ let val = settingsDefaults[prop];
121
+ let isBoolean = val === false || val === true;
122
+ let isNum = !isNaN(val)
123
+ let isArray = Array.isArray(val)
124
+
125
+ if (isBoolean) val = false
126
+ else if (!isArray && isNum) val = val === 1 ? 1 : (prop === 'decimals' ? -1 : 0);
127
+ else if (isArray) val = []
128
+ settingsNull[prop] = val;
129
+ }
130
+
131
+
132
+ export const presetSettings = {
133
+ default: settingsDefaults,
134
+
135
+ education: {
136
+ ...settingsDefaults,
137
+ ...{
138
+ keepSmaller: false,
139
+ toRelative: false,
140
+ toMixed: false,
141
+ toShorthands: false,
142
+ fixHref: true,
143
+ legacyHref: false,
144
+ addViewBox: true,
145
+ addDimensions: true,
146
+ removeComments: false,
147
+ decimals: 3,
148
+ minifyD: 2
149
+ }
150
+ },
151
+
152
+ null: settingsNull,
153
+
154
+ editor: {
155
+ ...settingsDefaults,
156
+ ...{
157
+ keepSmaller: false,
158
+ convertPathLength:true,
159
+ toRelative: true,
160
+ toMixed: true,
161
+ toShorthands: true,
162
+ //fixHref: true,
163
+ allowMeta:true,
164
+ allowDataAtts:true,
165
+ allowAriaAtts:true,
166
+ legacyHref: true,
167
+ addViewBox: true,
168
+ addDimensions: true,
169
+ removeComments: true,
170
+ autoAccuracy: true,
171
+ //decimals:5,
172
+ minifyD: 0.5
173
+ }
174
+ },
175
+
176
+ noSimplification: {
177
+ ...settingsDefaults,
178
+ ...{
179
+ simplifyBezier: false,
180
+ quadraticToCubic: false,
181
+ toRelative: true,
182
+ toShorthands: true,
183
+ fixHref: true,
184
+ optimizeOrder: false,
185
+ removeZeroLength: false,
186
+ refineExtremes: false,
187
+ refineClosing: false,
188
+ removeColinear: false,
189
+ flatBezierToLinetos: false,
190
+ //addViewBox: false,
191
+ addDimensions: false,
192
+ removeComments: true,
193
+ minifyD: 0
194
+ }
195
+
196
+ },
197
+ path: {
198
+ ...settingsDefaults,
199
+ ...{
200
+ shapeConvert: 'toPaths',
201
+ convertShapes: ['rect', 'ellipse', 'circle', 'line', 'polygon', 'polyline'],
202
+ addViewBox: true,
203
+ minifyD: 0.5
204
+ }
205
+ },
206
+
207
+ poly: {
208
+ ...settingsDefaults,
209
+ ...{
210
+ toPolygon: true,
211
+ }
212
+ },
213
+
214
+ curvefit: {
215
+ ...settingsDefaults,
216
+ ...{
217
+ smoothPoly: true,
218
+ }
219
+ },
220
+
221
+ detransform: {
222
+ ...settingsDefaults,
223
+ ...{
224
+ convertTransforms: true,
225
+ addViewBox: true,
226
+ minifyD: 0.5
227
+ }
228
+ },
229
+
230
+ high: {
231
+ ...settingsDefaults,
232
+ ...{
233
+ tolerance: 1.1,
234
+ toMixed: true,
235
+ refineExtremes: true,
236
+ simplifyCorners: true,
237
+ simplifyQuadraticCorners: true,
238
+ removeOrphanSubpaths: true,
239
+ simplifyRound: true,
240
+ removeClassNames: true,
241
+ cubicToArc: true,
242
+ minifyD: 0,
243
+ removeComments: true,
244
+ removeHidden: true,
245
+ addViewBox: true,
246
+ removeDimensions: true,
247
+ removeOffCanvas: true,
248
+
249
+ /*
250
+ */
251
+ }
252
+ }
253
+
254
+ }
@@ -9,13 +9,16 @@
9
9
  */
10
10
 
11
11
  import { harmonizeCubicCpts, harmonizeCubicCptsThird } from "./pathData_simplify_harmonize_cpts";
12
- import { getAngle, getDistance, getSquareDistance, pointAtT, rotatePoint } from "./svgii/geometry";
12
+ import { checkLineIntersection, getAngle, getDistance, getSquareDistance, pointAtT, reducePoints, rotatePoint } from "./svgii/geometry";
13
13
  import { getPolygonArea } from "./svgii/geometry_area";
14
+ import { detectRegularPolygon, getPolyCentroid } from "./svgii/poly_analyze";
14
15
  import { renderPath, renderPoint } from "./svgii/visualize";
15
16
 
16
17
 
17
18
 
18
19
 
20
+
21
+
19
22
  /**
20
23
  * Fit one or more Bezier curves to a set of pts.
21
24
  *
@@ -50,17 +53,18 @@ export function fitCurveSchneider(pts, {
50
53
 
51
54
  // create pathdata
52
55
  let pathData = bezierPtsToPathData(beziers)
53
-
54
-
55
-
56
56
  let cp1, cp2;
57
57
 
58
58
  adjustCpts = false
59
- harmonize = false;
59
+ //harmonize = false;
60
+
61
+ adjustCpts = true;
62
+ //harmonize = true;
63
+
60
64
 
61
65
  if (adjustCpts) {
62
66
 
63
- console.log('refine cpts');
67
+ //console.log('refine cpts');
64
68
 
65
69
  let len2 = pathData.length;
66
70
  let com1 = pathData[0]
@@ -91,13 +95,16 @@ export function fitCurveSchneider(pts, {
91
95
  com2.values[3] = cp2.y
92
96
  }
93
97
 
98
+ /*
94
99
  // harmonize too tight tangents
95
- //let harmonize= true;
100
+ let harmonize = true;
101
+ harmonize = false;
96
102
  if (harmonize) {
97
103
  pathData = harmonizeCubicCptsThird([{ type: 'M', values: [pts[0].x, pts[0].y] },
98
104
  ...pathData])
99
105
  pathData.shift()
100
106
  }
107
+ */
101
108
 
102
109
  }
103
110
 
@@ -0,0 +1,102 @@
1
+ import { getSquareDistance, reducePoints } from "./svgii/geometry";
2
+ import { getPolygonArea } from "./svgii/geometry_area";
3
+ import { getPolyBBox } from "./svgii/geometry_bbox";
4
+
5
+ export function simplifyRC(pts, quality = 1, shiftStart = true) {
6
+
7
+ if (pts.length < 4) return pts;
8
+
9
+ let l = pts.length;
10
+
11
+
12
+ // starting point
13
+ let M = pts[0];
14
+
15
+ // last point
16
+ let Z = pts[l - 1];
17
+
18
+ // remove unnecessary closing point
19
+ if (M.x === Z.x && M.y === Z.y) {
20
+ pts.pop();
21
+ l--;
22
+ Z = pts[l - 1];
23
+ }
24
+
25
+ // init new point array
26
+ let ptsSmp = [M];
27
+ let pt0 = M;
28
+ let pt1, pt2;
29
+
30
+ // loop through vertices by triangles
31
+ for (let i = 2; i < l; i++) {
32
+ pt1 = pts[i - 1];
33
+ pt2 = pts[i];
34
+ let isLast = i === l - 1;
35
+
36
+ /**
37
+ * 1. Skip zero-length segments
38
+ */
39
+ if ((pt1.x === pt0.x && pt1.y === pt0.y) || (pt1.x === pt2.x && pt1.y === pt2.y)) {
40
+ continue;
41
+ }
42
+
43
+ /**
44
+ * 2. Check for perfectly flat
45
+ * vertical/horizontal segments
46
+ */
47
+ let isVertical = (pt0.x === pt1.x);
48
+ let isHorizontal = (pt0.y === pt1.y);
49
+
50
+ if (isVertical || isHorizontal) {
51
+
52
+ let isVertical2 = (pt1.x === pt2.x);
53
+ let isHorizontal2 = (pt1.y === pt2.y);
54
+
55
+ if (((isVertical && isVertical2) || (isHorizontal && isHorizontal2))) {
56
+
57
+ // perfectly flat segment - skip
58
+ if (!isLast) continue;
59
+
60
+ // flat but last – add last and skip colinearity check
61
+ if (isLast && M.x !== pt2.x && M.y !== pt2.y) {
62
+
63
+ ptsSmp.push(pt2);
64
+ continue
65
+ }
66
+
67
+ }
68
+ }
69
+
70
+ // check area
71
+ let area = getPolygonArea([pt0, pt1, pt2], true)
72
+ let thresh = getSquareDistance(pt0, pt2) * 0.005;
73
+
74
+ // flat
75
+ if ( area <= thresh && i<l-1) {
76
+ //console.log(area, thresh, pt0, pt1, pt2, i);
77
+ pt0 = pt1;
78
+ continue
79
+ }
80
+
81
+ // no simplification - add mid pt
82
+ ptsSmp.push(pt1);
83
+
84
+ // add last point if not first
85
+ if (isLast && M.x !== pt2.x && M.y !== pt2.y) {
86
+ // console.log('add last', M, pt2);
87
+ ptsSmp.push(pt2);
88
+ }
89
+
90
+ // update previous point
91
+ pt0 = pt1;
92
+
93
+ }
94
+
95
+ // 1st and last are colinear
96
+ let area0 = getPolygonArea([ptsSmp[1], M, ptsSmp[ptsSmp.length-1]], true)
97
+ let thresh0 = getSquareDistance (ptsSmp[1], ptsSmp[ptsSmp.length-1]) * 0.005
98
+ // remove first point
99
+ if(area0 < thresh0) ptsSmp.shift()
100
+
101
+ return ptsSmp;
102
+ }
@@ -3,6 +3,113 @@ import { getPolyBBox } from "./svgii/geometry_bbox";
3
3
  import { renderPoint } from "./svgii/visualize";
4
4
 
5
5
 
6
+
7
+ export function simplifyRDP_rel(pts, quality = 0.9, width = 0, height = 0) {
8
+
9
+ /**
10
+ * switch between absolute or
11
+ * quality based relative thresholds
12
+ */
13
+ let isAbsolute = false;
14
+
15
+ if (typeof quality === 'string') {
16
+ isAbsolute = true;
17
+ quality = parseFloat(quality);
18
+ }
19
+
20
+ if (pts.length < 4 ) return pts;
21
+
22
+ // convert quality to squaredistance tolerance
23
+ let tolerance = quality;
24
+ //console.log('simplifyRDP', tolerance);
25
+
26
+ if (!isAbsolute) {
27
+
28
+ //tolerance = 1 - quality;
29
+ tolerance = quality;
30
+
31
+ // adjust for higher qualities
32
+ if (quality > 0.5) tolerance /= 2;
33
+
34
+ /**
35
+ * approximate dimensions
36
+ * adjust tolerance for
37
+ * very small polygons e.g geodata
38
+ */
39
+ if (!width && !height) {
40
+ let polyS = reducePoints(pts, 12);
41
+ ({ width, height } = getPolyBBox(polyS));
42
+ }
43
+
44
+ // average side lengths
45
+ let dimAvg = (width + height) / 2;
46
+ let scale = dimAvg / 100;
47
+ tolerance = (tolerance * (scale)) ** 2
48
+
49
+ console.log('!!!tolerance', tolerance);
50
+
51
+
52
+ }
53
+
54
+
55
+ // Square distance from point to segment
56
+ const segmentSquareDistance = (p, p1, p2) => {
57
+ let x = p1.x, y = p1.y;
58
+ let dx = p2.x - x, dy = p2.y - y;
59
+
60
+ if (dx !== 0 || dy !== 0) {
61
+ let t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);
62
+ if (t > 1) {
63
+ x = p2.x;
64
+ y = p2.y;
65
+ } else if (t > 0) {
66
+ x += dx * t;
67
+ y += dy * t;
68
+ }
69
+ }
70
+
71
+ return (p.x - x) ** 2 + (p.y - y) ** 2;
72
+ };
73
+
74
+
75
+ // start collecting ptsSmp polyline
76
+ let ptsSmp = [pts[0]];
77
+
78
+ // create processing stack
79
+ let stack = [];
80
+ stack.push([0, pts.length - 1]);
81
+
82
+ while (stack.length > 0) {
83
+ let [first, last] = stack.pop();
84
+ let maxDist = tolerance;
85
+ let index = -1;
86
+
87
+ // Find point with maximum distance
88
+ for (let i = first + 1; i < last; i++) {
89
+ let currentDist = segmentSquareDistance(pts[i], pts[first], pts[last]);
90
+ if (currentDist > maxDist) {
91
+ index = i;
92
+ maxDist = currentDist;
93
+ }
94
+ }
95
+
96
+ // If max distance > tolerance, split and process
97
+ if (maxDist > tolerance) {
98
+ stack.push([index, last]);
99
+ stack.push([first, index]);
100
+ } else {
101
+ ptsSmp.push(pts[last]);
102
+ }
103
+
104
+ }
105
+
106
+ return ptsSmp;
107
+ }
108
+
109
+
110
+
111
+
112
+
6
113
  export function simplifyPolyRDP(pts, {quality = 0.9, width = 0, height = 0}={}) {
7
114
 
8
115
  /**
@@ -16,7 +123,8 @@ export function simplifyPolyRDP(pts, {quality = 0.9, width = 0, height = 0}={})
16
123
  quality = parseFloat(quality);
17
124
  }
18
125
 
19
- if (pts.length < 4 || (!isAbsolute && quality) >= 1) return pts;
126
+ //|| (!isAbsolute && quality) >= 1
127
+ if (pts.length < 4 ) return pts;
20
128
 
21
129
  // convert quality to squaredistance tolerance
22
130
  let tolerance = quality;
@@ -31,7 +31,8 @@ export function simplifyPolyRD(pts, {quality = 0.9, width = 0, height = 0}={}) {
31
31
  }
32
32
 
33
33
  // nothing to do - exit
34
- if (pts.length < 4 || (!isAbsolute && quality) >= 1) return pts;
34
+ //|| (!isAbsolute && quality) >= 1
35
+ if (pts.length < 4 ) return pts;
35
36
 
36
37
  let p0 = pts[0];
37
38
  let pt;
@@ -43,8 +44,7 @@ export function simplifyPolyRD(pts, {quality = 0.9, width = 0, height = 0}={}) {
43
44
  if (!isAbsolute) {
44
45
 
45
46
  // quality to tolerance
46
- tolerance = 1 - quality;
47
-
47
+ tolerance = quality;
48
48
 
49
49
  /**
50
50
  * approximate dimensions