UNPKG

2.73 kBPlain TextView Raw
1import { BasicPoint, Point } from './point';
2
3export class Bezier {
4 public static fromPoints(
5 points: Point[],
6 widths: { start: number; end: number },
7 ): Bezier {
8 const c2 = this.calculateControlPoints(points[0], points[1], points[2]).c2;
9 const c3 = this.calculateControlPoints(points[1], points[2], points[3]).c1;
10
11 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
12 }
13
14 private static calculateControlPoints(
15 s1: BasicPoint,
16 s2: BasicPoint,
17 s3: BasicPoint,
18 ): {
19 c1: BasicPoint;
20 c2: BasicPoint;
21 } {
22 const dx1 = s1.x - s2.x;
23 const dy1 = s1.y - s2.y;
24 const dx2 = s2.x - s3.x;
25 const dy2 = s2.y - s3.y;
26
27 const m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
28 const m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
29
30 const l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
31 const l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
32
33 const dxm = m1.x - m2.x;
34 const dym = m1.y - m2.y;
35
36 const k = l2 / (l1 + l2);
37 const cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
38
39 const tx = s2.x - cm.x;
40 const ty = s2.y - cm.y;
41
42 return {
43 c1: new Point(m1.x + tx, m1.y + ty),
44 c2: new Point(m2.x + tx, m2.y + ty),
45 };
46 }
47
48 constructor(
49 public startPoint: Point,
50 public control2: BasicPoint,
51 public control1: BasicPoint,
52 public endPoint: Point,
53 public startWidth: number,
54 public endWidth: number,
55 ) {}
56
57 // Returns approximated length. Code taken from https://www.lemoda.net/maths/bezier-length/index.html.
58 public length(): number {
59 const steps = 10;
60 let length = 0;
61 let px;
62 let py;
63
64 for (let i = 0; i <= steps; i += 1) {
65 const t = i / steps;
66 const cx = this.point(
67 t,
68 this.startPoint.x,
69 this.control1.x,
70 this.control2.x,
71 this.endPoint.x,
72 );
73 const cy = this.point(
74 t,
75 this.startPoint.y,
76 this.control1.y,
77 this.control2.y,
78 this.endPoint.y,
79 );
80
81 if (i > 0) {
82 const xdiff = cx - (px as number);
83 const ydiff = cy - (py as number);
84
85 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
86 }
87
88 px = cx;
89 py = cy;
90 }
91
92 return length;
93 }
94
95 // Calculate parametric value of x or y given t and the four point coordinates of a cubic bezier curve.
96 private point(
97 t: number,
98 start: number,
99 c1: number,
100 c2: number,
101 end: number,
102 ): number {
103 // prettier-ignore
104 return ( start * (1.0 - t) * (1.0 - t) * (1.0 - t))
105 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
106 + (3.0 * c2 * (1.0 - t) * t * t)
107 + ( end * t * t * t);
108 }
109}