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
package/src/string_helpers.js
CHANGED
|
@@ -3,16 +3,142 @@ import { isNumericValue } from "./svgii/convert_units"
|
|
|
3
3
|
export function toCamelCase(str) {
|
|
4
4
|
return str
|
|
5
5
|
.split(/[-| ]/)
|
|
6
|
-
.map((e,i) => i
|
|
6
|
+
.map((e, i) => i
|
|
7
7
|
? e.charAt(0).toUpperCase() + e.slice(1).toLowerCase()
|
|
8
8
|
: e.toLowerCase()
|
|
9
9
|
)
|
|
10
10
|
.join('')
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export
|
|
14
|
-
if(isNumericValue(str)) return str
|
|
15
|
-
let strShort = str.split('-').map(str=>{return str.replace(/a|e|i|o|u/g,'') }).join('-')
|
|
13
|
+
export function toShortStr(str) {
|
|
14
|
+
if (isNumericValue(str)) return str
|
|
15
|
+
let strShort = str.split('-').map(str => { return str.replace(/a|e|i|o|u/g, '') }).join('-')
|
|
16
16
|
strShort = toCamelCase(strShort)
|
|
17
17
|
return strShort
|
|
18
18
|
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
export function stringifySVG(svg, {
|
|
22
|
+
omitNamespace = false,
|
|
23
|
+
removeComments = true,
|
|
24
|
+
format = 0,
|
|
25
|
+
} = {}) {
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
let markup = '';
|
|
29
|
+
|
|
30
|
+
if (format < 2) {
|
|
31
|
+
markup = new XMLSerializer().serializeToString(svg);
|
|
32
|
+
//if (format === 0) markup = minifySVGMarkup(markup, { removeComments })
|
|
33
|
+
markup = minifySVGMarkup(markup, { removeComments })
|
|
34
|
+
|
|
35
|
+
} else {
|
|
36
|
+
markup = serializeSVGPretty(svg)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if (omitNamespace) {
|
|
41
|
+
markup = markup.replaceAll('xmlns="http://www.w3.org/2000/svg"', '')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (removeComments) {
|
|
45
|
+
markup = markup
|
|
46
|
+
.replace(/(<!--.*?-->)|(<!--[\S\s]+?-->)|(<!--[\S\s]*?$)/g, '')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/*
|
|
50
|
+
markup = markup
|
|
51
|
+
.replace(/\t/g, "")
|
|
52
|
+
.replace(/[\n\r|]/g, "\n")
|
|
53
|
+
.replace(/\n\s*\n/g, '\n')
|
|
54
|
+
.replace(/ +/g, ' ')
|
|
55
|
+
//.replace(/ +/g, ' ')
|
|
56
|
+
.replace(/> </g, '><')
|
|
57
|
+
.trim()
|
|
58
|
+
// sanitize linebreaks within pathdata
|
|
59
|
+
.replaceAll(' ', '\n');
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
//console.log(markup);
|
|
63
|
+
|
|
64
|
+
return markup
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
export function minifySVGMarkup(svg, {
|
|
70
|
+
removeComments = true,
|
|
71
|
+
} = {}) {
|
|
72
|
+
|
|
73
|
+
if (removeComments) {
|
|
74
|
+
svg = svg.replace(/<!--[\s\S]*?-->/g, '')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Remove whitespace between tags
|
|
78
|
+
svg = svg.replace(/>\s+</g, '><')
|
|
79
|
+
// Trim leading/trailing whitespace
|
|
80
|
+
.trim()
|
|
81
|
+
// Remove extra whitespace within tags (attributes)
|
|
82
|
+
.replace(/\s+([=])\s+/g, '$1')
|
|
83
|
+
.replace(/\s+(?=[^<]*>)/g, ' ')
|
|
84
|
+
// Collapse multiple spaces to single space
|
|
85
|
+
.replace(/\s{2,}/g, ' ')
|
|
86
|
+
// Remove spaces around = signs in attributes
|
|
87
|
+
.replace(/\s*=\s*/g, '=');
|
|
88
|
+
|
|
89
|
+
return svg
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function serializeSVGPretty(xmlDoc, {
|
|
93
|
+
indentSize = 2 } = {}) {
|
|
94
|
+
if (typeof xmlDoc === 'string') {
|
|
95
|
+
xmlDoc = new DOMParser().parseFromString(xmlDoc, 'image/svg+xml').querySelector('svg')
|
|
96
|
+
}
|
|
97
|
+
return formatXMLNode(xmlDoc, 0, indentSize);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
function formatXMLNode(node, level, indentSize) {
|
|
102
|
+
let indent = " ".repeat(level * indentSize);
|
|
103
|
+
|
|
104
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
105
|
+
let text = node.textContent.trim();
|
|
106
|
+
return text ? text : "";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
110
|
+
let hasChildren = node.children.length > 0;
|
|
111
|
+
let hasTextContent = node.textContent.trim().length > 0 && node.children.length === 0;
|
|
112
|
+
|
|
113
|
+
let result = `${indent}<${node.nodeName}`;
|
|
114
|
+
|
|
115
|
+
// Add attributes
|
|
116
|
+
for (let i = 0; i < node.attributes.length; i++) {
|
|
117
|
+
let att = node.attributes[i];
|
|
118
|
+
result += ` ${att.name}="${att.value}"`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!hasChildren && !hasTextContent) {
|
|
122
|
+
return result + " />\n";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
result += ">";
|
|
126
|
+
|
|
127
|
+
if (hasChildren) {
|
|
128
|
+
result += "\n";
|
|
129
|
+
for (let child of node.children) {
|
|
130
|
+
result += formatXMLNode(child, level + 1, indentSize);
|
|
131
|
+
}
|
|
132
|
+
result += `${indent}</${node.nodeName}>\n`;
|
|
133
|
+
} else if (hasTextContent) {
|
|
134
|
+
result += node.textContent.trim();
|
|
135
|
+
result += `</${node.nodeName}>\n`;
|
|
136
|
+
} else {
|
|
137
|
+
result += `</${node.nodeName}>\n`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return "";
|
|
144
|
+
}
|
package/src/svg-getAttributes.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { normalizeUnits } from "./svgii/convert_units";
|
|
2
2
|
|
|
3
3
|
export function getElementAtts(el, {x=0, y=0, width=0, height=0}={}){
|
|
4
|
-
let attributes = [...el.attributes];
|
|
4
|
+
//let attributes = [...el.attributes];
|
|
5
|
+
let attributes = [...el.attributes].map(att=>att.name);
|
|
5
6
|
|
|
6
7
|
let atts={};
|
|
7
8
|
attributes.forEach(att=>{
|
|
8
|
-
let value = normalizeUnits(att.nodeValue, {x, y, width, height});
|
|
9
|
+
//let value = normalizeUnits(att.nodeValue, {x, y, width, height});
|
|
10
|
+
let value = normalizeUnits(el.getAttribute(att), {x, y, width, height});
|
|
9
11
|
atts[att.name] = value
|
|
10
12
|
})
|
|
11
13
|
|
package/src/svgii/geometry.js
CHANGED
|
@@ -3,7 +3,8 @@ import {abs, acos, asin, atan, atan2, ceil, cos, exp, floor,
|
|
|
3
3
|
log, max, min, pow, random, round, sin, sqrt, tan, PI} from '/.constants.js';
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { rad2Deg, root2 } from "../constants";
|
|
6
|
+
import { deg2rad, rad2Deg, root2 } from "../constants";
|
|
7
|
+
//import { roundTo } from "./rounding";
|
|
7
8
|
//import { getPolyBBox } from "./geometry_bbox";
|
|
8
9
|
import { renderPoint } from "./visualize";
|
|
9
10
|
|
|
@@ -23,6 +24,11 @@ export function getAngle(p1, p2, normalize = false) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
26
32
|
export function getDeltaAngle2(centerPoint, startPoint, endPoint, largeArc = false) {
|
|
27
33
|
|
|
28
34
|
const normalizeAngle = (angle) => {
|
|
@@ -315,6 +321,7 @@ export function pointAtT(pts, t = 0.5, getTangent = false, getCpts = false, retu
|
|
|
315
321
|
let t1 = 1 - t;
|
|
316
322
|
|
|
317
323
|
// cubic beziers
|
|
324
|
+
/*
|
|
318
325
|
if (isCubic) {
|
|
319
326
|
pt = {
|
|
320
327
|
x:
|
|
@@ -330,11 +337,30 @@ export function pointAtT(pts, t = 0.5, getTangent = false, getCpts = false, retu
|
|
|
330
337
|
};
|
|
331
338
|
|
|
332
339
|
}
|
|
340
|
+
*/
|
|
341
|
+
|
|
342
|
+
if (isCubic) {
|
|
343
|
+
pt = {
|
|
344
|
+
x:
|
|
345
|
+
t1 * t1 * t1 * p0.x +
|
|
346
|
+
3 * t1 * t1 * t * cp1.x +
|
|
347
|
+
3 * t1 * t * t * cp2.x +
|
|
348
|
+
t * t * t * p.x,
|
|
349
|
+
y:
|
|
350
|
+
t1 * t1 * t1 * p0.y +
|
|
351
|
+
3 * t1 * t1 * t * cp1.y +
|
|
352
|
+
3 * t1 * t * t * cp2.y +
|
|
353
|
+
t * t * t * p.y,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
333
359
|
// quadratic beziers
|
|
334
360
|
else {
|
|
335
361
|
pt = {
|
|
336
|
-
x: t1 * t1 * p0.x + 2 * t1 * t * cp1.x + t
|
|
337
|
-
y: t1 * t1 * p0.y + 2 * t1 * t * cp1.y + t
|
|
362
|
+
x: t1 * t1 * p0.x + 2 * t1 * t * cp1.x + t * t * p.x,
|
|
363
|
+
y: t1 * t1 * p0.y + 2 * t1 * t * cp1.y + t * t * p.y,
|
|
338
364
|
};
|
|
339
365
|
}
|
|
340
366
|
|
|
@@ -370,9 +396,40 @@ export function pointAtT(pts, t = 0.5, getTangent = false, getCpts = false, retu
|
|
|
370
396
|
|
|
371
397
|
|
|
372
398
|
|
|
399
|
+
|
|
373
400
|
/**
|
|
374
401
|
* get vertices from path command final on-path points
|
|
375
402
|
*/
|
|
403
|
+
|
|
404
|
+
export function getPathDataVertices(pathData=[], includeCpts = false, decimals = -1) {
|
|
405
|
+
let polyPoints = [];
|
|
406
|
+
//console.log(pathData);
|
|
407
|
+
|
|
408
|
+
pathData.forEach((com) => {
|
|
409
|
+
let { type, values } = com;
|
|
410
|
+
|
|
411
|
+
// get final on path point from last 2 values
|
|
412
|
+
if (values.length) {
|
|
413
|
+
|
|
414
|
+
// round
|
|
415
|
+
if (decimals > -1) values = values.map(val => +val.toFixed(decimals))
|
|
416
|
+
|
|
417
|
+
if (includeCpts) {
|
|
418
|
+
|
|
419
|
+
for (let i = 1; i < values.length; i += 2) {
|
|
420
|
+
polyPoints.push({ x: values[i - 1], y: values[i] });
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
} else {
|
|
424
|
+
polyPoints.push({ x: values[values.length - 2], y: values[values.length - 1] });
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
return polyPoints;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/*
|
|
376
433
|
export function getPathDataVertices(pathData) {
|
|
377
434
|
let polyPoints = [];
|
|
378
435
|
let p0 = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
@@ -389,6 +446,7 @@ export function getPathDataVertices(pathData) {
|
|
|
389
446
|
});
|
|
390
447
|
return polyPoints;
|
|
391
448
|
};
|
|
449
|
+
*/
|
|
392
450
|
|
|
393
451
|
|
|
394
452
|
|
|
@@ -396,7 +454,148 @@ export function getPathDataVertices(pathData) {
|
|
|
396
454
|
* based on @cuixiping;
|
|
397
455
|
* https://stackoverflow.com/questions/9017100/calculate-center-of-svg-arc/12329083#12329083
|
|
398
456
|
*/
|
|
399
|
-
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
export function svgArcToCenterParam(x1, y1, rx, ry, xAxisRotation, largeArc, sweep, x2, y2, normalize = true
|
|
460
|
+
) {
|
|
461
|
+
|
|
462
|
+
// helper for angle calculation
|
|
463
|
+
const getAngle = (cx, cy, x, y, normalize = true) => {
|
|
464
|
+
let angle = Math.atan2(y - cy, x - cx);
|
|
465
|
+
if (normalize && angle < 0) angle += Math.PI * 2
|
|
466
|
+
return angle
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// make sure rx, ry are positive
|
|
470
|
+
rx = Math.abs(rx);
|
|
471
|
+
ry = Math.abs(ry);
|
|
472
|
+
|
|
473
|
+
// normalize xAxis rotation
|
|
474
|
+
xAxisRotation = rx === ry ? 0 : (xAxisRotation < 0 && normalize ? xAxisRotation + 360 : xAxisRotation);
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
// create data object
|
|
478
|
+
let arcData = {
|
|
479
|
+
cx: 0,
|
|
480
|
+
cy: 0,
|
|
481
|
+
// rx/ry values may be deceptive in arc commands
|
|
482
|
+
rx: rx,
|
|
483
|
+
ry: ry,
|
|
484
|
+
startAngle: 0,
|
|
485
|
+
endAngle: 0,
|
|
486
|
+
deltaAngle: 0,
|
|
487
|
+
clockwise: sweep,
|
|
488
|
+
// copy explicit arc properties
|
|
489
|
+
xAxisRotation,
|
|
490
|
+
largeArc,
|
|
491
|
+
sweep
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
if (rx == 0 || ry == 0) {
|
|
496
|
+
// invalid arguments
|
|
497
|
+
throw Error("rx and ry can not be 0");
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* if rx===ry x-axis rotation is ignored
|
|
503
|
+
* otherwise convert degrees to radians
|
|
504
|
+
*/
|
|
505
|
+
let phi = rx === ry ? 0 : xAxisRotation * deg2rad;
|
|
506
|
+
let cx, cy
|
|
507
|
+
|
|
508
|
+
let s_phi = !phi ? 0 : Math.sin(phi);
|
|
509
|
+
let c_phi = !phi ? 1 : Math.cos(phi);
|
|
510
|
+
|
|
511
|
+
let hd_x = (x1 - x2) / 2;
|
|
512
|
+
let hd_y = (y1 - y2) / 2;
|
|
513
|
+
let hs_x = (x1 + x2) / 2;
|
|
514
|
+
let hs_y = (y1 + y2) / 2;
|
|
515
|
+
|
|
516
|
+
// F6.5.1
|
|
517
|
+
let x1_ = !phi ? hd_x : c_phi * hd_x + s_phi * hd_y;
|
|
518
|
+
let y1_ = !phi ? hd_y : c_phi * hd_y - s_phi * hd_x;
|
|
519
|
+
|
|
520
|
+
// F.6.6 Correction of out-of-range radii
|
|
521
|
+
// Step 3: Ensure radii are large enough
|
|
522
|
+
let lambda = (x1_ * x1_) / (rx * rx) + (y1_ * y1_) / (ry * ry);
|
|
523
|
+
if (lambda > 1) {
|
|
524
|
+
let lambdaRoot = Math.sqrt(lambda);
|
|
525
|
+
rx = rx * lambdaRoot;
|
|
526
|
+
ry = ry * lambdaRoot;
|
|
527
|
+
|
|
528
|
+
// save real rx/ry
|
|
529
|
+
arcData.rx = rx;
|
|
530
|
+
arcData.ry = ry;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
let rxry = rx * ry;
|
|
534
|
+
let rxy1_ = rx * y1_;
|
|
535
|
+
let ryx1_ = ry * x1_;
|
|
536
|
+
let sum_of_sq = rxy1_ ** 2 + ryx1_ ** 2; // sum of square
|
|
537
|
+
if (!sum_of_sq) {
|
|
538
|
+
//console.log('error:', rx, ry, rxy1_, ryx1_);
|
|
539
|
+
throw Error("start point can not be same as end point");
|
|
540
|
+
}
|
|
541
|
+
let coe = Math.sqrt(Math.abs((rxry * rxry - sum_of_sq) / sum_of_sq));
|
|
542
|
+
if (largeArc === sweep) {
|
|
543
|
+
coe = -coe;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// F6.5.2
|
|
547
|
+
let cx_ = (coe * rxy1_) / ry;
|
|
548
|
+
let cy_ = (-coe * ryx1_) / rx;
|
|
549
|
+
|
|
550
|
+
/** F6.5.3
|
|
551
|
+
* center point of ellipse
|
|
552
|
+
*/
|
|
553
|
+
cx = !phi ? hs_x + cx_ : c_phi * cx_ - s_phi * cy_ + hs_x;
|
|
554
|
+
cy = !phi ? hs_y + cy_ : s_phi * cx_ + c_phi * cy_ + hs_y;
|
|
555
|
+
arcData.cy = cy;
|
|
556
|
+
arcData.cx = cx;
|
|
557
|
+
|
|
558
|
+
/** F6.5.5
|
|
559
|
+
* calculate angles between center point and
|
|
560
|
+
* commands starting and final on path point
|
|
561
|
+
*/
|
|
562
|
+
let startAngle = getAngle(cx, cy, x1, y1, normalize);
|
|
563
|
+
let endAngle = getAngle(cx, cy, x2, y2, normalize);
|
|
564
|
+
|
|
565
|
+
// adjust end angle
|
|
566
|
+
|
|
567
|
+
// Adjust angles based on sweep direction
|
|
568
|
+
if (sweep) {
|
|
569
|
+
// Clockwise
|
|
570
|
+
if (endAngle < startAngle) {
|
|
571
|
+
endAngle += Math.PI * 2;
|
|
572
|
+
}
|
|
573
|
+
} else {
|
|
574
|
+
// Counterclockwise
|
|
575
|
+
if (endAngle > startAngle) {
|
|
576
|
+
endAngle -= Math.PI * 2;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
let deltaAngle = endAngle - startAngle;
|
|
581
|
+
|
|
582
|
+
// The rest of your code remains the same
|
|
583
|
+
arcData.startAngle = startAngle;
|
|
584
|
+
arcData.startAngle_deg = startAngle * rad2Deg;
|
|
585
|
+
arcData.endAngle = endAngle;
|
|
586
|
+
arcData.endAngle_deg = endAngle * rad2Deg;
|
|
587
|
+
arcData.deltaAngle = deltaAngle;
|
|
588
|
+
arcData.deltaAngle_deg = deltaAngle * rad2Deg;
|
|
589
|
+
|
|
590
|
+
//console.log('arc', arcData);
|
|
591
|
+
return arcData;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
export function svgArcToCenterParam_back(x1, y1, rx, ry, xAxisRotation, largeArc, sweep, x2, y2) {
|
|
400
599
|
|
|
401
600
|
// helper for angle calculation
|
|
402
601
|
const getAngle = (cx, cy, x, y) => {
|
|
@@ -784,6 +983,124 @@ export function getBezierExtremeT(pts, { addExtremes = true, addSemiExtremes = f
|
|
|
784
983
|
* https://stackoverflow.com/questions/87734/#75031511
|
|
785
984
|
* See also: https://github.com/foo123/Geometrize
|
|
786
985
|
*/
|
|
986
|
+
|
|
987
|
+
export function getArcExtemesParam({
|
|
988
|
+
cx=0, cy=0, rx=0, ry=0,
|
|
989
|
+
p=null,
|
|
990
|
+
p0=null,
|
|
991
|
+
endAngle=0,
|
|
992
|
+
deltaAngle=0,
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
}={}) {
|
|
996
|
+
// compute point on ellipse from angle around ellipse (theta)
|
|
997
|
+
const arc = (theta, cx, cy, rx, ry, alpha) => {
|
|
998
|
+
// theta is angle in radians around arc
|
|
999
|
+
// alpha is angle of rotation of ellipse in radians
|
|
1000
|
+
var cos = Math.cos(alpha),
|
|
1001
|
+
sin = Math.sin(alpha),
|
|
1002
|
+
x = rx * Math.cos(theta),
|
|
1003
|
+
y = ry * Math.sin(theta);
|
|
1004
|
+
|
|
1005
|
+
return {
|
|
1006
|
+
x: cx + cos * x - sin * y,
|
|
1007
|
+
y: cy + sin * x + cos * y
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
//parametrize arcto data
|
|
1012
|
+
//let arcData = svgArcToCenterParam(p0.x, p0.y, values[0], values[1], values[2], values[3], values[4], values[5], values[6]);
|
|
1013
|
+
//let { rx, ry, cx, cy, endAngle, deltaAngle } = arcData;
|
|
1014
|
+
|
|
1015
|
+
// arc rotation
|
|
1016
|
+
let deg = values[2];
|
|
1017
|
+
|
|
1018
|
+
// final on path point
|
|
1019
|
+
//let p = { x: values[5], y: values[6] }
|
|
1020
|
+
|
|
1021
|
+
// collect extreme points – add end point
|
|
1022
|
+
let extremes = [p]
|
|
1023
|
+
|
|
1024
|
+
// rotation to radians
|
|
1025
|
+
let alpha = deg * Math.PI / 180;
|
|
1026
|
+
let tan = Math.tan(alpha),
|
|
1027
|
+
p1, p2, p3, p4, theta;
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* find min/max from zeroes of directional derivative along x and y
|
|
1031
|
+
* along x axis
|
|
1032
|
+
*/
|
|
1033
|
+
theta = Math.atan2(-ry * tan, rx);
|
|
1034
|
+
|
|
1035
|
+
let angle1 = theta;
|
|
1036
|
+
let angle2 = theta + Math.PI;
|
|
1037
|
+
let angle3 = Math.atan2(ry, rx * tan);
|
|
1038
|
+
let angle4 = angle3 + Math.PI;
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
// inner bounding box
|
|
1042
|
+
let xArr = [p0.x, p.x]
|
|
1043
|
+
let yArr = [p0.y, p.y]
|
|
1044
|
+
let xMin = Math.min(...xArr)
|
|
1045
|
+
let xMax = Math.max(...xArr)
|
|
1046
|
+
let yMin = Math.min(...yArr)
|
|
1047
|
+
let yMax = Math.max(...yArr)
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
// on path point close after start
|
|
1051
|
+
let angleAfterStart = endAngle - deltaAngle * 0.001
|
|
1052
|
+
let pP2 = arc(angleAfterStart, cx, cy, rx, ry, alpha);
|
|
1053
|
+
|
|
1054
|
+
// on path point close before end
|
|
1055
|
+
let angleBeforeEnd = endAngle - deltaAngle * 0.999
|
|
1056
|
+
let pP3 = arc(angleBeforeEnd, cx, cy, rx, ry, alpha);
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* expected extremes
|
|
1061
|
+
* if leaving inner bounding box
|
|
1062
|
+
* (between segment start and end point)
|
|
1063
|
+
* otherwise exclude elliptic extreme points
|
|
1064
|
+
*/
|
|
1065
|
+
|
|
1066
|
+
// right
|
|
1067
|
+
if (pP2.x > xMax || pP3.x > xMax) {
|
|
1068
|
+
// get point for this theta
|
|
1069
|
+
p1 = arc(angle1, cx, cy, rx, ry, alpha);
|
|
1070
|
+
extremes.push(p1)
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// left
|
|
1074
|
+
if (pP2.x < xMin || pP3.x < xMin) {
|
|
1075
|
+
// get anti-symmetric point
|
|
1076
|
+
p2 = arc(angle2, cx, cy, rx, ry, alpha);
|
|
1077
|
+
extremes.push(p2)
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// top
|
|
1081
|
+
if (pP2.y < yMin || pP3.y < yMin) {
|
|
1082
|
+
// get anti-symmetric point
|
|
1083
|
+
p4 = arc(angle4, cx, cy, rx, ry, alpha);
|
|
1084
|
+
extremes.push(p4)
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// bottom
|
|
1088
|
+
if (pP2.y > yMax || pP3.y > yMax) {
|
|
1089
|
+
// get point for this theta
|
|
1090
|
+
p3 = arc(angle3, cx, cy, rx, ry, alpha);
|
|
1091
|
+
extremes.push(p3)
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
return extremes;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
|
|
787
1104
|
export function getArcExtemes(p0, values) {
|
|
788
1105
|
// compute point on ellipse from angle around ellipse (theta)
|
|
789
1106
|
const arc = (theta, cx, cy, rx, ry, alpha) => {
|
|
@@ -1292,7 +1609,7 @@ export function getDistance(p1, p2, isArray = false) {
|
|
|
1292
1609
|
let dy = isArray ? p2[1] - p1[1] : (p2.y - p1.y);
|
|
1293
1610
|
|
|
1294
1611
|
//console.log('dx', dx, dy, p1, p2);
|
|
1295
|
-
return sqrt(dx * dx + dy * dy);
|
|
1612
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
1296
1613
|
}
|
|
1297
1614
|
|
|
1298
1615
|
// just an alias
|