-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpathGenerator.js
86 lines (78 loc) · 2.71 KB
/
pathGenerator.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
class pathGenerator {
constructor() {
// The smoothing ratio
this.smoothing = 0.2;
}
/**
* Properties of a line
* @param {*} pointA (array) [x,y] coordinates
* @param {*} pointB (array) [x,y] coordinates
* @returns
*/
line(pointA, pointB) {
const lengthX = pointB[0] - pointA[0];
const lengthY = pointB[1] - pointA[1];
return {
length: Math.sqrt(Math.pow(lengthX, 2) + Math.pow(lengthY, 2)),
angle: Math.atan2(lengthY, lengthX),
};
}
/**
* Position of a control point
* @param {*} current (array) [x, y]: current point coordinates
* @param {*} previous (array) [x, y]: previous point coordinates
* @param {*} next (array) [x, y]: next point coordinates
* @param {*} reverse (boolean, optional): sets the direction
* @returns (array) [x,y]: a tuple of coordinates
*/
controlPoint(current, previous, next, reverse) {
// When 'current' is the first or last point of the array
// 'previous' or 'next' don't exist.
// Replace with 'current'
const p = previous || current;
const n = next || current;
// Properties of the opposed-line
const o = this.line(p, n);
// If is end-control-point, add PI to the angle to go backward
const angle = o.angle + (reverse ? Math.PI : 0);
const length = o.length * this.smoothing;
// The control point position is relative to the current point
const x = current[0] + Math.cos(angle) * length;
const y = current[1] + Math.sin(angle) * length;
return [x, y];
}
/**
* Create the bezier curve command
* @param {*} point (array) [x,y]: current point coordinates
* @param {*} i (integer): index of 'point' in the array 'a'
* @param {*} a (array): complete array of points coordinates
* @returns (string) 'C x2,y2 x1,y1 x,y': SVG cubic bezier C command
*/
bezierCommand(point, i, a) {
// start control point
const cps = this.controlPoint(a[i - 1], a[i - 2], point);
// end control point
const cpe = this.controlPoint(point, a[i - 1], a[i + 1], true);
return `C ${cps[0]},${cps[1]} ${cpe[0]},${cpe[1]} ${point[0]},${point[1]}`;
}
/**
* Create the svg d element of path
* @param {*} points (array): points coordinates
* @param {*} color string: color of stroke
* @param {boolean} fill bool: True = fill | false = dont fill
* @returns {string}: svg d element of path
*/
svgPath(points, color, fill = false) {
// build the d attributes by looping over the points
const d = points.reduce(
(acc, point, i, a) =>
i === 0
? fill
? ` L ${point[0]},${point[1]}`
: ` M ${point[0]},${point[1]}`
: `${acc} ${this.bezierCommand(point, i, a)}`,
""
);
return d;
}
}