svg-path-simplify 0.1.3 → 0.2.2
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 +10 -0
- package/dist/svg-path-simplify.esm.js +3905 -1533
- package/dist/svg-path-simplify.esm.min.js +13 -1
- package/dist/svg-path-simplify.js +3923 -1551
- package/dist/svg-path-simplify.min.js +13 -1
- package/dist/svg-path-simplify.min.js.gz +0 -0
- package/index.html +61 -31
- package/package.json +3 -5
- package/src/constants.js +3 -0
- package/src/index-node.js +0 -1
- package/src/index.js +26 -0
- package/src/pathData_simplify_cubic.js +74 -31
- package/src/pathData_simplify_cubicsToArcs.js +566 -0
- package/src/pathData_simplify_harmonize_cpts.js +170 -0
- package/src/pathData_simplify_revertToquadratics.js +21 -0
- package/src/pathSimplify-main.js +253 -86
- package/src/poly-fit-curve-schneider.js +570 -0
- package/src/simplify_poly_RDP.js +146 -0
- package/src/simplify_poly_radial_distance.js +100 -0
- package/src/svg_getViewbox.js +1 -1
- package/src/svgii/geometry.js +389 -63
- package/src/svgii/geometry_area.js +2 -1
- package/src/svgii/pathData_analyze.js +259 -212
- package/src/svgii/pathData_convert.js +91 -663
- package/src/svgii/pathData_fromPoly.js +12 -0
- package/src/svgii/pathData_parse.js +90 -89
- package/src/svgii/pathData_parse_els.js +3 -0
- package/src/svgii/pathData_parse_fontello.js +449 -0
- package/src/svgii/pathData_remove_collinear.js +44 -37
- package/src/svgii/pathData_reorder.js +2 -1
- package/src/svgii/pathData_simplify_redraw.js +343 -0
- package/src/svgii/pathData_simplify_refineCorners.js +18 -9
- package/src/svgii/pathData_simplify_refineExtremes.js +19 -78
- package/src/svgii/pathData_split.js +42 -45
- package/src/svgii/pathData_toPolygon.js +130 -4
- package/src/svgii/poly_analyze.js +470 -14
- package/src/svgii/poly_to_pathdata.js +224 -19
- package/src/svgii/rounding.js +55 -112
- package/src/svgii/svg_cleanup.js +13 -1
- package/src/svgii/visualize.js +8 -3
- package/{debug.cjs → tests/debug.cjs} +3 -0
- /package/{test.js → tests/test.js} +0 -0
- /package/{testSVG.js → tests/testSVG.js} +0 -0
|
@@ -1,315 +1,362 @@
|
|
|
1
1
|
import { splitSubpaths } from './pathData_split.js';
|
|
2
|
-
import { getAngle, bezierhasExtreme, getPathDataVertices, svgArcToCenterParam, getSquareDistance,
|
|
2
|
+
import { getAngle, bezierhasExtreme, getPathDataVertices, svgArcToCenterParam, getSquareDistance, getDistManhattan, isMultipleOf45, pointAtT, getTatAngles } from "./geometry.js";
|
|
3
3
|
import { getPolygonArea, getPathArea } from './geometry_area.js';
|
|
4
4
|
import { getPolyBBox } from './geometry_bbox.js';
|
|
5
5
|
import { renderPoint, renderPath } from "./visualize.js";
|
|
6
6
|
import { commandIsFlat } from './geometry_flatness.js';
|
|
7
|
-
|
|
7
|
+
import { roundPathData } from './rounding.js';
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* create pathdata super set
|
|
12
|
+
* including geometrical properties such as:
|
|
13
|
+
* segment introduces x/y extreme
|
|
14
|
+
* corner
|
|
15
|
+
* inflection/direction change
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
export function analyzePathData(pathData = [], {
|
|
19
|
+
detectExtremes = true,
|
|
20
|
+
detectCorners = true,
|
|
21
|
+
detectDirection = true,
|
|
22
|
+
detectSemiExtremes = false,
|
|
23
|
+
debug = false,
|
|
24
|
+
addSquareLength = true,
|
|
25
|
+
addArea = true,
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
let M = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
22
|
-
let p0 = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
23
|
-
let p;
|
|
24
|
-
|
|
25
|
-
pathData[0].dimA = 0;
|
|
26
|
-
let len = pathData.length
|
|
27
|
-
|
|
28
|
-
for (let c = 2; len && c <= len; c++) {
|
|
29
|
-
|
|
30
|
-
let com = pathData[c - 1];
|
|
31
|
-
let { type, values } = com;
|
|
32
|
-
let valsL = values.slice(-2);
|
|
33
|
-
|
|
34
|
-
p = valsL.length ? { x: valsL[0], y: valsL[1] } : M;
|
|
27
|
+
} = {}) {
|
|
35
28
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
else if (type.toLowerCase() === 'z') {
|
|
41
|
-
p = M;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
let dimA = getDistAv(p0, p);
|
|
45
|
-
com.dimA = dimA;
|
|
46
|
-
com.p0 = p0
|
|
47
|
-
com.p = p
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if(type==='C' || type==='Q') com.cp1 = {x:values[0], y:values[1]}
|
|
51
|
-
if(type==='C' ) com.cp2 = {x:values[2], y:values[3]}
|
|
52
|
-
|
|
53
|
-
p0=p
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
console.log('!!!pathData', pathData);
|
|
58
|
-
return pathData
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
export function analyzePathData(pathData = []) {
|
|
29
|
+
// get verbose control point data
|
|
30
|
+
pathData = getPathDataVerbose(pathData, { addSquareLength, addArea });
|
|
31
|
+
//pathData = roundPathData(pathData, 3)
|
|
63
32
|
|
|
33
|
+
// new pathdata adding properties
|
|
64
34
|
let pathDataPlus = [];
|
|
65
35
|
|
|
36
|
+
//console.log('pathData', pathData);
|
|
37
|
+
//return pathData
|
|
38
|
+
|
|
66
39
|
let pathPoly = getPathDataVertices(pathData);
|
|
67
40
|
let bb = getPolyBBox(pathPoly)
|
|
68
41
|
let { left, right, top, bottom, width, height } = bb;
|
|
69
42
|
|
|
70
|
-
// initial starting point coordinates
|
|
71
|
-
let M0 = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
72
|
-
let M = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
73
|
-
let p0 = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
74
|
-
let p;
|
|
75
43
|
|
|
76
44
|
// init starting point data
|
|
77
|
-
pathData[0].idx = 0;
|
|
78
|
-
pathData[0].p0 = M;
|
|
79
|
-
pathData[0].p = M;
|
|
80
|
-
pathData[0].lineto = false;
|
|
81
45
|
pathData[0].corner = false;
|
|
82
46
|
pathData[0].extreme = false;
|
|
47
|
+
pathData[0].semiExtreme = false;
|
|
83
48
|
pathData[0].directionChange = false;
|
|
84
49
|
pathData[0].closePath = false;
|
|
85
|
-
pathData[0].dimA = 0;
|
|
50
|
+
//pathData[0].dimA = 0;
|
|
86
51
|
|
|
87
52
|
|
|
88
53
|
// add first M command
|
|
89
54
|
let pathDataProps = [pathData[0]];
|
|
90
|
-
let area0 = 0;
|
|
91
55
|
let len = pathData.length;
|
|
92
56
|
|
|
57
|
+
// threshold for corner angles: 10 deg
|
|
58
|
+
let thresholdCorner = Math.PI * 2 / 360 * 10
|
|
59
|
+
|
|
60
|
+
// define angle threshold for semi extremes
|
|
61
|
+
let thresholdAngle = detectSemiExtremes ? 0.01 : 0.05
|
|
62
|
+
|
|
63
|
+
|
|
93
64
|
for (let c = 2; len && c <= len; c++) {
|
|
94
65
|
|
|
95
66
|
let com = pathData[c - 1];
|
|
96
|
-
let { type, values } = com;
|
|
97
|
-
|
|
67
|
+
let { type, values, p0, p, cp1 = null, cp2 = null, squareDist = 0, cptArea = 0, dimA = 0 } = com;
|
|
68
|
+
|
|
69
|
+
//next command
|
|
70
|
+
let comN = pathData[c] || null;
|
|
98
71
|
|
|
99
|
-
/**
|
|
100
|
-
* get command points for
|
|
101
|
-
* flatness checks:
|
|
102
|
-
* this way we can skip certain tests
|
|
103
|
-
*/
|
|
104
|
-
let commandPts = [p0];
|
|
105
|
-
let isFlat = false;
|
|
106
72
|
|
|
107
73
|
// init properties
|
|
108
|
-
com.idx = c - 1;
|
|
109
|
-
com.lineto = false;
|
|
110
74
|
com.corner = false;
|
|
111
75
|
com.extreme = false;
|
|
76
|
+
com.semiExtreme = false;
|
|
112
77
|
com.directionChange = false;
|
|
113
78
|
com.closePath = false;
|
|
114
|
-
|
|
115
|
-
//
|
|
79
|
+
|
|
80
|
+
// get command points
|
|
81
|
+
let commandPts = (type === 'C' || type === 'Q') ?
|
|
82
|
+
(type === 'C' ? [p0, cp1, cp2, p] : [p0, cp1, p]) :
|
|
83
|
+
([p0, p]);
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
// check flatness of command
|
|
87
|
+
let toleranceFlat = 0.01;
|
|
88
|
+
let thresholdLength = dimA * 0.1
|
|
89
|
+
let areaThresh = squareDist * toleranceFlat;
|
|
90
|
+
let isFlat = Math.abs(cptArea) < areaThresh;
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
// bezier types
|
|
94
|
+
let isBezier = type === 'Q' || type === 'C';
|
|
95
|
+
let isBezierN = comN && (comN.type === 'Q' || comN.type === 'C');
|
|
116
96
|
|
|
117
97
|
|
|
118
98
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
99
|
+
* detect extremes
|
|
100
|
+
* local or absolute
|
|
121
101
|
*/
|
|
122
|
-
let
|
|
123
|
-
p = valsL.length ? { x: valsL[0], y: valsL[1] } : M;
|
|
102
|
+
let hasExtremes = false;
|
|
124
103
|
|
|
104
|
+
//!isFlat &&
|
|
105
|
+
if (isBezier) {
|
|
125
106
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
107
|
+
|
|
108
|
+
let dx = type === 'C' ? Math.abs(com.cp2.x - com.p.x) : Math.abs(com.cp1.x - com.p.x)
|
|
109
|
+
let dy = type === 'C' ? Math.abs(com.cp2.y - com.p.y) : Math.abs(com.cp1.y - com.p.y)
|
|
110
|
+
|
|
111
|
+
let horizontal = dy === 0 && dx > 0
|
|
112
|
+
let vertical = dx === 0 && dy > 0
|
|
113
|
+
|
|
114
|
+
if (horizontal || vertical) hasExtremes = true;
|
|
115
|
+
|
|
116
|
+
// is extreme relative to bounding box
|
|
117
|
+
if ((p.x === left || p.y === top || p.x === right || p.y === bottom)) {
|
|
118
|
+
hasExtremes = true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// interpret segment as extreme if it has an implicit extreme
|
|
122
|
+
if (!hasExtremes) {
|
|
123
|
+
let couldHaveExtremes = bezierhasExtreme(null, commandPts)
|
|
124
|
+
if (couldHaveExtremes) {
|
|
125
|
+
let tArr = getTatAngles(commandPts)
|
|
126
|
+
if (tArr.length && (tArr[0] > 0.15)) {
|
|
127
|
+
hasExtremes = true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
133
131
|
}
|
|
134
132
|
|
|
135
|
-
|
|
136
|
-
com.p0 = p0;
|
|
137
|
-
com.p = p;
|
|
133
|
+
if (hasExtremes) com.extreme = true
|
|
138
134
|
|
|
139
|
-
|
|
135
|
+
// Corners and semi extremes
|
|
136
|
+
if (isBezier && isBezierN) {
|
|
140
137
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
com.dimA = dimA;
|
|
144
|
-
//com.a = dimA;
|
|
138
|
+
// semi extremes
|
|
139
|
+
if (detectSemiExtremes && !com.extreme) {
|
|
145
140
|
|
|
141
|
+
let dx1 = Math.abs(p.x - cp2.x)
|
|
142
|
+
let dy1 = Math.abs(p.y - cp2.y)
|
|
143
|
+
let hasSemiExtreme = false;
|
|
146
144
|
|
|
145
|
+
// exclude extremes or small deltas
|
|
146
|
+
if (dx1 && dy1 && dx1 > thresholdLength || dy1 > thresholdLength) {
|
|
147
|
+
let ang1 = getAngle(cp2, p)
|
|
148
|
+
let ang2 = getAngle(p, comN.cp1)
|
|
147
149
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
*/
|
|
152
|
-
if (type === 'L') com.lineto = true;
|
|
150
|
+
let ang3 = Math.abs(ang1 + ang2) / 2
|
|
151
|
+
hasSemiExtreme = isMultipleOf45(ang3)
|
|
152
|
+
}
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (M.x !== M0.x && M.y !== M0.y) {
|
|
158
|
-
com.lineto = true;
|
|
154
|
+
if (hasSemiExtreme) {
|
|
155
|
+
com.semiExtreme = true;
|
|
156
|
+
}
|
|
159
157
|
}
|
|
160
|
-
}
|
|
161
158
|
|
|
162
|
-
// if bezier
|
|
163
|
-
if (type === 'Q' || type === 'C') {
|
|
164
|
-
cp1 = { x: values[0], y: values[1] }
|
|
165
|
-
cp2 = type === 'C' ? { x: values[2], y: values[3] } : null;
|
|
166
|
-
com.cp1 = cp1;
|
|
167
|
-
if (cp2) com.cp2 = cp2;
|
|
168
|
-
}
|
|
169
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Detect direction change points
|
|
162
|
+
* this will prevent distortions when simplifying
|
|
163
|
+
* e.g in the "spine" of an "S" glyph
|
|
164
|
+
*/
|
|
165
|
+
let signChange = (com.cptArea < 0 && comN.cptArea > 0) || (com.cptArea > 0 && comN.cptArea < 0) ? true : false;
|
|
170
166
|
|
|
171
|
-
|
|
172
|
-
* check command flatness
|
|
173
|
-
* we leave it to the bezier simplifier
|
|
174
|
-
* to convert flat beziers to linetos
|
|
175
|
-
* otherwise we may strip rather flat starting segments
|
|
176
|
-
* preventing a better simplification
|
|
177
|
-
*/
|
|
167
|
+
if (signChange) com.directionChange = true;
|
|
178
168
|
|
|
179
|
-
|
|
180
|
-
if (
|
|
181
|
-
if (type === 'C') commandPts.push(cp2);
|
|
182
|
-
commandPts.push(p);
|
|
169
|
+
// check corners
|
|
170
|
+
if (!com.extreme) {
|
|
183
171
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
let commandFlatness = commandIsFlat(commandPts);
|
|
187
|
-
isFlat = commandFlatness.flat;
|
|
188
|
-
com.flat = isFlat;
|
|
172
|
+
let cp_0 = cp2 ? cp2 : cp1
|
|
173
|
+
let cp_1 = comN.cp1
|
|
189
174
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
175
|
+
let areaCpt = getPolygonArea([cp_0, p, cp_1], false)
|
|
176
|
+
let threshArea = getSquareDistance(cp_0, cp_1) * 0.01
|
|
177
|
+
let isFlat = Math.abs(areaCpt) < threshArea;
|
|
178
|
+
|
|
179
|
+
let signChange2 = (areaCpt < 0 && com.cptArea > 0) || (areaCpt > 0 && com.cptArea < 0) ? true : false;
|
|
195
180
|
|
|
181
|
+
let isCorner=!isFlat && signChange2;
|
|
182
|
+
if (isCorner) com.corner = true;
|
|
183
|
+
}
|
|
196
184
|
}
|
|
197
185
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
186
|
+
|
|
187
|
+
//debug = true;
|
|
188
|
+
if (debug) {
|
|
189
|
+
//if (com.semiExtreme) renderPoint(markers, com.p, 'blue', '2%', '0.5')
|
|
190
|
+
if (com.directionChange) renderPoint(markers, com.p, 'orange', '1.5%', '0.5')
|
|
191
|
+
if (com.corner) renderPoint(markers, com.p, 'magenta', '1.5%', '0.5')
|
|
192
|
+
if (com.extreme) renderPoint(markers, com.p, 'cyan', '1%', '0.5')
|
|
193
|
+
|
|
206
194
|
}
|
|
207
195
|
|
|
196
|
+
pathDataProps.push(com)
|
|
208
197
|
|
|
209
|
-
|
|
210
|
-
let comN = pathData[c] ? pathData[c] : null;
|
|
211
|
-
let comNValsL = comN ? comN.values.slice(-2) : null;
|
|
212
|
-
typeN = comN ? comN.type : null;
|
|
198
|
+
}
|
|
213
199
|
|
|
200
|
+
//pathDataProps.push(comLast)
|
|
214
201
|
|
|
215
|
-
// get bezier control points
|
|
216
|
-
if (comN && (comN.type === 'Q' || comN.type === 'C')) {
|
|
217
|
-
pN = comN ? { x: comNValsL[0], y: comNValsL[1] } : null;
|
|
218
202
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
203
|
+
let dimA = (width + height) / 2
|
|
204
|
+
//pathDataPlus.push({ pathData: pathDataProps, bb: bb, dimA: dimA })
|
|
205
|
+
pathDataPlus = { pathData: pathDataProps, bb: bb, dimA: dimA }
|
|
222
206
|
|
|
207
|
+
//console.log('pathDataPlus', pathDataPlus);
|
|
208
|
+
return pathDataPlus
|
|
223
209
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
export function addDimensionData(pathData) {
|
|
216
|
+
|
|
217
|
+
let M = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
218
|
+
let p0 = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
219
|
+
let p;
|
|
220
|
+
|
|
221
|
+
pathData[0].dimA = 0;
|
|
222
|
+
let len = pathData.length
|
|
223
|
+
|
|
224
|
+
for (let c = 2; len && c <= len; c++) {
|
|
225
|
+
|
|
226
|
+
let com = pathData[c - 1];
|
|
227
|
+
let { type, values } = com;
|
|
228
|
+
let valsL = values.slice(-2);
|
|
229
|
+
|
|
230
|
+
p = valsL.length ? { x: valsL[0], y: valsL[1] } : M;
|
|
231
|
+
|
|
232
|
+
// update M for Z starting points
|
|
233
|
+
if (type === 'M') {
|
|
234
|
+
M = p;
|
|
235
|
+
}
|
|
236
|
+
else if (type.toLowerCase() === 'z') {
|
|
237
|
+
p = M;
|
|
237
238
|
}
|
|
238
239
|
|
|
240
|
+
let dimA = getDistManhattan(p0, p);
|
|
241
|
+
com.dimA = dimA;
|
|
242
|
+
com.p0 = p0
|
|
243
|
+
com.p = p
|
|
239
244
|
|
|
240
|
-
/**
|
|
241
|
-
* check extremes or corners
|
|
242
|
-
* for adjacent curves by
|
|
243
|
-
* control point angles
|
|
244
|
-
*/
|
|
245
|
-
if ((type === 'Q' || type === 'C')) {
|
|
246
245
|
|
|
247
|
-
|
|
246
|
+
if (type === 'C' || type === 'Q') com.cp1 = { x: values[0], y: values[1] }
|
|
247
|
+
if (type === 'C') com.cp2 = { x: values[2], y: values[3] }
|
|
248
248
|
|
|
249
|
-
|
|
250
|
-
|
|
249
|
+
p0 = p
|
|
250
|
+
}
|
|
251
251
|
|
|
252
|
-
let w = pN ? Math.abs(pN.x - p0.x) : 0
|
|
253
|
-
let h = pN ? Math.abs(pN.y - p0.y) : 0
|
|
254
|
-
let thresh = (w + h) / 2 * 0.1;
|
|
255
|
-
let pts1 = type === 'C' ? [p, cp1N, cp2N, pN] : [p, cp1N, pN];
|
|
256
252
|
|
|
257
|
-
|
|
258
|
-
|
|
253
|
+
//console.log('!!!pathData', pathData);
|
|
254
|
+
return pathData
|
|
255
|
+
}
|
|
259
256
|
|
|
260
|
-
/**
|
|
261
|
-
* if current and next cubic are flat
|
|
262
|
-
* we don't flag them as extremes to allow simplification
|
|
263
|
-
*/
|
|
264
|
-
//let hasExtremes = (isFlat && isFlat2) ? false : (!com.extreme ? bezierhasExtreme(p0, cpts, angleThreshold) : true);
|
|
265
257
|
|
|
266
|
-
let hasExtremes = (isFlat) ? false : (!com.extreme ? bezierhasExtreme(p0, cpts, angleThreshold) : true);
|
|
267
258
|
|
|
268
259
|
|
|
269
|
-
|
|
260
|
+
/**
|
|
261
|
+
* create pathdata super set
|
|
262
|
+
* including geometrical properties such as:
|
|
263
|
+
* start and end points
|
|
264
|
+
* segment square distances and areas
|
|
265
|
+
* elliptic arc parameters
|
|
266
|
+
*/
|
|
267
|
+
export function getPathDataVerbose(pathData, {
|
|
268
|
+
addSquareLength = true,
|
|
269
|
+
addArea = false,
|
|
270
|
+
addArcParams = false,
|
|
271
|
+
addAverageDim = true
|
|
272
|
+
} = {}) {
|
|
270
273
|
|
|
271
|
-
|
|
274
|
+
// initial starting point coordinates
|
|
275
|
+
let com0 = pathData[0];
|
|
276
|
+
let M = { x: com0.values[0], y: com0.values[1] };
|
|
277
|
+
let p0 = M;
|
|
278
|
+
let p = M;
|
|
272
279
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
280
|
+
com0.p0 = p0;
|
|
281
|
+
com0.p = p;
|
|
282
|
+
com0.idx = 0
|
|
283
|
+
com0.dimA = 0
|
|
276
284
|
|
|
277
|
-
// check corners
|
|
278
|
-
else {
|
|
279
285
|
|
|
280
|
-
|
|
281
|
-
|
|
286
|
+
let len = pathData.length;
|
|
287
|
+
let pathDataVerbose = [com0];
|
|
282
288
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
289
|
+
for (let i = 1; i < len; i++) {
|
|
290
|
+
let com = pathData[i];
|
|
291
|
+
let { type, values } = com;
|
|
292
|
+
let valuesLen = values.length;
|
|
286
293
|
|
|
294
|
+
p = valuesLen ? { x: values[valuesLen - 2], y: values[valuesLen - 1] } : M;
|
|
295
|
+
let cp1, cp2;
|
|
287
296
|
|
|
288
|
-
|
|
289
|
-
|
|
297
|
+
// add on-path points
|
|
298
|
+
com.p0 = p0;
|
|
299
|
+
com.p = p;
|
|
300
|
+
com.dimA = getDistManhattan(p0, p)
|
|
290
301
|
|
|
291
|
-
|
|
292
|
-
|
|
302
|
+
// update M for Z starting points
|
|
303
|
+
if (type === 'M') {
|
|
304
|
+
M = p;
|
|
305
|
+
}
|
|
293
306
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
307
|
+
// add bezier control point properties
|
|
308
|
+
if (type === 'Q' || type === 'C') {
|
|
309
|
+
cp1 = { x: values[0], y: values[1] }
|
|
310
|
+
cp2 = type === 'C' ? { x: values[2], y: values[3] } : null;
|
|
311
|
+
com.cp1 = cp1;
|
|
312
|
+
if (cp2) {
|
|
313
|
+
com.cp2 = cp2;
|
|
298
314
|
}
|
|
299
315
|
}
|
|
300
316
|
|
|
317
|
+
else if (type === 'A') {
|
|
318
|
+
let { rx, ry, cx, cy, startAngle, endAngle, deltaAngle } = svgArcToCenterParam(p0.x, p0.y, ...values)
|
|
319
|
+
com.cx = cx
|
|
320
|
+
com.cy = cy
|
|
321
|
+
com.rx = rx
|
|
322
|
+
com.ry = ry
|
|
323
|
+
com.xAxisRotation = values[2] / 180 * Math.PI
|
|
324
|
+
com.largeArc = values[3]
|
|
325
|
+
com.sweep = values[4]
|
|
326
|
+
com.startAngle = startAngle
|
|
327
|
+
com.endAngle = endAngle
|
|
328
|
+
com.deltaAngle = deltaAngle
|
|
329
|
+
}
|
|
301
330
|
|
|
302
|
-
|
|
303
|
-
|
|
331
|
+
/**
|
|
332
|
+
* explicit and implicit linetos
|
|
333
|
+
* - introduced by Z
|
|
334
|
+
*/
|
|
335
|
+
if (type === 'Z') {
|
|
336
|
+
// if Z introduces an implicit lineto with a length
|
|
337
|
+
if (M.x !== p.x && M.y !== p.y) {
|
|
338
|
+
com.closePath = true;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
304
341
|
|
|
305
|
-
|
|
342
|
+
if (addSquareLength) {
|
|
343
|
+
com.squareDist = getSquareDistance(p0, p)
|
|
344
|
+
}
|
|
306
345
|
|
|
346
|
+
if (addArea) {
|
|
347
|
+
let cptArea = 0;
|
|
348
|
+
if (type === 'C') cptArea = getPolygonArea([p0, cp1, cp2, p], false)
|
|
349
|
+
if (type === 'Q') cptArea = getPolygonArea([p0, cp1, p], false)
|
|
350
|
+
com.cptArea = cptArea;
|
|
351
|
+
}
|
|
307
352
|
|
|
308
|
-
|
|
309
|
-
//pathDataPlus.push({ pathData: pathDataProps, bb: bb, dimA: dimA })
|
|
310
|
-
pathDataPlus = { pathData: pathDataProps, bb: bb, dimA: dimA }
|
|
353
|
+
com.idx = i;
|
|
311
354
|
|
|
312
|
-
|
|
313
|
-
|
|
355
|
+
// update previous point
|
|
356
|
+
p0 = p;
|
|
357
|
+
pathDataVerbose.push(com)
|
|
358
|
+
}
|
|
314
359
|
|
|
360
|
+
//console.log('pathDataVerbose', pathDataVerbose);
|
|
361
|
+
return pathDataVerbose;
|
|
315
362
|
}
|