1 | import { BasicPoint, Point } from './point';
|
2 |
|
3 | export 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 |
|
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 |
|
96 | private point(
|
97 | t: number,
|
98 | start: number,
|
99 | c1: number,
|
100 | c2: number,
|
101 | end: number,
|
102 | ): number {
|
103 |
|
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 | }
|