toosoon-utils 4.2.2 → 4.3.0

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.
Files changed (42) hide show
  1. package/README.md +499 -575
  2. package/lib/colors.d.ts +147 -66
  3. package/lib/colors.js +149 -63
  4. package/lib/constants.js +1 -1
  5. package/lib/dom.d.ts +1 -1
  6. package/lib/dom.js +1 -1
  7. package/lib/extras/colors/Color.d.ts +406 -0
  8. package/lib/extras/colors/Color.js +546 -0
  9. package/lib/extras/colors/ColorPalette.d.ts +105 -0
  10. package/lib/extras/colors/ColorPalette.js +124 -0
  11. package/lib/extras/colors/ColorScale.d.ts +257 -0
  12. package/lib/extras/colors/ColorScale.js +347 -0
  13. package/lib/extras/colors/_ColorScale.d.ts +62 -0
  14. package/lib/extras/colors/_ColorScale.js +156 -0
  15. package/lib/extras/colors/index.d.ts +3 -0
  16. package/lib/extras/colors/index.js +3 -0
  17. package/lib/extras/frame-rate/FrameRate.d.ts +1 -1
  18. package/lib/extras/frame-rate/FrameRate.js +2 -2
  19. package/lib/extras/geometry/Vector.d.ts +1 -1
  20. package/lib/extras/geometry/Vector2.d.ts +24 -11
  21. package/lib/extras/geometry/Vector2.js +41 -23
  22. package/lib/extras/geometry/Vector3.d.ts +12 -5
  23. package/lib/extras/geometry/Vector3.js +23 -10
  24. package/lib/extras/paths/Path.d.ts +3 -3
  25. package/lib/extras/paths/Path.js +10 -10
  26. package/lib/extras/paths/PathContext.d.ts +7 -17
  27. package/lib/extras/paths/PathContext.js +122 -144
  28. package/lib/extras/paths/PathSVG.d.ts +31 -25
  29. package/lib/extras/paths/PathSVG.js +36 -39
  30. package/lib/extras/paths/index.d.ts +1 -1
  31. package/lib/geometry.js +1 -1
  32. package/lib/maths.d.ts +19 -13
  33. package/lib/maths.js +23 -17
  34. package/lib/prng.d.ts +4 -4
  35. package/lib/prng.js +4 -4
  36. package/lib/random.d.ts +4 -4
  37. package/lib/random.js +4 -4
  38. package/lib/strings.d.ts +14 -8
  39. package/lib/strings.js +14 -8
  40. package/lib/tsconfig.tsbuildinfo +1 -1
  41. package/lib/types.d.ts +15 -8
  42. package/package.json +14 -14
@@ -1,5 +1,5 @@
1
- import { EPSILON, PI, TWO_PI } from '../../constants';
2
- import { angle, distance, toDegrees } from '../../geometry';
1
+ import { EPSILON } from '../../constants';
2
+ import { toDegrees } from '../../geometry';
3
3
  import { LineCurve, PolylineCurve, QuadraticBezierCurve, CubicBezierCurve, CatmullRomCurve, SplineCurve, EllipseCurve, ArcCurve } from '../curves';
4
4
  import { Vector2 } from '../geometry';
5
5
  import Path from './Path';
@@ -12,14 +12,8 @@ import Path from './Path';
12
12
  * @implements CanvasRenderingContext2D
13
13
  */
