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.
- package/CHANGELOG.md +21 -0
- package/README.md +7 -4
- package/dist/svg-path-simplify.esm.js +3593 -1279
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +3594 -1278
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +1017 -538
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/dist/svg-path-simplify.poly.cjs +9 -8
- package/docs/privacy-webapp.md +24 -0
- package/index.html +331 -152
- package/package.json +1 -1
- package/src/constants.js +4 -0
- package/src/css_parse.js +317 -0
- package/src/detect_input.js +76 -28
- package/src/index.js +8 -0
- package/src/pathData_simplify_cubic.js +26 -16
- package/src/pathData_simplify_harmonize_cpts.js +77 -1
- package/src/pathData_simplify_revertToquadratics.js +0 -1
- package/src/pathSimplify-main.js +304 -276
- package/src/pathSimplify-only-pathdata.js +7 -2
- package/src/pathSimplify-presets.js +254 -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/svg-getAttributes.js +4 -2
- package/src/svgii/convert_units.js +1 -1
- package/src/svgii/geometry.js +322 -5
- package/src/svgii/geometry_bbox_element.js +1 -1
- package/src/svgii/geometry_deduceRadius.js +116 -27
- package/src/svgii/geometry_length.js +253 -0
- package/src/svgii/pathData_analyze.js +18 -0
- package/src/svgii/pathData_convert.js +193 -89
- package/src/svgii/pathData_fix_directions.js +12 -14
- 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_reorder.js +122 -16
- package/src/svgii/pathData_simplify_refineCorners.js +130 -35
- package/src/svgii/pathData_simplify_refine_round.js +420 -0
- 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 +80 -78
- package/src/svgii/svg_cleanup.js +421 -619
- package/src/svgii/svg_cleanup_convertPathLength.js +39 -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 +77 -0
- package/src/svgii/svg_cleanup_ungroup.js +36 -0
- package/src/svgii/svg_el_parse_style_props.js +72 -47
- package/src/svgii/svg_getElementLength.js +67 -0
- package/src/svgii/svg_validate.js +220 -0
- package/tests/testSVG.js +14 -1
- package/src/svgii/pathData_refine_round.js +0 -222
|
@@ -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
|
};
|
|
@@ -5,15 +5,19 @@
|
|
|
5
5
|
* https://francoisromain.medium.com/smooth-a-svg-path-with-cubic-bezier-curves-e37b49d46c74
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { checkLineIntersection, getDistManhattan, interpolate, mirrorCpts } from "./geometry";
|
|
8
|
+
import { checkLineIntersection, getAngle, getDistance, getDistManhattan, getPathDataVertices, getPointOnEllipse, getSquareDistance, interpolate, mirrorCpts, reducePoints, rotatePoint } from "./geometry";
|
|
9
9
|
import { getPolyBBox } from "./geometry_bbox";
|
|
10
10
|
import { renderPath, renderPoint, renderPoly } from "./visualize";
|
|
11
11
|
import { simplifyPolyRDP } from "../simplify_poly_RDP";
|
|
12
12
|
import { pathDataFromPoly } from "./pathData_fromPoly";
|
|
13
13
|
import { getPolyChunks } from "./poly_analyze_get_chunks";
|
|
14
|
-
import { analyzePoly, isClosedPolygon } from "./poly_analyze";
|
|
14
|
+
import { analyzePoly, detectRegularPolygon, getPolyCentroid, getPolyCentroidWeighted, isClosedPolygon } from "./poly_analyze";
|
|
15
15
|
import { fitCurveSchneider } from "../poly-fit-curve-schneider";
|
|
16
16
|
import { simplifyPolyRD } from "../simplify_poly_radial_distance";
|
|
17
|
+
import { simplifyRC } from "../simplify_poly_RC";
|
|
18
|
+
import { getPolygonArea } from "./geometry_area";
|
|
19
|
+
import { pathDataToD } from "./pathData_stringify";
|
|
20
|
+
import { fixIntersectingCpts } from "../pathData_simplify_harmonize_cpts";
|
|
17
21
|
|
|
18
22
|
|
|
19
23
|
|
|
@@ -34,34 +38,89 @@ export function simplifyPolygonToPathData(pts, {
|
|
|
34
38
|
} = {}) {
|
|
35
39
|
|
|
36
40
|
|
|
41
|
+
let polyPath = [];
|
|
42
|
+
let l = pts.length;
|
|
43
|
+
let M = pts[0]
|
|
44
|
+
let Z = pts[l - 1]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// triangle
|
|
48
|
+
if (pts.length === 3) {
|
|
49
|
+
|
|
50
|
+
let pM1 = interpolate(M, pts[1], 0.5)
|
|
51
|
+
let pM2 = interpolate(pts[1], Z, 0.5)
|
|
52
|
+
let pM3 = interpolate(Z, pts[0], 0.5)
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
console.log('triangle');
|
|
56
|
+
renderPoint(markers, M)
|
|
57
|
+
renderPoint(markers, pM1)
|
|
58
|
+
renderPoint(markers, pM2)
|
|
59
|
+
renderPoint(markers, pM3)
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
if (closed) {
|
|
63
|
+
let t = 0.6666
|
|
64
|
+
let cp1_1 = interpolate(pM1, pts[1], t)
|
|
65
|
+
let cp2_1 = interpolate(pM2, pts[1], t)
|
|
66
|
+
let cp1_2 = interpolate(pM2, Z, t)
|
|
67
|
+
let cp2_2 = interpolate(pM3, Z, t)
|
|
68
|
+
let cp1_3 = interpolate(pM3, M, t)
|
|
69
|
+
let cp2_3 = interpolate(pM1, M, t)
|
|
70
|
+
|
|
71
|
+
polyPath = [
|
|
72
|
+
{ type: 'M', values: [pM1.x, pM1.y] },
|
|
73
|
+
{ type: 'C', values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, pM2.x, pM2.y] },
|
|
74
|
+
{ type: 'C', values: [cp1_2.x, cp1_2.y, cp2_2.x, cp2_2.y, pM3.x, pM3.y] },
|
|
75
|
+
{ type: 'C', values: [cp1_3.x, cp1_3.y, cp2_3.x, cp2_3.y, pM1.x, pM1.y] },
|
|
76
|
+
{ type: 'Z', values: [] },
|
|
77
|
+
]
|
|
37
78
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
absolute
|
|
47
|
-
})
|
|
79
|
+
} else {
|
|
80
|
+
polyPath = [
|
|
81
|
+
//{ type: 'M', values: [pM1.x, pM1.y] },
|
|
82
|
+
{ type: 'M', values: [M.x, M.y] },
|
|
83
|
+
{ type: 'C', values: [pts[1].x, pts[1].y, pts[1].x, pts[1].y, Z.x, Z.y] },
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
return polyPath;
|
|
48
87
|
}
|
|
49
|
-
*/
|
|
50
88
|
|
|
51
89
|
|
|
52
|
-
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
90
|
+
|
|
91
|
+
// remove colinear
|
|
92
|
+
//pts = simplifyRC(pts)
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* detect regular polygon
|
|
96
|
+
* curved path is a circle
|
|
97
|
+
*/
|
|
98
|
+
let centroid = getPolyCentroid(simplifyRC(pts))
|
|
99
|
+
let isRegularPolygon = detectRegularPolygon(pts, centroid)
|
|
57
100
|
|
|
58
101
|
|
|
59
|
-
if (
|
|
60
|
-
|
|
102
|
+
if (isRegularPolygon) {
|
|
103
|
+
//renderPoint(markers, centroid)
|
|
104
|
+
//let r = getDistance(centroid, pts[0])
|
|
105
|
+
let ptAd = rotatePoint(pts[0], centroid.x, centroid.y, Math.PI)
|
|
106
|
+
let sweep = getPolygonArea(pts) > 0 ? 1 : 0;
|
|
107
|
+
|
|
108
|
+
polyPath = [
|
|
109
|
+
{ type: 'M', values: [pts[0].x, pts[0].y] },
|
|
110
|
+
{ type: 'A', values: [1, 1, 0, 0, sweep, ptAd.x, ptAd.y] },
|
|
111
|
+
{ type: 'A', values: [1, 1, 0, 0, sweep, pts[0].x, pts[0].y] }
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
if (closed) {
|
|
115
|
+
polyPath.push({ type: 'Z', values: [] })
|
|
116
|
+
}
|
|
117
|
+
return polyPath;
|
|
61
118
|
}
|
|
62
|
-
*/
|
|
63
119
|
|
|
64
120
|
|
|
121
|
+
// remove colinear
|
|
122
|
+
//pts = simplifyRC(pts)
|
|
123
|
+
|
|
65
124
|
// get topology of poly
|
|
66
125
|
let polyAnalyzed = !keepExtremes && !keepCorners ? pts : analyzePoly(pts, {
|
|
67
126
|
debug: false
|
|
@@ -70,7 +129,6 @@ export function simplifyPolygonToPathData(pts, {
|
|
|
70
129
|
})
|
|
71
130
|
|
|
72
131
|
//console.log(polyAnalyzed, polyAnalyzed2);
|
|
73
|
-
//return
|
|
74
132
|
|
|
75
133
|
// split into segment chunks
|
|
76
134
|
let chunks = !keepExtremes && !keepCorners ? [polyAnalyzed] : getPolyChunks(polyAnalyzed, { keepCorners, keepExtremes, keepInflections });
|
|
@@ -81,12 +139,15 @@ export function simplifyPolygonToPathData(pts, {
|
|
|
81
139
|
|
|
82
140
|
//threshold = 2
|
|
83
141
|
|
|
84
|
-
|
|
142
|
+
polyPath = simplifyPolyChunks(chunks, {
|
|
85
143
|
closed,
|
|
86
144
|
tolerance: threshold,
|
|
87
|
-
keepCorners
|
|
145
|
+
keepCorners,
|
|
146
|
+
keepExtremes: true,
|
|
88
147
|
});
|
|
89
148
|
|
|
149
|
+
polyPath = fixIntersectingCpts(polyPath);
|
|
150
|
+
|
|
90
151
|
return polyPath;
|
|
91
152
|
}
|
|
92
153
|
|