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.
Files changed (61) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +7 -4
  3. package/dist/svg-path-simplify.esm.js +3593 -1279
  4. package/dist/svg-path-simplify.esm.min.js +2 -2
  5. package/dist/svg-path-simplify.js +3594 -1278
  6. package/dist/svg-path-simplify.min.js +2 -2
  7. package/dist/svg-path-simplify.pathdata.esm.js +1017 -538
  8. package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
  9. package/dist/svg-path-simplify.poly.cjs +9 -8
  10. package/docs/privacy-webapp.md +24 -0
  11. package/index.html +331 -152
  12. package/package.json +1 -1
  13. package/src/constants.js +4 -0
  14. package/src/css_parse.js +317 -0
  15. package/src/detect_input.js +76 -28
  16. package/src/index.js +8 -0
  17. package/src/pathData_simplify_cubic.js +26 -16
  18. package/src/pathData_simplify_harmonize_cpts.js +77 -1
  19. package/src/pathData_simplify_revertToquadratics.js +0 -1
  20. package/src/pathSimplify-main.js +304 -276
  21. package/src/pathSimplify-only-pathdata.js +7 -2
  22. package/src/pathSimplify-presets.js +254 -0
  23. package/src/poly-fit-curve-schneider.js +14 -7
  24. package/src/simplify_poly_RC.js +102 -0
  25. package/src/simplify_poly_RDP.js +109 -1
  26. package/src/simplify_poly_radial_distance.js +3 -3
  27. package/src/string_helpers.js +130 -4
  28. package/src/svg-getAttributes.js +4 -2
  29. package/src/svgii/convert_units.js +1 -1
  30. package/src/svgii/geometry.js +322 -5
  31. package/src/svgii/geometry_bbox_element.js +1 -1
  32. package/src/svgii/geometry_deduceRadius.js +116 -27
  33. package/src/svgii/geometry_length.js +253 -0
  34. package/src/svgii/pathData_analyze.js +18 -0
  35. package/src/svgii/pathData_convert.js +193 -89
  36. package/src/svgii/pathData_fix_directions.js +12 -14
  37. package/src/svgii/pathData_fromPoly.js +3 -3
  38. package/src/svgii/pathData_getLength.js +86 -0
  39. package/src/svgii/pathData_parse.js +2 -0
  40. package/src/svgii/pathData_parse_els.js +66 -68
  41. package/src/svgii/pathData_reorder.js +122 -16
  42. package/src/svgii/pathData_simplify_refineCorners.js +130 -35
  43. package/src/svgii/pathData_simplify_refine_round.js +420 -0
  44. package/src/svgii/pathData_split_to_groups.js +168 -0
  45. package/src/svgii/pathData_stringify.js +26 -64
  46. package/src/svgii/pathData_toPolygon.js +3 -4
  47. package/src/svgii/poly_analyze.js +61 -0
  48. package/src/svgii/poly_normalize.js +11 -2
  49. package/src/svgii/poly_to_pathdata.js +85 -24
  50. package/src/svgii/rounding.js +80 -78
  51. package/src/svgii/svg_cleanup.js +421 -619
  52. package/src/svgii/svg_cleanup_convertPathLength.js +39 -0
  53. package/src/svgii/svg_cleanup_general_svg_atts.js +97 -0
  54. package/src/svgii/svg_cleanup_normalize_transforms.js +83 -0
  55. package/src/svgii/svg_cleanup_remove_els_and_atts.js +77 -0
  56. package/src/svgii/svg_cleanup_ungroup.js +36 -0
  57. package/src/svgii/svg_el_parse_style_props.js +72 -47
  58. package/src/svgii/svg_getElementLength.js +67 -0
  59. package/src/svgii/svg_validate.js +220 -0
  60. package/tests/testSVG.js +14 -1
  61. package/src/svgii/pathData_refine_round.js +0 -222
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svg-path-simplify",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
 
5
5
  "type": "module",
6
6
 
package/src/constants.js CHANGED
@@ -7,6 +7,8 @@ export const rad2Deg = 180/Math.PI
7
7
  export const deg2rad = Math.PI/180