14
14
  export default class PathContext extends Path {
15
- /**
16
- * Path current position
17
- */
18
- currentPosition = new Vector2(NaN, NaN);
19
- /**
20
- * Path current transformation matrix
21
- */
22
- currentTransform = new DOMMatrix();
15
+ _currentPosition = new Vector2(NaN, NaN);
16
+ _currentTransform = new DOMMatrix();
23
17
  _transformStack = [];
24
18
  /**
25
19
  * Create a path from a given list of points
@@ -50,8 +44,8 @@ export default class PathContext extends Path {
50
44
  * @return {this}
51
45
  */
52
46
  closePath() {
53
- const startPoint = this.subpaths[0]?.getPoint(0);
54
- const endPoint = this.subpaths[this.subpaths.length - 1]?.getPoint(1);
47
+ const startPoint = this.curves[0]?.getPoint(0);
48
+ const endPoint = this.curves[this.curves.length - 1]?.getPoint(1);
55
49
  if (!startPoint.equals(endPoint)) {
56
50
  const curve = new LineCurve(endPoint.x, endPoint.y, startPoint.x, startPoint.y);
57
51
  this.add(curve);
@@ -66,8 +60,8 @@ export default class PathContext extends Path {
66
60
  * @return {this}
67
61
  */
68
62
  moveTo(x, y) {
69
- const [tX, tY] = this._transformPoint([x, y]);
70
- this._setCurrentPosition(tX, tY);
63
+ [x, y] = this._transformPoint([x, y]);
64
+ this._setCurrentPosition(x, y);
71
65
  return this;
72
66
  }
73
67
  /**
@@ -79,12 +73,12 @@ export default class PathContext extends Path {
79
73
  * @return {this}
80
74
  */
81
75
  lineTo(x, y) {
82
- const [tX, tY] = this._transformPoint([x, y]);
76
+ [x, y] = this._transformPoint([x, y]);
83
77
  if (!this._hasCurrentPosition())
84
- return this._setCurrentPosition(tX, tY);
85
- const curve = new LineCurve(this.currentPosition.x, this.currentPosition.y, tX, tY);
78
+ return this._setCurrentPosition(x, y);
79
+ const curve = new LineCurve(this._currentPosition.x, this._currentPosition.y, x, y);
86
80
  this.add(curve);
87
- this._setCurrentPosition(tX, tY);
81
+ this._setCurrentPosition(x, y);
88
82
  return this;
89
83
  }
90
84
  /**
@@ -95,12 +89,12 @@ export default class PathContext extends Path {
95
89
  * @returns {this}
96
90
  */
97
91
  polylineTo(points) {
98
- const tPoints = this._transformPoints(points);
92
+ points = this._transformPoints(points);
99
93
  if (!this._hasCurrentPosition())
100
- this._setCurrentPosition(...tPoints[0]);
101
- const curve = new PolylineCurve([this.currentPosition.toArray()].concat(tPoints));
94
+ this._setCurrentPosition(...points[0]);
95
+ const curve = new PolylineCurve([this._currentPosition.toArray()].concat(points));
102
96
  this.add(curve);
103
- this._setCurrentPosition(...tPoints[tPoints.length - 1]);
97
+ this._setCurrentPosition(...points[points.length - 1]);
104
98
  return this;
105
99
  }
106
100
  /**
@@ -114,13 +108,13 @@ export default class PathContext extends Path {
114
108
  * @return {this}
115
109
  */
116
110
  quadraticCurveTo(cpx, cpy, x2, y2) {
117
- const [tCpx, tCpy] = this._transformPoint([cpx, cpy]);
118
- const [tX2, tY2] = this._transformPoint([x2, y2]);
111
+ [cpx, cpy] = this._transformPoint([cpx, cpy]);
112
+ [x2, y2] = this._transformPoint([x2, y2]);
119
113
  if (!this._hasCurrentPosition())
120
- this._setCurrentPosition(tCpx, tCpy);
121
- const curve = new QuadraticBezierCurve(this.currentPosition.x, this.currentPosition.y, tCpx, tCpy, tX2, tY2);
114
+ this._setCurrentPosition(cpx, cpy);
115
+ const curve = new QuadraticBezierCurve(this._currentPosition.x, this._currentPosition.y, cpx, cpy, x2, y2);
122
116
  this.add(curve);
123
- this._setCurrentPosition(tX2, tY2);
117
+ this._setCurrentPosition(x2, y2);
124
118
  return this;
125
119
  }
126
120
  /**
@@ -136,14 +130,14 @@ export default class PathContext extends Path {
136
130
  * @return {this}
137
131
  */
138
132
  bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x2, y2) {
139
- const [tCp1x, tCp1y] = this._transformPoint([cp1x, cp1y]);
140
- const [tCp2x, tCp2y] = this._transformPoint([cp2x, cp2y]);
141
- const [tX2, tY2] = this._transformPoint([x2, y2]);
133
+ [cp1x, cp1y] = this._transformPoint([cp1x, cp1y]);
134
+ [cp2x, cp2y] = this._transformPoint([cp2x, cp2y]);
135
+ [x2, y2] = this._transformPoint([x2, y2]);
142
136
  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);
137
+ this._setCurrentPosition(cp1x, cp1y);
138
+ const curve = new CubicBezierCurve(this._currentPosition.x, this._currentPosition.y, cp1x, cp1y, cp2x, cp2y, x2, y2);
145
139
  this.add(curve);
146
- this._setCurrentPosition(tX2, tY2);
140
+ this._setCurrentPosition(x2, y2);
147
141
  return this;
148
142
  }
149
143
  /**
@@ -159,14 +153,14 @@ export default class PathContext extends Path {
159
153
  * @return {this}
160
154
  */
161
155
  catmullRomCurveTo(cp1x, cp1y, cp2x, cp2y, x2, y2) {
162
- const [tCp1x, tCp1y] = this._transformPoint([cp1x, cp1y]);
163
- const [tCp2x, tCp2y] = this._transformPoint([cp2x, cp2y]);
164
- const [tX2, tY2] = this._transformPoint([x2, y2]);
156
+ [cp1x, cp1y] = this._transformPoint([cp1x, cp1y]);
157
+ [cp2x, cp2y] = this._transformPoint([cp2x, cp2y]);
158
+ [x2, y2] = this._transformPoint([x2, y2]);
165
159
  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);
160
+ this._setCurrentPosition(cp1x, cp1y);
161
+ const curve = new CatmullRomCurve(this._currentPosition.x, this._currentPosition.y, cp1x, cp1y, cp2x, cp2y, x2, y2);
168
162
  this.add(curve);
169
- this._setCurrentPosition(tX2, tY2);
163
+ this._setCurrentPosition(x2, y2);
170
164
  return this;
171
165
  }
172
166
  /**
@@ -177,12 +171,12 @@ export default class PathContext extends Path {
177
171
  * @return {this}
178
172
  */
179
173
  splineTo(points) {
180
- const tPoints = this._transformPoints(points);
174
+ points = this._transformPoints(points);
181
175
  if (!this._hasCurrentPosition())
182
- this._setCurrentPosition(...tPoints[0]);
183
- const curve = new SplineCurve([this.currentPosition.toArray()].concat(tPoints));
176
+ this._setCurrentPosition(...points[0]);
177
+ const curve = new SplineCurve([this._currentPosition.toArray()].concat(points));
184
178
  this.add(curve);
185
- this._setCurrentPosition(...tPoints[tPoints.length - 1]);
179
+ this._setCurrentPosition(...points[points.length - 1]);
186
180
  return this;
187
181
  }
188
182
  /**
@@ -200,18 +194,16 @@ export default class PathContext extends Path {
200
194
  * @return {this}
201
195
  */
202
196
  ellipse(cx, cy, rx, ry, rotation, startAngle, endAngle, counterclockwise) {
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);
197
+ [cx, cy, rx, ry, rotation] = this._transformEllipse(cx, cy, rx, ry, rotation);
198
+ const start = EllipseCurve.interpolate(0, cx, cy, rx, ry, rotation, startAngle, endAngle, counterclockwise);
199
+ const end = EllipseCurve.interpolate(1, cx, cy, rx, ry, rotation, startAngle, endAngle, counterclockwise);
200
+ if (this._hasCurrentPosition() && !this._currentPosition.equals(start)) {
201
+ const line = new LineCurve(this._currentPosition.x, this._currentPosition.y, ...start);
202
+ this.add(line);
211
203
  }
212
- if (tRx <= EPSILON && tRy <= EPSILON)
204
+ if (rx <= EPSILON && ry <= EPSILON)
213
205
  return this;
214
- const curve = new EllipseCurve(tCx, tCy, tRx, tRy, tRotation, startAngle, endAngle, counterclockwise);
206
+ const curve = new EllipseCurve(cx, cy, rx, ry, rotation, startAngle, endAngle, counterclockwise);
215
207
  this.add(curve);
216
208
  this._setCurrentPosition(...end);
217
209
  return this;
@@ -232,25 +224,23 @@ export default class PathContext extends Path {
232
224
  if (!this._isUniform || this._isRotated) {
233
225
  return this.ellipse(cx, cy, radius, radius, 0, startAngle, endAngle, counterclockwise);
234
226
  }
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);
227
+ [cx, cy, radius] = this._transformEllipse(cx, cy, radius, radius, 0);
228
+ const start = EllipseCurve.interpolate(0, cx, cy, radius, radius, 0, startAngle, endAngle, counterclockwise);
229
+ const end = EllipseCurve.interpolate(1, cx, cy, radius, radius, 0, startAngle, endAngle, counterclockwise);
230
+ if (this._hasCurrentPosition() && !this._currentPosition.equals(start)) {
231
+ const line = new LineCurve(this._currentPosition.x, this._currentPosition.y, ...start);
232
+ this.add(line);
243
233
  }
244
- if (tRadius <= EPSILON)
234
+ if (radius <= EPSILON)
245
235
  return this;
246
- const curve = new ArcCurve(tCx, tCy, tRadius, startAngle, endAngle, counterclockwise);
236
+ const curve = new ArcCurve(cx, cy, radius, startAngle, endAngle, counterclockwise);
247
237
  this.add(curve);
248
238
  this._setCurrentPosition(...end);
249
239
  return this;
250
240
  }
