svg-path-simplify 0.4.3 → 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 +11 -0
- package/README.md +1 -0
- package/dist/svg-path-simplify.esm.js +1610 -495
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +1611 -494
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +893 -456
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/dist/svg-path-simplify.poly.cjs +9 -8
- package/index.html +58 -17
- package/package.json +1 -1
- package/src/constants.js +4 -0
- package/src/detect_input.js +47 -29
- package/src/index.js +8 -0
- package/src/pathData_simplify_cubic.js +26 -16
- package/src/pathData_simplify_revertToquadratics.js +0 -1
- package/src/pathSimplify-main.js +75 -20
- package/src/pathSimplify-only-pathdata.js +7 -2
- package/src/pathSimplify-presets.js +15 -4
- package/src/svg-getAttributes.js +4 -2
- package/src/svgii/convert_units.js +1 -1
- package/src/svgii/geometry.js +140 -2
- package/src/svgii/geometry_bbox_element.js +1 -1
- package/src/svgii/geometry_deduceRadius.js +116 -27
- package/src/svgii/geometry_length.js +17 -1
- package/src/svgii/pathData_analyze.js +18 -0
- package/src/svgii/pathData_convert.js +188 -88
- package/src/svgii/pathData_fix_directions.js +10 -18
- 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/rounding.js +79 -78
- package/src/svgii/svg_cleanup.js +68 -20
- package/src/svgii/svg_cleanup_convertPathLength.js +22 -15
- package/src/svgii/svg_cleanup_remove_els_and_atts.js +6 -1
- package/src/svgii/svg_el_parse_style_props.js +13 -10
- package/src/svgii/svg_validate.js +220 -0
- package/tests/testSVG.js +14 -1
- package/src/svgii/pathData_refine_round.js +0 -222
|
@@ -1,42 +1,92 @@
|
|
|
1
|
-
import { getDeltaAngle, getDistance } from "./geometry";
|
|
1
|
+
import { checkLineIntersection, getDeltaAngle, getDistance, getDistManhattan, getSquareDistance } from "./geometry";
|
|
2
|
+
import { renderPoint } from "./visualize";
|
|
3
|
+
//import { arcToBezierResolved } from "./pathData_convert";
|
|
4
|
+
//import { renderPoint, renderPoly } from "./visualize";
|
|
2
5
|
|
|
3
|
-
export function getArcFromPoly(pts) {
|
|
6
|
+
export function getArcFromPoly(pts, precise = false) {
|
|
4
7
|
if (pts.length < 3) return false
|
|
5
8
|
|
|
6
9
|
// Pick 3 well-spaced points
|
|
7
|
-
let
|
|
8
|
-
let
|
|
9
|
-
let
|
|
10
|
+
let len = pts.length
|
|
11
|
+
let idx1 = Math.floor(len * 0.333)
|
|
12
|
+
let idx2 = Math.floor(len * 0.666)
|
|
13
|
+
let idx3 = Math.floor(len * 0.5)
|
|
10
14
|
|
|
11
|
-
let x1 = p1.x, y1 = p1.y;
|
|
12
|
-
let x2 = p2.x, y2 = p2.y;
|
|
13
|
-
let x3 = p3.x, y3 = p3.y;
|
|
14
15
|
|
|
15
|
-
let
|
|
16
|
-
let
|
|
17
|
-
let
|
|
18
|
-
let d = y1 - y3;
|
|
16
|
+
let p1 = pts[0];
|
|
17
|
+
let p2 = pts[idx3];
|
|
18
|
+
let p3 = pts[len - 1];
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
let
|
|
20
|
+
// Radius (use start point)
|
|
21
|
+
let pts1 = [p1, p2, p3];
|
|
22
|
+
let centroid = getPolyArcCentroid(pts1);
|
|
22
23
|
|
|
23
|
-
let
|
|
24
|
+
let r = 0, deltaAngle = 0, startAngle = 0, endAngle = 0, angleData = {};
|
|
24
25
|
|
|
25
|
-
if (Math.abs(det) < 1e-10) {
|
|
26
|
-
console.warn("Points are collinear or numerically unstable");
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
26
|
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
// check if radii are consistent
|
|
28
|
+
if (precise) {
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* check multiple centroids
|
|
33
|
+
* if the polyline can be expressed as
|
|
34
|
+
* an arc - all centroids should be close
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
if (len > 3) {
|
|
38
|
+
let centroid1 = getPolyArcCentroid([p1, pts[idx1], p3]);
|
|
39
|
+
let centroid2 = getPolyArcCentroid([p1, pts[idx2], p3]);
|
|
40
|
+
|
|
41
|
+
if (!centroid1 || !centroid2) return false;
|
|
42
|
+
|
|
43
|
+
//let dist0 = getDistManhattan(p1, p3)
|
|
44
|
+
let dist0 = getDistManhattan(centroid, p2)
|
|
45
|
+
let dist1 = getDistManhattan(centroid, centroid1)
|
|
46
|
+
let dist2 = getDistManhattan(centroid, centroid2)
|
|
47
|
+
let errorCentroid = (dist1 + dist2)
|
|
48
|
+
|
|
49
|
+
// centroids diverging too much
|
|
50
|
+
if (errorCentroid > dist0 * 0.05) {
|
|
51
|
+
//renderPoint(markers, centroid, 'magenta')
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let rSqMid = getSquareDistance(centroid, p2);
|
|
58
|
+
|
|
59
|
+
//check if radii are close enough
|
|
60
|
+
for (let i = 0; i < len; i++) {
|
|
61
|
+
let pt = pts[i]
|
|
62
|
+
let rSq = getSquareDistance(centroid, pt);
|
|
63
|
+
let error = Math.abs(rSqMid - rSq) / rSqMid
|
|
64
|
+
|
|
65
|
+
if (error > 0.0025) {
|
|
66
|
+
/*
|
|
67
|
+
console.log('error', error, len, idx1, idx2, idx3);
|
|
68
|
+
renderPoint(markers, centroid, 'orange')
|
|
69
|
+
renderPoint(markers, p1, 'green')
|
|
70
|
+
renderPoint(markers, p2)
|
|
71
|
+
renderPoint(markers, p3, 'purple')
|
|
72
|
+
*/
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// calculate proper radius
|
|
78
|
+
r = Math.sqrt(rSqMid);
|
|
79
|
+
angleData = getDeltaAngle(centroid, p1, p3);
|
|
80
|
+
({ deltaAngle, startAngle, endAngle } = angleData);
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
} else {
|
|
84
|
+
r = getDistance(centroid, p1);
|
|
85
|
+
angleData = getDeltaAngle(centroid, p1, p3);
|
|
86
|
+
({ deltaAngle, startAngle, endAngle } = angleData);
|
|
87
|
+
}
|
|
34
88
|
|
|
35
|
-
// Radius (use start point)
|
|
36
|
-
let r = getDistance(centroid, p1);
|
|
37
89
|
|
|
38
|
-
let angleData = getDeltaAngle(centroid, p1, p3)
|
|
39
|
-
let {deltaAngle, startAngle, endAngle} = angleData;
|
|
40
90
|
|
|
41
91
|
return {
|
|
42
92
|
centroid,
|
|
@@ -48,3 +98,42 @@ export function getArcFromPoly(pts) {
|
|
|
48
98
|
}
|
|
49
99
|
|
|
50
100
|
|
|
101
|
+
|
|
102
|
+
export function getPolyArcCentroid(pts = []) {
|
|
103
|
+
|
|
104
|
+
pts = pts.filter(pt => pt !== undefined);
|
|
105
|
+
if (pts.length < 3) return false
|
|
106
|
+
//console.log(pts);
|
|
107
|
+
|
|
108
|
+
let p1 = pts[0];
|
|
109
|
+
let p2 = pts[Math.floor(pts.length / 2)];
|
|
110
|
+
let p3 = pts[pts.length - 1];
|
|
111
|
+
|
|
112
|
+
let x1 = p1.x, y1 = p1.y;
|
|
113
|
+
let x2 = p2.x, y2 = p2.y;
|
|
114
|
+
let x3 = p3.x, y3 = p3.y;
|
|
115
|
+
|
|
116
|
+
let a = x1 - x2;
|
|
117
|
+
let b = y1 - y2;
|
|
118
|
+
let c = x1 - x3;
|
|
119
|
+
let d = y1 - y3;
|
|
120
|
+
|
|
121
|
+
let e = ((x1 * x1 - x2 * x2) + (y1 * y1 - y2 * y2)) / 2;
|
|
122
|
+
let f = ((x1 * x1 - x3 * x3) + (y1 * y1 - y3 * y3)) / 2;
|
|
123
|
+
|
|
124
|
+
let det = a * d - b * c;
|
|
125
|
+
|
|
126
|
+
// colinear points
|
|
127
|
+
if (Math.abs(det) < 1e-10) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// find center of arc
|
|
132
|
+
let cx = (d * e - b * f) / det;
|
|
133
|
+
let cy = (-c * e + a * f) / det;
|
|
134
|
+
let centroid = { x: cx, y: cy };
|
|
135
|
+
return centroid
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDistance } from "./geometry";
|
|
1
|
+
import { getDistance, getDistManhattan } from "./geometry";
|
|
2
2
|
|
|
3
3
|
// Legendre Gauss weight and abscissa values
|
|
4
4
|
export const waArr_global = [];
|
|
@@ -176,6 +176,22 @@ export function getPolygonLength(pts=[], isPoly=false){
|
|
|
176
176
|
return len
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
export function getPolygonLengthManhattan(pts=[], isPoly=false){
|
|
180
|
+
|
|
181
|
+
let len = 0;
|
|
182
|
+
let l=pts.length;
|
|
183
|
+
|
|
184
|
+
for(let i=1; i<l; i++){
|
|
185
|
+
let p1 = pts[i-1]
|
|
186
|
+
let p2 = pts[i]
|
|
187
|
+
len += getDistManhattan(p1, p2)
|
|
188
|
+
}
|
|
189
|
+
if(isPoly){
|
|
190
|
+
len += getDistManhattan(pts[l-1], pts[0])
|
|
191
|
+
}
|
|
192
|
+
return len
|
|
193
|
+
}
|
|
194
|
+
|
|
179
195
|
|
|
180
196
|
/**
|
|
181
197
|
* Ramanujan approximation
|
|
@@ -67,6 +67,7 @@ export function analyzePathData(pathData = [], {
|
|
|
67
67
|
let { type, values, p0, p, cp1 = null, cp2 = null, squareDist = 0, cptArea = 0, dimA = 0 } = com;
|
|
68
68
|
|
|
69
69
|
//next command
|
|
70
|
+
let comPrev = pathData[c-2];
|
|
70
71
|
let comN = pathData[c] || null;
|
|
71
72
|
|
|
72
73
|
|
|
@@ -93,6 +94,7 @@ export function analyzePathData(pathData = [], {
|
|
|
93
94
|
|
|
94
95
|
// bezier types
|
|
95
96
|
let isBezier = type === 'Q' || type === 'C';
|
|
97
|
+
let isArc = type === 'A';
|
|
96
98
|
let isBezierN = comN && (comN.type === 'Q' || comN.type === 'C');
|
|
97
99
|
|
|
98
100
|
|
|
@@ -149,6 +151,22 @@ export function analyzePathData(pathData = [], {
|
|
|
149
151
|
}
|
|
150
152
|
}
|
|
151
153
|
|
|
154
|
+
// check extremes introduce by small arcs
|
|
155
|
+
else if(isArc && comN && ((comPrev.type==='C' || comPrev.type==='Q') || (comN.type==='C' || comN.type==='Q')) ){
|
|
156
|
+
let distN = comN ? comN.dimA : 0
|
|
157
|
+
let isShort = com.dimA < (comPrev.dimA + distN) * 0.1;
|
|
158
|
+
let smallRadius = com.values[0] === com.values[1] && (com.values[0] < 1)
|
|
159
|
+
|
|
160
|
+
if(isShort && smallRadius){
|
|
161
|
+
let bb = getPolyBBox([comPrev.p0, comN.p])
|
|
162
|
+
if(p.x>bb.right || p.x<bb.x || p.y<bb.y || p.y>bb.bottom){
|
|
163
|
+
hasExtremes = true;
|
|
164
|
+
//renderPoint(markers, p)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
}
|
|
169
|
+
|
|
152
170
|
if (hasExtremes) com.extreme = true
|
|
153
171
|
|
|
154
172
|
// Corners and semi extremes
|
|
@@ -17,54 +17,10 @@ import { pathDataToD } from './pathData_stringify';
|
|
|
17
17
|
import { roundPathData } from './rounding';
|
|
18
18
|
import { renderPoint } from './visualize';
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
toAbsolute = true,
|
|
25
|
-
toLonghands = true,
|
|
26
|
-
|
|
27
|
-
// not necessary unless you need cubics only
|
|
28
|
-
quadraticToCubic = false,
|
|
29
|
-
|
|
30
|
-
// mostly a fallback if arc calculations fail
|
|
31
|
-
arcToCubic = false,
|
|
32
|
-
// arc to cubic precision - adds more segments for better precision
|
|
33
|
-
arcAccuracy = 4,
|
|
34
|
-
} = {}
|
|
35
|
-
) {
|
|
36
|
-
|
|
37
|
-
// is already array
|
|
38
|
-
let isArray = Array.isArray(d);
|
|
39
|
-
|
|
40
|
-
// normalize native pathData to regular array
|
|
41
|
-
let hasConstructor = isArray && typeof d[0] === 'object' && typeof d[0].constructor === 'function'
|
|
42
|
-
/*
|
|
43
|
-
if (hasConstructor) {
|
|
44
|
-
d = d.map(com => { return { type: com.type, values: com.values } })
|
|
45
|
-
console.log('hasConstructor', hasConstructor, (typeof d[0].constructor), d);
|
|
46
|
-
}
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
let pathDataObj = isArray ? d : parsePathDataString(d);
|
|
50
|
-
|
|
51
|
-
let { hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true } = pathDataObj;
|
|
52
|
-
let pathData = hasConstructor ? pathDataObj : pathDataObj.pathData;
|
|
53
|
-
|
|
54
|
-
//console.log('???quadraticToCubic', quadraticToCubic);
|
|
55
|
-
|
|
56
|
-
// normalize
|
|
57
|
-
pathData = normalizePathData(pathData,
|
|
58
|
-
{
|
|
59
|
-
toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy,
|
|
60
|
-
hasRelatives, hasShorthands, hasQuadratics, hasArcs
|
|
61
|
-
},
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
return pathData;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
20
|
+
/**
|
|
21
|
+
* wrapper function for
|
|
22
|
+
* all path data conversion
|
|
23
|
+
*/
|
|
68
24
|
export function convertPathData(pathData, {
|
|
69
25
|
toShorthands = true,
|
|
70
26
|
toLonghands = false,
|
|
@@ -126,23 +82,25 @@ export function convertPathData(pathData, {
|
|
|
126
82
|
//console.log(toShorthands, toRelative, decimals);
|
|
127
83
|
if (hasQuadratics && quadraticToCubic) pathData = pathDataQuadraticToCubic(pathData);
|
|
128
84
|
|
|
129
|
-
if(toMixed) toRelative = true
|
|
85
|
+
if (toMixed) toRelative = true
|
|
130
86
|
|
|
131
87
|
// pre round - before relative conversion to minimize distortions
|
|
132
88
|
if (decimals > -1 && toRelative) pathData = roundPathData(pathData, decimals);
|
|
133
89
|
|
|
134
90
|
// clone absolute pathdata
|
|
135
|
-
if(toMixed){
|
|
91
|
+
if (toMixed) {
|
|
136
92
|
pathDataAbs = JSON.parse(JSON.stringify(pathData))
|
|
137
93
|
}
|
|
138
94
|
|
|
139
95
|
if (toRelative) pathData = pathDataToRelative(pathData);
|
|
96
|
+
|
|
97
|
+
// final rounding
|
|
140
98
|
if (decimals > -1) pathData = roundPathData(pathData, decimals);
|
|
141
99
|
|
|
142
100
|
|
|
143
101
|
// choose most compact commands: relative or absolute
|
|
144
|
-
if(toMixed){
|
|
145
|
-
for(let i=0; i<pathData.length; i++){
|
|
102
|
+
if (toMixed) {
|
|
103
|
+
for (let i = 0; i < pathData.length; i++) {
|
|
146
104
|
let com = pathData[i]
|
|
147
105
|
let comA = pathDataAbs[i]
|
|
148
106
|
// compare Lengths
|
|
@@ -152,7 +110,7 @@ export function convertPathData(pathData, {
|
|
|
152
110
|
let lenR = comStr.length;
|
|
153
111
|
let lenA = comStrA.length;
|
|
154
112
|
|
|
155
|
-
if(lenA<lenR){
|
|
113
|
+
if (lenA < lenR) {
|
|
156
114
|
//console.log('absolute is shorter', comStrA, comStr);
|
|
157
115
|
pathData[i] = pathDataAbs[i]
|
|
158
116
|
}
|
|
@@ -162,6 +120,56 @@ export function convertPathData(pathData, {
|
|
|
162
120
|
return pathData
|
|
163
121
|
}
|
|
164
122
|
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
export function parsePathDataNormalized(d,
|
|
127
|
+
{
|
|
128
|
+
// necessary for most calculations
|
|
129
|
+
toAbsolute = true,
|
|
130
|
+
toLonghands = true,
|
|
131
|
+
|
|
132
|
+
// not necessary unless you need cubics only
|
|
133
|
+
quadraticToCubic = false,
|
|
134
|
+
|
|
135
|
+
// mostly a fallback if arc calculations fail
|
|
136
|
+
arcToCubic = false,
|
|
137
|
+
// arc to cubic precision - adds more segments for better precision
|
|
138
|
+
arcAccuracy = 4,
|
|
139
|
+
} = {}
|
|
140
|
+
) {
|
|
141
|
+
|
|
142
|
+
// is already array
|
|
143
|
+
let isArray = Array.isArray(d);
|
|
144
|
+
|
|
145
|
+
// normalize native pathData to regular array
|
|
146
|
+
let hasConstructor = isArray && typeof d[0] === 'object' && typeof d[0].constructor === 'function'
|
|
147
|
+
/*
|
|
148
|
+
if (hasConstructor) {
|
|
149
|
+
d = d.map(com => { return { type: com.type, values: com.values } })
|
|
150
|
+
console.log('hasConstructor', hasConstructor, (typeof d[0].constructor), d);
|
|
151
|
+
}
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
let pathDataObj = isArray ? d : parsePathDataString(d);
|
|
155
|
+
|
|
156
|
+
let { hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true } = pathDataObj;
|
|
157
|
+
let pathData = hasConstructor ? pathDataObj : pathDataObj.pathData;
|
|
158
|
+
|
|
159
|
+
//console.log('???quadraticToCubic', quadraticToCubic);
|
|
160
|
+
|
|
161
|
+
// normalize
|
|
162
|
+
pathData = normalizePathData(pathData,
|
|
163
|
+
{
|
|
164
|
+
toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy,
|
|
165
|
+
hasRelatives, hasShorthands, hasQuadratics, hasArcs
|
|
166
|
+
},
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
return pathData;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
165
173
|
/**
|
|
166
174
|
*
|
|
167
175
|
* @param {*} pathData
|
|
@@ -170,50 +178,100 @@ export function convertPathData(pathData, {
|
|
|
170
178
|
|
|
171
179
|
export function optimizeArcPathData(pathData = []) {
|
|
172
180
|
|
|
173
|
-
|
|
181
|
+
//return pathData
|
|
174
182
|
|
|
175
|
-
|
|
183
|
+
let remove = []
|
|
184
|
+
let l = pathData.length;
|
|
185
|
+
let pathDataN = [];
|
|
186
|
+
|
|
187
|
+
for (let i = 0; i < l; i++) {
|
|
188
|
+
let com = pathData[i];
|
|
176
189
|
let { type, values } = com;
|
|
177
|
-
if (type === 'A') {
|
|
178
|
-
let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
|
|
179
|
-
let comPrev = pathData[i - 1]
|
|
180
|
-
let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
|
|
181
|
-
let M = { x: x0, y: y0 };
|
|
182
|
-
let p = { x, y };
|
|
183
|
-
//largeArc=true
|
|
184
|
-
//let pMid = {x: Math.abs(x-x0), y:Math.abs(y-y0)}
|
|
185
|
-
|
|
186
|
-
if(rx===0 || ry===0){
|
|
187
|
-
pathData[i]= null
|
|
188
|
-
remove.push(i)
|
|
189
|
-
//console.log('!!!');
|
|
190
|
-
}
|
|
191
190
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
if (type !== 'A') {
|
|
192
|
+
pathDataN.push(com)
|
|
193
|
+
continue
|
|
194
|
+
}
|
|
195
195
|
|
|
196
|
-
// rx~==ry
|
|
197
|
-
if (diff < 0.01) {
|
|
198
196
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
197
|
+
let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
|
|
198
|
+
let comPrev = pathData[i - 1]
|
|
199
|
+
let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
|
|
200
|
+
let M = { x: x0, y: y0 };
|
|
201
|
+
let p = { x, y };
|
|
202
|
+
//largeArc=true
|
|
203
|
+
//let pMid = {x: Math.abs(x-x0), y:Math.abs(y-y0)}
|
|
203
204
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
205
|
+
if (rx === 0 || ry === 0) {
|
|
206
|
+
pathData[i] = null
|
|
207
|
+
remove.push(i)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// test for elliptic
|
|
211
|
+
let rat = rx / ry
|
|
212
|
+
let error = rx !== ry ? Math.abs(1 - rat) : 0
|
|
213
|
+
|
|
214
|
+
if (error > 0.01) {
|
|
215
|
+
//console.log('is elliptic');
|
|
216
|
+
pathDataN.push(com)
|
|
217
|
+
continue
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// xAxis rotation is futile for circular arcs - reset
|
|
222
|
+
com.values[2] = 0
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* test semi circles
|
|
226
|
+
* rx and ry are large enough
|
|
227
|
+
*/
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
// 1. horizontal or vertical
|
|
231
|
+
let thresh = getDistManhattan(M, p) * 0.001;
|
|
232
|
+
let diffX = Math.abs(x - x0)
|
|
233
|
+
let diffY = Math.abs(y - y0)
|
|
234
|
+
|
|
235
|
+
let isHorizontal = diffY < thresh
|
|
236
|
+
let isVertical = diffX < thresh
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
// minify rx and ry
|
|
240
|
+
if (isHorizontal || isVertical) {
|
|
241
|
+
|
|
242
|
+
// check if semi circle
|
|
243
|
+
let needsTrueR = isHorizontal ? rx*1.9 > diffX : ry*1.9 > diffY;
|
|
244
|
+
|
|
245
|
+
// is semicircle we can simplify rx
|
|
246
|
+
if (!needsTrueR) {
|
|
247
|
+
//console.log('needsTrueR', needsTrueR, diffX, rx, diffY, ry);
|
|
248
|
+
rx = rx >= 1 ? 1 : (rx > 0.5 ? 0.5 : rx);
|
|
211
249
|
}
|
|
250
|
+
|
|
251
|
+
com.values[0] = rx
|
|
252
|
+
com.values[1] = rx
|
|
253
|
+
pathDataN.push(com)
|
|
254
|
+
continue
|
|
255
|
+
|
|
256
|
+
|
|
212
257
|
}
|
|
213
|
-
})
|
|
214
258
|
|
|
215
|
-
|
|
216
|
-
|
|
259
|
+
// 2. get true radius - if rx ~= diameter/distance we have a semicircle
|
|
260
|
+
let r = getDistance(M, p) * 0.5
|
|
261
|
+
error = rx / r
|
|
262
|
+
//console.log('rx', rx, r, error);
|
|
263
|
+
|
|
264
|
+
if (error < 0.5) {
|
|
265
|
+
rx = r >= 1 ? 1 : (r > 0.5 ? 0.5 : r);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
com.values[0] = rx;
|
|
269
|
+
com.values[1] = rx;
|
|
270
|
+
pathDataN.push(com)
|
|
271
|
+
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return pathDataN;
|
|
217
275
|
}
|
|
218
276
|
|
|
219
277
|
|
|
@@ -281,6 +339,48 @@ export function normalizePathData(pathData = [],
|
|
|
281
339
|
}
|
|
282
340
|
*/
|
|
283
341
|
|
|
342
|
+
export function convertSmallArcsToLinetos(pathData) {
|
|
343
|
+
|
|
344
|
+
let l = pathData.length;
|
|
345
|
+
|
|
346
|
+
// add fist command
|
|
347
|
+
let pathDataN = [pathData[0]]
|
|
348
|
+
|
|
349
|
+
for (let i = 1; i < l; i++) {
|
|
350
|
+
let com = pathData[i];
|
|
351
|
+
let comPrev = pathData[i - 1];
|
|
352
|
+
let comN = pathData[i + 1] || null;
|
|
353
|
+
|
|
354
|
+
if (!comN) {
|
|
355
|
+
pathDataN.push(com);
|
|
356
|
+
break
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
let { type, values, extreme = false, p0, p, dimA = 0 } = com;
|
|
360
|
+
// for short segment detection
|
|
361
|
+
let dimAN = comN.dimA;
|
|
362
|
+
let dimA0 = comPrev.dimA + dimA + dimAN;
|
|
363
|
+
let thresh = 0.05
|
|
364
|
+
let isShort = dimA < dimA0 * thresh;
|
|
365
|
+
//let isShortN = dimAN < dimA0 * thresh;
|
|
366
|
+
|
|
367
|
+
if (type === 'A' && isShort && values[0] < 1 && values[1] < 1) {
|
|
368
|
+
|
|
369
|
+
//renderPoint(markers, p0, 'green', '0.1%')
|
|
370
|
+
//renderPoint(markers, p, 'magenta', '0.1%')
|
|
371
|
+
com.type = 'L';
|
|
372
|
+
com.values = [p.x, p.y];
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
pathDataN.push(com)
|
|
376
|
+
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
return pathDataN;
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
}
|
|
284
384
|
|
|
285
385
|
|
|
286
386
|
|
|
@@ -54,33 +54,25 @@ export function fixPathDataDirections(pathDataArr = [], toClockwise = false) {
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
// reverse paths
|
|
57
|
-
for (let i = 0; i < l; i++) {
|
|
57
|
+
for (let i = 0; l && i < l; i++) {
|
|
58
58
|
|
|
59
59
|
let poly = polys[i]
|
|
60
60
|
let { cw, includedIn, includes } = poly
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|| !includedIn.length && !cw && toClockwise
|
|
65
|
-
) {
|
|
66
|
-
//console.log('reverse outer');
|
|
67
|
-
|
|
68
|
-
pathDataArr[i].pathData = reversePathData(pathDataArr[i].pathData);
|
|
69
|
-
polys[i].cw = polys[i].cw ? false : true
|
|
70
|
-
cw = polys[i].cw
|
|
71
|
-
|
|
72
|
-
}
|
|
62
|
+
let len = includes.length;
|
|
63
|
+
//console.log('try reverse', includes);
|
|
73
64
|
|
|
74
65
|
// reverse inner sub paths
|
|
75
|
-
for (let j = 0; j <
|
|
66
|
+
for (let j = 0; len && j < len; j++) {
|
|
76
67
|
let ind = includes[j];
|
|
77
68
|
let child = polys[ind];
|
|
78
69
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
70
|
+
// nothing to do
|
|
71
|
+
if (child.cw !== cw) continue
|
|
72
|
+
|
|
73
|
+
pathDataArr[ind].pathData = reversePathData(pathDataArr[ind].pathData);
|
|
74
|
+
polys[ind].cw = polys[ind].cw ? false : true
|
|
75
|
+
|
|
84
76
|
}
|
|
85
77
|
}
|
|
86
78
|
|