svg-path-simplify 0.4.1 → 0.4.3
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 +19 -0
- package/README.md +6 -4
- package/dist/svg-path-simplify.esm.js +2450 -888
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +2450 -888
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +167 -85
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/docs/privacy-webapp.md +24 -0
- package/index.html +333 -132
- package/package.json +5 -2
- package/src/css_parse.js +317 -0
- package/src/detect_input.js +34 -4
- package/src/pathData_simplify_harmonize_cpts.js +77 -1
- package/src/pathSimplify-main.js +246 -262
- package/src/pathSimplify-presets.js +243 -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 +144 -0
- package/src/svgii/convert_units.js +8 -2
- package/src/svgii/geometry.js +182 -3
- package/src/svgii/geometry_length.js +237 -0
- package/src/svgii/pathData_convert.js +43 -1
- package/src/svgii/pathData_fix_directions.js +6 -0
- 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 +189 -189
- 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 +8 -7
- package/src/svgii/svg-styles-to-attributes-const.js +1 -0
- package/src/svgii/svg_cleanup.js +467 -421
- package/src/svgii/svg_cleanup_convertPathLength.js +32 -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 +72 -0
- package/src/svgii/svg_cleanup_ungroup.js +36 -0
- package/src/svgii/svg_el_parse_style_props.js +76 -28
- package/src/svgii/svg_getElementLength.js +67 -0
- package/tests/testSVG_shape.js +59 -0
- package/tests/testSVG_transform.js +61 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svg-path-simplify",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
|
|
@@ -34,7 +34,10 @@
|
|
|
34
34
|
"watch": "rollup -c --watch",
|
|
35
35
|
"test": "node tests/testSVG.js",
|
|
36
36
|
"test1": "node tests/test.js",
|
|
37
|
-
"test2": "node tests/testSVG2.js"
|
|
37
|
+
"test2": "node tests/testSVG2.js",
|
|
38
|
+
"test3": "node tests/testSVG_transform.js",
|
|
39
|
+
"test4": "node tests/testSVG_shape.js"
|
|
40
|
+
|
|
38
41
|
},
|
|
39
42
|
|
|
40
43
|
"keywords": [
|
package/src/css_parse.js
ADDED
|
@@ -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
|
+
}
|
package/src/detect_input.js
CHANGED
|
@@ -14,12 +14,12 @@ export function detectInputType(input) {
|
|
|
14
14
|
if (Array.isArray(input[0])) {
|
|
15
15
|
//console.log('is array', input[0], input[0][0])
|
|
16
16
|
|
|
17
|
-
if(input[0].length===2){
|
|
17
|
+
if (input[0].length === 2) {
|
|
18
18
|
//console.log('is single poly value array')
|
|
19
19
|
return 'polyArray'
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
else if (Array.isArray(input[0][0]) && input[0][0].length === 2
|
|
22
|
+
else if (Array.isArray(input[0][0]) && input[0][0].length === 2) {
|
|
23
23
|
//console.log('is complex poly point value array', input[0][0])
|
|
24
24
|
return 'polyComplexArray'
|
|
25
25
|
}
|
|
@@ -30,7 +30,7 @@ export function detectInputType(input) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// is point array
|
|
33
|
-
else if (input[0].x!==undefined && input[0].y!==undefined) {
|
|
33
|
+
else if (input[0].x !== undefined && input[0].y !== undefined) {
|
|
34
34
|
//console.log('is nested point object array')
|
|
35
35
|
return 'polyObjectArray'
|
|
36
36
|
}
|
|
@@ -48,13 +48,23 @@ export function detectInputType(input) {
|
|
|
48
48
|
if (typeof input === "string") {
|
|
49
49
|
input = input.trim();
|
|
50
50
|
let isSVG = input.includes('<svg') && input.includes('</svg');
|
|
51
|
+
let isSymbol = input.startsWith('<symbol') && input.includes('</symbol');
|
|
51
52
|
let isPathData = input.startsWith('M') || input.startsWith('m');
|
|
52
53
|
let isPolyString = !isNaN(input.substring(0, 1)) && !isNaN(input.substring(input.length - 1, input.length))
|
|
53
|
-
|
|
54
|
+
let isJson = isNumberJson(input)
|
|
55
|
+
//console.log('isNumberJson', isJson);
|
|
54
56
|
|
|
55
57
|
if (isSVG) {
|
|
56
58
|
type = 'svgMarkup'
|
|
57
59
|
}
|
|
60
|
+
|
|
61
|
+
else if (isJson) {
|
|
62
|
+
type = 'json'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
else if (isSymbol) {
|
|
66
|
+
type = 'symbol'
|
|
67
|
+
}
|
|
58
68
|
else if (isPathData) {
|
|
59
69
|
type = 'pathDataString'
|
|
60
70
|
}
|
|
@@ -68,11 +78,31 @@ export function detectInputType(input) {
|
|
|
68
78
|
type = url || dataUrl ? "url" : "string";
|
|
69
79
|
}
|
|
70
80
|
|
|
81
|
+
|
|
71
82
|
return type
|
|
72
83
|
}
|
|
73
84
|
|
|
74
85
|
type = typeof input
|
|
75
86
|
let constructor = input.constructor.name
|
|
76
87
|
|
|
88
|
+
|
|
89
|
+
|
|
77
90
|
return (constructor || type).toLowerCase();
|
|
78
91
|
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
function isNumberJson(str) {
|
|
95
|
+
|
|
96
|
+
str = str.trim();
|
|
97
|
+
|
|
98
|
+
let hasNumber = /\d/.test(str)
|
|
99
|
+
let hasInvalid = /[abcdfghijklmnopqrstuvwz]/gi.test(str)
|
|
100
|
+
if (!hasNumber || hasInvalid) return false
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
// is JSON like
|
|
104
|
+
let isJson = str.startsWith('[') && str.endsWith(']');
|
|
105
|
+
|
|
106
|
+
return isJson
|
|
107
|
+
|
|
108
|
+
}
|
|
@@ -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
|
|