251
241
  /**
252
242
  * 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
243
+ * Add an instance of {@link EllipseCurve} to this path
254
244
  *
255
245
  * @param {number} x1 X-axis coordinate of the first control point
256
246
  * @param {number} y1 Y-axis coordinate of the first control point
@@ -263,35 +253,46 @@ export default class PathContext extends Path {
263
253
  if (radius < 0) {
264
254
  throw new Error(`IndexSizeError: Failed to execute 'arcTo' on 'PathContext': The radius provided (${radius}) is negative.`);
265
255
  }
266
- const [x0, y0] = this._inversePoint(this.currentPosition.toArray());
267
- if (Vector2.equals([x0, y0], [x1, y1])) {
256
+ const p0 = new Vector2(...this._currentPosition).applyMatrix(this._currentTransform.inverse());
257
+ const p1 = new Vector2(x1, y1);
258
+ const p2 = new Vector2(x2, y2);
259
+ if (Vector2.equals(p0, p1)) {
268
260
  return this;
269
261
  }
270
- if (Vector2.collinear([x0, y0], [x1, y1], [x2, y2]) || radius === 0) {
262
+ if (Vector2.collinear(p0, p1, p2) || radius === 0) {
271
263
  return this.lineTo(x1, y1);
272
264
  }
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 deltaAngle = endAngle - startAngle;
288
- while (deltaAngle > PI)
289
- deltaAngle -= TWO_PI;
290
- while (deltaAngle < -PI)
291
- deltaAngle += TWO_PI;
265
+ const v1 = p0.clone().sub(p1).normalize();
266
+ const v2 = p2.clone().sub(p1).normalize();
267
+ const n1 = new Vector2(-v1.y, v1.x);
268
+ const n2 = new Vector2(-v2.y, v2.x);
269
+ const angle = Math.acos(v1.dot(v2));
270
+ const tangentLength = radius / Math.tan(angle / 2);
271
+ const t1 = p1.clone().add(v1.clone().multiplyScalar(tangentLength));
272
+ const t2 = p1.clone().add(v2.clone().multiplyScalar(tangentLength));
273
+ const dx = t2.x - t1.x;
274
+ const dy = t2.y - t1.y;
275
+ const determinant = n2.x * n1.y - n2.y * n1.x;
276
+ if (Math.abs(determinant) <= EPSILON) {
277
+ throw new Error(`Failed to execute 'arcTo' on 'PathContext': Failed to compute arc center.`);
278
+ }
279
+ const normalLength = (n2.x * dy - n2.y * dx) / determinant;
280
+ const c = t1.clone().add(n1.clone().multiplyScalar(normalLength));
281
+ const startAngle = Math.atan2(t1.y - c.y, t1.x - c.x);
282
+ const endAngle = Math.atan2(t2.y - c.y, t2.x - c.x);
283
+ const deltaAngle = endAngle - startAngle;
292
284
  const counterclockwise = deltaAngle < 0;
293
- this.lineTo(t1x, t1y);
294
- this.arc(cx, cy, radius, startAngle, endAngle, counterclockwise);
285
+ t1.applyMatrix(this._currentTransform);
286
+ t2.applyMatrix(this._currentTransform);
287
+ c.applyMatrix(this._currentTransform);
288
+ const rx = this._scaleX * radius;
289
+ const ry = this._scaleY * radius;
290
+ const rotation = this._rotation;
291
+ const line = new LineCurve(this._currentPosition.x, this._currentPosition.y, t1.x, t1.y);
292
+ this.add(line);
293
+ const ellipse = new EllipseCurve(c.x, c.y, rx, ry, rotation, startAngle, endAngle, counterclockwise);
294
+ this.add(ellipse);
295
+ this._setCurrentPosition(t2.x, t2.y);
295
296
  return this;
296
297
  }
297
298
  /**
@@ -339,7 +340,6 @@ export default class PathContext extends Path {
339
340
  bottomLeftRadius = Math.min(bottomLeftRadius, maxRadius);
340
341
  const curve = new PathContext({ autoClose: true });
341
342
  curve.setTransform(this.getTransform());
342
- this.add(curve);
343
343
  // Top-Right corner
344
344
  if (topRightRadius > 0) {
345
345
  curve.lineTo(x + width - topRightRadius, y);
@@ -372,19 +372,20 @@ export default class PathContext extends Path {
372
372
  else {
373
373
  curve.lineTo(x, y);
374
374
  }
375
+ this.add(curve);
375
376
  return this;
376
377
  }
377
378
  setTransform(a, b, c, d, e, f) {
378
379
  if (a instanceof DOMMatrix) {
379
380
  const matrix = a;
380
- this.currentTransform = new DOMMatrix([matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f]);
381
+ this._currentTransform = new DOMMatrix([matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f]);
381
382
  }
382
383
  else {
383
- this.currentTransform = new DOMMatrix([a, b, c, d, e, f]);
384
+ this._currentTransform = new DOMMatrix([a, b, c, d, e, f]);
384
385
  }
385
386
  }
386
387
  getTransform() {
387
- const { a, b, c, d, e, f } = this.currentTransform;
388
+ const { a, b, c, d, e, f } = this._currentTransform;
388
389
  return new DOMMatrix([a, b, c, d, e, f]);
389
390
  }
390
391
  resetTransform() {
@@ -392,23 +393,23 @@ export default class PathContext extends Path {
392
393
  }
393
394
  transform(a, b, c, d, e, f) {
394
395
  const matrix = new DOMMatrix([a, b, c, d, e, f]);
395
- this.currentTransform = this.currentTransform.multiply(matrix);
396
+ this._currentTransform = this._currentTransform.multiply(matrix);
396
397
  }
397
398
  translate(x, y) {
398
- this.currentTransform = this.currentTransform.translate(x, y);
399
+ this._currentTransform = this._currentTransform.translate(x, y);
399
400
  }
400
401
  rotate(angle) {
401
- this.currentTransform = this.currentTransform.rotate(toDegrees(angle));
402
+ this._currentTransform = this._currentTransform.rotate(toDegrees(angle));
402
403
  }
403
404
  scale(x, y) {
404
- this.currentTransform = this.currentTransform.scale(x, y);
405
+ this._currentTransform = this._currentTransform.scale(x, y);
405
406
  }
406
407
  save() {
407
408
  this._transformStack.push(this.getTransform());
408
409
  }
409
410
  restore() {
410
411
  if (this._transformStack.length > 0) {
411
- this.currentTransform = this._transformStack.pop();
412
+ this._currentTransform = this._transformStack.pop();
412
413
  }
413
414
  }
414
415
  reset() {
@@ -416,79 +417,62 @@ export default class PathContext extends Path {
416
417
  this._transformStack = [];
417
418
  }
418
419
  _hasCurrentPosition() {
419
- return !isNaN(this.currentPosition.x) && !isNaN(this.currentPosition.y);
420
+ return !isNaN(this._currentPosition.x) && !isNaN(this._currentPosition.y);
420
421
  }
421
422
  _setCurrentPosition(x, y) {
422
- this.currentPosition.set(x, y);
423
+ this._currentPosition.set(x, y);
423
424
  return this;
424
425
  }
425
426
  // ****************************
426
427
  // Matrix transformations
427
428
  // ****************************
428
- _transformPoint(point, matrix = this.currentTransform) {
429
+ _transformPoint(point) {
429
430
  if (this._isIdentity)
430
431
  return point;
431
- const { x, y } = matrix.transformPoint({ x: point[0], y: point[1] });
432
+ const { x, y } = this._currentTransform.transformPoint({ x: point[0], y: point[1] });
432
433
  return [x, y];
433
434
  }
434
- _transformPoints(points, matrix) {
435
+ _transformPoints(points) {
435
436
  if (this._isIdentity)
436
437
  return points;
437
- return points.map((point) => this._transformPoint(point, matrix));
438
+ return points.map((point) => this._transformPoint(point));
438
439
  }
439
- _transformVector(vector, matrix) {
440
+ _transformVector(vector) {
440
441
  if (this._isIdentity)
441
442
  return vector;
442
- const [x0, y0] = this._transformPoint([0, 0], matrix);
443
- const [vx, vy] = this._transformPoint(vector, matrix);
443
+ const [x0, y0] = this._transformPoint([0, 0]);
444
+ const [vx, vy] = this._transformPoint(vector);
444
445
  return [vx - x0, vy - y0];
445
446
  }
446
- _transformEllipse(cx, cy, rx, ry, rotation, matrix) {
447
+ _transformEllipse(cx, cy, rx, ry, rotation) {
447
448
  if (this._isIdentity)
448
449
  return [cx, cy, rx, ry, rotation];
449
- const [tCx, tCy] = this._transformPoint([cx, cy], matrix);
450
- const [ux1, uy1] = this._transformVector([Math.cos(rotation) * rx, Math.sin(rotation) * rx], matrix);
451
- const [ux2, uy2] = this._transformVector([-Math.sin(rotation) * ry, Math.cos(rotation) * ry], matrix);
452
- const tRx = Math.hypot(ux1, uy1);
453
- const tRy = Math.hypot(ux2, uy2);
454
- const tRotation = Math.atan2(uy1, ux1);
455
- return [tCx, tCy, tRx, tRy, tRotation];
456
- }
457
- _inversePoint(point) {
458
- return this._transformPoint(point, this.currentTransform.inverse());
450
+ [cx, cy] = this._transformPoint([cx, cy]);
451
+ const [u1x, u1y] = this._transformVector([Math.cos(rotation) * rx, Math.sin(rotation) * rx]);
452
+ const [u2x, u2y] = this._transformVector([-Math.sin(rotation) * ry, Math.cos(rotation) * ry]);
453
+ rx = Math.hypot(u1x, u1y);
454
+ ry = Math.hypot(u2x, u2y);
455
+ rotation = Math.atan2(u1y, u1x);
456
+ return [cx, cy, rx, ry, rotation];
459
457
  }
460
458
  get _translateX() {
461
- return this.currentTransform.e;
459
+ return this._currentTransform.e;
462
460
  }
463
461
  get _translateY() {
464
- return this.currentTransform.f;
462
+ return this._currentTransform.f;
465
463
  }
466
464
  get _scaleX() {
467
- const { a, b } = this.currentTransform;
465
+ const { a, b } = this._currentTransform;
468
466
  return Math.hypot(a, b);
469
467
  }
470
468
  get _scaleY() {
471
- const { c, d } = this.currentTransform;
469
+ const { c, d } = this._currentTransform;
472
470
  return Math.hypot(c, d);
473
471
  }
474
472
  get _rotation() {
475
- const { a, b } = this.currentTransform;
473
+ const { a, b } = this._currentTransform;
476
474
  return Math.atan2(b, a);
477
475
  }
478
- get _skewX() {
479
- const { c, d } = this.currentTransform;
480
- const cos = Math.cos(this._rotation);
481
- const sin = Math.sin(this._rotation);
482
- const m11 = c * cos + d * sin;
483
- return Math.atan(m11 / this._scaleX);
484
- }
485
- get _skewY() {
486
- const { a, b } = this.currentTransform;
487
- const cos = Math.cos(this._rotation);
488
- const sin = Math.sin(this._rotation);
489
- const m21 = a * sin - b * cos;
490
- return Math.atan(m21 / this._scaleY);
491
- }
492
476
  get _isTranslated() {
493
477
  return Math.abs(this._translateX) > EPSILON || Math.abs(this._translateY) > EPSILON;
494
478
  }
@@ -496,20 +480,14 @@ export default class PathContext extends Path {
496
480
  return Math.abs(this._scaleX - 1) > EPSILON || Math.abs(this._scaleY - 1) > EPSILON;
497
481
  }
498
482
  get _isRotated() {
499
- const { b, c } = this.currentTransform;
483
+ const { b, c } = this._currentTransform;
500
484
  return Math.abs(b) > EPSILON || Math.abs(c) > EPSILON;
501
485
  }
502
- get _isSkewed() {
503
- const { a, b, c, d } = this.currentTransform;
504
- const angleX = Math.atan2(b, a);
505
- const angleY = Math.atan2(-c, d);
506
- return Math.abs(angleX - angleY) > EPSILON;
507
- }
508
486
  get _isUniform() {
509
487
  return Math.abs(this._scaleX - this._scaleY) <= EPSILON;
510
488
  }
511
489
  get _isIdentity() {
512
- return this.currentTransform.isIdentity;
490
+ return this._currentTransform.isIdentity;
513
491
  }
514
492
  // ****************************
515
493
  // Canvas 2D interface
@@ -2,9 +2,18 @@ import { Curve, LineCurve, PolylineCurve, QuadraticBezierCurve, CubicBezierCurve
2
2
  import { type Vector2 } from '../geometry';
3
3
  import PathContext from './PathContext';
4
4
  import Path from './Path';
5
- export type PathSVGSerializationParams = {
5
+ /**
6
+ * Parameters used for SVG serialization of a path
7
+ */
8
+ export type PathSVGSerializationParameters = {
9
+ /**
10
+ * Flag indicating if given curve should be approximated into straight lines
11
+ */
6
12
  approximate?: boolean;
7
- curveResolution?: number;
13
+ /**
14
+ * Resolution used for approximations
15
+ */
16
+ resolution?: number;
8
17
  };
