toosoon-utils 4.1.9 → 4.2.1
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/README.md +97 -885
- package/lib/constants.d.ts +0 -1
- package/lib/constants.js +3 -1
- package/lib/extras/color-scale/ColorScale.d.ts +2 -2
- package/lib/extras/color-scale/ColorScale.js +2 -2
- package/lib/extras/curves/ArcCurve.d.ts +3 -3
- package/lib/extras/curves/ArcCurve.js +5 -4
- package/lib/extras/curves/CatmullRomCurve.d.ts +20 -5
- package/lib/extras/curves/CatmullRomCurve.js +23 -5
- package/lib/extras/curves/CatmullRomCurve3.d.ts +101 -0
- package/lib/extras/curves/CatmullRomCurve3.js +122 -0
- package/lib/extras/curves/CubicBezierCurve.d.ts +20 -5
- package/lib/extras/curves/CubicBezierCurve.js +23 -5
- package/lib/extras/curves/CubicBezierCurve3.d.ts +101 -0
- package/lib/extras/curves/CubicBezierCurve3.js +122 -0
- package/lib/extras/curves/Curve.d.ts +23 -24
- package/lib/extras/curves/Curve.js +19 -26
- package/lib/extras/curves/EllipseCurve.d.ts +25 -9
- package/lib/extras/curves/EllipseCurve.js +64 -15
- package/lib/extras/curves/LineCurve.d.ts +34 -10
- package/lib/extras/curves/LineCurve.js +35 -13
- package/lib/extras/curves/LineCurve3.d.ts +87 -0
- package/lib/extras/curves/LineCurve3.js +108 -0
- package/lib/extras/curves/PolylineCurve.d.ts +9 -8
- package/lib/extras/curves/PolylineCurve.js +6 -6
- package/lib/extras/curves/PolylineCurve3.d.ts +28 -0
- package/lib/extras/curves/PolylineCurve3.js +39 -0
- package/lib/extras/curves/QuadraticBezierCurve.d.ts +19 -5
- package/lib/extras/curves/QuadraticBezierCurve.js +22 -5
- package/lib/extras/curves/QuadraticBezierCurve3.d.ts +84 -0
- package/lib/extras/curves/QuadraticBezierCurve3.js +102 -0
- package/lib/extras/curves/SplineCurve.d.ts +12 -8
- package/lib/extras/curves/SplineCurve.js +9 -6
- package/lib/extras/curves/SplineCurve3.d.ts +28 -0
- package/lib/extras/curves/SplineCurve3.js +41 -0
- package/lib/extras/curves/index.d.ts +6 -0
- package/lib/extras/curves/index.js +6 -0
- package/lib/extras/geometry/Matrix2.d.ts +1 -0
- package/lib/extras/geometry/Matrix2.js +230 -0
- package/lib/extras/geometry/Matrix4.d.ts +1 -0
- package/lib/extras/geometry/Matrix4.js +632 -0
- package/lib/extras/geometry/Vector.d.ts +42 -0
- package/lib/extras/geometry/Vector.js +1 -0
- package/lib/extras/geometry/Vector2.d.ts +480 -0
- package/lib/extras/geometry/Vector2.js +709 -0
- package/lib/extras/geometry/Vector3.d.ts +486 -0
- package/lib/extras/geometry/Vector3.js +765 -0
- package/lib/extras/geometry/index.d.ts +3 -0
- package/lib/extras/geometry/index.js +2 -0
- package/lib/extras/paths/Path.d.ts +24 -18
- package/lib/extras/paths/Path.js +48 -35
- package/lib/extras/paths/PathContext.d.ts +97 -67
- package/lib/extras/paths/PathContext.js +326 -183
- package/lib/extras/paths/PathSVG.d.ts +43 -31
- package/lib/extras/paths/PathSVG.js +69 -56
- package/lib/extras/paths/index.d.ts +1 -1
- package/lib/geometry.d.ts +0 -135
- package/lib/geometry.js +1 -219
- package/lib/maths.d.ts +54 -22
- package/lib/maths.js +77 -27
- package/lib/random.d.ts +12 -16
- package/lib/random.js +19 -27
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types.d.ts +43 -1
- package/package.json +2 -1
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { EPSILON, PI } from '../../constants';
|
|
2
|
-
import {
|
|
3
|
-
import LineCurve from '../curves
|
|
4
|
-
import
|
|
5
|
-
import QuadraticBezierCurve from '../curves/QuadraticBezierCurve';
|
|
6
|
-
import CubicBezierCurve from '../curves/CubicBezierCurve';
|
|
7
|
-
import CatmullRomCurve from '../curves/CatmullRomCurve';
|
|
8
|
-
import SplineCurve from '../curves/SplineCurve';
|
|
9
|
-
import EllipseCurve from '../curves/EllipseCurve';
|
|
10
|
-
import ArcCurve from '../curves/ArcCurve';
|
|
2
|
+
import { angle, distance, toDegrees } from '../../geometry';
|
|
3
|
+
import { LineCurve, PolylineCurve, QuadraticBezierCurve, CubicBezierCurve, CatmullRomCurve, SplineCurve, EllipseCurve, ArcCurve } from '../curves';
|
|
4
|
+
import { Vector2 } from '../geometry';
|
|
11
5
|
import Path from './Path';
|
|
12
6
|
/**
|
|
13
7
|
* Utility class for manipulating connected curves providing methods similar to the 2D Canvas API
|
|
@@ -15,47 +9,53 @@ import Path from './Path';
|
|
|
15
9
|
* @exports
|
|
16
10
|
* @class PathContext
|
|
17
11
|
* @extends Path
|
|
12
|
+
* @implements CanvasRenderingContext2D
|
|
18
13
|
*/
|
|
19
14
|
export default class PathContext extends Path {
|
|
20
15
|
/**
|
|
21
|
-
* Path current
|
|
16
|
+
* Path current position
|
|
22
17
|
*/
|
|
23
|
-
currentPosition =
|
|
18
|
+
currentPosition = new Vector2(NaN, NaN);
|
|
24
19
|
/**
|
|
25
|
-
*
|
|
20
|
+
* Path current transformation matrix
|
|
21
|
+
*/
|
|
22
|
+
currentTransform = new DOMMatrix();
|
|
23
|
+
_transformStack = [];
|
|
24
|
+
/**
|
|
25
|
+
* Create a path from a given list of points
|
|
26
26
|
*
|
|
27
|
-
* @param {
|
|
28
|
-
* @param {Point[]} type Type of curve used for creating the path
|
|
27
|
+
* @param {Point2[]} points Array of points defining the path
|
|
29
28
|
* @return {this}
|
|
30
29
|
*/
|
|
31
|
-
setFromPoints(points
|
|
30
|
+
setFromPoints(points) {
|
|
32
31
|
this.moveTo(...points[0]);
|
|
33
|
-
if (type === 'polyline')
|
|
34
|
-
return this.polylineTo(points);
|
|
35
|
-
if (type === 'spline')
|
|
36
|
-
return this.splineTo(points);
|
|
37
32
|
for (let i = 1, l = points.length; i < l; i++) {
|
|
38
33
|
this.lineTo(...points[i]);
|
|
39
34
|
}
|
|
40
35
|
return this;
|
|
41
36
|
}
|
|
42
37
|
/**
|
|
43
|
-
* Begin
|
|
44
|
-
*
|
|
38
|
+
* Begin this path
|
|
39
|
+
*
|
|
40
|
+
* @return {this}
|
|
45
41
|
*/
|
|
46
42
|
beginPath() {
|
|
47
43
|
this._setCurrentPosition(NaN, NaN);
|
|
44
|
+
return this;
|
|
48
45
|
}
|
|
49
46
|
/**
|
|
50
|
-
* Draw a line from the ending position to the beginning position of
|
|
51
|
-
* Add an instance of {@link LineCurve} to
|
|
47
|
+
* Draw a line from the ending position to the beginning position of this path
|
|
48
|
+
* Add an instance of {@link LineCurve} to this path
|
|
49
|
+
*
|
|
50
|
+
* @return {this}
|
|
52
51
|
*/
|
|
53
52
|
closePath() {
|
|
54
|
-
const startPoint = this.
|
|
55
|
-
const endPoint = this.
|
|
56
|
-
if (!
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
const startPoint = this.subpaths[0]?.getPoint(0);
|
|
54
|
+
const endPoint = this.subpaths[this.subpaths.length - 1]?.getPoint(1);
|
|
55
|
+
if (!startPoint.equals(endPoint)) {
|
|
56
|
+
const curve = new LineCurve(endPoint.x, endPoint.y, startPoint.x, startPoint.y);
|
|
57
|
+
this.add(curve);
|
|
58
|
+
}
|
|
59
59
|
return this;
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
@@ -66,91 +66,46 @@ export default class PathContext extends Path {
|
|
|
66
66
|
* @return {this}
|
|
67
67
|
*/
|
|
68
68
|
moveTo(x, y) {
|
|
69
|
-
this.
|
|
69
|
+
const [tX, tY] = this._transformPoint([x, y]);
|
|
70
|
+
this._setCurrentPosition(tX, tY);
|
|
70
71
|
return this;
|
|
71
72
|
}
|
|
72
73
|
/**
|
|
73
74
|
* Draw a line from the current position to the position specified by `x` and `y`
|
|
74
|
-
* Add an instance of {@link LineCurve} to
|
|
75
|
+
* Add an instance of {@link LineCurve} to this path
|
|
75
76
|
*
|
|
76
77
|
* @param {number} x X-axis coordinate of the point
|
|
77
78
|
* @param {number} y Y-axis coordinate of the point
|
|
78
79
|
* @return {this}
|
|
79
80
|
*/
|
|
80
81
|
lineTo(x, y) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
const [tX, tY] = this._transformPoint([x, y]);
|
|
83
|
+
if (!this._hasCurrentPosition())
|
|
84
|
+
return this._setCurrentPosition(tX, tY);
|
|
85
|
+
const curve = new LineCurve(this.currentPosition.x, this.currentPosition.y, tX, tY);
|
|
84
86
|
this.add(curve);
|
|
85
|
-
this._setCurrentPosition(
|
|
87
|
+
this._setCurrentPosition(tX, tY);
|
|
86
88
|
return this;
|
|
87
89
|
}
|
|
88
90
|
/**
|
|
89
|
-
* Draw a Polyline curve from the current position through
|
|
90
|
-
* Add an instance of {@link PolylineCurve} to
|
|
91
|
+
* Draw a Polyline curve from the current position through given points
|
|
92
|
+
* Add an instance of {@link PolylineCurve} to this path
|
|
91
93
|
*
|
|
92
|
-
* @param {
|
|
94
|
+
* @param {Point2[]} points Array of points defining the curve
|
|
93
95
|
* @returns {this}
|
|
94
96
|
*/
|
|
95
97
|
polylineTo(points) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
const tPoints = this._transformPoints(points);
|
|
99
|
+
if (!this._hasCurrentPosition())
|
|
100
|
+
this._setCurrentPosition(...tPoints[0]);
|
|
101
|
+
const curve = new PolylineCurve([this.currentPosition.toArray()].concat(tPoints));
|
|
99
102
|
this.add(curve);
|
|
100
|
-
this._setCurrentPosition(...
|
|
101
|
-
return this;
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Draw an Arc curve from the current position, tangential to the 2 segments created by both control points
|
|
105
|
-
* Add an instance of {@link ArcCurve} to the path
|
|
106
|
-
*
|
|
107
|
-
* @param {number} x1 X-axis coordinate of the first control point
|
|
108
|
-
* @param {number} y1 Y-axis coordinate of the first control point
|
|
109
|
-
* @param {number} x2 X-axis coordinate of the second control point
|
|
110
|
-
* @param {number} y2 Y-axis coordinate of the second control point
|
|
111
|
-
* @param {number} radius Arc radius (Must be non-negative)
|
|
112
|
-
* @returns {this}
|
|
113
|
-
*/
|
|
114
|
-
arcTo(x1, y1, x2, y2, radius) {
|
|
115
|
-
if (this.currentPosition.every(isNaN))
|
|
116
|
-
this.moveTo(x1, y1);
|
|
117
|
-
const x0 = this.currentPosition[0];
|
|
118
|
-
const y0 = this.currentPosition[1];
|
|
119
|
-
if (radius < 0) {
|
|
120
|
-
throw new Error(`IndexSizeError: Failed to execute 'arcTo' on 'PathContext': The radius provided (${radius}) is negative.`);
|
|
121
|
-
}
|
|
122
|
-
if (isCoincident(x0, y0, x1, y1)) {
|
|
123
|
-
return this;
|
|
124
|
-
}
|
|
125
|
-
if (isCollinear(x0, y0, x1, y1, x2, y2)) {
|
|
126
|
-
return this.lineTo(x2, y2);
|
|
127
|
-
}
|
|
128
|
-
const l01 = distance(x0, y0, x1, y1);
|
|
129
|
-
const l21 = distance(x2, y2, x1, y1);
|
|
130
|
-
const [t1x, t1y] = line(radius / l01, x1, y1, x0, y0);
|
|
131
|
-
const [t2x, t2y] = line(radius / l21, x1, y1, x2, y2);
|
|
132
|
-
const v1x = (t1x - x1) / radius;
|
|
133
|
-
const v1y = (t1y - y1) / radius;
|
|
134
|
-
const v2x = (t2x - x1) / radius;
|
|
135
|
-
const v2y = (t2y - y1) / radius;
|
|
136
|
-
const normalX = v2y - v1y;
|
|
137
|
-
const normalY = v1x - v2x;
|
|
138
|
-
const cx = x1 + normalX * radius;
|
|
139
|
-
const cy = y1 + normalY * radius;
|
|
140
|
-
const startAngle = Math.atan2(t1y - cy, t1x - cx);
|
|
141
|
-
const endAngle = Math.atan2(t2y - cy, t2x - cx);
|
|
142
|
-
let clockwise = endAngle - startAngle > 0;
|
|
143
|
-
if (clockwise && endAngle - startAngle > PI)
|
|
144
|
-
clockwise = false;
|
|
145
|
-
else if (!clockwise && startAngle - endAngle > PI)
|
|
146
|
-
clockwise = true;
|
|
147
|
-
this.lineTo(t1x, t1y);
|
|
148
|
-
this.arc(cx, cy, radius, startAngle, endAngle, !clockwise);
|
|
103
|
+
this._setCurrentPosition(...tPoints[tPoints.length - 1]);
|
|
149
104
|
return this;
|
|
150
105
|
}
|
|
151
106
|
/**
|
|
152
107
|
* Draw a Quadratic Bézier curve from the current position to the end point specified by `x` and `y`, using the control point specified by `cpx` and `cpy`
|
|
153
|
-
* Add an instance of {@link QuadraticBezierCurve} to
|
|
108
|
+
* Add an instance of {@link QuadraticBezierCurve} to this path
|
|
154
109
|
*
|
|
155
110
|
* @param {number} cpx X-axis coordinate of the control point
|
|
156
111
|
* @param {number} cpy Y-axis coordinate of the control point
|
|
@@ -159,16 +114,18 @@ export default class PathContext extends Path {
|
|
|
159
114
|
* @return {this}
|
|
160
115
|
*/
|
|
161
116
|
quadraticCurveTo(cpx, cpy, x2, y2) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
117
|
+
const [tCpx, tCpy] = this._transformPoint([cpx, cpy]);
|
|
118
|
+
const [tX2, tY2] = this._transformPoint([x2, y2]);
|
|
119
|
+
if (!this._hasCurrentPosition())
|
|
120
|
+
this._setCurrentPosition(tCpx, tCpy);
|
|
121
|
+
const curve = new QuadraticBezierCurve(this.currentPosition.x, this.currentPosition.y, tCpx, tCpy, tX2, tY2);
|
|
165
122
|
this.add(curve);
|
|
166
|
-
this._setCurrentPosition(
|
|
123
|
+
this._setCurrentPosition(tX2, tY2);
|
|
167
124
|
return this;
|
|
168
125
|
}
|
|
169
126
|
/**
|
|
170
127
|
* Draw a Cubic Bézier curve from the current position to the end point specified by `x` and `y`, using the control point specified by (`cp1x`, `cp1y`) and (`cp2x`, `cp2y`)
|
|
171
|
-
* Add an instance of {@link CubicBezierCurve} to
|
|
128
|
+
* Add an instance of {@link CubicBezierCurve} to this path
|
|
172
129
|
*
|
|
173
130
|
* @param {number} cp1x X-axis coordinate of the first control point
|
|
174
131
|
* @param {number} cp1y Y-axis coordinate of the first control point
|
|
@@ -179,16 +136,19 @@ export default class PathContext extends Path {
|
|
|
179
136
|
* @return {this}
|
|
180
137
|
*/
|
|
181
138
|
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x2, y2) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const
|
|
139
|
+
const [tCp1x, tCp1y] = this._transformPoint([cp1x, cp1y]);
|
|
140
|
+
const [tCp2x, tCp2y] = this._transformPoint([cp2x, cp2y]);
|
|
141
|
+
const [tX2, tY2] = this._transformPoint([x2, y2]);
|
|
142
|
+
if (!this._hasCurrentPosition())
|
|
143
|
+
this._setCurrentPosition(tCp1x, tCp1y);
|
|
144
|
+
const curve = new CubicBezierCurve(this.currentPosition.x, this.currentPosition.y, tCp1x, tCp1y, tCp2x, tCp2y, tX2, tY2);
|
|
185
145
|
this.add(curve);
|
|
186
|
-
this._setCurrentPosition(
|
|
146
|
+
this._setCurrentPosition(tX2, tY2);
|
|
187
147
|
return this;
|
|
188
148
|
}
|
|
189
149
|
/**
|
|
190
150
|
* Draw a Catmull-Rom curve from the current position to the end point specified by `x` and `y`, using the control points specified by (`cp1x`, `cp1y`) and (`cp2x`, `cp2y`)
|
|
191
|
-
* Add an instance of {@link CatmullRomCurve} to
|
|
151
|
+
* Add an instance of {@link CatmullRomCurve} to this path
|
|
192
152
|
*
|
|
193
153
|
* @param {number} cp1x X-axis coordinate of the first control point
|
|
194
154
|
* @param {number} cp1y Y-axis coordinate of the first control point
|
|
@@ -199,85 +159,143 @@ export default class PathContext extends Path {
|
|
|
199
159
|
* @return {this}
|
|
200
160
|
*/
|
|
201
161
|
catmullRomCurveTo(cp1x, cp1y, cp2x, cp2y, x2, y2) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const
|
|
162
|
+
const [tCp1x, tCp1y] = this._transformPoint([cp1x, cp1y]);
|
|
163
|
+
const [tCp2x, tCp2y] = this._transformPoint([cp2x, cp2y]);
|
|
164
|
+
const [tX2, tY2] = this._transformPoint([x2, y2]);
|
|
165
|
+
if (!this._hasCurrentPosition())
|
|
166
|
+
this._setCurrentPosition(tCp1x, tCp1y);
|
|
167
|
+
const curve = new CatmullRomCurve(this.currentPosition.x, this.currentPosition.y, tCp1x, tCp1y, tCp2x, tCp2y, tX2, tY2);
|
|
205
168
|
this.add(curve);
|
|
206
|
-
this._setCurrentPosition(
|
|
169
|
+
this._setCurrentPosition(tX2, tY2);
|
|
207
170
|
return this;
|
|
208
171
|
}
|
|
209
172
|
/**
|
|
210
|
-
* Draw a Spline curve from the current position through
|
|
211
|
-
* Add an instance of {@link SplineCurve} to
|
|
173
|
+
* Draw a Spline curve from the current position through given points
|
|
174
|
+
* Add an instance of {@link SplineCurve} to this path
|
|
212
175
|
*
|
|
213
|
-
* @param {
|
|
176
|
+
* @param {Point2[]} points Array of points defining the curve
|
|
214
177
|
* @return {this}
|
|
215
178
|
*/
|
|
216
179
|
splineTo(points) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
180
|
+
const tPoints = this._transformPoints(points);
|
|
181
|
+
if (!this._hasCurrentPosition())
|
|
182
|
+
this._setCurrentPosition(...tPoints[0]);
|
|
183
|
+
const curve = new SplineCurve([this.currentPosition.toArray()].concat(tPoints));
|
|
220
184
|
this.add(curve);
|
|
221
|
-
this._setCurrentPosition(...
|
|
185
|
+
this._setCurrentPosition(...tPoints[tPoints.length - 1]);
|
|
222
186
|
return this;
|
|
223
187
|
}
|
|
224
188
|
/**
|
|
225
189
|
* Draw an Ellispe curve which is centered at (`cx`, `cy`) position
|
|
226
|
-
* Add an instance of {@link EllipseCurve} to
|
|
190
|
+
* Add an instance of {@link EllipseCurve} to this path
|
|
227
191
|
*
|
|
228
192
|
* @param {number} cx X-axis coordinate of the center of the circle
|
|
229
193
|
* @param {number} cy Y-axis coordinate of the center of the circle
|
|
230
194
|
* @param {number} rx X-radius of the ellipse
|
|
231
195
|
* @param {number} ry Y-radius of the ellipse
|
|
232
|
-
* @param {number}
|
|
233
|
-
* @param {number}
|
|
234
|
-
* @param {number}
|
|
196
|
+
* @param {number} rotation Rotation angle of the ellipse (in radians), counterclockwise from the positive X-axis
|
|
197
|
+
* @param {number} startAngle Start angle of the arc (in radians)
|
|
198
|
+
* @param {number} endAngle End angle of the arc (in radians)
|
|
235
199
|
* @param {boolean} [counterclockwise] Flag indicating the direction of the arc
|
|
236
200
|
* @return {this}
|
|
237
201
|
*/
|
|
238
202
|
ellipse(cx, cy, rx, ry, rotation, startAngle, endAngle, counterclockwise) {
|
|
239
|
-
const
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
203
|
+
const [tCx, tCy, tRx, tRy, tRotation] = this._transformEllipse(cx, cy, rx, ry, rotation);
|
|
204
|
+
const start = EllipseCurve.interpolate(0, tCx, tCy, tRx, tRy, tRotation, startAngle, endAngle, counterclockwise);
|
|
205
|
+
const end = EllipseCurve.interpolate(1, tCx, tCy, tRx, tRy, tRotation, startAngle, endAngle, counterclockwise);
|
|
206
|
+
if (!this._hasCurrentPosition())
|
|
207
|
+
this._setCurrentPosition(...start);
|
|
208
|
+
else if (!this.currentPosition.equals(start)) {
|
|
209
|
+
const curve = new LineCurve(this.currentPosition.x, this.currentPosition.y, ...start);
|
|
210
|
+
this.add(curve);
|
|
211
|
+
}
|
|
212
|
+
if (tRx <= EPSILON && tRy <= EPSILON)
|
|
246
213
|
return this;
|
|
247
|
-
const curve = new EllipseCurve(
|
|
214
|
+
const curve = new EllipseCurve(tCx, tCy, tRx, tRy, tRotation, startAngle, endAngle, counterclockwise);
|
|
248
215
|
this.add(curve);
|
|
249
216
|
this._setCurrentPosition(...end);
|
|
250
217
|
return this;
|
|
251
218
|
}
|
|
252
219
|
/**
|
|
253
220
|
* Draw an Arc curve which is centered at (`cx`, `cy`) position
|
|
254
|
-
* Add an instance of {@link ArcCurve} to
|
|
221
|
+
* Add an instance of {@link ArcCurve} to this path
|
|
255
222
|
*
|
|
256
223
|
* @param {number} cx X-axis coordinate of the center of the circle
|
|
257
224
|
* @param {number} cy Y-axis coordinate of the center of the circle
|
|
258
225
|
* @param {number} radius Radius of the circle
|
|
259
|
-
* @param {number}
|
|
260
|
-
* @param {number}
|
|
226
|
+
* @param {number} startAngle Start angle of the arc (in radians)
|
|
227
|
+
* @param {number} endAngle End angle of the arc (in radians)
|
|
261
228
|
* @param {boolean} [counterclockwise] Flag indicating the direction of the arc
|
|
262
229
|
* @return {this}
|
|
263
230
|
*/
|
|
264
231
|
arc(cx, cy, radius, startAngle, endAngle, counterclockwise) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (
|
|
232
|
+
if (!this._isUniform) {
|
|
233
|
+
return this.ellipse(cx, cy, radius, radius, 0, startAngle, endAngle, counterclockwise);
|
|
234
|
+
}
|
|
235
|
+
const [tCx, tCy, tRadius] = this._transformEllipse(cx, cy, radius, radius, 0);
|
|
236
|
+
const start = EllipseCurve.interpolate(0, tCx, tCy, tRadius, tRadius, 0, startAngle, endAngle, counterclockwise);
|
|
237
|
+
const end = EllipseCurve.interpolate(1, tCx, tCy, tRadius, tRadius, 0, startAngle, endAngle, counterclockwise);
|
|
238
|
+
if (!this._hasCurrentPosition())
|
|
239
|
+
this._setCurrentPosition(...start);
|
|
240
|
+
else if (!this.currentPosition.equals(start)) {
|
|
241
|
+
const curve = new LineCurve(this.currentPosition.x, this.currentPosition.y, ...start);
|
|
242
|
+
this.add(curve);
|
|
243
|
+
}
|
|
244
|
+
if (tRadius <= EPSILON)
|
|
272
245
|
return this;
|
|
273
|
-
const curve = new ArcCurve(
|
|
246
|
+
const curve = new ArcCurve(tCx, tCy, tRadius, startAngle, endAngle, counterclockwise);
|
|
274
247
|
this.add(curve);
|
|
275
248
|
this._setCurrentPosition(...end);
|
|
276
249
|
return this;
|
|
277
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Draw an Arc curve from the current position, tangential to the 2 segments created by both control points
|
|
253
|
+
* Add an instance of {@link ArcCurve} to this path
|
|
254
|
+
*
|
|
255
|
+
* @param {number} x1 X-axis coordinate of the first control point
|
|
256
|
+
* @param {number} y1 Y-axis coordinate of the first control point
|
|
257
|
+
* @param {number} x2 X-axis coordinate of the second control point
|
|
258
|
+
* @param {number} y2 Y-axis coordinate of the second control point
|
|
259
|
+
* @param {number} radius Arc radius (Must be non-negative)
|
|
260
|
+
* @returns {this}
|
|
261
|
+
*/
|
|
262
|
+
arcTo(x1, y1, x2, y2, radius) {
|
|
263
|
+
if (radius < 0) {
|
|
264
|
+
throw new Error(`IndexSizeError: Failed to execute 'arcTo' on 'PathContext': The radius provided (${radius}) is negative.`);
|
|
265
|
+
}
|
|
266
|
+
const [x0, y0] = this._inversePoint(this.currentPosition.toArray());
|
|
267
|
+
if (Vector2.equals([x0, y0], [x1, y1])) {
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
if (Vector2.collinear([x0, y0], [x1, y1], [x2, y2]) || radius === 0) {
|
|
271
|
+
return this.lineTo(x1, y1);
|
|
272
|
+
}
|
|
273
|
+
const l01 = distance(x0, y0, x1, y1);
|
|
274
|
+
const l21 = distance(x2, y2, x1, y1);
|
|
275
|
+
const [t1x, t1y] = LineCurve.interpolate(radius / l01, x1, y1, x0, y0);
|
|
276
|
+
const [t2x, t2y] = LineCurve.interpolate(radius / l21, x1, y1, x2, y2);
|
|
277
|
+
const v1x = (t1x - x1) / radius;
|
|
278
|
+
const v1y = (t1y - y1) / radius;
|
|
279
|
+
const v2x = (t2x - x1) / radius;
|
|
280
|
+
const v2y = (t2y - y1) / radius;
|
|
281
|
+
const normalX = v2y - v1y;
|
|
282
|
+
const normalY = v1x - v2x;
|
|
283
|
+
const cx = x1 + normalX * radius;
|
|
284
|
+
const cy = y1 + normalY * radius;
|
|
285
|
+
const startAngle = angle(cx, cy, t1x, t1y);
|
|
286
|
+
const endAngle = angle(cx, cy, t2x, t2y);
|
|
287
|
+
let clockwise = endAngle - startAngle > 0;
|
|
288
|
+
if (clockwise && endAngle - startAngle > PI)
|
|
289
|
+
clockwise = false;
|
|
290
|
+
else if (!clockwise && startAngle - endAngle > PI)
|
|
291
|
+
clockwise = true;
|
|
292
|
+
this.lineTo(t1x, t1y);
|
|
293
|
+
this.arc(cx, cy, radius, startAngle, endAngle, !clockwise);
|
|
294
|
+
return this;
|
|
295
|
+
}
|
|
278
296
|
/**
|
|
279
297
|
* Draw a rectangular path from the start position specified by `x` and `y` to the end position using `width` and `height`
|
|
280
|
-
* Add an instance of {@link PolylineCurve} to
|
|
298
|
+
* Add an instance of {@link PolylineCurve} to this path
|
|
281
299
|
*
|
|
282
300
|
* @param {number} x X-axis coordinate of the rectangle starting point
|
|
283
301
|
* @param {number} y Y-axis coordinate of the rectangle starting point
|
|
@@ -286,22 +304,19 @@ export default class PathContext extends Path {
|
|
|
286
304
|
* @return {this}
|
|
287
305
|
*/
|
|
288
306
|
rect(x, y, width, height) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
this.lineTo(x, y);
|
|
293
|
-
const curve = new PolylineCurve([
|
|
307
|
+
this.moveTo(x, y);
|
|
308
|
+
this.polylineTo([
|
|
309
|
+
[x, y],
|
|
294
310
|
[x + width, y],
|
|
295
311
|
[x + width, y + height],
|
|
296
312
|
[x, y + height],
|
|
297
313
|
[x, y]
|
|
298
314
|
]);
|
|
299
|
-
this.add(curve);
|
|
300
|
-
this._setCurrentPosition(x, y);
|
|
301
315
|
return this;
|
|
302
316
|
}
|
|
303
317
|
/**
|
|
304
318
|
* Draw a rounded rectangular path from the start position specified by `x` and `y` to the end position using `width` and `height`
|
|
319
|
+
* Add an instance of {@link Path} to this path
|
|
305
320
|
*
|
|
306
321
|
* @param {number} x X-axis coordinate of the rectangle starting point
|
|
307
322
|
* @param {number} y Y-axis coordinate of the rectangle starting point
|
|
@@ -311,6 +326,7 @@ export default class PathContext extends Path {
|
|
|
311
326
|
* @return {this}
|
|
312
327
|
*/
|
|
313
328
|
roundRect(x, y, width, height, radius) {
|
|
329
|
+
this.moveTo(x, y);
|
|
314
330
|
let topLeftRadius = typeof radius === 'number' ? radius : radius[0];
|
|
315
331
|
let topRightRadius = typeof radius === 'number' ? radius : radius[1] ?? radius[0];
|
|
316
332
|
let bottomRightRadius = typeof radius === 'number' ? radius : radius[2] ?? topLeftRadius;
|
|
@@ -320,52 +336,183 @@ export default class PathContext extends Path {
|
|
|
320
336
|
topRightRadius = Math.min(topRightRadius, maxRadius);
|
|
321
337
|
bottomRightRadius = Math.min(bottomRightRadius, maxRadius);
|
|
322
338
|
bottomLeftRadius = Math.min(bottomLeftRadius, maxRadius);
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
this.lineTo(x + topLeftRadius, y);
|
|
339
|
+
const curve = new PathContext({ autoClose: true });
|
|
340
|
+
curve.setTransform(this.getTransform());
|
|
341
|
+
this.add(curve);
|
|
327
342
|
// Top-Right corner
|
|
328
343
|
if (topRightRadius > 0) {
|
|
329
|
-
|
|
330
|
-
|
|
344
|
+
curve.lineTo(x + width - topRightRadius, y);
|
|
345
|
+
curve.arcTo(x + width, y, x + width, y + topRightRadius, topRightRadius);
|
|
331
346
|
}
|
|
332
347
|
else {
|
|
333
|
-
|
|
348
|
+
curve.lineTo(x + width, y);
|
|
334
349
|
}
|
|
335
350
|
// Bottom-Right corner
|
|
336
351
|
if (bottomRightRadius > 0) {
|
|
337
|
-
|
|
338
|
-
|
|
352
|
+
curve.lineTo(x + width, y + height - bottomRightRadius);
|
|
353
|
+
curve.arcTo(x + width, y + height, x + width - bottomRightRadius, y + height, bottomRightRadius);
|
|
339
354
|
}
|
|
340
355
|
else {
|
|
341
|
-
|
|
356
|
+
curve.lineTo(x + width, y + height);
|
|
342
357
|
}
|
|
343
358
|
// Bottom-Left corner
|
|
344
359
|
if (bottomLeftRadius > 0) {
|
|
345
|
-
|
|
346
|
-
|
|
360
|
+
curve.lineTo(x + bottomLeftRadius, y + height);
|
|
361
|
+
curve.arcTo(x, y + height, x, y + height - bottomLeftRadius, bottomLeftRadius);
|
|
347
362
|
}
|
|
348
363
|
else {
|
|
349
|
-
|
|
364
|
+
curve.lineTo(x, y + height);
|
|
350
365
|
}
|
|
351
366
|
// Top-Left corner
|
|
352
367
|
if (topLeftRadius > 0) {
|
|
353
|
-
|
|
354
|
-
|
|
368
|
+
curve.lineTo(x, y + topLeftRadius);
|
|
369
|
+
curve.arcTo(x, y, x + topLeftRadius, y, topLeftRadius);
|
|
355
370
|
}
|
|
356
371
|
else {
|
|
357
|
-
|
|
372
|
+
curve.lineTo(x, y);
|
|
358
373
|
}
|
|
359
374
|
return this;
|
|
360
375
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
// public restore() {}
|
|
376
|
+
_hasCurrentPosition() {
|
|
377
|
+
return !isNaN(this.currentPosition.x) && !isNaN(this.currentPosition.y);
|
|
378
|
+
}
|
|
365
379
|
_setCurrentPosition(x, y) {
|
|
366
|
-
this.currentPosition
|
|
367
|
-
this
|
|
380
|
+
this.currentPosition.set(x, y);
|
|
381
|
+
return this;
|
|
382
|
+
}
|
|
383
|
+
setTransform(a, b, c, d, e, f) {
|
|
384
|
+
if (a instanceof DOMMatrix) {
|
|
385
|
+
const matrix = a;
|
|
386
|
+
this.currentTransform = new DOMMatrix([matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f]);
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
this.currentTransform = new DOMMatrix([a, b, c, d, e, f]);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
getTransform() {
|
|
393
|
+
const { a, b, c, d, e, f } = this.currentTransform;
|
|
394
|
+
return new DOMMatrix([a, b, c, d, e, f]);
|
|
395
|
+
}
|
|
396
|
+
resetTransform() {
|
|
397
|
+
this.setTransform(1, 0, 0, 1, 0, 0);
|
|
398
|
+
}
|
|
399
|
+
transform(a, b, c, d, e, f) {
|
|
400
|
+
const matrix = new DOMMatrix([a, b, c, d, e, f]);
|
|
401
|
+
this.currentTransform = this.currentTransform.multiply(matrix);
|
|
402
|
+
}
|
|
403
|
+
translate(x, y) {
|
|
404
|
+
this.currentTransform = this.currentTransform.translate(x, y);
|
|
405
|
+
}
|
|
406
|
+
rotate(angle) {
|
|
407
|
+
this.currentTransform = this.currentTransform.rotate(toDegrees(angle));
|
|
408
|
+
}
|
|
409
|
+
scale(x, y) {
|
|
410
|
+
this.currentTransform = this.currentTransform.scale(x, y);
|
|
411
|
+
}
|
|
412
|
+
save() {
|
|
413
|
+
this._transformStack.push(this.getTransform());
|
|
414
|
+
}
|
|
415
|
+
restore() {
|
|
416
|
+
if (this._transformStack.length > 0) {
|
|
417
|
+
this.currentTransform = this._transformStack.pop();
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
reset() {
|
|
421
|
+
this.resetTransform();
|
|
422
|
+
this._transformStack = [];
|
|
423
|
+
}
|
|
424
|
+
// ******************************************
|
|
425
|
+
// Path context transformations
|
|
426
|
+
// ******************************************
|
|
427
|
+
_transformPoint(point, matrix = this.currentTransform) {
|
|
428
|
+
if (this._isIdentity)
|
|
429
|
+
return point;
|
|
430
|
+
const { x, y } = matrix.transformPoint({ x: point[0], y: point[1] });
|
|
431
|
+
return [x, y];
|
|
368
432
|
}
|
|
433
|
+
_transformPoints(points, matrix) {
|
|
434
|
+
if (this._isIdentity)
|
|
435
|
+
return points;
|
|
436
|
+
return points.map((point) => this._transformPoint(point, matrix));
|
|
437
|
+
}
|
|
438
|
+
_transformVector(vector, matrix) {
|
|
439
|
+
if (this._isIdentity)
|
|
440
|
+
return vector;
|
|
441
|
+
const [x0, y0] = this._transformPoint([0, 0], matrix);
|
|
442
|
+
const [vx, vy] = this._transformPoint(vector, matrix);
|
|
443
|
+
return [vx - x0, vy - y0];
|
|
444
|
+
}
|
|
445
|
+
_transformEllipse(cx, cy, rx, ry, rotation, matrix) {
|
|
446
|
+
if (this._isIdentity)
|
|
447
|
+
return [cx, cy, rx, ry, rotation];
|
|
448
|
+
[cx, cy] = this._transformPoint([cx, cy], matrix);
|
|
449
|
+
const [rx1, ry1] = this._transformVector([rx, 0], matrix);
|
|
450
|
+
const [rx2, ry2] = this._transformVector([0, ry], matrix);
|
|
451
|
+
rx = Math.hypot(rx1, ry1);
|
|
452
|
+
ry = Math.hypot(rx2, ry2);
|
|
453
|
+
rotation += angle(0, 0, rx1, ry1);
|
|
454
|
+
return [cx, cy, rx, ry, rotation];
|
|
455
|
+
}
|
|
456
|
+
_inversePoint(point) {
|
|
457
|
+
return this._transformPoint(point, this.currentTransform.inverse());
|
|
458
|
+
}
|
|
459
|
+
get _translateX() {
|
|
460
|
+
return this.currentTransform.e;
|
|
461
|
+
}
|
|
462
|
+
get _translateY() {
|
|
463
|
+
return this.currentTransform.f;
|
|
464
|
+
}
|
|
465
|
+
get _scaleX() {
|
|
466
|
+
const { a, b } = this.currentTransform;
|
|
467
|
+
return Math.hypot(a, b);
|
|
468
|
+
}
|
|
469
|
+
get _scaleY() {
|
|
470
|
+
const { c, d } = this.currentTransform;
|
|
471
|
+
return Math.hypot(c, d);
|
|
472
|
+
}
|
|
473
|
+
get _rotation() {
|
|
474
|
+
const { a, b } = this.currentTransform;
|
|
475
|
+
return Math.atan2(b, a);
|
|
476
|
+
}
|
|
477
|
+
get _skewX() {
|
|
478
|
+
const { c, d } = this.currentTransform;
|
|
479
|
+
const cos = Math.cos(this._rotation);
|
|
480
|
+
const sin = Math.sin(this._rotation);
|
|
481
|
+
const m11 = c * cos + d * sin;
|
|
482
|
+
return Math.atan(m11 / this._scaleX);
|
|
483
|
+
}
|
|
484
|
+
get _skewY() {
|
|
485
|
+
const { a, b } = this.currentTransform;
|
|
486
|
+
const cos = Math.cos(this._rotation);
|
|
487
|
+
const sin = Math.sin(this._rotation);
|
|
488
|
+
const m21 = a * sin - b * cos;
|
|
489
|
+
return Math.atan(m21 / this._scaleY);
|
|
490
|
+
}
|
|
491
|
+
get _isTranslated() {
|
|
492
|
+
return Math.abs(this._translateX) > EPSILON || Math.abs(this._translateY) > EPSILON;
|
|
493
|
+
}
|
|
494
|
+
get _isScaled() {
|
|
495
|
+
return Math.abs(this._scaleX - 1) > EPSILON || Math.abs(this._scaleY - 1) > EPSILON;
|
|
496
|
+
}
|
|
497
|
+
get _isRotated() {
|
|
498
|
+
const { b, c } = this.currentTransform;
|
|
499
|
+
return Math.abs(b) > EPSILON || Math.abs(c) > EPSILON;
|
|
500
|
+
}
|
|
501
|
+
get _isSkewed() {
|
|
502
|
+
const { a, b, c, d } = this.currentTransform;
|
|
503
|
+
const angleX = Math.atan2(b, a);
|
|
504
|
+
const angleY = Math.atan2(-c, d);
|
|
505
|
+
return Math.abs(angleX - angleY) > EPSILON;
|
|
506
|
+
}
|
|
507
|
+
get _isUniform() {
|
|
508
|
+
return Math.abs(this._scaleX - this._scaleY) <= EPSILON;
|
|
509
|
+
}
|
|
510
|
+
get _isIdentity() {
|
|
511
|
+
return this.currentTransform.isIdentity;
|
|
512
|
+
}
|
|
513
|
+
// ******************************************
|
|
514
|
+
// Canvas 2D interface
|
|
515
|
+
// ******************************************
|
|
369
516
|
canvas;
|
|
370
517
|
fillStyle;
|
|
371
518
|
strokeStyle;
|
|
@@ -373,8 +520,11 @@ export default class PathContext extends Path {
|
|
|
373
520
|
lineCap;
|
|
374
521
|
lineJoin;
|
|
375
522
|
lineDashOffset;
|
|
523
|
+
setLineDash;
|
|
524
|
+
getLineDash;
|
|
376
525
|
fillText;
|
|
377
526
|
strokeText;
|
|
527
|
+
miterLimit;
|
|
378
528
|
fill;
|
|
379
529
|
stroke;
|
|
380
530
|
clearRect;
|
|
@@ -382,6 +532,9 @@ export default class PathContext extends Path {
|
|
|
382
532
|
strokeRect;
|
|
383
533
|
drawImage;
|
|
384
534
|
clip;
|
|
535
|
+
filter;
|
|
536
|
+
globalAlpha;
|
|
537
|
+
globalCompositeOperation;
|
|
385
538
|
createLinearGradient;
|
|
386
539
|
createRadialGradient;
|
|
387
540
|
createConicGradient;
|
|
@@ -402,29 +555,19 @@ export default class PathContext extends Path {
|
|
|
402
555
|
wordSpacing;
|
|
403
556
|
direction;
|
|
404
557
|
measureText;
|
|
405
|
-
filter;
|
|
406
|
-
globalAlpha;
|
|
407
|
-
globalCompositeOperation;
|
|
408
|
-
miterLimit;
|
|
409
|
-
save;
|
|
410
|
-
restore;
|
|
411
|
-
reset;
|
|
412
|
-
transform;
|
|
413
|
-
translate;
|
|
414
|
-
rotate;
|
|
415
|
-
scale;
|
|
416
|
-
getTransform;
|
|
417
|
-
setTransform;
|
|
418
|
-
resetTransform;
|
|
419
|
-
getLineDash;
|
|
420
|
-
setLineDash;
|
|
421
|
-
shadowBlur;
|
|
422
558
|
shadowColor;
|
|
559
|
+
shadowBlur;
|
|
423
560
|
shadowOffsetX;
|
|
424
561
|
shadowOffsetY;
|
|
425
562
|
drawFocusIfNeeded;
|
|
426
563
|
isPointInPath;
|
|
427
564
|
isPointInStroke;
|
|
428
|
-
getContextAttributes
|
|
429
|
-
|
|
565
|
+
getContextAttributes() {
|
|
566
|
+
return {
|
|
567
|
+
alpha: false
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
isContextLost() {
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
430
573
|
}
|