svg-path-simplify 0.0.5 → 0.0.8

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.
@@ -0,0 +1,239 @@
1
+ //import { pathDataToAbsoluteOrRelative, pathDataToLonghands, cubicToArc } from './pathData_convert.js';
2
+ import { parsePathDataString, parsePathDataNormalized, stringifyPathData } from './pathData_parse.js';
3
+
4
+ export function shapeElToPath(el){
5
+
6
+ let nodeName = el.nodeName.toLowerCase();
7
+ if(nodeName==='path')return el;
8
+
9
+ let pathData = getPathDataFromEl(el);
10
+ let d = pathData.map(com=>{return `${com.type} ${com.values} `}).join(' ')
11
+ let attributes = [...el.attributes].map(att=>att.name);
12
+
13
+ let pathN = document.createElementNS('http://www.w3.org/2000/svg', 'path');
14
+ pathN.setAttribute('d', d );
15
+
16
+ let exclude = ['x', 'y', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points']
17
+
18
+ attributes.forEach(att=>{
19
+ if(!exclude.includes(att)){
20
+ let val = el.getAttribute(att);
21
+ pathN.setAttribute(att, val)
22
+ }
23
+ })
24
+
25
+ //el.replaceWith(pathN)
26
+ return pathN
27
+
28
+ }
29
+
30
+
31
+ // retrieve pathdata from svg geometry elements
32
+ export function getPathDataFromEl(el, stringify=false) {
33
+
34
+ let pathData = [];
35
+ let type = el.nodeName;
36
+ let atts, attNames, d, x, y, width, height, r, rx, ry, cx, cy, x1, x2, y1, y2;
37
+
38
+ // convert relative or absolute units
39
+ const svgElUnitsToPixel = (el, decimals = 9) => {
40
+ //console.log(this);
41
+ const svg = el.nodeName !== "svg" ? el.closest("svg") : el;
42
+
43
+ // convert real life units to pixels
44
+ const translateUnitToPixel = (value) => {
45
+
46
+ if (value === null) {
47
+ return 0
48
+ }
49
+ //default dpi = 96
50
+ let dpi = 96;
51
+ let unit = value.match(/([a-z]+)/gi);
52
+ unit = unit ? unit[0] : "";
53
+ let val = parseFloat(value);
54
+ let rat;
55
+
56
+ // no unit - already pixes/user unit
57
+ if (!unit) {
58
+ return val;
59
+ }
60
+
61
+ switch (unit) {
62
+ case "in":
63
+ rat = dpi;
64
+ break;
65
+ case "pt":
66
+ rat = (1 / 72) * 96;
67
+ break;
68
+ case "cm":
69
+ rat = (1 / 2.54) * 96;
70
+ break;
71
+ case "mm":
72
+ rat = ((1 / 2.54) * 96) / 10;
73
+ break;
74
+ // just a default approximation
75
+ case "em":
76
+ case "rem":
77
+ rat = 16;
78
+ break;
79
+ default:
80
+ rat = 1;
81
+ }
82
+ let valuePx = val * rat;
83
+ return +valuePx.toFixed(decimals);
84
+ };
85
+
86
+ // svg width and height attributes
87
+ let width = svg.getAttribute("width");
88
+ width = width ? translateUnitToPixel(width) : 300;
89
+ let height = svg.getAttribute("height");
90
+ height = width ? translateUnitToPixel(height) : 150;
91
+
92
+ //prefer viewBox values
93
+ let vB = svg.getAttribute("viewBox");
94
+ vB = vB
95
+ ? vB
96
+ .replace(/,/g, " ")
97
+ .split(" ")
98
+ .filter(Boolean)
99
+ .map((val) => {
100
+ return +val;
101
+ })
102
+ : [];
103
+
104
+ let w = vB.length ? vB[2] : width;
105
+ let h = vB.length ? vB[3] : height;
106
+ let scaleX = w / 100;
107
+ let scaleY = h / 100;
108
+ let scalRoot = Math.sqrt((Math.pow(scaleX, 2) + Math.pow(scaleY, 2)) / 2);
109
+
110
+ let attsH = ["x", "width", "x1", "x2", "rx", "cx", "r"];
111
+ let attsV = ["y", "height", "y1", "y2", "ry", "cy"];
112
+
113
+
114
+ let atts = el.getAttributeNames();
115
+ atts.forEach((att) => {
116
+ let val = el.getAttribute(att);
117
+ let valAbs = val;
118
+ if (attsH.includes(att) || attsV.includes(att)) {
119
+ let scale = attsH.includes(att) ? scaleX : scaleY;
120
+ scale = att === "r" && w != h ? scalRoot : scale;
121
+ let unit = val.match(/([a-z|%]+)/gi);
122
+ unit = unit ? unit[0] : "";
123
+ if (val.includes("%")) {
124
+ valAbs = parseFloat(val) * scale;
125
+ }
126
+ //absolute units
127
+ else {
128
+ valAbs = translateUnitToPixel(val);
129
+ }
130
+ el.setAttribute(att, +valAbs);
131
+ }
132
+ });
133
+ }
134
+
135
+ svgElUnitsToPixel(el)
136
+
137
+ const getAtts = (attNames) => {
138
+ atts = {}
139
+ attNames.forEach(att => {
140
+ atts[att] = +el.getAttribute(att)
141
+ })
142
+ return atts
143
+ }
144
+
145
+ switch (type) {
146
+ case 'path':
147
+ d = el.getAttribute("d");
148
+ pathData = parsePathDataNormalized(d);
149
+ break;
150
+
151
+ case 'rect':
152
+ attNames = ['x', 'y', 'width', 'height', 'rx', 'ry'];
153
+ ({ x, y, width, height, rx, ry } = getAtts(attNames));
154
+
155
+
156
+ if (!rx && !ry) {
157
+ pathData = [
158
+ { type: "M", values: [x, y] },
159
+ { type: "L", values: [x + width, y] },
160
+ { type: "L", values: [x + width, y + height] },
161
+ { type: "L", values: [x, y + height] },
162
+ { type: "Z", values: [] }
163
+ ];
164
+ } else {
165
+
166
+ if (rx > width / 2) {
167
+ rx = width / 2;
168
+ }
169
+ if (ry > height / 2) {
170
+ ry = height / 2;
171
+ }
172
+ pathData = [
173
+ { type: "M", values: [x + rx, y] },
174
+ { type: "L", values: [x + width - rx, y] },
175
+ { type: "A", values: [rx, ry, 0, 0, 1, x + width, y + ry] },
176
+ { type: "L", values: [x + width, y + height - ry] },
177
+ { type: "A", values: [rx, ry, 0, 0, 1, x + width - rx, y + height] },
178
+ { type: "L", values: [x + rx, y + height] },
179
+ { type: "A", values: [rx, ry, 0, 0, 1, x, y + height - ry] },
180
+ { type: "L", values: [x, y + ry] },
181
+ { type: "A", values: [rx, ry, 0, 0, 1, x + rx, y] },
182
+ { type: "Z", values: [] }
183
+ ];
184
+ }
185
+ break;
186
+
187
+ case 'circle':
188
+ case 'ellipse':
189
+
190
+ attNames = ['cx', 'cy', 'rx', 'ry', 'r'];
191
+ ({ cx, cy, r, rx, ry } = getAtts(attNames));
192
+
193
+ if (type === 'circle') {
194
+ r = r;
195
+ rx = r
196
+ ry = r
197
+ } else {
198
+ rx = rx ? rx : r;
199
+ ry = ry ? ry : r;
200
+ }
201
+
202
+ pathData = [
203
+ { type: "M", values: [cx + rx, cy] },
204
+ { type: "A", values: [rx, ry, 0, 1, 1, cx - rx, cy] },
205
+ { type: "A", values: [rx, ry, 0, 1, 1, cx + rx, cy] },
206
+ ];
207
+
208
+ break;
209
+ case 'line':
210
+ attNames = ['x1', 'y1', 'x2', 'y2'];
211
+ ({ x1, y1, x2, y2 } = getAtts(attNames));
212
+ pathData = [
213
+ { type: "M", values: [x1, y1] },
214
+ { type: "L", values: [x2, y2] }
215
+ ];
216
+ break;
217
+ case 'polygon':
218
+ case 'polyline':
219
+
220
+ let points = el.getAttribute('points').replaceAll(',', ' ').split(' ').filter(Boolean)
221
+
222
+ for (let i = 0; i < points.length; i += 2) {
223
+ pathData.push({
224
+ type: (i === 0 ? "M" : "L"),
225
+ values: [+points[i], +points[i + 1]]
226
+ });
227
+ }
228
+ if (type === 'polygon') {
229
+ pathData.push({
230
+ type: "Z",
231
+ values: []
232
+ });
233
+ }
234
+ break;
235
+ }
236
+
237
+ return stringify ? stringifyPathData(pathData): pathData;
238
+
239
+ };
@@ -1,5 +1,6 @@
1
- import { checkBezierFlatness, getDistAv, getSquareDistance } from "./geometry.js";
1
+ import { getDistAv, getSquareDistance } from "./geometry.js";
2
2
  import { getPolygonArea } from "./geometry_area.js";
3
+ import { checkBezierFlatness, commandIsFlat } from "./geometry_flatness.js";
3
4
  import { renderPoint } from "./visualize.js";
4
5
 
5
6
  export function pathDataRemoveColinear(pathData, tolerance = 1, flatBezierToLinetos = true) {
@@ -25,8 +26,12 @@ export function pathDataRemoveColinear(pathData, tolerance = 1, flatBezierToLine
25
26
 
26
27
  let area = getPolygonArea([p0, p, p1], true)
27
28
 
29
+ let distSquare0 = getSquareDistance(p0, p)
30
+ let distSquare1 = getSquareDistance(p, p1)
28
31
  let distSquare = getSquareDistance(p0, p1)
29
- let distMax = distSquare / 100 * tolerance
32
+ //distSquare = (distSquare0+distSquare1) / 2;
33
+
34
+ let distMax = distSquare / 200 * tolerance
30
35
 
31
36
  let isFlat = area < distMax;
32
37
  let isFlatBez = false;
@@ -46,6 +51,7 @@ export function pathDataRemoveColinear(pathData, tolerance = 1, flatBezierToLine
46
51
  //let areaBez = getPolygonArea([p0, ...cpts, p], true)
47
52
 
48
53
  isFlatBez = checkBezierFlatness(p0, cpts, p)
54
+ //isFlatBez = commandIsFlat([p0, ...cpts, p]).flat
49
55
  // console.log();
50
56
 
51
57
  //isFlatBez = areaBez < distMax * 0.25
@@ -67,8 +73,12 @@ export function pathDataRemoveColinear(pathData, tolerance = 1, flatBezierToLine
67
73
  p0 = p;
68
74
 
69
75
  // colinear – exclude arcs (as always =) as semicircles won't have an area
70
- if ( isFlat && c < l - 1 && (type === 'L' || (flatBezierToLinetos && isFlatBez))) {
76
+ //&& comN.type==='L'
77
+ if ( isFlat && c < l - 1 && (type === 'L' || (flatBezierToLinetos && isFlatBez)) ) {
71
78
  //console.log(area,distMax );
79
+ //renderPoint(markers, p)
80
+
81
+ //if(comN.type!=='L' ){}
72
82
  continue;
73
83
  }
74
84
 
@@ -0,0 +1,20 @@
1
+ export function removeOrphanedM(pathData) {
2
+
3
+ for (let i = 0, l = pathData.length; i < l; i++) {
4
+ let com = pathData[i];
5
+ if (!com) continue;
6
+ let { type = null, values = [] } = com;
7
+ let comN = pathData[i + 1] ? pathData[i + 1] : null;
8
+ if ((type === 'M' || type === 'm')) {
9
+
10
+ if (!comN || (comN && (comN.type === 'Z' || comN.type === 'z'))) {
11
+ pathData[i] = null
12
+ pathData[i + 1] = null
13
+ }
14
+ }
15
+ }
16
+
17
+ pathData = pathData.filter(Boolean);
18
+ return pathData;
19
+
20
+ }
@@ -1,3 +1,21 @@
1
+
2
+ /*
3
+ // remove zero-length segments introduced by rounding
4
+ export function removeZeroLengthLinetos_post(pathData) {
5
+ let pathDataOpt = []
6
+ pathData.forEach((com, i) => {
7
+ let { type, values } = com;
8
+ if (type === 'l' || type === 'v' || type === 'h') {
9
+ let hasLength = type === 'l' ? (values.join('') !== '00') : values[0] !== 0
10
+ if (hasLength) pathDataOpt.push(com)
11
+ } else {
12
+ pathDataOpt.push(com)
13
+ }
14
+ })
15
+ return pathDataOpt
16
+ }
17
+ */
18
+
1
19
  export function removeZeroLengthLinetos(pathData) {
2
20
 
3
21
  let M = { x: pathData[0].values[0], y: pathData[0].values[1] }
@@ -10,14 +28,22 @@ export function removeZeroLengthLinetos(pathData) {
10
28
  let com = pathData[c];
11
29
  let { type, values } = com;
12
30
 
13
- let valsL = values.slice(-2);
14
- p = { x: valsL[0], y: valsL[1] };
31
+ let valsLen = values.length;
32
+ //let valsL = values.slice(-2);
33
+ //p = { x: valsL[0], y: valsL[1] };
34
+ p = { x: values[valsLen-2], y: values[valsLen-1] };
15
35
 
16
36
  // skip lineto
17
37
  if (type === 'L' && p.x === p0.x && p.y === p0.y) {
18
38
  continue
19
39
  }
20
40
 
41
+ // skip minified zero length
42
+ if (type === 'l' || type === 'v' || type === 'h') {
43
+ let noLength = type === 'l' ? (values.join('') === '00') : values[0] === 0;
44
+ if(noLength) continue
45
+ }
46
+
21
47
  pathDataN.push(com)
22
48
  p0 = p;
23
49
  }
@@ -1,10 +1,12 @@
1
1
  import { splitSubpaths, addExtemesToCommand } from './pathData_split.js';
2
- import { getComThresh, commandIsFlat, getPathDataVertices, getSquareDistance } from './geometry.js';
2
+ import { getComThresh, getPathDataVertices, getSquareDistance } from './geometry.js';
3
3
  import { getPolyBBox } from './geometry_bbox.js';
4
4
 
5
5
 
6
6
  import { renderPoint, renderPath } from './visualize.js';
7
7
  import { getPolygonArea } from './geometry_area.js';
8
+ import { checkBezierFlatness, commandIsFlat } from "./geometry_flatness.js";
9
+
8
10
 
9
11
 
10
12
  export function pathDataToTopLeft(pathData) {
@@ -2,6 +2,7 @@ import { pointAtT, svgArcToCenterParam, getBezierExtremeT } from "./geometry";
2
2
  import { renderPoint, renderPath } from "./visualize";
3
3
 
4
4
 
5
+
5
6
  /**
6
7
  * split segments into chunks to
7
8
  * prevent simplification across
@@ -280,6 +281,8 @@ export function addExtemesToCommand(p0, values, tMin=0, tMax=1) {
280
281
 
281
282
  if(tArr.length){
282
283
  let commandsSplit = splitCommandAtTValues(p0, values, tArr)
284
+ //console.log('commandsSplit', commandsSplit);
285
+
283
286
  pathDataNew.push(...commandsSplit)
284
287
  extremeCount += commandsSplit.length;
285
288
  }else{
@@ -322,6 +325,8 @@ export function addExtremePoints(pathData, tMin=0, tMax=1) {
322
325
  // add extremes
323
326
  if (type === 'C' || type === 'Q') {
324
327
  let comExt = addExtemesToCommand(p0, values, tMin, tMax).pathData;
328
+ let comExt2 = com;
329
+ //comExt2.valu
325
330
  //console.log('comExt', comExt);
326
331
  pathDataNew.push(...comExt )
327
332
  }
@@ -1,12 +1,15 @@
1
- import { pointAtT } from "./geometry";
1
+ import { getDistAv, pointAtT } from "./geometry";
2
2
  import { getPolyBBox } from "./geometry_bbox";
3
3
  import { addDimensionData, analyzePathData } from "./pathData_analyze";
4
4
  import { addExtremePoints } from "./pathData_split";
5
+ import { pathDataToD } from "./pathData_stringify";
6
+ import { analyzePoly } from "./poly_analyze";
7
+ import { getCurvePathData } from "./poly_to_pathdata";
8
+ import { renderPoint } from "./visualize";
5
9
 
6
- export function pathDataToPolyPlus(pathDataArr = [], addExtremes=true) {
7
10
 
8
- // check if splitting sub paths is required
9
- pathDataArr = Object.hasOwnProperty(pathDataArr[0].type) ? splitSubpaths(pathDataArr) : pathDataArr;
11
+ export function pathDataToPolySingle(pathData, addExtremes = true) {
12
+
10
13
 
11
14
  let dimMin = Infinity;
12
15
  let dimMax = 0;
@@ -16,77 +19,125 @@ export function pathDataToPolyPlus(pathDataArr = [], addExtremes=true) {
16
19
  * add extremes to beziers
17
20
  * to reproduce the shape better
18
21
  */
19
- if(addExtremes){
20
- pathDataArr.forEach((pathData, i) => {
21
-
22
- //pathDataArr[i] = addExtremePoints(pathData)
23
- let pathDataE = addExtremePoints(pathData)
24
-
25
- //pathDataArr[i] = analyzePathData(pathDataE).pathData
26
- pathDataArr[i] = addDimensionData(pathDataE)
27
-
28
- })
22
+ if (addExtremes) {
23
+ pathData = addExtremePoints(pathData, 0.1, 0.9)
29
24
  }
30
25
 
26
+ //console.log(pathData);
27
+
28
+ let pathDataPlus = analyzePathData(pathData);
29
+ let { bb } = pathDataPlus;
30
+ let thresh = (bb.width + bb.height) / 2 / 50
31
+
32
+ pathData = pathDataPlus.pathData
33
+
31
34
 
32
35
  /**
33
36
  * approximate min and max segment sizes
34
37
  * for segment splitting
35
38
  */
36
- pathDataArr.forEach(pathData => {
37
-
38
- let dimArr = pathData.filter(com => com.dimA).sort((a, b) => a.dimA - b.DimA)
39
- let dimMinL = dimArr[0].dimA
40
- let dimMaxL = dimArr[dimArr.length - 1].dimA
41
- //console.log('dimArr', dimArr, dimMaxL);
42
- if (dimMinL && dimMinL < dimMin) dimMin = dimMinL;
43
- if (dimMaxL && dimMaxL > dimMax) dimMax = dimMaxL;
44
-
45
- })
39
+ let dimArr = pathData.filter(com => com.dimA).sort((a, b) => a.dimA - b.DimA)
40
+ let dimMinL = dimArr[0].dimA
41
+ let dimMaxL = dimArr[dimArr.length - 1].dimA
42
+ //console.log('dimArr', dimArr, dimMaxL);
43
+ if (dimMinL && dimMinL < dimMin) dimMin = dimMinL;
44
+ if (dimMaxL && dimMaxL > dimMax) dimMax = dimMaxL;
46
45
 
47
46
  //console.log(dimMin, dimMax);
48
47
 
49
48
  // find split point based on smallest point distance
50
- dimMin = (dimMin * 2 + dimMax) / 2 * 0.5
49
+ dimMin = (dimMin * 2 + dimMax) / 2 / 4
50
+ //dimMin = (bb.width + bb.height) / 2 / 8
51
51
 
52
52
  // collect vertices
53
53
  let polyArr = [];
54
54
 
55
+ let p0 = { x: pathData[0].p0.x, y: pathData[0].p0.y, extreme: pathData[0].extreme, corner: pathData[0].corner }
56
+ let poly = [p0];
57
+
58
+ for (let i = 1, l = pathData.length; i < l; i++) {
55
59
 
56
- pathDataArr.forEach(pathData => {
60
+ let com = pathData[i];
61
+ let { type, values, extreme = false, corner = false, dimA = null, p0, p, cp1 = null, cp2 = null } = com;
57
62
 
58
- let poly = [pathData[0].p0];
63
+ dimA = getDistAv(p0, p);
59
64
 
60
- pathData.forEach(com => {
61
65
 
62
- let { type, values, dimA = null, p0, p, cp1 = null, cp2 = null } = com;
63
- let split = type === 'C' && dimA ? Math.ceil(dimA / dimMin) : 0;
66
+ if(extreme){
67
+ //renderPoint(markers, p, 'cyan')
68
+ }
64
69
 
65
- if (type === 'C' && split) {
70
+ let split = (type === 'C' || type === 'Q') && dimA ? Math.ceil(dimA / dimMin) : 0;
66
71
 
67
- let splitT = 1 / split;
68
72
 
69
- for (let i = 1; i < split; i++) {
73
+ //console.log(com);
74
+ p.extreme = extreme
75
+ p.corner = corner
70
76
 
71
- let t = splitT * i;
72
- let ptI = pointAtT([p0, cp1, cp2, p], t)
73
- poly.push(ptI)
74
- }
77
+ //console.log(p);
75
78
 
79
+ if ((type === 'C' || type === 'Q') && split) {
80
+ let splitT = 1 / split;
81
+ for (let i = 1; i < split; i++) {
82
+ let t = splitT * i;
83
+ let cpts = type === 'C' ? [cp1, cp2] : [cp1];
84
+ let ptI = pointAtT([p0, ...cpts, p], t)
85
+ poly.push(ptI)
76
86
  }
77
- poly.push(p)
78
- })
87
+ }
88
+ poly.push(p)
89
+
90
+ }
91
+
92
+
93
+ // remove short
94
+ let remove = new Set([])
95
+ for (let i = 1, l = poly.length; i < l; i++) {
96
+ let p = poly[i - 1]
97
+ let pN = poly[i]
98
+
99
+ let dist1 = getDistAv(p, pN)
100
+ if (dist1 < thresh && pN.extreme) {
101
+ let pR = p.extreme ? pN : p
102
+ let idx = p.extreme ? i : i - 1
103
+ //console.log('remove', idx);
104
+ remove.add(idx)
105
+ }
106
+ }
107
+
79
108
 
80
- polyArr.push(poly)
109
+ remove = Array.from(remove).reverse();
110
+ //console.log(remove);
81
111
 
82
- /*
83
- let bb = getPolyBBox(poly);
84
- let dimAv = (bb.width+bb.height)/2
85
- console.log('dimAv', dimAv);
86
- */
112
+ for (let i = 0; i < remove.length; i++) {
113
+ let idx = remove[i];
114
+ //console.log('idx', idx);
115
+ poly.splice(idx, 1)
116
+ }
117
+
118
+ poly.splice(poly.length-1, poly.length)
119
+
120
+
121
+ let polyAtt = poly.map(pt => `${pt.x} ${pt.y} `).join(' ')
122
+ //console.log('polyAtt', polyAtt);
123
+
124
+ //markers.insertAdjacentHTML('beforeend', `<polygon points="${polyAtt}" stroke="red" fill="none"/>`)
125
+
126
+
127
+ poly = analyzePoly(poly, false)
128
+ let pathDataP = getCurvePathData(poly, 0.666, true)
129
+ let d = pathDataToD(pathDataP)
87
130
 
88
- })
131
+ console.log(d);
132
+ //markers.insertAdjacentHTML('beforeend', `<path d="${d}" stroke="green" fill="none" stroke-width="1%"/>`)
89
133
 
90
- return polyArr
134
+
135
+
136
+
137
+
138
+ return poly
91
139
 
92
140
  }
141
+
142
+
143
+
@@ -3,7 +3,7 @@ import { getPolygonArea } from "./geometry_area";
3
3
  import { getPolyBBox } from "./geometry_bbox";
4
4
  import { renderPoint } from "./visualize";
5
5
 
6
- export function analyzePoly(pts) {
6
+ export function analyzePoly(pts, debug=false) {
7
7
 
8
8
  let l = pts.length;
9
9
  let polyArea = getPolygonArea(pts, true)
@@ -75,22 +75,26 @@ export function analyzePoly(pts) {
75
75
  /*
76
76
  */
77
77
 
78
- if ((isExtreme && isCorner)) {
79
- isExtreme = false;
80
- directionChange = false;
81
- //isCorner = false;
82
- }
83
-
84
- if (isExtreme) {
85
- renderPoint(markers, pt1, 'cyan', '1%');
86
- }
87
-
88
- if (isCorner) {
89
- renderPoint(markers, pt1, 'purple', '0.5%');
90
- }
78
+ if(debug){
79
+
80
+ if ((isExtreme && isCorner)) {
81
+ isExtreme = false;
82
+ directionChange = false;
83
+ //isCorner = false;
84
+ }
85
+
86
+ if (isExtreme) {
87
+ renderPoint(markers, pt1, 'cyan', '1%');
88
+ }
89
+
90
+ if (isCorner) {
91
+ renderPoint(markers, pt1, 'purple', '0.5%');
92
+ }
93
+
94
+ if (directionChange) {
95
+ renderPoint(markers, pt1, 'orange', '1.5%', '0.5');
96
+ }
91
97
 
92
- if (directionChange) {
93
- renderPoint(markers, pt1, 'orange', '1.5%', '0.5');
94
98
  }
95
99
 
96
100