9
18
  /**
10
19
  * Utility class for manipulating connected curves and generating SVG path
@@ -19,10 +28,10 @@ export default class PathSVG extends PathContext {
19
28
  /**
20
29
  * Serialize this path into a SVG path string
21
30
  *
22
- * @param {object} [params]
31
+ * @param {PathSVGSerializationParameters} [params] Serialization parameters
23
32
  * @returns {string}
24
33
  */
25
- toString(params?: PathSVGSerializationParams): string;
34
+ toString(params?: PathSVGSerializationParameters): string;
26
35
  /**
27
36
  * Convert a {@link Curve} into spaced points
28
37
  *
@@ -35,77 +44,74 @@ export default class PathSVG extends PathContext {
35
44
  * Serialize a {@link Curve}
36
45
  *
37
46
  * @param {Curve} curve Curve to serialize
38
- * @param {object} [params] Serialization parameters
39
- * @param {boolean} [params.approximate] Flag indicating if given curve should be approximated into straight lines
40
- * @param {number} [params.curveResolution] Resolution used for curve approximations
41
- * @returns string
47
+ * @param {PathSVGSerializationParameters} [params] Serialization parameters
48
+ * @returns {string}
42
49
  */
43
- static serialize(curve: Curve<Vector2>, { approximate, curveResolution }?: PathSVGSerializationParams): string;
50
+ static serialize(curve: Curve<Vector2>, { approximate, resolution }?: PathSVGSerializationParameters): string;
44
51
  /**
45
52
  * Serialize a {@link LineCurve}
46
53
  *
47
54
  * @param {LineCurve} curve LineCurve to serialize
48
- * @returns string
55
+ * @returns {string}
49
56
  */
