svg-path-simplify 0.4.2 → 0.4.3
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/CHANGELOG.md +10 -0
- package/README.md +6 -4
- package/dist/svg-path-simplify.esm.js +2139 -940
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +2139 -940
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +127 -85
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/docs/privacy-webapp.md +24 -0
- package/index.html +290 -152
- package/package.json +1 -1
- package/src/css_parse.js +317 -0
- package/src/detect_input.js +34 -4
- package/src/pathData_simplify_harmonize_cpts.js +77 -1
- package/src/pathSimplify-main.js +242 -269
- package/src/pathSimplify-presets.js +243 -0
- package/src/poly-fit-curve-schneider.js +14 -7
- package/src/simplify_poly_RC.js +102 -0
- package/src/simplify_poly_RDP.js +109 -1
- package/src/simplify_poly_radial_distance.js +3 -3
- package/src/string_helpers.js +130 -4
- package/src/svgii/geometry.js +182 -3
- package/src/svgii/geometry_length.js +237 -0
- package/src/svgii/pathData_convert.js +5 -1
- package/src/svgii/pathData_fix_directions.js +6 -0
- package/src/svgii/pathData_fromPoly.js +3 -3
- package/src/svgii/pathData_getLength.js +86 -0
- package/src/svgii/pathData_parse.js +2 -0
- package/src/svgii/pathData_parse_els.js +66 -68
- package/src/svgii/pathData_split_to_groups.js +168 -0
- package/src/svgii/pathData_stringify.js +26 -64
- package/src/svgii/pathData_toPolygon.js +3 -4
- package/src/svgii/poly_analyze.js +61 -0
- package/src/svgii/poly_normalize.js +11 -2
- package/src/svgii/poly_to_pathdata.js +85 -24
- package/src/svgii/rounding.js +8 -7
- package/src/svgii/svg_cleanup.js +374 -620
- package/src/svgii/svg_cleanup_convertPathLength.js +32 -0
- package/src/svgii/svg_cleanup_general_svg_atts.js +97 -0
- package/src/svgii/svg_cleanup_normalize_transforms.js +83 -0
- package/src/svgii/svg_cleanup_remove_els_and_atts.js +72 -0
- package/src/svgii/svg_cleanup_ungroup.js +36 -0
- package/src/svgii/svg_el_parse_style_props.js +65 -43
- package/src/svgii/svg_getElementLength.js +67 -0
|
@@ -16,29 +16,25 @@ import { autoRound, roundTo } from './rounding.js';
|
|
|
16
16
|
import { attLookup } from './svg-styles-to-attributes-const.js';
|
|
17
17
|
import { qrDecomposeMatrix } from './transform_qr_decompose.js';
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Convert shapes to paths
|
|
21
|
+
* converts also transforms
|
|
22
|
+
*/
|
|
20
23
|
export function shapeElToPath(el, { width = 0,
|
|
21
24
|
height = 0,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
convert_poly = false,
|
|
25
|
-
convert_lines = false,
|
|
26
|
-
//matrix={a:1, b:0, c:0, d:1, e:0, f:0},
|
|
27
|
-
matrix=null
|
|
25
|
+
convertShapes = [],
|
|
26
|
+
matrix = null
|
|
28
27
|
|
|
29
28
|
} = {}) {
|
|
30
29
|
|
|
30
|
+
|
|
31
31
|
let nodeName = el.nodeName.toLowerCase();
|
|
32
32
|
//console.log('shapeElToPath', nodeName);
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
(nodeName === 'circle' || nodeName === 'ellipse') && !convert_ellipses ||
|
|
39
|
-
(nodeName === 'polygon' || nodeName === 'polyline') && !convert_poly ||
|
|
40
|
-
(nodeName === 'line') && !convert_lines
|
|
41
|
-
) return el;
|
|
35
|
+
|
|
36
|
+
if (!convertShapes.includes(nodeName)) return el;
|
|
37
|
+
//console.log(convertShapes);
|
|
42
38
|
|
|
43
39
|
|
|
44
40
|
let pathData = getPathDataFromEl(el, { width, height });
|
|
@@ -47,8 +43,9 @@ export function shapeElToPath(el, { width = 0,
|
|
|
47
43
|
let exclude = ['d', 'x', 'y', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points'];
|
|
48
44
|
|
|
49
45
|
// transform pathData
|
|
50
|
-
if(matrix && Object.values(matrix).join('')!=='100100'){
|
|
46
|
+
if (matrix && Object.values(matrix).join('') !== '100100') {
|
|
51
47
|
pathData = transformPathData(pathData, matrix)
|
|
48
|
+
//console.log('transformPathData', pathData);
|
|
52
49
|
exclude.push('transform', 'transform-origin')
|
|
53
50
|
}
|
|
54
51
|
|
|
@@ -72,14 +69,7 @@ export function shapeElToPath(el, { width = 0,
|
|
|
72
69
|
return pathN
|
|
73
70
|
|
|
74
71
|
}
|
|
75
|
-
/*
|
|
76
|
-
export function copyAttributes(newEl, oldEl){
|
|
77
72
|
|
|
78
|
-
let attributes = [...oldEl.attributes].map(att => att.name);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
*/
|
|
83
73
|
|
|
84
74
|
|
|
85
75
|
// retrieve pathdata from svg geometry elements
|
|
@@ -112,39 +102,7 @@ export function getPathDataFromEl(el, {
|
|
|
112
102
|
case 'rect':
|
|
113
103
|
attNames = ['x', 'y', 'width', 'height', 'rx', 'ry'];
|
|
114
104
|
({ x=0, y=0, width=0, height=0, rx=0, ry=0 } = atts);
|
|
115
|
-
|
|
116
|
-
if (!rx && !ry) {
|
|
117
|
-
pathData = [
|
|
118
|
-
{ type: "M", values: [x, y] },
|
|
119
|
-
{ type: "L", values: [x + width, y] },
|
|
120
|
-
{ type: "L", values: [x + width, y + height] },
|
|
121
|
-
{ type: "L", values: [x, y + height] },
|
|
122
|
-
{ type: "Z", values: [] }
|
|
123
|
-
];
|
|
124
|
-
} else {
|
|
125
|
-
|
|
126
|
-
rx = rx ? rx : ry;
|
|
127
|
-
ry = ry ? ry : rx;
|
|
128
|
-
|
|
129
|
-
if (rx > width / 2) {
|
|
130
|
-
rx = width / 2;
|
|
131
|
-
}
|
|
132
|
-
if (ry > height / 2) {
|
|
133
|
-
ry = height / 2;
|
|
134
|
-
}
|
|
135
|
-
pathData = [
|
|
136
|
-
{ type: "M", values: [x + rx, y] },
|
|
137
|
-
{ type: "L", values: [x + width - rx, y] },
|
|
138
|
-
{ type: "A", values: [rx, ry, 0, 0, 1, x + width, y + ry] },
|
|
139
|
-
{ type: "L", values: [x + width, y + height - ry] },
|
|
140
|
-
{ type: "A", values: [rx, ry, 0, 0, 1, x + width - rx, y + height] },
|
|
141
|
-
{ type: "L", values: [x + rx, y + height] },
|
|
142
|
-
{ type: "A", values: [rx, ry, 0, 0, 1, x, y + height - ry] },
|
|
143
|
-
{ type: "L", values: [x, y + ry] },
|
|
144
|
-
{ type: "A", values: [rx, ry, 0, 0, 1, x + rx, y] },
|
|
145
|
-
{ type: "Z", values: [] }
|
|
146
|
-
];
|
|
147
|
-
}
|
|
105
|
+
pathData = rectToPathData(x, y, width, height, rx, ry);
|
|
148
106
|
break;
|
|
149
107
|
|
|
150
108
|
case 'circle':
|
|
@@ -164,7 +122,6 @@ export function getPathDataFromEl(el, {
|
|
|
164
122
|
ry = ry ? ry : r;
|
|
165
123
|
}
|
|
166
124
|
|
|
167
|
-
|
|
168
125
|
// simplified radii for circles
|
|
169
126
|
let rxS = isCircle && r >= 1 ? 1 : rx;
|
|
170
127
|
let ryS = isCircle && r >= 1 ? 1 : ry;
|
|
@@ -210,13 +167,51 @@ export function getPathDataFromEl(el, {
|
|
|
210
167
|
};
|
|
211
168
|
|
|
212
169
|
|
|
170
|
+
export function rectToPathData(x = 0, y = 0, width = 0, height = 0, rx = 0, ry = 0) {
|
|
171
|
+
let pathData = [];
|
|
172
|
+
|
|
173
|
+
if (!rx && !ry) {
|
|
174
|
+
pathData = [
|
|
175
|
+
{ type: "M", values: [x, y] },
|
|
176
|
+
{ type: "L", values: [x + width, y] },
|
|
177
|
+
{ type: "L", values: [x + width, y + height] },
|
|
178
|
+
{ type: "L", values: [x, y + height] },
|
|
179
|
+
{ type: "Z", values: [] }
|
|
180
|
+
];
|
|
181
|
+
} else {
|
|
182
|
+
|
|
183
|
+
rx = rx ? rx : ry;
|
|
184
|
+
ry = ry ? ry : rx;
|
|
185
|
+
|
|
186
|
+
if (rx > width / 2) {
|
|
187
|
+
rx = width / 2;
|
|
188
|
+
}
|
|
189
|
+
if (ry > height / 2) {
|
|
190
|
+
ry = height / 2;
|
|
191
|
+
}
|
|
192
|
+
pathData = [
|
|
193
|
+
{ type: "M", values: [x + rx, y] },
|
|
194
|
+
{ type: "L", values: [x + width - rx, y] },
|
|
195
|
+
{ type: "A", values: [rx, ry, 0, 0, 1, x + width, y + ry] },
|
|
196
|
+
{ type: "L", values: [x + width, y + height - ry] },
|
|
197
|
+
{ type: "A", values: [rx, ry, 0, 0, 1, x + width - rx, y + height] },
|
|
198
|
+
{ type: "L", values: [x + rx, y + height] },
|
|
199
|
+
{ type: "A", values: [rx, ry, 0, 0, 1, x, y + height - ry] },
|
|
200
|
+
{ type: "L", values: [x, y + ry] },
|
|
201
|
+
{ type: "A", values: [rx, ry, 0, 0, 1, x + rx, y] },
|
|
202
|
+
{ type: "Z", values: [] }
|
|
203
|
+
];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return pathData
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
213
211
|
|
|
214
212
|
|
|
215
213
|
export function pathElToShape(el, {
|
|
216
|
-
|
|
217
|
-
convert_ellipses = false,
|
|
218
|
-
convert_poly = false,
|
|
219
|
-
convert_lines = false
|
|
214
|
+
convertShapes = [],
|
|
220
215
|
} = {}) {
|
|
221
216
|
|
|
222
217
|
//console.log('pathElToShape', convert_rects, convert_ellipses, convert_lines );
|
|
@@ -236,10 +231,13 @@ export function pathElToShape(el, {
|
|
|
236
231
|
let attsNew = {}
|
|
237
232
|
let decimals = 7;
|
|
238
233
|
|
|
234
|
+
|
|
239
235
|
if (isPoly) {
|
|
240
236
|
|
|
237
|
+
//console.log('pathsToShapes', isPoly);
|
|
238
|
+
|
|
241
239
|
// is line
|
|
242
|
-
if (pathData.length === 2 &&
|
|
240
|
+
if (pathData.length === 2 && convertShapes.includes('line')) {
|
|
243
241
|
type = 'line'
|
|
244
242
|
shape = document.createElementNS(svgNs, type)
|
|
245
243
|
let [x1, y1, x2, y2] = [...pathData[0].values, ...pathData[1].values].map(val => roundTo(val, decimals))
|
|
@@ -255,7 +253,7 @@ export function pathElToShape(el, {
|
|
|
255
253
|
let areaDiff = Math.abs(1 - areaRect / areaPoly);
|
|
256
254
|
|
|
257
255
|
// is rect
|
|
258
|
-
if (
|
|
256
|
+
if (convertShapes.includes('rect') && areaDiff < 0.01) {
|
|
259
257
|
type = 'rect'
|
|
260
258
|
shape = document.createElementNS(svgNs, type)
|
|
261
259
|
let { x, y, width, height } = bb
|
|
@@ -263,7 +261,7 @@ export function pathElToShape(el, {
|
|
|
263
261
|
|
|
264
262
|
}
|
|
265
263
|
// polyline or polygon
|
|
266
|
-
else if(
|
|
264
|
+
else if (convertShapes.includes('polygon') || convertShapes.includes('polyline')) {
|
|
267
265
|
type = closed ? 'polygon' : 'polyline';
|
|
268
266
|
shape = document.createElementNS(svgNs, type)
|
|
269
267
|
let points = vertices.map(pt => { return [pt.x, pt.y] }).flat().map(val => roundTo(val, decimals)).join(' ')
|
|
@@ -272,7 +270,7 @@ export function pathElToShape(el, {
|
|
|
272
270
|
}
|
|
273
271
|
}
|
|
274
272
|
// circles or ellipses
|
|
275
|
-
else if (!hasLines &&
|
|
273
|
+
else if (!hasLines && (convertShapes.includes('circle') || convertShapes.includes('ellipse'))) {
|
|
276
274
|
|
|
277
275
|
// try to convert cubics to arcs
|
|
278
276
|
if (!hasArcs && hasBeziers) {
|
|
@@ -308,11 +306,11 @@ export function pathElToShape(el, {
|
|
|
308
306
|
rxVals = Array.from(rxVals)
|
|
309
307
|
ryVals = Array.from(ryVals)
|
|
310
308
|
|
|
311
|
-
if(cxVals.length===1 && cyVals.length===1 && rxVals.length===1 && ryVals.length===1){
|
|
309
|
+
if (cxVals.length === 1 && cyVals.length === 1 && rxVals.length === 1 && ryVals.length === 1) {
|
|
312
310
|
let [rx, ry, cx, cy] = [rxVals[0], ryVals[0], cxVals[0], cyVals[0]]
|
|
313
|
-
type = rx===ry ? 'circle' : 'ellipse';
|
|
311
|
+
type = rx === ry ? 'circle' : 'ellipse';
|
|
314
312
|
shape = document.createElementNS(svgNs, type)
|
|
315
|
-
attsNew = type==='circle' ? { r:rx, cx, cy } : {rx, ry, cx, cy}
|
|
313
|
+
attsNew = type === 'circle' ? { r: rx, cx, cy } : { rx, ry, cx, cy }
|
|
316
314
|
}
|
|
317
315
|
}
|
|
318
316
|
}
|
|
@@ -334,11 +332,11 @@ export function pathElToShape(el, {
|
|
|
334
332
|
shape.setAttribute(att, attributes[att])
|
|
335
333
|
}
|
|
336
334
|
}
|
|
337
|
-
|
|
338
335
|
// replace
|
|
339
336
|
el = shape;
|
|
340
337
|
}
|
|
341
338
|
|
|
339
|
+
//console.log(el);
|
|
342
340
|
return el;
|
|
343
341
|
|
|
344
342
|
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { svgNs } from "../constants";
|
|
2
|
+
import { getPathDataVertices, isPointInPolygon } from "./geometry";
|
|
3
|
+
import { checkBBoxIntersections, getPolyBBox } from "./geometry_bbox";
|
|
4
|
+
import { convertPathData } from "./pathData_convert";
|
|
5
|
+
import { pathDataToD } from "./pathData_stringify";
|
|
6
|
+
import { roundTo } from "./rounding";
|
|
7
|
+
import { renderPoint } from "./visualize";
|
|
8
|
+
|
|
9
|
+
export function splitCompundGroups(pathDataPlusArr = [], {
|
|
10
|
+
toRelative = true,
|
|
11
|
+
toShorthands = true,
|
|
12
|
+
minifyD = 0,
|
|
13
|
+
decimals = 3,
|
|
14
|
+
addDimensions = false
|
|
15
|
+
} = {}) {
|
|
16
|
+
|
|
17
|
+
//console.log('???pathDataPlusArr', pathDataPlusArr);
|
|
18
|
+
let pathDataSplit = [];
|
|
19
|
+
pathDataPlusArr = JSON.parse(JSON.stringify(pathDataPlusArr))
|
|
20
|
+
let len = pathDataPlusArr.length;
|
|
21
|
+
|
|
22
|
+
//let bb0 =
|
|
23
|
+
let xArr = [];
|
|
24
|
+
let yArr = []
|
|
25
|
+
|
|
26
|
+
// refine bbox and add cpt polygon
|
|
27
|
+
for (let i = 0; i < len; i++) {
|
|
28
|
+
let sub = pathDataPlusArr[i]
|
|
29
|
+
let { pathData, bb } = sub
|
|
30
|
+
|
|
31
|
+
// console.log(bb);
|
|
32
|
+
// include control points for better overlapping approximation
|
|
33
|
+
//let poly = getPathDataVertices(pathData, true);
|
|
34
|
+
//let bb2 = getPolyBBox(poly);
|
|
35
|
+
|
|
36
|
+
if (bb.width && bb.height) {
|
|
37
|
+
} else {
|
|
38
|
+
let poly = getPathDataVertices(pathData, true);
|
|
39
|
+
bb = getPolyBBox(poly);
|
|
40
|
+
pathDataPlusArr[i].bb = bb;
|
|
41
|
+
//console.log(bb, sub);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
xArr.push(bb.left, bb.right)
|
|
45
|
+
yArr.push(bb.top, bb.bottom)
|
|
46
|
+
sub.includes = []
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* check overlapping
|
|
52
|
+
* sub paths
|
|
53
|
+
*/
|
|
54
|
+
for (let i = 0, l = pathDataPlusArr.length; i < l; i++) {
|
|
55
|
+
let sub1 = pathDataPlusArr[i];
|
|
56
|
+
let { bb, poly } = sub1;
|
|
57
|
+
|
|
58
|
+
for (let j = 0; j < l; j++) {
|
|
59
|
+
|
|
60
|
+
let sub1 = pathDataPlusArr[j];
|
|
61
|
+
if (i === j) continue;
|
|
62
|
+
|
|
63
|
+
//let [bb1, poly1] = [sub1.bb, sub1.poly];
|
|
64
|
+
let bb1 = sub1.bb;
|
|
65
|
+
//let poly1 = sub1.poly
|
|
66
|
+
|
|
67
|
+
// test sample on-path points
|
|
68
|
+
let ptM = { x: bb1.x + bb1.width * 0.5, y: bb1.y + bb1.height * 0.5 };
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
let inPoly = false;
|
|
72
|
+
if (ptM.x >= bb.x && ptM.y >= bb.y && ptM.x <= bb.right && ptM.y <= bb.bottom) {
|
|
73
|
+
inPoly = true;
|
|
74
|
+
pathDataPlusArr[i].includes.push(j);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* combine overlapping
|
|
83
|
+
* compound paths
|
|
84
|
+
*/
|
|
85
|
+
for (let i = 0, l = pathDataPlusArr.length; i < l; i++) {
|
|
86
|
+
let sub = pathDataPlusArr[i];
|
|
87
|
+
let { includes } = sub;
|
|
88
|
+
|
|
89
|
+
includes.forEach(s => {
|
|
90
|
+
let pathData = pathDataPlusArr[s].pathData;
|
|
91
|
+
if (pathData.length) {
|
|
92
|
+
pathDataPlusArr[i].pathData.push(...pathData);
|
|
93
|
+
pathDataPlusArr[s].pathData = [];
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// remove empty els due to grouping
|
|
99
|
+
pathDataPlusArr = pathDataPlusArr.filter(sub => sub.pathData.length);
|
|
100
|
+
|
|
101
|
+
// try to find row left to right order
|
|
102
|
+
//pathDataPlusArr = pathDataPlusArr.sort((a, b) => ((a.bb.x + a.bb.y * 3) - (b.bb.x + b.bb.y * 3)))
|
|
103
|
+
//pathDataPlusArr = pathDataPlusArr.sort((a, b) => ((a.bb.x + a.bb.y * 2) - (b.bb.x + b.bb.y * 2)))
|
|
104
|
+
pathDataPlusArr = pathDataPlusArr.sort((a, b) => ((a.bb.x ) - (b.bb.x)))
|
|
105
|
+
|
|
106
|
+
// create SVG
|
|
107
|
+
let x = Math.min(...xArr);
|
|
108
|
+
let y = Math.min(...yArr);
|
|
109
|
+
let right = Math.max(...xArr);
|
|
110
|
+
let bottom = Math.max(...yArr);
|
|
111
|
+
let width = right - x;
|
|
112
|
+
let height = bottom - y;
|
|
113
|
+
|
|
114
|
+
[x, y, width, height] = [x, y, width, height].map(val => roundTo(val, decimals));
|
|
115
|
+
|
|
116
|
+
let dimensionAtts = addDimensions ? `width="${width}" height="${height}"` : ''
|
|
117
|
+
let svgSplit = `<svg ${dimensionAtts} viewBox="${x} ${y} ${width} ${height}" xmlns="${svgNs}">`;
|
|
118
|
+
|
|
119
|
+
pathDataPlusArr.forEach(sub => {
|
|
120
|
+
let { pathData } = sub;
|
|
121
|
+
|
|
122
|
+
pathData = convertPathData(pathData, { toRelative, toShorthands, decimals });
|
|
123
|
+
let d = pathDataToD(pathData, minifyD);
|
|
124
|
+
svgSplit += `<path d="${d}"/>`;
|
|
125
|
+
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
svgSplit += '</svg>';
|
|
129
|
+
|
|
130
|
+
let splitObj = { pathData: pathDataPlusArr, svg: svgSplit }
|
|
131
|
+
//console.log('splitObj', splitObj);
|
|
132
|
+
return splitObj
|
|
133
|
+
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
/*
|
|
138
|
+
function checkBBoxIntersections2(bb, bb1) {
|
|
139
|
+
let [x, y, width, height, right, bottom] = [
|
|
140
|
+
bb.x,
|
|
141
|
+
bb.y,
|
|
142
|
+
bb.width,
|
|
143
|
+
bb.height,
|
|
144
|
+
bb.x + bb.width,
|
|
145
|
+
bb.y + bb.height
|
|
146
|
+
];
|
|
147
|
+
let [x1, y1, width1, height1, right1, bottom1] = [
|
|
148
|
+
bb1.x,
|
|
149
|
+
bb1.y,
|
|
150
|
+
bb1.width,
|
|
151
|
+
bb1.height,
|
|
152
|
+
bb1.x + bb1.width,
|
|
153
|
+
bb1.y + bb1.height
|
|
154
|
+
];
|
|
155
|
+
let intersects = false;
|
|
156
|
+
//console.log('bb', bb, bb1);
|
|
157
|
+
//console.log();
|
|
158
|
+
|
|
159
|
+
if (x < x1 && right > right1 && y < y1 && bottom > bottom1) {
|
|
160
|
+
intersects = true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
console.log('???', intersects, 'dims', width, height, '2', width1, height1);
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
return intersects;
|
|
167
|
+
}
|
|
168
|
+
*/
|
|
@@ -4,22 +4,24 @@
|
|
|
4
4
|
* d attribute string
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export function pathDataToD(pathData,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
export function pathDataToD(pathData, mode = 0) {
|
|
8
|
+
|
|
9
|
+
mode = parseFloat(mode)
|
|
10
|
+
/*
|
|
11
|
+
0 = max minification
|
|
12
|
+
0.5 = safe
|
|
13
|
+
1 = verbose
|
|
14
|
+
2 = beautify
|
|
15
|
+
*/
|
|
12
16
|
let len = pathData.length;
|
|
13
|
-
let beautify = optimize > 1;
|
|
14
|
-
let minify = beautify || optimize ? false : true;
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
let d = '';
|
|
18
18
|
let valsString = pathData[0].values.join(" ");
|
|
19
|
-
let separator_command =
|
|
20
|
-
|
|
19
|
+
let separator_command = mode > 1 ? `\n` :
|
|
20
|
+
((mode < 1) ? '' : ' ');
|
|
21
|
+
let separator_type = mode > 0.5 ? ' ' : '';
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
// 1st command
|
|
24
|
+
let d = `${pathData[0].type}${separator_type}${valsString}${separator_command}`;
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
for (let i = 1; i < len; i++) {
|
|
@@ -29,7 +31,7 @@ export function pathDataToD(pathData, optimize = 0) {
|
|
|
29
31
|
valsString = '';
|
|
30
32
|
|
|
31
33
|
// Minify Arc commands (A/a) – actually sucks!
|
|
32
|
-
if (
|
|
34
|
+
if (!mode && (type === 'A' || type === 'a')) {
|
|
33
35
|
values = [
|
|
34
36
|
values[0], values[1], values[2],
|
|
35
37
|
`${values[3]}${values[4]}${values[5]}`,
|
|
@@ -38,16 +40,15 @@ export function pathDataToD(pathData, optimize = 0) {
|
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
// Omit type for repeated commands
|
|
41
|
-
type = (
|
|
43
|
+
type = ((mode < 1) && com0.type === com.type && com.type.toLowerCase() !== 'm')
|
|
42
44
|
? " "
|
|
43
|
-
: (
|
|
45
|
+
: ((mode < 1) && com0.type === "M" && com.type === "L"
|
|
44
46
|
? " "
|
|
45
47
|
: com.type);
|
|
46
48
|
|
|
47
49
|
|
|
48
50
|
// concatenate subsequent floating point values
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
+
if (!mode) {
|
|
51
52
|
|
|
52
53
|
let prevWasFloat = false;
|
|
53
54
|
|
|
@@ -67,71 +68,32 @@ export function pathDataToD(pathData, optimize = 0) {
|
|
|
67
68
|
if (v > 0 && !(prevWasFloat && isSmallFloat)) {
|
|
68
69
|
valsString += ' ';
|
|
69
70
|
}
|
|
70
|
-
//console.log(isSmallFloat, prevWasFloat, valStr);
|
|
71
71
|
|
|
72
72
|
valsString += valStr
|
|
73
73
|
prevWasFloat = isSmallFloat;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
//console.log('minify', valsString);
|
|
77
|
-
d += `${type}${separator_type}${valsString}${separator_command}`;
|
|
78
|
-
|
|
79
76
|
}
|
|
80
77
|
// regular non-minified output
|
|
81
78
|
else {
|
|
82
|
-
|
|
79
|
+
valsString = values.join(' ')
|
|
83
80
|
}
|
|
81
|
+
|
|
82
|
+
if(i===len-1) separator_command=''
|
|
83
|
+
d += `${type}${separator_type}${valsString}${separator_command}`;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
|
|
87
|
+
if (mode < 1) {
|
|
87
88
|
d = d
|
|
88
89
|
.replace(/[A-Za-z]0(?=\.)/g, m => m[0])
|
|
89
90
|
.replace(/ 0\./g, " .") // Space before small decimals
|
|
90
91
|
.replace(/ -/g, "-") // Remove space before negatives
|
|
91
92
|
.replace(/-0\./g, "-.") // Remove leading zero from negative decimals
|
|
92
|
-
.replace(/Z/g, "z")
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return d;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
export function pathDataToD_0(pathData, decimals = -1, minify = false) {
|
|
100
|
-
// implicit l command
|
|
101
|
-
if (pathData[1].type === "l" && minify) {
|
|
102
|
-
pathData[0].type = "m";
|
|
103
|
-
}
|
|
104
|
-
let d = `${pathData[0].type}${pathData[0].values.join(" ")}`;
|
|
105
|
-
|
|
106
|
-
for (let i = 1; i < pathData.length; i++) {
|
|
107
|
-
let com0 = pathData[i - 1];
|
|
108
|
-
let com = pathData[i];
|
|
109
|
-
|
|
110
|
-
let type = (com0.type === com.type && minify) ?
|
|
111
|
-
" " : (
|
|
112
|
-
(com0.type === "m" && com.type === "l") ||
|
|
113
|
-
(com0.type === "M" && com.type === "l") ||
|
|
114
|
-
(com0.type === "M" && com.type === "L")
|
|
115
|
-
) && minify ?
|
|
116
|
-
" " : com.type;
|
|
117
|
-
|
|
118
|
-
// round
|
|
119
|
-
if (com.values.length && decimals > -1) {
|
|
120
|
-
com.values = com.values.map(val => { return +val.toFixed(decimals) })
|
|
121
|
-
}
|
|
122
|
-
d += `${type}${com.values.join(" ")}`;
|
|
93
|
+
.replace(/Z/g, "z") // Convert uppercase 'Z' to lowercase
|
|
123
94
|
}
|
|
124
95
|
|
|
125
|
-
|
|
126
|
-
if (minify) {
|
|
127
|
-
d = d
|
|
128
|
-
.replaceAll(" 0.", " .")
|
|
129
|
-
.replaceAll(" -", "-")
|
|
130
|
-
.replaceAll("-0.", "-.")
|
|
131
|
-
.replace(/\s+([mlcsqtahvz])/gi, "$1")
|
|
132
|
-
.replaceAll("Z", "z");
|
|
133
|
-
}
|
|
96
|
+
//console.log(`"${d}"`);
|
|
134
97
|
|
|
135
98
|
return d;
|
|
136
99
|
}
|
|
137
|
-
|
|
@@ -21,7 +21,7 @@ import { renderPoint } from "./visualize";
|
|
|
21
21
|
* creates precise polygon approximation from pathdata
|
|
22
22
|
* converts arc to cubis
|
|
23
23
|
*/
|
|
24
|
-
export function
|
|
24
|
+
export function pathDataToPolygonOpt(pathData, {
|
|
25
25
|
precisionPoly = 1,
|
|
26
26
|
autoAccuracy=false,
|
|
27
27
|
polyFormat='points',
|
|
@@ -105,12 +105,12 @@ simplifyRDP=1,
|
|
|
105
105
|
|
|
106
106
|
// simplify polygon
|
|
107
107
|
if(simplifyRD>0){
|
|
108
|
-
pts2 = simplifyPolyRD(pts2, {quality:simplifyRD
|
|
108
|
+
pts2 = simplifyPolyRD(pts2, {quality:simplifyRD})
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
|
|
112
112
|
if(simplifyRDP>0){
|
|
113
|
-
pts2 = simplifyPolyRDP(pts2, {quality:simplifyRDP
|
|
113
|
+
pts2 = simplifyPolyRDP(pts2, {quality:simplifyRDP})
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
|
|
@@ -120,7 +120,6 @@ simplifyRDP=1,
|
|
|
120
120
|
|
|
121
121
|
if(autoAccuracy){
|
|
122
122
|
decimals = detectAccuracyPoly(pts)
|
|
123
|
-
//console.log('decimals', decimals);
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
let poly = decimals>-1 ? pts2.map(pt => { return { x: roundTo(pt.x, decimals), y: roundTo(pt.y, decimals) } }) : pts2.map(pt => { return { x: pt.x, y: pt.y } })
|
|
@@ -9,6 +9,67 @@ import { pathDataToD } from "./pathData_stringify";
|
|
|
9
9
|
import { renderPath, renderPoint, renderPoly } from "./visualize";
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
export function getPolyCentroid(pts) {
|
|
13
|
+
|
|
14
|
+
let l = pts.length;
|
|
15
|
+
let x = 0, y = 0;
|
|
16
|
+
for (let i = 0; l && i < l; i++) {
|
|
17
|
+
let pt = pts[i];
|
|
18
|
+
x += pt.x
|
|
19
|
+
y += pt.y
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let centroid = {x: x/l, y:y/l}
|
|
23
|
+
return centroid
|
|
24
|
+
//console.log(centroid);
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getPolyCentroidWeighted(points) {
|
|
29
|
+
if (!points || points.length === 0) return null;
|
|
30
|
+
|
|
31
|
+
let totalWeight = 0;
|
|
32
|
+
let sumX = 0;
|
|
33
|
+
let sumY = 0;
|
|
34
|
+
|
|
35
|
+
for (const point of points) {
|
|
36
|
+
let weight = point.weight || 1; // default weight = 1
|
|
37
|
+
sumX += point.x * weight;
|
|
38
|
+
sumY += point.y * weight;
|
|
39
|
+
totalWeight += weight;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (totalWeight === 0) return null;
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
x: sumX / totalWeight,
|
|
46
|
+
y: sumY / totalWeight
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
export function detectRegularPolygon(pts, centroid={x:0, y:0}) {
|
|
53
|
+
let rSq = getSquareDistance(pts[0], centroid);
|
|
54
|
+
let isRegular = true;
|
|
55
|
+
|
|
56
|
+
for (let i = 1, l = pts.length; i < l; i++) {
|
|
57
|
+
let pt1 = pts[i];
|
|
58
|
+
let dist = getSquareDistance(pt1, centroid);
|
|
59
|
+
|
|
60
|
+
let diff = Math.abs(rSq-dist);
|
|
61
|
+
let diffRel = diff/rSq
|
|
62
|
+
//console.log('diffRel', diffRel);
|
|
63
|
+
|
|
64
|
+
if (diffRel > 0.05) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
return isRegular;
|
|
71
|
+
}
|
|
72
|
+
|
|
12
73
|
|
|
13
74
|
export function analyzePoly(pts, {
|
|
14
75
|
x = 0,
|
|
@@ -64,8 +64,17 @@ export function polyPtsToArray(pts) {
|
|
|
64
64
|
// convert flat point value array to point object array
|
|
65
65
|
export function toPointArray(pts) {
|
|
66
66
|
let ptArr = [];
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
|
|
68
|
+
if(pts[0].length===2){
|
|
69
|
+
for (let i = 0, l = pts.length; i < l; i ++) {
|
|
70
|
+
let pt = pts[i]
|
|
71
|
+
ptArr.push({ x: pt[0], y:pt[1] });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
}else{
|
|
75
|
+
for (let i = 1, l = pts.length; i < l; i += 2) {
|
|
76
|
+
ptArr.push({ x: pts[i - 1], y: pts[i] });
|
|
77
|
+
}
|
|
69
78
|
}
|
|
70
79
|
return ptArr;
|
|
71
80
|
};
|