svg-path-simplify 0.0.1 → 0.0.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/LICENSE +339 -21
- package/README.md +61 -2
- package/dist/svg-path-simplify.esm.js +4308 -0
- package/dist/svg-path-simplify.esm.min.js +1 -0
- package/dist/svg-path-simplify.js +4334 -0
- package/dist/svg-path-simplify.min.js +1 -0
- package/dist/svg-path-simplify.node.js +4331 -0
- package/dist/svg-path-simplify.node.min.js +1 -0
- package/index.html +230 -0
- package/package.json +5 -6
- package/src/constants.js +4 -0
- package/src/detect_input.js +42 -0
- package/src/index.js +21 -3
- package/src/pathData_simplify_cubic.js +324 -0
- package/src/pathData_simplify_cubic_arr.js +50 -0
- package/src/pathData_simplify_cubic_extrapolate.js +220 -0
- package/src/pathSimplify-main.js +400 -0
- package/src/svg_getViewbox.js +32 -0
- package/src/svgii/...parse.js +402 -0
- package/src/svgii/geometry.js +1143 -0
- package/src/svgii/geometry_area.js +265 -0
- package/src/svgii/geometry_bbox.js +223 -0
- package/src/svgii/pathData_analyze.js +896 -0
- package/src/svgii/pathData_convert.js +1180 -0
- package/src/svgii/pathData_parse.js +487 -0
- package/src/svgii/pathData_remove_collinear.js +98 -0
- package/src/svgii/pathData_remove_zerolength.js +28 -0
- package/src/svgii/pathData_reorder.js +238 -0
- package/src/svgii/pathData_reverse.js +124 -0
- package/src/svgii/pathData_scale.js +42 -0
- package/src/svgii/pathData_split.js +449 -0
- package/src/svgii/pathData_stringify.js +145 -0
- package/src/svgii/pathData_toPolygon.js +92 -0
- package/src/svgii/pathdata_cleanup.js +363 -0
- package/src/svgii/poly_analyze.js +172 -0
- package/src/svgii/poly_to_pathdata.js +185 -0
- package/src/svgii/rounding.js +162 -0
- package/src/svgii/simplify.js +248 -0
- package/src/svgii/simplify_bezier.js +470 -0
- package/src/svgii/simplify_linetos.js +93 -0
- package/src/svgii/simplify_polygon.js +135 -0
- package/src/svgii/stringify.js +103 -0
- package/src/svgii/svg_cleanup.js +86 -0
- package/src/svgii/visualize.js +317 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialize pathData array to a minified "d" attribute string.
|
|
3
|
+
*/
|
|
4
|
+
export function pathDataToD(pathData, optimize = 1) {
|
|
5
|
+
|
|
6
|
+
let beautify = optimize>1;
|
|
7
|
+
let minify = beautify ? false : true;
|
|
8
|
+
|
|
9
|
+
// Convert first "M" to "m" if followed by "l" (when minified)
|
|
10
|
+
if (pathData[1].type === "l" && minify) {
|
|
11
|
+
pathData[0].type = "m";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let d = '';
|
|
15
|
+
if(beautify) {
|
|
16
|
+
d = `${pathData[0].type} ${pathData[0].values.join(" ")}\n`;
|
|
17
|
+
}else{
|
|
18
|
+
d = `${pathData[0].type}${pathData[0].values.join(" ")}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
for (let i = 1, len = pathData.length; i < len; i++) {
|
|
23
|
+
let com0 = pathData[i - 1];
|
|
24
|
+
let com = pathData[i];
|
|
25
|
+
let { type, values } = com;
|
|
26
|
+
|
|
27
|
+
// Minify Arc commands (A/a) – actually sucks!
|
|
28
|
+
if (minify && (type === 'A' || type === 'a')) {
|
|
29
|
+
values = [
|
|
30
|
+
values[0], values[1], values[2],
|
|
31
|
+
`${values[3]}${values[4]}${values[5]}`,
|
|
32
|
+
values[6]
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Omit type for repeated commands
|
|
37
|
+
type = (com0.type === com.type && com.type.toLowerCase() !== 'm' && minify)
|
|
38
|
+
? " "
|
|
39
|
+
: (
|
|
40
|
+
(com0.type === "m" && com.type === "l") ||
|
|
41
|
+
(com0.type === "M" && com.type === "l") ||
|
|
42
|
+
(com0.type === "M" && com.type === "L")
|
|
43
|
+
) && minify
|
|
44
|
+
? " "
|
|
45
|
+
: com.type;
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
// concatenate subsequent floating point values
|
|
49
|
+
if (minify) {
|
|
50
|
+
|
|
51
|
+
//console.log(optimize, beautify, minify);
|
|
52
|
+
|
|
53
|
+
let valsString = '';
|
|
54
|
+
let prevWasFloat = false;
|
|
55
|
+
|
|
56
|
+
for (let v = 0, l = values.length; v < l; v++) {
|
|
57
|
+
let val = values[v];
|
|
58
|
+
let valStr = val.toString();
|
|
59
|
+
let isFloat = valStr.includes('.');
|
|
60
|
+
let isSmallFloat = isFloat && Math.abs(val) < 1;
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
// Remove leading zero from small floats *only* if the previous was also a float
|
|
64
|
+
if (isSmallFloat && prevWasFloat) {
|
|
65
|
+
valStr = valStr.replace(/^0\./, '.');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Add space unless this is the first value OR previous was a small float
|
|
69
|
+
if (v > 0 && !(prevWasFloat && isSmallFloat)) {
|
|
70
|
+
valsString += ' ';
|
|
71
|
+
}
|
|
72
|
+
//console.log(isSmallFloat, prevWasFloat, valStr);
|
|
73
|
+
|
|
74
|
+
valsString += valStr
|
|
75
|
+
//.replace(/-0./g, '-.').replace(/ -./g, '-.')
|
|
76
|
+
prevWasFloat = isSmallFloat;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
//console.log('minify', valsString);
|
|
80
|
+
d += `${type}${valsString}`;
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
// regular non-minified output
|
|
84
|
+
else{
|
|
85
|
+
if(beautify) {
|
|
86
|
+
d += `${type} ${values.join(' ')}\n`;
|
|
87
|
+
}else{
|
|
88
|
+
d += `${type}${values.join(' ')}`;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (minify) {
|
|
94
|
+
d = d
|
|
95
|
+
.replace(/ 0\./g, " .") // Space before small decimals
|
|
96
|
+
.replace(/ -/g, "-") // Remove space before negatives
|
|
97
|
+
.replace(/-0\./g, "-.") // Remove leading zero from negative decimals
|
|
98
|
+
.replace(/Z/g, "z"); // Convert uppercase 'Z' to lowercase
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
return d;
|
|
103
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export function cleanUpSVG(svgMarkup, {
|
|
2
|
+
returnDom=false,
|
|
3
|
+
removeHidden=true,
|
|
4
|
+
removeUnused=true,
|
|
5
|
+
}={}) {
|
|
6
|
+
svgMarkup = cleanSvgPrologue(svgMarkup);
|
|
7
|
+
|
|
8
|
+
// replace namespaced refs
|
|
9
|
+
svgMarkup = svgMarkup.replaceAll("xlink:href=", "href=");
|
|
10
|
+
|
|
11
|
+
let svg = new DOMParser()
|
|
12
|
+
.parseFromString(svgMarkup, "text/html")
|
|
13
|
+
.querySelector("svg");
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
let allowed=['viewBox', 'xmlns', 'width', 'height', 'id', 'class'];
|
|
17
|
+
removeExcludedAttribues(svg, allowed)
|
|
18
|
+
|
|
19
|
+
let removeEls = ['metadata', 'script']
|
|
20
|
+
|
|
21
|
+
let els = svg.querySelectorAll('*')
|
|
22
|
+
els.forEach(el=>{
|
|
23
|
+
let name = el.nodeName;
|
|
24
|
+
// remove hidden elements
|
|
25
|
+
let style = el.getAttribute('style') || ''
|
|
26
|
+
let isHiddenByStyle = style ? style.trim().includes('display:none') : false;
|
|
27
|
+
let isHidden = (el.getAttribute('display') && el.getAttribute('display') === 'none') || isHiddenByStyle;
|
|
28
|
+
if(name.includes(':') || removeEls.includes(name) || (removeHidden && isHidden )) {
|
|
29
|
+
el.remove();
|
|
30
|
+
}else{
|
|
31
|
+
// remove BS elements
|
|
32
|
+
removeNameSpaceAtts(el)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
if(returnDom) return svg
|
|
37
|
+
|
|
38
|
+
let markup = stringifySVG(svg)
|
|
39
|
+
console.log(markup);
|
|
40
|
+
|
|
41
|
+
return markup;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function cleanSvgPrologue(svgString) {
|
|
45
|
+
return (
|
|
46
|
+
svgString
|
|
47
|
+
// Remove XML prologues like <?xml ... ?>
|
|
48
|
+
.replace(/<\?xml[\s\S]*?\?>/gi, "")
|
|
49
|
+
// Remove DOCTYPE declarations
|
|
50
|
+
.replace(/<!DOCTYPE[\s\S]*?>/gi, "")
|
|
51
|
+
// Remove comments <!-- ... -->
|
|
52
|
+
.replace(/<!--[\s\S]*?-->/g, "")
|
|
53
|
+
// Trim extra whitespace
|
|
54
|
+
.trim()
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function removeExcludedAttribues(el, allowed=['viewBox', 'xmlns', 'width', 'height', 'id', 'class']){
|
|
59
|
+
let atts = [...el.attributes].map((att) => att.name);
|
|
60
|
+
atts.forEach((att) => {
|
|
61
|
+
if (!allowed.includes(att)) {
|
|
62
|
+
el.removeAttribute(att);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
function removeNameSpaceAtts(el) {
|
|
69
|
+
let atts = [...el.attributes].map((att) => att.name);
|
|
70
|
+
atts.forEach((att) => {
|
|
71
|
+
if (att.includes(":")) {
|
|
72
|
+
el.removeAttribute(att);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function stringifySVG(svg){
|
|
78
|
+
let markup = new XMLSerializer().serializeToString(svg);
|
|
79
|
+
markup = markup
|
|
80
|
+
.replace(/\t/g, "")
|
|
81
|
+
.replace(/[\n\r|]/g, "\n")
|
|
82
|
+
.replace(/\n\s*\n/g, '\n')
|
|
83
|
+
.replace(/ +/g, ' ')
|
|
84
|
+
|
|
85
|
+
return markup
|
|
86
|
+
}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
|
|
2
|
+
//import { splitSubpaths } from "./pathData_split";
|
|
3
|
+
|
|
4
|
+
//import { getPathDataVertices } from "./geometry";
|
|
5
|
+
//import { getPolygonArea } from "./geometry_area";
|
|
6
|
+
|
|
7
|
+
export function renderCommands(pathData, target = 'svg1') {
|
|
8
|
+
|
|
9
|
+
const splitSubpaths = (pathData) => {
|
|
10
|
+
let subPathArr = [];
|
|
11
|
+
|
|
12
|
+
//split segments after M command
|
|
13
|
+
let subPathIndices = pathData.map((com, i) => (com.type.toLowerCase() === 'm' ? i : -1)).filter(i => i !== -1);
|
|
14
|
+
|
|
15
|
+
// no compound path
|
|
16
|
+
if (subPathIndices.length === 1) {
|
|
17
|
+
return [pathData]
|
|
18
|
+
}
|
|
19
|
+
subPathIndices.forEach((index, i) => {
|
|
20
|
+
subPathArr.push(pathData.slice(index, subPathIndices[i + 1]));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return subPathArr;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//parent group
|
|
27
|
+
let svg_g_top = `<g class="cpt_g cpt_g_top">`;
|
|
28
|
+
let svg_g = `<g class="cpt_g">`;
|
|
29
|
+
|
|
30
|
+
svg_g += `
|
|
31
|
+
|
|
32
|
+
<style>
|
|
33
|
+
|
|
34
|
+
.cpt_handle {
|
|
35
|
+
stroke: #ccc;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.cpt_g {
|
|
39
|
+
stroke-width: 0.3%;
|
|
40
|
+
color: red;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.cpt_handle {
|
|
44
|
+
stroke: currentColor;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.cpt_handle_tip {
|
|
48
|
+
r: 1%;
|
|
49
|
+
fill: currentColor;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.cpt_handle_tip_m {
|
|
53
|
+
r: 1.5%;
|
|
54
|
+
display:none;
|
|
55
|
+
fill: transparent;
|
|
56
|
+
stroke: currentColor;
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
<symbol id="ccw_circle" viewBox="-5 -5 110 110" overflow="visible">
|
|
62
|
+
<path fill="none" stroke="currentColor"
|
|
63
|
+
d="M 4 31 c 8 -19 26 -31 46 -31 c 28 0 50 22 50 50 c 0 28 -22 50 -50 50 c -14 0 -36 -6 -46 -31
|
|
64
|
+
m -2 -76 v 37 h 37
|
|
65
|
+
"/>
|
|
66
|
+
</symbol>
|
|
67
|
+
|
|
68
|
+
<symbol id="cw_circle" viewBox="-5 -5 110 110" overflow="visible">
|
|
69
|
+
<path fill="none" stroke="currentColor" transform="scale(-1 1)" transform-origin="center"
|
|
70
|
+
d="M 4 31 c 8 -19 26 -31 46 -31 c 28 0 50 22 50 50 c 0 28 -22 50 -50 50 c -14 0 -36 -6 -46 -31
|
|
71
|
+
m -2 -76 v 37 h 37
|
|
72
|
+
"/>
|
|
73
|
+
</symbol>
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
//split in sub paths
|
|
81
|
+
let pathDataArr = splitSubpaths(pathData);
|
|
82
|
+
let M, cp1, cp2, cp1_0, cp2_0, p0, p;
|
|
83
|
+
|
|
84
|
+
pathDataArr.forEach((sub, s) => {
|
|
85
|
+
|
|
86
|
+
M = { x: sub[0].values[0], y: sub[0].values[1] }
|
|
87
|
+
p0 = M
|
|
88
|
+
|
|
89
|
+
let svg = `<g class="cpt_g">`;
|
|
90
|
+
let svg_cpts_top = ``
|
|
91
|
+
|
|
92
|
+
let polyPts = getPathDataVertices(sub);
|
|
93
|
+
let area = getPolygonArea(polyPts);
|
|
94
|
+
let cw = area<0 ? false : true;
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
sub.forEach((com, i) => {
|
|
98
|
+
let { type, values } = com;
|
|
99
|
+
|
|
100
|
+
if (type === 'Z') {
|
|
101
|
+
//p = M
|
|
102
|
+
//renderPoint(svg1, p, 'red', '2%')
|
|
103
|
+
} else {
|
|
104
|
+
|
|
105
|
+
let valsL = type != 'V' && type != 'H' ? values.slice(-2) :
|
|
106
|
+
(type === 'V' ? [p0.x, values[0]] : [values[0], p0.y]);
|
|
107
|
+
p = { x: valsL[0], y: valsL[1] };
|
|
108
|
+
//renderPoint(svg1, p, 'green', '3%')
|
|
109
|
+
|
|
110
|
+
let transCW = cw ? 'scale(-1 1)' : 'scale(1 1)';
|
|
111
|
+
|
|
112
|
+
if (type === 'M') {
|
|
113
|
+
|
|
114
|
+
//<use href="#ccw_circle" stroke-width="4"/>
|
|
115
|
+
|
|
116
|
+
if(cw){
|
|
117
|
+
svg_g_top += `<use href="#cw_circle" stroke-width="50%" style="color:#fff" x="${M.x}" y="${M.y}" width="2.5%" height="2.5%" transform="translate(-1.25 -1.25)" transform-origin="center" overflow="visible" />
|
|
118
|
+
<use href="#cw_circle" stroke-width="15%" x="${M.x}" y="${M.y}" width="2.5%" height="2.5%" data-transform-origin="center" transform="translate(-1.25 -1.25)" overflow="visible" />`
|
|
119
|
+
}else{
|
|
120
|
+
svg_g_top += `<use href="#ccw_circle" stroke-width="50%" style="color:#fff" x="${M.x}" y="${M.y}" width="2.5%" height="2.5%" transform="translate(-1.25 -1.25)" transform-origin="center" overflow="visible" />
|
|
121
|
+
<use href="#ccw_circle" stroke-width="15%" x="${M.x}" y="${M.y}" width="2.5%" height="2.5%" data-transform-origin="center" transform="translate(-1.25 -1.25)" overflow="visible" />`
|
|
122
|
+
}
|
|
123
|
+
//M = p;
|
|
124
|
+
//renderPoint(svg1, M, 'green', '2%');
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (type === 'C' || type === 'Q') {
|
|
129
|
+
cp1 = { x: values[0], y: values[1] }
|
|
130
|
+
//renderPoint(svg1, cp1, 'cyan');
|
|
131
|
+
|
|
132
|
+
if (type === 'Q') {
|
|
133
|
+
cp2 = cp1
|
|
134
|
+
svg += `<polyline fill="none" class="cpt_handle cpt_handle_q cpt_handle_cp1 " points="${p0.x} ${p0.y} ${cp1.x} ${cp1.y} ${p.x} ${p.y}"/>`;
|
|
135
|
+
svg += `<circle class="cpt_handle_tip cpt_handle_tip_q" cx="${cp1.x}" cy="${cp1.y}" r="1%" >
|
|
136
|
+
<title>${type.toLowerCase()} - x:${cp1.x}, y: ${cp1.y}</title></circle>`
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (type === 'C') {
|
|
140
|
+
cp2 = { x: values[2], y: values[3] }
|
|
141
|
+
//renderPoint(svg1, cp2, 'cyan')
|
|
142
|
+
|
|
143
|
+
svg += `<line class="cpt_handle cpt_handle_c cpt_handle_cp1 " x1="${p0.x}" y1="${p0.y}" x2="${cp1.x}" y2="${cp1.y}"/>`;
|
|
144
|
+
svg += `<circle class="cpt_handle_tip" cx="${cp1.x}" cy="${cp1.y}" r="1%" >
|
|
145
|
+
<title>${type.toLowerCase()} - x:${cp1.x}, y: ${cp1.y}</title></circle>`
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
svg += `<line class="cpt_handle cpt_handle_c cpt_handle_cp2" x1="${p.x}" y1="${p.y}" x2="${cp2.x}" y2="${cp2.y}"/>`;
|
|
149
|
+
svg += `<circle class="cpt_handle_tip" cx="${cp2.x}" cy="${cp2.y}" r="1%" >
|
|
150
|
+
<title>${type.toLowerCase()} - x:${cp2.x}, y: ${cp2.y}</title></circle>`
|
|
151
|
+
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (type === 'T') {
|
|
156
|
+
|
|
157
|
+
//get reflected cpt
|
|
158
|
+
// new control points
|
|
159
|
+
cp1 = { x: 2 * p0.x - cp2_0.x, y: 2 * p0.y - cp2_0.y };
|
|
160
|
+
cp2 = cp1
|
|
161
|
+
svg += `<polyline fill="none" class="cpt_handle cpt_handle_t cpt_handle_cp1 " points="${p0.x} ${p0.y} ${cp1.x} ${cp1.y} ${p.x} ${p.y}"/>`;
|
|
162
|
+
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (type === 'S') {
|
|
166
|
+
|
|
167
|
+
//get reflected cpt
|
|
168
|
+
let cp2_2 = { x: values[0], y: values[1] }
|
|
169
|
+
// new control points
|
|
170
|
+
let cp1_2 = { x: 2 * p0.x - cp2_0.x, y: 2 * p0.y - cp2_0.y };
|
|
171
|
+
|
|
172
|
+
svg += `<line class="cpt_handle cpt_handle_s cpt_handle_cp1 " x1="${p0.x}" y1="${p0.y}" x2="${cp1_2.x}" y2="${cp1_2.y}"/>`;
|
|
173
|
+
svg += `<line class="cpt_handle cpt_handle_s cpt_handle_cp2" x1="${p.x}" y1="${p.y}" x2="${cp2_2.x}" y2="${cp2_2.y}"/>`;
|
|
174
|
+
svg += `<circle class="cpt_handle_tip cpt_handle_tip_s" cx="${cp2_2.x}" cy="${cp2_2.y}" r="1%" >
|
|
175
|
+
<title>${type.toLowerCase()} - x:${cp2_2.x}, y: ${cp2_2.y}</title></circle>`
|
|
176
|
+
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (type !== 'Z') {
|
|
180
|
+
svg += `<circle class="cpt_handle_tip cpt_handle_tip_${type.toLowerCase()}" cx="${p.x}" cy="${p.y}" r="1%" >
|
|
181
|
+
<title>P x:${p.x}, y: ${p.y}</title></circle>`
|
|
182
|
+
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
p0 = p
|
|
188
|
+
cp1_0 = cp1
|
|
189
|
+
cp2_0 = cp2
|
|
190
|
+
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
svg_g += svg + '</g>';
|
|
195
|
+
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
svg_g += `</g>`;
|
|
199
|
+
svg_g += svg_g_top+'</g>';
|
|
200
|
+
//console.log('svg', svg, svg1);
|
|
201
|
+
//svg1.insertAdjacentHTML('beforeend', svg)
|
|
202
|
+
|
|
203
|
+
return svg_g
|
|
204
|
+
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
export function renderPoint(
|
|
209
|
+
svg,
|
|
210
|
+
coords,
|
|
211
|
+
fill = "red",
|
|
212
|
+
r = "1%",
|
|
213
|
+
opacity = "1",
|
|
214
|
+
title = '',
|
|
215
|
+
render = true,
|
|
216
|
+
id = "",
|
|
217
|
+
className = ""
|
|
218
|
+
) {
|
|
219
|
+
if (Array.isArray(coords)) {
|
|
220
|
+
coords = {
|
|
221
|
+
x: coords[0],
|
|
222
|
+
y: coords[1]
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
let marker = `<circle class="${className}" opacity="${opacity}" id="${id}" cx="${coords.x}" cy="${coords.y}" r="${r}" fill="${fill}">
|
|
226
|
+
<title>${title}</title></circle>`;
|
|
227
|
+
|
|
228
|
+
if (render) {
|
|
229
|
+
svg.insertAdjacentHTML("beforeend", marker);
|
|
230
|
+
} else {
|
|
231
|
+
return marker;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
export function renderPath(svg, d = '', stroke = 'green', strokeWidth = '1%', render = true) {
|
|
237
|
+
|
|
238
|
+
let path = `<path d="${d}" fill="none" stroke="${stroke}" stroke-width="${strokeWidth}" /> `;
|
|
239
|
+
|
|
240
|
+
if (render) {
|
|
241
|
+
svg.insertAdjacentHTML("beforeend", path);
|
|
242
|
+
} else {
|
|
243
|
+
return path;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
// debug helper: render lines
|
|
253
|
+
export function renderPoly(svg, pts, strokeWidth = "1%", stroke = "purple", render = true) {
|
|
254
|
+
pts = pts.map(pt => { return [pt.x, pt.y] }).flat().join(' ');
|
|
255
|
+
|
|
256
|
+
let poly =
|
|
257
|
+
`<polyline stroke-width="${strokeWidth}" points="${pts}" stroke="${stroke}" />`;
|
|
258
|
+
|
|
259
|
+
if (render) {
|
|
260
|
+
svg.insertAdjacentHTML("beforeend", poly);
|
|
261
|
+
|
|
262
|
+
} else {
|
|
263
|
+
return poly
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
//!!! delete- rename
|
|
269
|
+
export function renderPerpendicularLine(pt, len = 10, angle) {
|
|
270
|
+
let ptA = {
|
|
271
|
+
x: pt.x + len * Math.cos(angle),
|
|
272
|
+
y: pt.y + len * Math.sin(angle)
|
|
273
|
+
};
|
|
274
|
+
return ptA;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
export function addMarkers() {
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
let markerMarkup =
|
|
283
|
+
`<svg id="svgMarkers" style="width:0; height:0; position:absolute; z-index:-1;float:left;">
|
|
284
|
+
<defs>
|
|
285
|
+
<marker id="markerStart" overflow="visible" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="strokeWidth"
|
|
286
|
+
markerWidth="10" markerHeight="10" orient="auto-start-reverse">
|
|
287
|
+
<circle cx="5" cy="5" r="3" fill="green" fill-opacity="1" />>
|
|
288
|
+
|
|
289
|
+
<marker id="markerEnd" overflow="visible" viewBox="0 0 10 10" refX="5" refY="5"
|
|
290
|
+
markerUnits="strokeWidth" markerWidth="10" markerHeight="10" orient="auto-start-reverse">
|
|
291
|
+
<circle cx="5" cy="5" r="2" fill="red" fill-opacity="0.5" />
|
|
292
|
+
</marker>
|
|
293
|
+
</defs>
|
|
294
|
+
</svg>`
|
|
295
|
+
|
|
296
|
+
let style = `
|
|
297
|
+
<style>
|
|
298
|
+
.showMarkers {
|
|
299
|
+
marker-start: url(#markerStart);
|
|
300
|
+
marker-mid: url(#markerEnd);
|
|
301
|
+
stroke-width: 0.33%;
|
|
302
|
+
}
|
|
303
|
+
</style>
|
|
304
|
+
`
|
|
305
|
+
document.body.insertAdjacentHTML('afterbegin', style + markerMarkup)
|
|
306
|
+
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* adjust viewBox
|
|
312
|
+
*/
|
|
313
|
+
export function adjustViewBox(svg) {
|
|
314
|
+
let bb = svg.getBBox();
|
|
315
|
+
let [x, y, width, height] = [bb.x, bb.y, bb.width, bb.height];
|
|
316
|
+
svg.setAttribute("viewBox", [x, y, width, height].join(" "));
|
|
317
|
+
}
|