8
8
  export const root2 = 1.4142135623730951
9
9
  export const svgNs = 'http://www.w3.org/2000/svg'
10
+ export const dummySVG = `<svg id="svgInvalid" xmlns="${svgNs}" viewBox="0 0 1 1"><path d="M0 0 h0" /></svg>`
11
+
10
12
 
11
13
  // 1/2.54
12
14
  export const inch2cm = 0.39370078;
@@ -14,3 +16,5 @@ export const inch2cm = 0.39370078;
14
16
  // 1/72
15
17
  export const inch2pt = 0.01388889;
16
18
 
19
+
20
+
@@ -0,0 +1,317 @@
1
+ import { parseValue } from "./svgii/svg_el_parse_style_props";
2
+
3
+ /**
4
+ * Parse nested CSS text into a flat object structure
5
+ * Supports arbitrary nesting depth and & parent selector reference
6
+ * Respects !important modifiers and handles data URLs
7
+ */
8
+ export function parseSvgCss(css, {
9
+ parent=null,
10
+ removeUnused=true,
11
+ flatten = true
12
+ }={}) {
13
+
14
+ let type = typeof css
15
+ if(type==='string') removeUnused = false;
16
+
17
+ // get style element text content
18
+ if(type!=='string' ){
19
+ if(css.nodeName==='style'){
20
+ css = css.innerHTML;
21
+ }
22
+ else if(css.nodeName==='svg'){
23
+ let styleEl = css.querySelector('style')
24
+ if(!styleEl) return {}
25
+ parent = css;
26
+ css = styleEl.innerHTML;
27
+ }
28
+
29
+ //invalid input
30
+ else{
31
+ console.warn('invalid CSS input')
32
+ return {}
33
+ }
34
+ }
35
+
36
+ css = css.trim();
37
+ if (!css) return {};
38
+
39
+ // Remove comments
40
+ css = css.replace(/\/\*[\s\S]*?\*\//g, "");
41
+
42
+
43
+ function parseBlock(text, parentSelector = "") {
44
+ let i = 0;
45
+ let rules = {};
46
+ let l = text.length
47
+
48
+
49
+ while (i < l) {
50
+ // Skip whitespace
51
+ while (/\s/.test(text[i])) i++;
52
+ if (i >= l) break;
53
+
54
+ // Peek ahead to check if this is a selector or a declaration
55
+ let peekIdx = i;
56
+ let isSelector = false;
57
+
58
+ // Look for '{' before ';' to determine if it's a selector
59
+ while (peekIdx < l && text[peekIdx] !== ";") {
60
+ if (text[peekIdx] === "{") {
61
+ isSelector = true;
62
+ break;
63
+ }
64
+ peekIdx++;
65
+ }
66
+
67
+ if (!isSelector) {
68
+ // It's a declaration, skip it (will be handled below)
69
+ i = peekIdx + 1;
70
+ continue;
71
+ }
72
+
73
+ // Read selector (up to '{')
74
+ let selector = "";
75
+ while (i < l && text[i] !== "{") {
76
+ selector += text[i];
77
+ i++;
78
+ }
79
+
80
+ selector = selector.trim();
81
+ if (!selector || text[i] !== "{") continue;
82
+
83
+ i++; // skip '{'
84
+
85
+ // Find matching closing brace
86
+ let blockContent = "";
87
+ let depth = 1;
88
+
89
+ while (i < l && depth > 0) {
90
+ if (text[i] === "{") depth++;
91
+ else if (text[i] === "}") depth--;
92
+
93
+ if (depth > 0) blockContent += text[i];
94
+ i++;
95
+ }
96
+
97
+ // Compose full selector
98
+ let fullSelector = selector;
99
+ if (parentSelector) {
100
+ if (selector.includes("&")) {
101
+ fullSelector = selector.replace(/&/g, parentSelector);
102
+ } else {
103
+ fullSelector = parentSelector + " " + selector;
104
+ }
105
+ }
106
+ fullSelector = fullSelector.replace(/\s+/g, " ").trim();
107
+
108
+ // Separate declarations from nested rules
109
+ let { declarations, hasNested } = extractDeclarations(blockContent, fullSelector);
110
+
111
+ // Add declarations for this selector (respect !important)
112
+ if (Object.keys(declarations).length) {
113
+ if (!rules[fullSelector]) {
114
+ rules[fullSelector] = declarations;
115
+ } else {
116
+ // Merge declarations, preserving !important
117
+ for (let prop in declarations) {
118
+ let existingValue = rules[fullSelector][prop];
119
+ let newValue = declarations[prop];
120
+
121
+ // Only override if existing doesn't have !important, or new has !important
122
+ let existingHasImportant =
123
+ existingValue && existingValue.includes("!important");
124
+ let newHasImportant = newValue.includes("!important");
125
+
126
+ if (!existingHasImportant || newHasImportant) {
127
+ rules[fullSelector][prop] = newValue;
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ // If block contains nested rules, parse them recursively
134
+ if (hasNested) {
135
+ parseBlock(blockContent, fullSelector);
136
+ }
137
+ }
138
+
139
+ return rules
140
+
141
+ }
142
+
143
+ function extractDeclarations(content) {
144
+ let declarations = {};
145
+ let i = 0;
146
+ let l= content.length;
147
+ let hasNested = false;
148
+
149
+ while (i < l) {
150
+ // Skip whitespace
151
+ while (i < l && /\s/.test(content[i])) i++;
152
+ if (i >= l) break;
153
+
154
+ // Check if next thing is a nested selector or a declaration
155
+ let checkIdx = i;
156
+ let isNested = false;
157
+
158
+ // Scan until we hit ':' or '{' or ';'
159
+ while (checkIdx < l) {
160
+ if (content[checkIdx] === "{") {
161
+ isNested = true;
162
+ break;
163
+ }
164
+ if (content[checkIdx] === ":") {
165
+ // It's a declaration
166
+ break;
167
+ }
168
+ if (content[checkIdx] === ";") {
169
+ // Empty or malformed
170
+ break;
171
+ }
172
+ checkIdx++;
173
+ }
174
+
175
+ if (isNested) {
176
+ // Skip nested rule (will be handled by recursive call)
177
+ hasNested = true;
178
+ // Skip to closing brace of this nested rule
179
+ let depth = 0;
180
+ while (i < l) {
181
+ if (content[i] === "{") depth++;
182
+ if (content[i] === "}") depth--;
183
+ i++;
184
+ if (depth === 0) break;
185
+ }
186
+ } else {
187
+ // It's a declaration, read until ';' (but respect url() and quotes)
188
+ let decl = "";
189
+ let inUrl = false;
190
+ let inQuotes = false;
191
+ let quoteChar = "";
192
+
193
+ while (i < l) {
194
+ let char = content[i];
195
+ let nextChar = content[i + 1];
196
+
197
+ // Track if we're inside url()
198
+ if (
199
+ char === "u" &&
200
+ nextChar === "r" &&
201
+ content.slice(i, i + 4) === "url("
202
+ ) {
203
+ inUrl = true;
204
+ }
205
+
206
+ // Track quotes
207
+ if (
208
+ (char === '"' || char === "'") &&
209
+ (i === 0 || content[i - 1] !== "\\")
210
+ ) {
211
+ if (!inQuotes) {
212
+ inQuotes = true;
213
+ quoteChar = char;
214
+ } else if (char === quoteChar) {
215
+ inQuotes = false;
216
+ quoteChar = "";
217
+ }
218
+ }
219
+
220
+ // Check for end of url()
221
+ if (inUrl && char === ")" && !inQuotes) {
222
+ inUrl = false;
223
+ }
224
+
225
+ // Only break on semicolon if we're not inside url() or quotes
226
+ if (char === ";" && !inUrl && !inQuotes) {
227
+ i++; // skip ';'
228
+ break;
229
+ }
230
+
231
+ decl += char;
232
+ i++;
233
+ }
234
+
235
+ decl = decl.trim();
236
+ if (decl) {
237
+ let colonIdx = decl.indexOf(":");
238
+ if (colonIdx > -1) {
239
+ let prop = decl.substring(0, colonIdx).trim();
240
+ let value = decl.substring(colonIdx + 1).trim();
241
+ if (prop && value) {
242
+ //console.log('selector', selector, isId);
243
+ //declarations[prop] = isId && !value.includes('!important') ? value+'!important' : value;
244
+ declarations[prop] = value;
245
+ }
246
+ }
247
+ }
248
+ }
249
+ }
250
+
251
+ return { declarations, hasNested };
252
+ }
253
+
254
+ let rules = parseBlock(css);
255
+ if(parent && removeUnused) rules = removeUnusedSelectors(parent, rules)
256
+ if(flatten) rules = flattenCssProps(rules)
257
+
258
+ // emulate specificity: prioritize ids and important
259
+ let rulesID = {};
260
+ let rulesImportant = {};
261
+ for(let rule in rules){
262
+ if(rule.startsWith('#')){
263
+ rulesID[rule] = rules[rule]
264
+ delete rules[rule];
265
+ }
266
+
267
+ for(let prop in rules[rule]){
268
+ let val = rules[rule][prop]
269
+ if(val.includes('!important')){
270
+ if(!rulesImportant[rule]) rulesImportant[rule]={}
271
+ rulesImportant[rule][prop] = val
272
+ }
273
+ }
274
+ }
275
+
276
+ rules= {
277
+ ...rules,
278
+ ...rulesID,
279
+ ...rulesImportant
280
+ }
281
+
282
+ return rules;
283
+ }
284
+
285
+ function flattenCssProps(rules) {
286
+ for (let selector in rules) {
287
+ let targets = selector.split(/,/).map((sel) => sel.trim());
288
+ let values = rules[selector];
289
+ if (targets.length > 1) {
290
+ targets.forEach((target) => {
291
+ let props = rules[target];
292
+ for (let prop in props) {
293
+ let value = props[prop];
294
+ if (!value.includes("!important")) {
295
+ rules[target][prop] = value;
296
+ }
297
+ }
298
+ });
299
+ delete rules[selector];
300
+ }
301
+ }
302
+ return rules;
303
+ }
304
+
305
+
306
+ function removeUnusedSelectors(parent=null, props={}){
307
+ let selectors = Object.keys(props);
308
+ selectors.forEach(selector=>{
309
+ let el = parent.querySelector(selector)
310
+ // remove
311
+ if(!el && selector!==':root') {
312
+ //console.log( selector, 'doesnt exist')
313
+ delete props[selector]
314
+ }
315
+ })
316
+ return props
317
+ }
@@ -1,78 +1,126 @@
1
+ import { dummySVG, svgNs } from "./constants";
2
+ import { validateSVG } from "./svgii/svg_validate";
3
+
1
4
  export function detectInputType(input) {
2
5
  let type = 'string';
3
- /*
4
- if (input instanceof HTMLImageElement) return "img";
5
- if (input instanceof SVGElement) return "svg";
6
- if (input instanceof HTMLCanvasElement) return "canvas";
7
- if (input instanceof File) return "file";
8
- if (input instanceof ArrayBuffer) return "buffer";
9
- if (input instanceof Blob) return "blob";
10
- */
6
+ let log = '';
7
+ let isValid = true;
8
+
9
+ let result = {
10
+ inputType:'',
11
+ isValid:true,
12
+ fileReport:{},
13
+ }
14
+
15
+
11
16
  if (Array.isArray(input)) {
12
17
 
18
+ result.inputType = "array";
19
+
20
+
13
21
  // nested array
14
22
  if (Array.isArray(input[0])) {
15
23
  //console.log('is array', input[0], input[0][0])
16
24
 
17
- if(input[0].length===2){
25
+ if (input[0].length === 2) {
18
26
  //console.log('is single poly value array')
19
- return 'polyArray'
27
+ result.inputType = 'polyArray'
20
28
  }
21
29
 
22
- else if (Array.isArray(input[0][0]) && input[0][0].length === 2 ) {
30
+ else if (Array.isArray(input[0][0]) && input[0][0].length === 2) {
23
31
  //console.log('is complex poly point value array', input[0][0])
24
- return 'polyComplexArray'
32
+ result.inputType = 'polyComplexArray'
25
33
  }
26
34
  else if (input[0][0].x !== undefined && input[0][0].y !== undefined) {
27
35
  //console.log('is nested point object array')
28
- return 'polyComplexObjectArray'
36
+ result.inputType = 'polyComplexObjectArray'
29
37
  }
38
+ //return result
30
39
  }
31
40
 
32
41
  // is point array
33
- else if (input[0].x!==undefined && input[0].y!==undefined) {
42
+ else if (input[0].x !== undefined && input[0].y !== undefined) {
34
43
  //console.log('is nested point object array')
35
- return 'polyObjectArray'
44
+ result.inputType = 'polyObjectArray'
36
45
  }
37
46
 
38
47
  // path data array
39
48
  else if (input[0]?.type && input[0]?.values
40
49
  ) {
41
- return "pathData"
42
-
50
+ result.inputType = "pathData"
43
51
  }
44
- //console.log(input[0], typeof input[0]);
45
- return "array";
52
+
53
+
54
+ return result;
46
55
  }
47
56
 
48
57
  if (typeof input === "string") {
49
58
  input = input.trim();
50
59
  let isSVG = input.includes('<svg') && input.includes('</svg');
60
+ let isSymbol = input.startsWith('<symbol') && input.includes('</symbol');
51
61
  let isPathData = input.startsWith('M') || input.startsWith('m');
52
62
  let isPolyString = !isNaN(input.substring(0, 1)) && !isNaN(input.substring(input.length - 1, input.length))
53
-
63
+ let isJson = isNumberJson(input)
64
+ //console.log('isNumberJson', isJson);
54
65
 
55
66
  if (isSVG) {
56
- type = 'svgMarkup'
67
+ let validate = validateSVG(input);
68
+ ({isValid, log} = validate) ;
69
+ if(!isValid){
70
+ //input = dummySVG
71
+ result.inputType = 'invalid'
72
+ result.isValid=false,
73
+ //result.log = JSON.stringify(log, null, ' ')
74
+ result.log = log
75
+ }else{
76
+ result.inputType = 'svgMarkup'
77
+ }
78
+
79
+ result.fileReport = validate.fileReport
80
+
81
+ }
82
+
83
+ else if (isJson) {
84
+ result.inputType = 'json'
85
+ }
86
+
87
+ else if (isSymbol) {
88
+ result.inputType = 'symbol'
57
89
  }
58
90
  else if (isPathData) {
59
- type = 'pathDataString'
91
+ result.inputType = 'pathDataString'
60
92
  }
61
93
  else if (isPolyString) {
62
- type = 'polyString'
94
+ result.inputType = 'polyString'
63
95
  }
64
96
 
65
97
  else {
66
98
  let url = /^(file:|https?:\/\/|\/|\.\/|\.\.\/)/.test(input);
67
99
  let dataUrl = input.startsWith('data:image');
68
- type = url || dataUrl ? "url" : "string";
100
+ result.inputType = url || dataUrl ? "url" : "string";
69
101
  }
70
102
 
71
- return type
103
+ return result
72
104
  }
73
105
 
74
- type = typeof input
75
- let constructor = input.constructor.name
106
+ result.inputType = (input.constructor.name || typeof input ).toLowerCase()
76
107
 
77
- return (constructor || type).toLowerCase();
108
+ return result;
78
109
  }
110
+
111
+
112
+ function isNumberJson(str) {
113
+
114
+ str = str.trim();
115
+
116
+ let hasNumber = /\d/.test(str)
117
+ let hasInvalid = /[abcdfghijklmnopqrstuvwz]/gi.test(str)
118
+ if (!hasNumber || hasInvalid) return false
119
+
120
+
121
+ // is JSON like
122
+ let isJson = str.startsWith('[') && str.endsWith(']');
123
+
124
+ return isJson
125
+
126
+ }
package/src/index.js CHANGED
@@ -44,11 +44,19 @@ export{getElementTransform as getElementTransform};
44
44
  import {getViewBox} from './svg_getViewbox.js';
45
45
  export{getViewBox as getViewBox};
46
46
 
47
+ import { validateSVG } from './svgii/svg_validate.js';
48
+ export { validateSVG as validateSVG};
49
+ import { detectInputType } from './detect_input.js';
50
+ export { detectInputType as detectInputType};
51
+
52
+
47
53
 
48
54
  // IIFE
49
55
  if (typeof window !== 'undefined') {
50
56
  window.svgPathSimplify = svgPathSimplify;
51
57
  window.getElementTransform = getElementTransform;
58
+ window.validateSVG = validateSVG;
59
+ window.detectInputType = detectInputType;
52
60
  //window.simplifyPolySchneider = simplifyPolySchneider;
53
61
  //window.getPathDataFromEl = getPathDataFromEl;
54
62
  //window.parsePathDataString = parsePathDataString;
@@ -143,10 +143,10 @@ export function combineCubicPairs(com1, com2, {
143
143
  let comS = getExtrapolatedCommand(com1, com2, t)
144
144
 
145
145
  // test new point-at-t against original mid segment starting point
146
- let pt = pointAtT([comS.p0, comS.cp1, comS.cp2, comS.p], t)
146
+ let ptI = pointAtT([comS.p0, comS.cp1, comS.cp2, comS.p], t)
147
147
 
148
148
 
149
- let dist0 = getDistManhattan(com1.p, pt)
149
+ let dist0 = getDistManhattan(com1.p, ptI)
150
150
  let dist1 = 0, dist2 = 0;
151
151
  let close = dist0 < maxDist;
152
152
  let success = false;
@@ -162,32 +162,42 @@ export function combineCubicPairs(com1, com2, {
162
162
  * to prevent distortions
163
163
  */
164
164
 
165
- // 2nd segment mid
166
- let pt_2 = pointAtT([com2.p0, com2.cp1, com2.cp2, com2.p], 0.5)
165
+ // 1st segment mid
166
+ let ptM_seg1 = pointAtT([com1.p0, com1.cp1, com1.cp2, com1.p], 0.5)
167
+
168
+ let t2 = t * 0.5;
169
+ // combined interpolated mid point
170
+ let ptI_seg1 = pointAtT([comS.p0, comS.cp1, comS.cp2, comS.p], t2)
171
+ dist1 = getDistManhattan(ptM_seg1, ptI_seg1)
167
172
 
168
- // simplified path
169
- let t3 = (1 + t) * 0.5;
170
- let ptS_2 = pointAtT([comS.p0, comS.cp1, comS.cp2, comS.p], t3)
171
- dist1 = getDistManhattan(pt_2, ptS_2)
172
173
 
173
174
  error += dist1;
174
175
 
175
176
  if (dist1 < maxDist) {
176
177
 
177
- //renderPoint(markers, pt_2, 'magenta')
178
- //renderPoint(markers, ptS_2, 'green', '0.5%')
179
-
180
- // 1st segment mid
181
- let pt_1 = pointAtT([com1.p0, com1.cp1, com1.cp2, com1.p], 0.5)
178
+ // 2nd segment mid
179
+ let ptM_seg2 = pointAtT([com2.p0, com2.cp1, com2.cp2, com2.p], 0.5)
182
180
 
183
- let t2 = t * 0.5;
184
- let ptS_1 = pointAtT([comS.p0, comS.cp1, comS.cp2, comS.p], t2)
185
- dist2 = getDistManhattan(pt_1, ptS_1)
181
+ // simplified path
182
+ let t3 = (1 + t) * 0.5;
183
+ let ptI_seg2 = pointAtT([comS.p0, comS.cp1, comS.cp2, comS.p], t3)
184
+ dist2 = getDistManhattan(ptM_seg2, ptI_seg2)
186
185
 
187
186
  error += dist2;
188
187
 
189
188
  if (error < maxDist) success = true;
190
189
 
190
+
191
+ /*
192
+ renderPoint(markers, ptM_seg1, 'cyan')
193
+ renderPoint(markers, pt, 'orange', '1.5%', '1')
194
+ renderPoint(markers, ptM_seg2, 'orange')
195
+
196
+ renderPoint(markers, com1.p, 'green')
197
+ //renderPoint(markers, com2.p, 'green')
198
+ renderPoint(markers, ptI_seg1, 'purple')
199
+ */
200
+
191
201
  }
192
202
 
193
203
  } // end 1st try
@@ -1,5 +1,81 @@
1
1
  import { checkLineIntersection, getDistManhattan, interpolate, pointAtT } from "./svgii/geometry";
2
- import { renderPoint } from "./svgii/visualize";
2
+ import { renderPoint, renderPoly } from "./svgii/visualize";
3
+
4
+
5
+ export function fixIntersectingCpts(pathData = []) {
6
+
7
+ let l = pathData.length;
8
+ let p0 = { x: pathData[0].values[0], y: pathData[0].values[1] }
9
+ let p = p0
10
+
11
+
12
+ for (let i = 1; i < l; i++) {
13
+ let com = pathData[i]
14
+ let comPrev = pathData[i - 1] || null
15
+ let { type, values } = com
16
+ let comN = pathData[i + 1] ? pathData[i + 1] : null;
17
+ let adjust = false;
18
+
19
+ //comN && comN.type==='C' &&
20
+ if (type === 'C') {
21
+ let tx = 1.2;
22
+ let cp1 = { x: values[0], y: values[1] }
23
+ p = { x: values[4], y: values[5] }
24
+ let cp2 = { x: values[2], y: values[3] }
25
+
26
+ let cp1_ex = interpolate(p0, cp1, tx )
27
+ let cp2_ex = interpolate( p, cp2, tx)
28
+
29
+ let valuesL = comPrev.values.slice(-2)
30
+ //let p0 = { x: valuesL[0], y: valuesL[1] }
31
+
32
+ // extend tangents
33
+ //let ptI = checkLineIntersection(p0, cp1_ex, p, cp2_ex, false, true)
34
+ let ptI = checkLineIntersection(p0, cp1, p, cp2_ex, true, true)
35
+
36
+
37
+ /*
38
+ renderPoly(markers, [p0, cp1], 'orange', '0.75')
39
+ renderPoly(markers, [p, cp2], 'blue', '0.75')
40
+ renderPoly(markers, [p, cp2_ex], 'magenta', '0.75')
41
+
42
+ renderPoint(markers, p0, 'orange', '0.75')
43
+ renderPoint(markers, cp1, 'magenta', '0.75')
44
+ renderPoint(markers, cp1_ex, 'blue', '0.5')
45
+
46
+ renderPoint(markers, cp2, 'cyan', '0.75')
47
+ */
48
+ //renderPoint(markers, cp2_ex, 'purple', '0.75')
49
+
50
+ //renderPoint(markers, cp1, 'purple', '0.75')
51
+ //renderPoint(markers, cp1_ex, 'blue', '0.75')
52
+
53
+ //renderPoint(markers, cp2, 'cyan')
54
+ /*
55
+ renderPoint(markers, p0, 'orange')
56
+ renderPoint(markers, p, 'magenta', '0.5')
57
+ */
58
+
59
+
60
+ if (ptI) {
61
+ let t = 0.666
62
+ cp1 = interpolate(p0, ptI, t)
63
+ cp2 = interpolate(p, ptI, t)
64
+ com.values = [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y]
65
+ //renderPoint(markers, ptI)
66
+ }
67
+
68
+ }
69
+
70
+ if (values.length) {
71
+ p0 = p
72
+ }
73
+ }
74
+
75
+
76
+ return pathData;
77
+ }
78
+
3
79
 
4
80
 
5
81
 
@@ -6,7 +6,6 @@ export function pathDataRevertCubicToQuadratic(pathData, tolerance=1) {
6
6
  let com = pathData[c]
7
7
  let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
8
8
  if (type === 'C') {
9
- //console.log(com);
10
9
  let comQ = revertCubicQuadratic(p0, cp1, cp2, p, tolerance)
11
10
  if (comQ.type === 'Q') {
12
11
  comQ.extreme = com.extreme