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,220 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export function validateSVG(markup, allowed = {}) {
|
|
4
|
+
allowed = {
|
|
5
|
+
...{
|
|
6
|
+
//useEls: 10,
|
|
7
|
+
//hasPrologue: false,
|
|
8
|
+
//hasXmlns: true,
|
|
9
|
+
useElsNested: 5000,
|
|
10
|
+
hasScripts: false,
|
|
11
|
+
hasEntity: false,
|
|
12
|
+
fileSizeKB: 10000,
|
|
13
|
+
isSymbolSprite: false,
|
|
14
|
+
isSvgFont: false
|
|
15
|
+
},
|
|
16
|
+
...allowed
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
let fileReport = analyzeSVG(markup, allowed);
|
|
21
|
+
let isValid = true;
|
|
22
|
+
let log = [];
|
|
23
|
+
|
|
24
|
+
if (!fileReport.hasEls) {
|
|
25
|
+
log.push("no elements");
|
|
26
|
+
isValid = false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (Object.keys(fileReport).length) {
|
|
30
|
+
if (fileReport.isBillionLaugh === true) {
|
|
31
|
+
log.push(`suspicious: might contain billion laugh attack`);
|
|
32
|
+
isValid = false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for (let key in allowed) {
|
|
36
|
+
let val = allowed[key];
|
|
37
|
+
let valRep = fileReport[key];
|
|
38
|
+
if (typeof val === "number" && valRep > val) {
|
|
39
|
+
log.push(`allowed "${key}" exceeded: ${valRep} / ${val} `);
|
|
40
|
+
isValid = false;
|
|
41
|
+
}
|
|
42
|
+
if (valRep === true && val === false) {
|
|
43
|
+
log.push(`not allowed: "${key}" `);
|
|
44
|
+
isValid = false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
isValid = false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/*
|
|
52
|
+
if (!isValid) {
|
|
53
|
+
log = ["SVG not valid"].concat(log);
|
|
54
|
+
//console.log(log.join("\n"));
|
|
55
|
+
if (Object.keys(fileReport).length) {
|
|
56
|
+
console.warn(fileReport);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
return { isValid, log, fileReport };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function analyzeSVG(markup, allowed = {}) {
|
|
65
|
+
markup = markup.trim();
|
|
66
|
+
let doc, svg;
|
|
67
|
+
let fileSizeKB = +(markup.length / 1024).toFixed(3);
|
|
68
|
+
|
|
69
|
+
let fileReport = {
|
|
70
|
+
totalEls: 1,
|
|
71
|
+
hasEls: true,
|
|
72
|
+
hasDefs: false,
|
|
73
|
+
geometryEls: [],
|
|
74
|
+
useEls: 0,
|
|
75
|
+
useElsNested: 0,
|
|
76
|
+
nonsensePaths: 0,
|
|
77
|
+
isSuspicious: false,
|
|
78
|
+
isBillionLaugh: false,
|
|
79
|
+
hasScripts: false,
|
|
80
|
+
hasPrologue: false,
|
|
81
|
+
hasEntity: false,
|
|
82
|
+
isPathData:false,
|
|
83
|
+
fileSizeKB,
|
|
84
|
+
hasXmlns: markup.includes("http://www.w3.org/2000/svg"),
|
|
85
|
+
isSymbolSprite: false,
|
|
86
|
+
isSvgFont: markup.includes("<glyph>")
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
let maxNested = allowed.useElsNested ? allowed.useElsNested : 2000;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* analyze nestes use references
|
|
94
|
+
*/
|
|
95
|
+
const countUseRefs = (useEls, maxNested = 2000) => {
|
|
96
|
+
let nestedCount = 0;
|
|
97
|
+
//stop loop if number of nested use references is exceeded
|
|
98
|
+
for (let i = 0; i < useEls.length && nestedCount < maxNested; i++) {
|
|
99
|
+
let use = useEls[i];
|
|
100
|
+
let refId = use.getAttribute("xlink:href")
|
|
101
|
+
? use.getAttribute("xlink:href")
|
|
102
|
+
: use.getAttribute("href");
|
|
103
|
+
refId = refId ? refId.replace("#", "") : "";
|
|
104
|
+
|
|
105
|
+
//normalize href attributes to facilitate JS selection
|
|
106
|
+
use.setAttribute("href", "#" + refId);
|
|
107
|
+
|
|
108
|
+
let refEl = svg.getElementById(refId);
|
|
109
|
+
let nestedUse = refEl.querySelectorAll("use");
|
|
110
|
+
let nestedUseLength = nestedUse.length;
|
|
111
|
+
nestedCount += nestedUseLength;
|
|
112
|
+
|
|
113
|
+
// query nested use references
|
|
114
|
+
for (let n = 0; n < nestedUse.length && nestedCount < maxNested; n++) {
|
|
115
|
+
let nested = nestedUse[n];
|
|
116
|
+
let id1 = nested.getAttribute("href").replace("#", "");
|
|
117
|
+
let refEl1 = svg.getElementById(id1);
|
|
118
|
+
let nestedUse1 = refEl1.querySelectorAll("use");
|
|
119
|
+
nestedCount += nestedUse1.length;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
fileReport.useElsNested = nestedCount;
|
|
123
|
+
return nestedCount;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* check on raw text level
|
|
128
|
+
*/
|
|
129
|
+
let hasPrologue = /\<\?xml.+\?\>|\<\!DOCTYPE.+]\>/g.test(markup);
|
|
130
|
+
let hasEntity = /\<\!ENTITY/gi.test(markup);
|
|
131
|
+
let hasScripts = /\<script/gi.test(markup) ? true : false;
|
|
132
|
+
let hasUse = /\<use/gi.test(markup) ? true : false;
|
|
133
|
+
let hasEls = /[\<path|\<polygon|\<polyline|\<rect|\<circle|\<ellipse|\<line|\<text|\<foreignObject]/gi.test(markup);
|
|
134
|
+
let hasDefs = /[\<filter|\<linearGradient|\<radialGradient|\<pattern|\<animate|\<animateMotion|\<animateTransform|\<clipPath|\<mask|\<symbol|\<marker]/gi.test(markup);
|
|
135
|
+
|
|
136
|
+
let isPathData = (markup.startsWith('M') || markup.startsWith('m')) && !/[\<svg|\<\/svg]/gi.test(markup);
|
|
137
|
+
fileReport.isPathData = isPathData;
|
|
138
|
+
|
|
139
|
+
// seems OK
|
|
140
|
+
if (!hasEntity && !hasUse && !hasScripts && (hasEls || hasDefs) && fileSizeKB < allowed.fileSizeKB) {
|
|
141
|
+
fileReport.hasEls = hasEls
|
|
142
|
+
fileReport.hasDefs = hasDefs
|
|
143
|
+
//console.log('Looks OK!', fileReport, allowed);
|
|
144
|
+
return fileReport
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
// Contains xml entity definition: highly suspicious - stop parsing!
|
|
149
|
+
if (allowed.hasEntity === false && hasEntity) {
|
|
150
|
+
fileReport.hasEntity = true;
|
|
151
|
+
//return fileReport;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* sanitizing for parsing:
|
|
156
|
+
* remove xml prologue and comments
|
|
157
|
+
*/
|
|
158
|
+
markup = markup
|
|
159
|
+
.replace(/\<\?xml.+\?\>|\<\!DOCTYPE.+]\>/g, "")
|
|
160
|
+
.replace(/(<!--.*?-->)|(<!--[\S\s]+?-->)|(<!--[\S\s]*?$)/g, "");
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Try to parse svg:
|
|
164
|
+
* invalid svg will return false via "catch"
|
|
165
|
+
*/
|
|
166
|
+
try {
|
|
167
|
+
//doc = new DOMParser().parseFromString(markup, "image/svg+xml");
|
|
168
|
+
doc = new DOMParser().parseFromString(markup, "text/html");
|
|
169
|
+
svg = doc.querySelector("svg");
|
|
170
|
+
|
|
171
|
+
// paths containing only a M command
|
|
172
|
+
let nonsensePaths = svg.querySelectorAll('path[d="M0,0"], path[d="M0 0"]').length;
|
|
173
|
+
let useEls = svg.querySelectorAll("use").length;
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
// create analyzing object
|
|
177
|
+
fileReport.totalEls = svg.querySelectorAll("*").length;
|
|
178
|
+
fileReport.geometryEls = svg.querySelectorAll(
|
|
179
|
+
"path, rect, circle, ellipse, polygon, polyline, line"
|
|
180
|
+
).length
|
|
181
|
+
|
|
182
|
+
fileReport.hasScripts = hasScripts
|
|
183
|
+
fileReport.useEls = useEls;
|
|
184
|
+
fileReport.nonsensePaths = nonsensePaths;
|
|
185
|
+
fileReport.isSuspicious = false;
|
|
186
|
+
fileReport.isBillionLaugh = false;
|
|
187
|
+
fileReport.hasXmlns = svg.getAttribute("xmlns")
|
|
188
|
+
? svg.getAttribute("xmlns") === "http://www.w3.org/2000/svg"
|
|
189
|
+
? true
|
|
190
|
+
: false
|
|
191
|
+
: false;
|
|
192
|
+
fileReport.isSymbolSprite =
|
|
193
|
+
svg.querySelectorAll("symbol").length &&
|
|
194
|
+
svg.querySelectorAll("use").length === 0
|
|
195
|
+
? true
|
|
196
|
+
: false;
|
|
197
|
+
fileReport.isSvgFont = svg.querySelectorAll("glyph").length ? true : false;
|
|
198
|
+
|
|
199
|
+
let totalEls = fileReport.totalEls;
|
|
200
|
+
let totalUseEls = fileReport.useEls;
|
|
201
|
+
let usePercentage = (100 / totalEls) * totalUseEls;
|
|
202
|
+
|
|
203
|
+
// if percentage of use elements is higher than 75% - suspicious
|
|
204
|
+
if (usePercentage > 75) {
|
|
205
|
+
fileReport.isSuspicious = true;
|
|
206
|
+
|
|
207
|
+
// check nested use references
|
|
208
|
+
let nestedCount = countUseRefs(svg.querySelectorAll("use"), maxNested);
|
|
209
|
+
if (nestedCount >= maxNested) {
|
|
210
|
+
fileReport.isBillionLaugh = true;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return fileReport;
|
|
215
|
+
} catch {
|
|
216
|
+
// svg file has malformed markup
|
|
217
|
+
console.warn("svg could not be parsed");
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
package/tests/testSVG.js
CHANGED
|
@@ -19,8 +19,21 @@ let svgMarkup =
|
|
|
19
19
|
</g>
|
|
20
20
|
</svg>`
|
|
21
21
|
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
let document = new DOMParser().parseFromString(svgMarkup, 'image/svg+xml');
|
|
25
|
+
let svg = document.querySelector('svg');
|
|
26
|
+
let path = svg.querySelector('path');
|
|
27
|
+
let els = svg.querySelectorAll('path')
|
|
28
|
+
let d = path.getAttribute('d').substring(0, 10)
|
|
29
|
+
console.log(d);
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
22
35
|
// try to simplify
|
|
23
|
-
let svgOpt = svgPathSimplify(svgMarkup, {
|
|
36
|
+
let svgOpt = svgPathSimplify(svgMarkup, {preset:'high'});
|
|
24
37
|
|
|
25
38
|
// simplified pathData
|
|
26
39
|
console.log(svgOpt)
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import { checkLineIntersection, getAngle, getDistAv, getDistance, getPointOnEllipse, getSquareDistance, pointAtT, rotatePoint } from "./geometry";
|
|
2
|
-
import { getPolygonArea } from "./geometry_area";
|
|
3
|
-
import { getArcFromPoly } from "./geometry_deduceRadius";
|
|
4
|
-
import { arcToBezierResolved, revertCubicQuadratic } from "./pathData_convert";
|
|
5
|
-
import { pathDataToD } from "./pathData_stringify";
|
|
6
|
-
import { renderPath, renderPoint, renderPoly } from "./visualize";
|
|
7
|
-
|
|
8
|
-
export function refineRoundSegments(pathData, {
|
|
9
|
-
threshold = 0,
|
|
10
|
-
tolerance = 1,
|
|
11
|
-
// take arcs or cubic beziers
|
|
12
|
-
toCubic = false,
|
|
13
|
-
debug = false
|
|
14
|
-
} = {}) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// min size threshold for corners
|
|
18
|
-
threshold *= tolerance;
|
|
19
|
-
|
|
20
|
-
let l = pathData.length;
|
|
21
|
-
|
|
22
|
-
// add fist command
|
|
23
|
-
let pathDataN = [pathData[0]]
|
|
24
|
-
|
|
25
|
-
// just for debugging
|
|
26
|
-
let pathDataTest = []
|
|
27
|
-
|
|
28
|
-
for (let i = 1; i < l; i++) {
|
|
29
|
-
let com = pathData[i];
|
|
30
|
-
let { type } = com;
|
|
31
|
-
let comP = pathData[i - 1];
|
|
32
|
-
let comN = pathData[i + 1] ? pathData[i + 1] : null;
|
|
33
|
-
let comN2 = pathData[i + 2] ? pathData[i + 2] : null;
|
|
34
|
-
let comN3 = pathData[i + 3] ? pathData[i + 3] : null;
|
|
35
|
-
let comBez = null;
|
|
36
|
-
|
|
37
|
-
if ((com.type === 'C' || com.type === 'Q')) comBez = com;
|
|
38
|
-
else if (comN && (comN.type === 'C' || comN.type === 'Q')) comBez = comN;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
let cpts = comBez ? (comBez.type === 'C' ? [comBez.p0, comBez.cp1, comBez.cp2, comBez.p] : [comBez.p0, comBez.cp1, comBez.p]) : []
|
|
42
|
-
|
|
43
|
-
let areaBez = 0;
|
|
44
|
-
let areaLines = 0;
|
|
45
|
-
let signChange = false;
|
|
46
|
-
let L1, L2;
|
|
47
|
-
let combine = false
|
|
48
|
-
|
|
49
|
-
let p0_S, p_S;
|
|
50
|
-
let poly = []
|
|
51
|
-
let pMid;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// 2. line-line-bezier-line-line
|
|
55
|
-
if (
|
|
56
|
-
comP.type === 'L' &&
|
|
57
|
-
type === 'L' &&
|
|
58
|
-
comBez &&
|
|
59
|
-
comN2.type === 'L' &&
|
|
60
|
-
comN3 && (comN3.type === 'L' || comN3.type === 'Z')
|
|
61
|
-
) {
|
|
62
|
-
|
|
63
|
-
L1 = [com.p0, com.p];
|
|
64
|
-
L2 = [comN2.p0, comN2.p];
|
|
65
|
-
p0_S = com.p0
|
|
66
|
-
p_S = comN2.p
|
|
67
|
-
|
|
68
|
-
// don't allow sign changes
|
|
69
|
-
areaBez = getPolygonArea(cpts, false)
|
|
70
|
-
areaLines = getPolygonArea([...L1, ...L2], false)
|
|
71
|
-
signChange = (areaBez < 0 && areaLines > 0) || (areaBez > 0 && areaLines < 0)
|
|
72
|
-
|
|
73
|
-
if (!signChange) {
|
|
74
|
-
|
|
75
|
-
// mid point of mid bezier
|
|
76
|
-
pMid = pointAtT(cpts, 0.5)
|
|
77
|
-
|
|
78
|
-
// add to poly
|
|
79
|
-
poly = [p0_S, pMid, p_S]
|
|
80
|
-
|
|
81
|
-
combine = true
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 1. line-bezier-bezier-line
|
|
87
|
-
else if ((type === 'C' || type === 'Q') && comP.type === 'L') {
|
|
88
|
-
|
|
89
|
-
// 1.2 next is cubic next is lineto
|
|
90
|
-
if ((comN.type === 'C' || comN.type === 'Q') && comN2.type === 'L') {
|
|
91
|
-
|
|
92
|
-
combine = true
|
|
93
|
-
|
|
94
|
-
L1 = [comP.p0, comP.p];
|
|
95
|
-
L2 = [comN2.p0, comN2.p];
|
|
96
|
-
p0_S = comP.p
|
|
97
|
-
p_S = comN2.p0
|
|
98
|
-
|
|
99
|
-
// mid point of mid bezier
|
|
100
|
-
pMid = comBez.p
|
|
101
|
-
|
|
102
|
-
// add to poly
|
|
103
|
-
poly = [p0_S, comBez.p, p_S]
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* calculate either combined
|
|
112
|
-
* cubic or arc commands
|
|
113
|
-
*/
|
|
114
|
-
if (combine) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// try to find center of arc
|
|
118
|
-
let arcProps = getArcFromPoly(poly)
|
|
119
|
-
if (arcProps) {
|
|
120
|
-
|
|
121
|
-
let { centroid, r, deltaAngle, startAngle, endAngle } = arcProps;
|
|
122
|
-
|
|
123
|
-
let xAxisRotation = 0;
|
|
124
|
-
let sweep = deltaAngle > 0 ? 1 : 0;
|
|
125
|
-
let largeArc = Math.abs(deltaAngle) > Math.PI ? 1 : 0;
|
|
126
|
-
|
|
127
|
-
let pCM = rotatePoint(p0_S, centroid.x, centroid.y, deltaAngle * 0.5)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
let dist2 = getDistAv(pCM, pMid)
|
|
131
|
-
let thresh = getDistAv(p0_S, p_S) * 0.05
|
|
132
|
-
let bezierCommands;
|
|
133
|
-
|
|
134
|
-
// point is close enough
|
|
135
|
-
if (dist2 < thresh) {
|
|
136
|
-
|
|
137
|
-
//toCubic = false;
|
|
138
|
-
|
|
139
|
-
bezierCommands = arcToBezierResolved(
|
|
140
|
-
{
|
|
141
|
-
p0: p0_S,
|
|
142
|
-
p: p_S,
|
|
143
|
-
centroid,
|
|
144
|
-
rx: r,
|
|
145
|
-
ry: r,
|
|
146
|
-
xAxisRotation,
|
|
147
|
-
sweep,
|
|
148
|
-
largeArc,
|
|
149
|
-
deltaAngle,
|
|
150
|
-
startAngle,
|
|
151
|
-
endAngle
|
|
152
|
-
}
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
if(bezierCommands.length === 1){
|
|
156
|
-
|
|
157
|
-
// prefer more compact quadratic - otherwise arcs
|
|
158
|
-
let comBezier = revertCubicQuadratic(p0_S, bezierCommands[0].cp1, bezierCommands[0].cp2, p_S)
|
|
159
|
-
|
|
160
|
-
if (comBezier.type === 'Q') {
|
|
161
|
-
toCubic = true
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
com = comBezier
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// prefer arcs if 2 cubics are required
|
|
169
|
-
if (bezierCommands.length > 1) toCubic = false;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
//toCubic = false
|
|
173
|
-
|
|
174
|
-
// return elliptic arc commands
|
|
175
|
-
if (!toCubic) {
|
|
176
|
-
// rewrite simplified command
|
|
177
|
-
com.type = 'A'
|
|
178
|
-
com.values = [r, r, xAxisRotation, largeArc, sweep, p_S.x, p_S.y];
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
com.p0 = p0_S;
|
|
182
|
-
com.p = p_S;
|
|
183
|
-
com.extreme = false;
|
|
184
|
-
com.corner = false;
|
|
185
|
-
|
|
186
|
-
// test rendering
|
|
187
|
-
//debug=true
|
|
188
|
-
|
|
189
|
-
if (debug) {
|
|
190
|
-
// arcs
|
|
191
|
-
if (!toCubic) {
|
|
192
|
-
pathDataTest = [
|
|
193
|
-
{ type: 'M', values: [p0_S.x, p0_S.y] },
|
|
194
|
-
{ type: 'A', values: [r, r, xAxisRotation, largeArc, sweep, p_S.x, p_S.y] },
|
|
195
|
-
]
|
|
196
|
-
}
|
|
197
|
-
// cubics
|
|
198
|
-
else {
|
|
199
|
-
pathDataTest = [
|
|
200
|
-
{ type: 'M', values: [p0_S.x, p0_S.y] },
|
|
201
|
-
...bezierCommands
|
|
202
|
-
]
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
let d = pathDataToD(pathDataTest);
|
|
206
|
-
renderPath(markers, d, 'orange', '0.5%', '0.5')
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
pathDataN.push(com);
|
|
210
|
-
i++
|
|
211
|
-
continue
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// pass through
|
|
218
|
-
pathDataN.push(com)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return pathDataN;
|
|
222
|
-
}
|