50
57
  static serializeLineCurve(curve: LineCurve): string;
51
58
  /**
52
59
  * Serialize a {@link PolylineCurve}
53
60
  *
54
61
  * @param {PolylineCurve} curve PolylineCurve to serialize
55
- * @returns string
62
+ * @returns {string}
56
63
  */
57
64
  static serializePolylineCurve(curve: PolylineCurve): string;
58
65
  /**
59
66
  * Serialize a {@link QuadraticBezierCurve}
60
67
  *
61
68
  * @param {QuadraticBezierCurve} curve QuadraticBezierCurve to serialize
62
- * @returns string
69
+ * @returns {string}
63
70
  */
64
71
  static serializeQuadraticBezierCurve(curve: QuadraticBezierCurve): string;
65
72
  /**
66
73
  * Serialize a {@link CubicBezierCurve}
67
74
  *
68
75
  * @param {CubicBezierCurve} curve CubicBezierCurve to serialize
69
- * @returns string
76
+ * @returns {string}
70
77
  */
71
78
  static serializeCubicBezierCurve(curve: CubicBezierCurve): string;
72
79
  /**
73
80
  * Serialize a {@link CatmullRomCurve} by approximating it into straight lines
74
81
  *
75
82
  * @param {CatmullRomCurve} curve CatmullRomCurve to serialize
76
- * @param {number} [curveResolution] Approximation resolution
77
- * @returns string
83
+ * @param {number} [resolution] Approximation resolution
84
+ * @returns {string}
78
85
  */
79
- static serializeCatmullRomCurve(curve: CatmullRomCurve, curveResolution?: number): string;
86
+ static serializeCatmullRomCurve(curve: CatmullRomCurve, resolution?: number): string;
80
87
  /**
81
88
  * Serialize a {@link SplineCurve} by approximating it into straight lines
82
89
  *
83
90
  * @param {SplineCurve} curve SplineCurve to serialize
84
- * @param {number} [curveResolution] Approximation resolution
85
- * @returns string
91
+ * @param {number} [resolution] Approximation resolution
92
+ * @returns {string}
86
93
  */
87
- static serializeSplineCurve(curve: SplineCurve, curveResolution?: number): string;
94
+ static serializeSplineCurve(curve: SplineCurve, resolution?: number): string;
88
95
  /**
89
96
  * Serialize an {@link EllipseCurve}
90
97
  *
91
98
  * @param {EllipseCurve} curve EllipseCurve to serialize
92
- * @returns string
99
+ * @returns {string}
93
100
  */
94
101
  static serializeEllipseCurve(curve: EllipseCurve): string;
95
102
  /**
96
103
  * Serialize an {@link ArcCurve}
97
104
  *
98
105
  * @param {ArcCurve} curve ArcCurve to serialize
99
- * @returns string
106
+ * @returns {string}
100
107
  */
101
108
  static serializeArcCurve(curve: ArcCurve): string;
102
109
  /**
103
110
  * Serialize an {@link Path}
104
111
  *
105
112
  * @param {Path} path Path to serialize
106
- * @param {object} [params]
107
- * @param {object} [params.curveResolution] Resolution used for curves approximations
108
- * @returns string
113
+ * @param {PathSVGSerializationParameters} [params] Serialization parameters
114
+ * @returns {string}
109
115
  */
110
- static serializePath(path: Path<Vector2>, params?: PathSVGSerializationParams): string;
116
+ static serializePath(path: Path<Vector2>, params?: PathSVGSerializationParameters): string;
111
117
  }