@bitblit/ratchet-common 6.0.146-alpha → 6.0.148-alpha
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/package.json +2 -1
- package/src/2d/line-2d.ts +6 -0
- package/src/2d/matrix-factory.ts +94 -0
- package/src/2d/plane-2d-type.ts +6 -0
- package/src/2d/plane-2d.ts +7 -0
- package/src/2d/point-2d.ts +4 -0
- package/src/2d/poly-line-2d.ts +5 -0
- package/src/2d/ratchet-2d.spec.ts +205 -0
- package/src/2d/ratchet-2d.ts +350 -0
- package/src/2d/transformation-matrix.ts +19 -0
- package/src/build/build-information.ts +8 -0
- package/src/build/ratchet-common-info.ts +19 -0
- package/src/histogram/histogram-entry.ts +4 -0
- package/src/histogram/histogram.spec.ts +25 -0
- package/src/histogram/histogram.ts +61 -0
- package/src/jwt/common-jwt-token.ts +17 -0
- package/src/jwt/expired-jwt-handling.ts +5 -0
- package/src/jwt/jwt-decode-only-ratchet.ts +26 -0
- package/src/jwt/jwt-payload-expiration-ratchet.ts +45 -0
- package/src/jwt/jwt-token-base.ts +14 -0
- package/src/lang/array-ratchet.spec.ts +79 -0
- package/src/lang/array-ratchet.ts +141 -0
- package/src/lang/base64-ratchet.spec.ts +48 -0
- package/src/lang/base64-ratchet.ts +247 -0
- package/src/lang/boolean-ratchet.spec.ts +95 -0
- package/src/lang/boolean-ratchet.ts +52 -0
- package/src/lang/composite-last-success-provider.spec.ts +31 -0
- package/src/lang/composite-last-success-provider.ts +30 -0
- package/src/lang/currency-ratchet.ts +29 -0
- package/src/lang/date-ratchet.spec.ts +27 -0
- package/src/lang/date-ratchet.ts +42 -0
- package/src/lang/duration-ratchet.spec.ts +47 -0
- package/src/lang/duration-ratchet.ts +77 -0
- package/src/lang/enum-ratchet.spec.ts +45 -0
- package/src/lang/enum-ratchet.ts +41 -0
- package/src/lang/error-handling-approach.ts +6 -0
- package/src/lang/error-ratchet.spec.ts +25 -0
- package/src/lang/error-ratchet.ts +70 -0
- package/src/lang/esm-ratchet.ts +81 -0
- package/src/lang/expiring-object.spec.ts +56 -0
- package/src/lang/expiring-object.ts +84 -0
- package/src/lang/geolocation-ratchet.spec.ts +177 -0
- package/src/lang/geolocation-ratchet.ts +341 -0
- package/src/lang/global-ratchet.spec.ts +17 -0
- package/src/lang/global-ratchet.ts +105 -0
- package/src/lang/key-value.ts +8 -0
- package/src/lang/last-success-provider.ts +4 -0
- package/src/lang/map-ratchet.spec.ts +113 -0
- package/src/lang/map-ratchet.ts +220 -0
- package/src/lang/no.spec.ts +9 -0
- package/src/lang/no.ts +7 -0
- package/src/lang/number-ratchet.spec.ts +154 -0
- package/src/lang/number-ratchet.ts +253 -0
- package/src/lang/parsed-url.ts +10 -0
- package/src/lang/promise-ratchet.spec.ts +104 -0
- package/src/lang/promise-ratchet.ts +196 -0
- package/src/lang/range.ts +4 -0
- package/src/lang/require-ratchet.spec.ts +85 -0
- package/src/lang/require-ratchet.ts +68 -0
- package/src/lang/simple-arg-ratchet.spec.ts +13 -0
- package/src/lang/simple-arg-ratchet.ts +47 -0
- package/src/lang/simple-encryption-ratchet.ts +88 -0
- package/src/lang/sort-ratchet.spec.ts +58 -0
- package/src/lang/sort-ratchet.ts +50 -0
- package/src/lang/stop-watch.spec.ts +53 -0
- package/src/lang/stop-watch.ts +202 -0
- package/src/lang/string-ratchet.spec.ts +226 -0
- package/src/lang/string-ratchet.ts +676 -0
- package/src/lang/time-zone-ratchet.spec.ts +51 -0
- package/src/lang/time-zone-ratchet.ts +148 -0
- package/src/lang/timeout-token.spec.ts +12 -0
- package/src/lang/timeout-token.ts +21 -0
- package/src/lang/uint-8-array-ratchet.spec.ts +22 -0
- package/src/lang/uint-8-array-ratchet.ts +48 -0
- package/src/lang/web-stream-ratchet.spec.ts +12 -0
- package/src/lang/web-stream-ratchet.ts +96 -0
- package/src/logger/classic-single-line-log-message-formatter.ts +19 -0
- package/src/logger/log-message-builder.ts +60 -0
- package/src/logger/log-message-format-type.ts +11 -0
- package/src/logger/log-message-formatter.ts +6 -0
- package/src/logger/log-message-processor.ts +6 -0
- package/src/logger/log-message.ts +9 -0
- package/src/logger/log-snapshot.ts +6 -0
- package/src/logger/logger-instance.ts +269 -0
- package/src/logger/logger-level-name.ts +11 -0
- package/src/logger/logger-meta.ts +7 -0
- package/src/logger/logger-options.ts +14 -0
- package/src/logger/logger-output-function.ts +10 -0
- package/src/logger/logger-ring-buffer.ts +89 -0
- package/src/logger/logger-util.spec.ts +11 -0
- package/src/logger/logger-util.ts +68 -0
- package/src/logger/logger.spec.ts +177 -0
- package/src/logger/logger.ts +213 -0
- package/src/logger/none-log-message-formatter.ts +10 -0
- package/src/logger/single-line-no-level-log-message-formatter.ts +18 -0
- package/src/logger/structured-json-log-message-formatter.ts +25 -0
- package/src/mail/archive-email-result.ts +8 -0
- package/src/mail/email-attachment.ts +23 -0
- package/src/mail/mail-sending-provider.ts +21 -0
- package/src/mail/mailer-config.ts +30 -0
- package/src/mail/mailer-like.ts +38 -0
- package/src/mail/mailer-util.ts +65 -0
- package/src/mail/mailer.spec.ts +120 -0
- package/src/mail/mailer.ts +214 -0
- package/src/mail/ready-to-send-email.ts +67 -0
- package/src/mail/resolved-ready-to-send-email.ts +17 -0
- package/src/mail/send-email-result.ts +16 -0
- package/src/mail/test-mail-sending-provider.ts +35 -0
- package/src/network/browser-local-ip-provider.spec.ts +23 -0
- package/src/network/browser-local-ip-provider.ts +26 -0
- package/src/network/fixed-local-ip-provider.ts +9 -0
- package/src/network/local-ip-provider.ts +4 -0
- package/src/network/network-ratchet.spec.ts +17 -0
- package/src/network/network-ratchet.ts +209 -0
- package/src/network/remote-file-tracker/backup-result.ts +6 -0
- package/src/network/remote-file-tracker/file-transfer-result-type.ts +5 -0
- package/src/network/remote-file-tracker/file-transfer-result.ts +9 -0
- package/src/network/remote-file-tracker/remote-file-tracker-options.ts +6 -0
- package/src/network/remote-file-tracker/remote-file-tracker-push-options.ts +4 -0
- package/src/network/remote-file-tracker/remote-file-tracker.ts +117 -0
- package/src/network/remote-file-tracker/remote-file-tracking-provider.ts +19 -0
- package/src/network/remote-file-tracker/remote-status-data-and-content.ts +6 -0
- package/src/network/remote-file-tracker/remote-status-data.ts +7 -0
- package/src/network/restful-api-http-error.spec.ts +13 -0
- package/src/network/restful-api-http-error.ts +173 -0
- package/src/template/ratchet-template-renderer.ts +8 -0
- package/src/third-party/google/google-recaptcha-ratchet.spec.ts +27 -0
- package/src/third-party/google/google-recaptcha-ratchet.ts +36 -0
- package/src/third-party/twilio/twilio-ratchet.ts +92 -0
- package/src/third-party/twilio/twilio-verify-ratchet.ts +83 -0
- package/src/transform/built-in-transforms.ts +214 -0
- package/src/transform/transform-ratchet.spec.ts +134 -0
- package/src/transform/transform-ratchet.ts +88 -0
- package/src/transform/transform-rule.ts +7 -0
- package/src/tx/transaction-configuration.ts +8 -0
- package/src/tx/transaction-final-state.ts +7 -0
- package/src/tx/transaction-ratchet.spec.ts +150 -0
- package/src/tx/transaction-ratchet.ts +98 -0
- package/src/tx/transaction-result.ts +10 -0
- package/src/tx/transaction-step.ts +5 -0
package/package.json
CHANGED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { TransformationMatrix } from "./transformation-matrix.js";
|
|
2
|
+
|
|
3
|
+
export class MatrixFactory {
|
|
4
|
+
// Prevent instantiation
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
6
|
+
private constructor() {}
|
|
7
|
+
|
|
8
|
+
public static multiply(tx: TransformationMatrix[]): TransformationMatrix {
|
|
9
|
+
let rval: TransformationMatrix = MatrixFactory.identity();
|
|
10
|
+
tx.forEach(t=>{
|
|
11
|
+
rval = {
|
|
12
|
+
a: (rval.a * t.a) + (rval.b * t.c) + ((rval.u ?? 0) * 0),
|
|
13
|
+
b: (rval.a * t.b) + (rval.b * t.d) + ((rval.u ?? 0) * 0),
|
|
14
|
+
u: (rval.a * (t.u ?? 0)) + (rval.b * (t.v ?? 0)) + ((rval.u ?? 0) * 1),
|
|
15
|
+
c: (rval.c * t.a) + (rval.d * t.c) + ((rval.v ?? 0) * 0),
|
|
16
|
+
d: (rval.c * t.b) + (rval.d * t.d) + ((rval.v ?? 0) * 0),
|
|
17
|
+
v: (rval.c * (t.u ?? 0)) + (rval.d * (t.v ?? 0)) + ((rval.v ?? 0) * 1),
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
return rval;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public static translate(xMove: number, yMove: number): TransformationMatrix {
|
|
24
|
+
const rval: TransformationMatrix = MatrixFactory.identity();
|
|
25
|
+
rval.u = xMove;
|
|
26
|
+
rval.v = yMove;
|
|
27
|
+
return rval;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
public static scaleUniform(scale: number): TransformationMatrix {
|
|
32
|
+
return MatrixFactory.scale(scale, scale);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public static scale(xScale: number, yScale: number): TransformationMatrix {
|
|
36
|
+
return {
|
|
37
|
+
a: xScale,
|
|
38
|
+
b: 0,
|
|
39
|
+
c: 0,
|
|
40
|
+
d: yScale,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public static rotate(angleTheta: number): TransformationMatrix {
|
|
45
|
+
return {
|
|
46
|
+
a: Math.cos(angleTheta),
|
|
47
|
+
b: -1 * Math.sin(angleTheta),
|
|
48
|
+
c: Math.sin(angleTheta),
|
|
49
|
+
d: Math.cos(angleTheta)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public static shear(xShear: number, yShear: number): TransformationMatrix {
|
|
54
|
+
return {
|
|
55
|
+
a: 1,
|
|
56
|
+
b: xShear,
|
|
57
|
+
c: yShear,
|
|
58
|
+
d: 1
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public static mirrorAboutYAxis(): TransformationMatrix {
|
|
63
|
+
return {
|
|
64
|
+
a: -1,
|
|
65
|
+
b: 0,
|
|
66
|
+
c: 0,
|
|
67
|
+
d: 1
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public static mirrorAboutXAxis(): TransformationMatrix {
|
|
72
|
+
return {
|
|
73
|
+
a: 1,
|
|
74
|
+
b: 0,
|
|
75
|
+
c: 0,
|
|
76
|
+
d: -1
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
public static identity(): TransformationMatrix {
|
|
82
|
+
return {
|
|
83
|
+
a: 1,
|
|
84
|
+
b: 0,
|
|
85
|
+
c: 0,
|
|
86
|
+
d: 1,
|
|
87
|
+
u: 0,
|
|
88
|
+
v: 0
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export enum Plane2dType {
|
|
2
|
+
BottomLeft = 'BottomLeft', // x increases to the right, y increases going up (common cartesian)
|
|
3
|
+
BottomRight = 'BottomRight', // x increases to the left, y increases going up (uncommon)
|
|
4
|
+
TopLeft = 'TopLeft', // x increases to the right, y increases going down (common graphics)
|
|
5
|
+
TopRight = 'TopRight' // x increases to the left, y increases going down (uncommon)
|
|
6
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { Ratchet2d } from "./ratchet-2d.js";
|
|
2
|
+
import { Point2d } from "./point-2d.js";
|
|
3
|
+
import { Plane2d } from "./plane-2d.js";
|
|
4
|
+
import { describe, expect, test } from "vitest";
|
|
5
|
+
import { TransformationMatrix } from "./transformation-matrix.js";
|
|
6
|
+
import { MatrixFactory } from "./matrix-factory.js";
|
|
7
|
+
import { Plane2dType } from "./plane-2d-type.js";
|
|
8
|
+
|
|
9
|
+
describe('#ratchet2d', function () {
|
|
10
|
+
test('should scale', () => {
|
|
11
|
+
const pts: Point2d[] = [{x:0,y:0},{x:1, y:1}];
|
|
12
|
+
const ptEven: Point2d[] = Ratchet2d.applyTransform(pts, MatrixFactory.scaleUniform(2));
|
|
13
|
+
expect(ptEven.length).toEqual(2);
|
|
14
|
+
expect(ptEven[0].x).toEqual(0);
|
|
15
|
+
expect(ptEven[1].x).toEqual(2);
|
|
16
|
+
expect(ptEven[1].y).toEqual(2);
|
|
17
|
+
|
|
18
|
+
const ptX: Point2d[] = Ratchet2d.applyTransform(pts, MatrixFactory.scale(2,1));
|
|
19
|
+
expect(ptX.length).toEqual(2);
|
|
20
|
+
expect(ptX[0].x).toEqual(0);
|
|
21
|
+
expect(ptX[1].x).toEqual(2);
|
|
22
|
+
expect(ptX[1].y).toEqual(1);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('should mirror', () => {
|
|
26
|
+
const pts: Point2d[] = [{x:0,y:0},{x:1, y:1}];
|
|
27
|
+
const ptY: Point2d[] = Ratchet2d.applyTransform(pts, MatrixFactory.mirrorAboutYAxis());
|
|
28
|
+
expect(ptY.length).toEqual(2);
|
|
29
|
+
expect(ptY[0].x).toEqual(0);
|
|
30
|
+
expect(ptY[1].x).toEqual(-1);
|
|
31
|
+
expect(ptY[1].y).toEqual(1);
|
|
32
|
+
|
|
33
|
+
const ptX: Point2d[] = Ratchet2d.applyTransform(pts, MatrixFactory.mirrorAboutXAxis());
|
|
34
|
+
expect(ptX.length).toEqual(2);
|
|
35
|
+
expect(ptX[0].x).toEqual(0);
|
|
36
|
+
expect(ptX[1].x).toEqual(1);
|
|
37
|
+
expect(ptX[1].y).toEqual(-1);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should combine', () => {
|
|
41
|
+
const pts: Point2d[] = [{x:0,y:0},{x:1, y:1}];
|
|
42
|
+
const tx: TransformationMatrix = MatrixFactory.multiply([MatrixFactory.scaleUniform(2), MatrixFactory.mirrorAboutYAxis()]);
|
|
43
|
+
const ptC: Point2d[] = Ratchet2d.applyTransform(pts, tx);
|
|
44
|
+
expect(ptC.length).toEqual(2);
|
|
45
|
+
expect(ptC[0].x).toEqual(0);
|
|
46
|
+
expect(ptC[1].x).toEqual(-2);
|
|
47
|
+
expect(ptC[1].y).toEqual(2);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('should check for valid planes', () => {
|
|
51
|
+
expect(Ratchet2d.validPlane(null)).toEqual(false);
|
|
52
|
+
expect(Ratchet2d.validPlane({ width: null, height: null, type: Plane2dType.BottomLeft })).toEqual(false);
|
|
53
|
+
expect(Ratchet2d.validPlane({ width: 0, height: null, type: Plane2dType.BottomLeft })).toEqual(false);
|
|
54
|
+
expect(Ratchet2d.validPlane({ width: 0, height: 2, type: Plane2dType.BottomLeft })).toEqual(false);
|
|
55
|
+
expect(Ratchet2d.validPlane({ width: 1, height: 7, type: Plane2dType.BottomLeft })).toEqual(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('should check for valid lines', () => {
|
|
59
|
+
expect(Ratchet2d.validLine(null)).toEqual(false);
|
|
60
|
+
expect(Ratchet2d.validLine({ p1: null, p2: null })).toEqual(false);
|
|
61
|
+
expect(Ratchet2d.validLine({ p1: { x: null, y: 0 }, p2: null })).toEqual(false);
|
|
62
|
+
expect(Ratchet2d.validLine({ p1: { x: 0, y: 0 }, p2: { x: 27, y: null } })).toEqual(false);
|
|
63
|
+
expect(Ratchet2d.validLine({ p1: { x: 0, y: 0 }, p2: { x: 27, y: 0 } })).toEqual(true);
|
|
64
|
+
expect(Ratchet2d.validLine({ p1: { x: 1, y: 0 }, p2: { x: 1, y: 0 } })).toEqual(false); // same point
|
|
65
|
+
expect(Ratchet2d.validLine({ p1: { x: 0, y: 0 }, p2: { x: 27, y: 13 } })).toEqual(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('should transform points from one plane to another (scale)', () => {
|
|
69
|
+
const plane1: Plane2d = { height: 10, width: 10, type: Plane2dType.BottomLeft };
|
|
70
|
+
const plane2: Plane2d = { height: 100, width: 50, type: Plane2dType.BottomLeft };
|
|
71
|
+
|
|
72
|
+
const p1: Point2d = { x: 5, y: 5 };
|
|
73
|
+
const out1: Point2d = Ratchet2d.transformPointToNewPlane(p1, plane1, plane2);
|
|
74
|
+
const out2: Point2d = Ratchet2d.transformPointToNewPlane(out1, plane2, plane1);
|
|
75
|
+
|
|
76
|
+
expect(out1.x).toEqual(25);
|
|
77
|
+
expect(out1.y).toEqual(50);
|
|
78
|
+
|
|
79
|
+
expect(out2.x).toEqual(p1.x);
|
|
80
|
+
expect(out2.y).toEqual(p1.y);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('should transform points from one plane to another (mirror)', () => {
|
|
84
|
+
const plane1: Plane2d = { height: 10, width: 10, type: Plane2dType.BottomLeft };
|
|
85
|
+
const plane2: Plane2d = { height: 10, width: 10, type: Plane2dType.TopRight };
|
|
86
|
+
|
|
87
|
+
const p1: Point2d = { x: 2, y: 8 };
|
|
88
|
+
const out1: Point2d = Ratchet2d.transformPointToNewPlane(p1, plane1, plane2);
|
|
89
|
+
const out2: Point2d = Ratchet2d.transformPointToNewPlane(out1, plane2, plane1);
|
|
90
|
+
|
|
91
|
+
expect(out1.x).toEqual(7);
|
|
92
|
+
expect(out1.y).toEqual(1);
|
|
93
|
+
|
|
94
|
+
expect(out2.x).toEqual(p1.x);
|
|
95
|
+
expect(out2.y).toEqual(p1.y);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('should check for containment', () => {
|
|
99
|
+
const plane: Plane2d = {
|
|
100
|
+
width: 100,
|
|
101
|
+
height: 100,
|
|
102
|
+
type: Plane2dType.BottomLeft
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
expect(Ratchet2d.planeContainsPoint({ x: -1, y: 10 }, plane)).toEqual(false);
|
|
106
|
+
expect(Ratchet2d.planeContainsPoint({ x: 101, y: 10 }, plane)).toEqual(false);
|
|
107
|
+
expect(Ratchet2d.planeContainsPoint({ x: 1, y: -10 }, plane)).toEqual(false);
|
|
108
|
+
expect(Ratchet2d.planeContainsPoint({ x: 10, y: 101 }, plane)).toEqual(false);
|
|
109
|
+
expect(Ratchet2d.planeContainsPoint({ x: 10, y: 100 }, plane)).toEqual(false); // Boundary condition
|
|
110
|
+
expect(Ratchet2d.planeContainsPoint({ x: 1, y: 10 }, plane)).toEqual(true);
|
|
111
|
+
expect(Ratchet2d.planeContainsPoint({ x: 10, y: 10 }, plane)).toEqual(true);
|
|
112
|
+
|
|
113
|
+
expect(
|
|
114
|
+
Ratchet2d.planeContainsPoints(
|
|
115
|
+
[
|
|
116
|
+
{ x: 10, y: 10 },
|
|
117
|
+
{ x: 100, y: 10 },
|
|
118
|
+
],
|
|
119
|
+
plane,
|
|
120
|
+
),
|
|
121
|
+
).toEqual(false);
|
|
122
|
+
expect(
|
|
123
|
+
Ratchet2d.planeContainsPoints(
|
|
124
|
+
[
|
|
125
|
+
{ x: 10, y: 10 },
|
|
126
|
+
{ x: 11, y: 10 },
|
|
127
|
+
],
|
|
128
|
+
plane,
|
|
129
|
+
),
|
|
130
|
+
).toEqual(true);
|
|
131
|
+
|
|
132
|
+
expect(
|
|
133
|
+
Ratchet2d.planeContainsLine(
|
|
134
|
+
{
|
|
135
|
+
p1: { x: 10, y: 10 },
|
|
136
|
+
p2: { x: 100, y: 10 },
|
|
137
|
+
},
|
|
138
|
+
plane,
|
|
139
|
+
),
|
|
140
|
+
).toEqual(false);
|
|
141
|
+
expect(
|
|
142
|
+
Ratchet2d.planeContainsLine(
|
|
143
|
+
{
|
|
144
|
+
p1: { x: 10, y: 10 },
|
|
145
|
+
p2: { x: 11, y: 10 },
|
|
146
|
+
},
|
|
147
|
+
plane,
|
|
148
|
+
),
|
|
149
|
+
).toEqual(true);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('should fit input to curve', function () {
|
|
153
|
+
const curve: Point2d[] = [
|
|
154
|
+
{ x: 0, y: 50 },
|
|
155
|
+
{ x: 0.5, y: 50 },
|
|
156
|
+
{ x: 0.8, y: 60 },
|
|
157
|
+
{ x: 1, y: 70 },
|
|
158
|
+
{ x: 1.2, y: 80 },
|
|
159
|
+
{ x: 1.5, y: 90 },
|
|
160
|
+
{ x: 1.6, y: 91 },
|
|
161
|
+
{ x: 1.7, y: 92 },
|
|
162
|
+
{ x: 1.8, y: 93 },
|
|
163
|
+
{ x: 1.9, y: 94 },
|
|
164
|
+
{ x: 2, y: 95 },
|
|
165
|
+
{
|
|
166
|
+
x: 3,
|
|
167
|
+
y: 98,
|
|
168
|
+
},
|
|
169
|
+
{ x: 4, y: 99 },
|
|
170
|
+
{ x: 5, y: 100 },
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
expect(Ratchet2d.fitCurve(curve, -1)).toEqual(50);
|
|
174
|
+
expect(Ratchet2d.fitCurve(curve, 0)).toEqual(50);
|
|
175
|
+
expect(Ratchet2d.fitCurve(curve, 0.3)).toEqual(50);
|
|
176
|
+
expect(Ratchet2d.fitCurve(curve, 0.5)).toEqual(50);
|
|
177
|
+
expect(Ratchet2d.fitCurve(curve, 0.8)).toEqual(60);
|
|
178
|
+
expect(Ratchet2d.fitCurve(curve, 1)).toEqual(70);
|
|
179
|
+
expect(Ratchet2d.fitCurve(curve, 5)).toEqual(100);
|
|
180
|
+
expect(Ratchet2d.fitCurve(curve, 6)).toEqual(100);
|
|
181
|
+
expect(Ratchet2d.fitCurve(curve, 1.65)).toEqual(91.5);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test('rotate about origin', function () {
|
|
185
|
+
const points: Point2d[] = [
|
|
186
|
+
{ x: 1, y: 0 },
|
|
187
|
+
{ x: 2, y: 1 },
|
|
188
|
+
{ x: 3, y: 2 },
|
|
189
|
+
{ x: 4, y: 3 },
|
|
190
|
+
{ x: 4, y: 1 },
|
|
191
|
+
{ x: 6, y: 1 },
|
|
192
|
+
];
|
|
193
|
+
//const plane: Plane2d = { height: 4, width: 8 };
|
|
194
|
+
|
|
195
|
+
const r1: Point2d[] = Ratchet2d.rotateRightAboutCartesianOrigin90Degrees(points); //, plane);
|
|
196
|
+
expect(r1.length).toEqual(6);
|
|
197
|
+
|
|
198
|
+
// Full rotation should be the same
|
|
199
|
+
const _r3: Point2d[] = Ratchet2d.rotateRightAboutCartesianOrigin90Degrees(points, 3);
|
|
200
|
+
const r2: Point2d[] = Ratchet2d.rotateRightAboutCartesianOrigin90Degrees(points, 4);
|
|
201
|
+
expect(r2.length).toEqual(6);
|
|
202
|
+
expect(r2[0].x).toEqual(points[0].x);
|
|
203
|
+
expect(r2[0].y).toEqual(points[0].y);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { Point2d } from "./point-2d.js";
|
|
2
|
+
import { Plane2d } from "./plane-2d.js";
|
|
3
|
+
import { Line2d } from "./line-2d.js";
|
|
4
|
+
import { PolyLine2d } from "./poly-line-2d.js";
|
|
5
|
+
import { BooleanRatchet } from "../lang/boolean-ratchet.js";
|
|
6
|
+
import { ErrorRatchet } from "../lang/error-ratchet.js";
|
|
7
|
+
import { TransformationMatrix } from "./transformation-matrix.js";
|
|
8
|
+
import { RequireRatchet } from "../lang/require-ratchet.js";
|
|
9
|
+
import { MatrixFactory } from "./matrix-factory.js";
|
|
10
|
+
import { Plane2dType } from "./plane-2d-type.js";
|
|
11
|
+
|
|
12
|
+
export class Ratchet2d {
|
|
13
|
+
// Prevent instantiation
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
15
|
+
private constructor() {}
|
|
16
|
+
|
|
17
|
+
public static translateToOriginVector(points: Point2d[]): Point2d {
|
|
18
|
+
let rval: Point2d;
|
|
19
|
+
if (points?.length && Ratchet2d.validPoints(points)) {
|
|
20
|
+
rval = structuredClone(points[0]);
|
|
21
|
+
points.forEach((p) => {
|
|
22
|
+
rval.x = rval.x === null || p.x < rval.x ? p.x : rval.x;
|
|
23
|
+
rval.y = rval.y === null || p.y < rval.y ? p.y : rval.y;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
rval.x *= -1;
|
|
27
|
+
rval.y *= -1;
|
|
28
|
+
} else {
|
|
29
|
+
throw ErrorRatchet.fErr('Cannot translate, points are not valid or empty : %j', points);
|
|
30
|
+
}
|
|
31
|
+
return rval;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public static minimalContainingPlane(points: Point2d[], type: Plane2dType=Plane2dType.BottomLeft): Plane2d {
|
|
35
|
+
let rval: Plane2d = null;
|
|
36
|
+
if (Ratchet2d.validPoints(points)) {
|
|
37
|
+
rval = {
|
|
38
|
+
width: 0,
|
|
39
|
+
height: 0,
|
|
40
|
+
type: type
|
|
41
|
+
};
|
|
42
|
+
points.forEach((p) => {
|
|
43
|
+
rval.width = Math.max(rval.width, p.x + 1);
|
|
44
|
+
rval.height = Math.max(rval.height, p.y + 1);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return rval;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public static scaleVector(src: Plane2d, dst: Plane2d): Point2d {
|
|
51
|
+
let rval: Point2d = null;
|
|
52
|
+
if (Ratchet2d.validPlane(src) && Ratchet2d.validPlane(dst)) {
|
|
53
|
+
rval = {
|
|
54
|
+
x: dst.width / src.width,
|
|
55
|
+
y: dst.height / src.height,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return rval;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public static samePoint(p1: Point2d, p2: Point2d): boolean {
|
|
62
|
+
return !!p1 && !!p2 && p1.x === p2.x && p1.y === p2.y;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public static atLeastOneDimensionShared(p1: Point2d, p2: Point2d): boolean {
|
|
66
|
+
return !!p1 && !!p2 && (p1.x === p2.x || p1.y === p2.y);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public static validPlane(plane: Plane2d): boolean {
|
|
70
|
+
return !!plane && !!plane.height && !!plane.width && !!plane.type; // Can use this since 0 isn't an accepted value;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public static validLine(line: Line2d): boolean {
|
|
74
|
+
return !!line && Ratchet2d.validPoint(line.p1) && Ratchet2d.validPoint(line.p2) && !Ratchet2d.samePoint(line.p1, line.p2);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public static validLines(lines: Line2d[]): boolean {
|
|
78
|
+
return !!lines && BooleanRatchet.allTrue(lines.map((l) => Ratchet2d.validLine(l)));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public static validPolyLine(pline: PolyLine2d): boolean {
|
|
82
|
+
return !!pline && pline.pts?.length > 1 && BooleanRatchet.allTrue(pline.pts.map((p) => Ratchet2d.validPoint(p)));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public static polyLineToLines(pline: PolyLine2d): Line2d[] {
|
|
86
|
+
let rval: Line2d[] = null;
|
|
87
|
+
if (Ratchet2d.validPolyLine(pline)) {
|
|
88
|
+
rval = [];
|
|
89
|
+
for (let i = 1; i < pline.pts.length; i++) {
|
|
90
|
+
rval.push({ p1: pline.pts[i - 1], p2: pline.pts[i] });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return rval;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public static lastPointOnPolyLine(polyLine: PolyLine2d): Point2d {
|
|
97
|
+
return Ratchet2d.validPolyLine(polyLine) ? polyLine.pts[polyLine.pts.length - 1] : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public static lineToPolyLine(line: Line2d): PolyLine2d {
|
|
101
|
+
return Ratchet2d.validLine(line) ? { pts: [line.p1, line.p2] } : null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Multiple lines are collapsed into a polyline if the end of line N is the start of line N+1
|
|
105
|
+
public static linesToPolyLines(lines: Line2d[]): PolyLine2d[] {
|
|
106
|
+
let rval: PolyLine2d[] = null;
|
|
107
|
+
if (Ratchet2d.validLines(lines)) {
|
|
108
|
+
rval = [];
|
|
109
|
+
let currentPLine: PolyLine2d = Ratchet2d.lineToPolyLine(lines[0]);
|
|
110
|
+
for (let i = 1; i < lines.length; i++) {
|
|
111
|
+
if (Ratchet2d.samePoint(Ratchet2d.lastPointOnPolyLine(currentPLine), lines[i].p1)) {
|
|
112
|
+
currentPLine.pts.push(lines[i].p2);
|
|
113
|
+
} else {
|
|
114
|
+
rval.push(currentPLine);
|
|
115
|
+
currentPLine = Ratchet2d.lineToPolyLine(lines[i]);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
rval.push(currentPLine);
|
|
119
|
+
}
|
|
120
|
+
return rval;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public static validPoint(pt: Point2d): boolean {
|
|
124
|
+
return !!pt && pt.x != null && pt.x != undefined && pt.y !== null && pt.y !== undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public static validPoints(pt: Point2d[]): boolean {
|
|
128
|
+
return (
|
|
129
|
+
!!pt &&
|
|
130
|
+
BooleanRatchet.allTrue(
|
|
131
|
+
pt.map((p) => Ratchet2d.validPoint(p)),
|
|
132
|
+
false,
|
|
133
|
+
)
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public static planeContainsPoint(pt: Point2d, plane: Plane2d): boolean {
|
|
138
|
+
return Ratchet2d.validPlane(plane) && Ratchet2d.validPoint(pt) && pt.x >= 0 && pt.x < plane.width && pt.y >= 0 && pt.y < plane.height;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public static planeContainsPoints(pt: Point2d[], plane: Plane2d): boolean {
|
|
142
|
+
return pt && pt.map((p) => Ratchet2d.planeContainsPoint(p, plane)).reduce((a, i) => a && i, true);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public static linesToPoints(lines: Line2d[]): Point2d[] {
|
|
146
|
+
let rval: Point2d[] = null;
|
|
147
|
+
if (Ratchet2d.validLines(lines)) {
|
|
148
|
+
rval = lines.map((l) => [l.p1, l.p2]).flat();
|
|
149
|
+
}
|
|
150
|
+
return rval;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public static pointsToLines(points: Point2d[]): Line2d[] {
|
|
154
|
+
let rval: Line2d[] = null;
|
|
155
|
+
if (Ratchet2d.validPoints(points)) {
|
|
156
|
+
if (points.length % 2 == 0) {
|
|
157
|
+
rval = [];
|
|
158
|
+
for (let i = 0; i < points.length; i += 2) {
|
|
159
|
+
rval.push({ p1: points[i], p2: points[i + 1] });
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
throw new Error('Cannot convert non-even array of points into lines');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return rval;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public static rotateRightAboutCartesianOrigin90Degrees(points: Point2d[], times = 1): Point2d[] {
|
|
169
|
+
let rval: Point2d[] = null;
|
|
170
|
+
let translate: Point2d = { x: 0, y: 0 };
|
|
171
|
+
if (Ratchet2d.validPoints(points)) {
|
|
172
|
+
rval = Object.assign([], points);
|
|
173
|
+
translate = Ratchet2d.translateToOriginVector(rval);
|
|
174
|
+
rval = Ratchet2d.translate(rval, translate); // Move it to the origin first
|
|
175
|
+
let plane: Plane2d = Ratchet2d.minimalContainingPlane(rval);
|
|
176
|
+
|
|
177
|
+
rval = Ratchet2d.xToY(rval);
|
|
178
|
+
plane = { width: plane.height, height: plane.width, type: Plane2dType.BottomLeft };
|
|
179
|
+
translate = { x: translate.y * -1, y: translate.x * -1 };
|
|
180
|
+
rval = Ratchet2d.mirrorPointsOnPlane(rval, plane, true, false);
|
|
181
|
+
// Move it back
|
|
182
|
+
rval = Ratchet2d.translate(rval, translate);
|
|
183
|
+
}
|
|
184
|
+
const timesToRotate: number = times > 4 ? times % 4 : times; // Allow 4 since its used for testing, but not more than that
|
|
185
|
+
if (timesToRotate > 1) {
|
|
186
|
+
rval = Ratchet2d.rotateRightAboutCartesianOrigin90Degrees(rval, timesToRotate - 1);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return rval;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public static planeContainsLines(lines: Line2d[], plane: Plane2d): boolean {
|
|
193
|
+
return lines && Ratchet2d.planeContainsPoints(lines.map((l) => [l.p1, l.p2]).flat(), plane);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public static planeContainsPolyLine(pline: PolyLine2d, plane: Plane2d): boolean {
|
|
197
|
+
return Ratchet2d.planeContainsLines(Ratchet2d.polyLineToLines(pline), plane);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
public static planeContainsLine(line: Line2d, plane: Plane2d): boolean {
|
|
201
|
+
return (
|
|
202
|
+
Ratchet2d.validPlane(plane) &&
|
|
203
|
+
Ratchet2d.validLine(line) &&
|
|
204
|
+
Ratchet2d.planeContainsPoint(line.p1, plane) &&
|
|
205
|
+
Ratchet2d.planeContainsPoint(line.p2, plane)
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
public static xToY(points: Point2d[]): Point2d[] {
|
|
210
|
+
let rval: Point2d[] = null;
|
|
211
|
+
if (Ratchet2d.validPoints(points)) {
|
|
212
|
+
rval = points.map((p) => {
|
|
213
|
+
const next: Point2d = { x: p.y, y: p.x };
|
|
214
|
+
return next;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return rval;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
public static translate(points: Point2d[], xlate: Point2d): Point2d[] {
|
|
221
|
+
let rval: Point2d[] = null;
|
|
222
|
+
if (Ratchet2d.validPoints(points) && Ratchet2d.validPoint(xlate)) {
|
|
223
|
+
rval = Ratchet2d.applyTransform(points, MatrixFactory.translate(xlate.x, xlate.y));
|
|
224
|
+
}
|
|
225
|
+
return rval;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
public static mirrorPointsOnPlane(points: Point2d[], src: Plane2d, mirrorX: boolean, mirrorY: boolean): Point2d[] {
|
|
229
|
+
let rval: Point2d[] = null;
|
|
230
|
+
if (Ratchet2d.validPoints(points) && Ratchet2d.validPlane(src)) {
|
|
231
|
+
if (mirrorX || mirrorY) {
|
|
232
|
+
rval = points.map((p) => {
|
|
233
|
+
const next: Point2d = {
|
|
234
|
+
x: mirrorX ? src.width - 1 - p.x : p.x,
|
|
235
|
+
y: mirrorY ? src.height - 1 - p.y : p.y,
|
|
236
|
+
};
|
|
237
|
+
return next;
|
|
238
|
+
});
|
|
239
|
+
} else {
|
|
240
|
+
// If not mirroring in either direction, then just copy
|
|
241
|
+
rval = Object.assign([], points);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return rval;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
public static matchingVerticalOrientation(pt1: Plane2dType, pt2: Plane2dType): boolean {
|
|
248
|
+
return ((pt1===Plane2dType.BottomRight || pt1===Plane2dType.BottomLeft) && (pt2===Plane2dType.BottomRight || pt2===Plane2dType.BottomLeft)) ||
|
|
249
|
+
((pt1===Plane2dType.TopLeft || pt1===Plane2dType.TopRight) && (pt2===Plane2dType.TopLeft || pt2===Plane2dType.TopRight));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
public static matchingHorizontalOrientation(pt1: Plane2dType, pt2: Plane2dType): boolean {
|
|
253
|
+
return ((pt1===Plane2dType.BottomRight || pt1===Plane2dType.TopRight) && (pt2===Plane2dType.BottomRight || pt2===Plane2dType.TopRight)) ||
|
|
254
|
+
((pt1===Plane2dType.BottomLeft || pt1===Plane2dType.TopLeft) && (pt2===Plane2dType.BottomLeft || pt2===Plane2dType.TopLeft));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
public static transformPointsToNewPlane(points: Point2d[], src: Plane2d, dst: Plane2d): Point2d[] {
|
|
258
|
+
let rval: Point2d[] = null;
|
|
259
|
+
if (Ratchet2d.validPoints(points) && Ratchet2d.validPlane(src) && Ratchet2d.validPlane(dst)) {
|
|
260
|
+
const scale: Point2d = Ratchet2d.scaleVector(src, dst);
|
|
261
|
+
rval = Ratchet2d.applyTransform(points, MatrixFactory.scale(scale.x, scale.y));
|
|
262
|
+
rval = Ratchet2d.mirrorPointsOnPlane(rval, dst, !Ratchet2d.matchingHorizontalOrientation(src.type, dst.type), !Ratchet2d.matchingVerticalOrientation(src.type, dst.type));
|
|
263
|
+
}
|
|
264
|
+
return rval;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public static transformPointToNewPlane(point: Point2d, src: Plane2d, dst: Plane2d): Point2d {
|
|
268
|
+
const tmp: Point2d[] = Ratchet2d.transformPointsToNewPlane([point], src, dst);
|
|
269
|
+
return tmp.length === 1 ? tmp[0] : null;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
public static transformLines(_lines: Line2d[], _src: Plane2d, _dst: Plane2d): Line2d[] {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Given a set of points defining a curve, and a particular X, find the "best" Y
|
|
277
|
+
// for the curve defined by the points
|
|
278
|
+
public static fitCurve(curveDef: Point2d[], inputX: number): number {
|
|
279
|
+
curveDef.sort((a, b) => {
|
|
280
|
+
return a.x - b.x;
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
if (inputX <= curveDef[0].x) {
|
|
284
|
+
return curveDef[0].y;
|
|
285
|
+
} else if (inputX >= curveDef[curveDef.length - 1].x) {
|
|
286
|
+
return curveDef[curveDef.length - 1].y;
|
|
287
|
+
} else {
|
|
288
|
+
let idx = 0;
|
|
289
|
+
while (curveDef[idx + 1].x < inputX) {
|
|
290
|
+
idx++;
|
|
291
|
+
}
|
|
292
|
+
const xSpread: number = curveDef[idx + 1].x - curveDef[idx].x;
|
|
293
|
+
const ySpread: number = curveDef[idx + 1].y - curveDef[idx].y;
|
|
294
|
+
const pct: number = (inputX - curveDef[idx].x) / xSpread;
|
|
295
|
+
const yAdd: number = pct * ySpread;
|
|
296
|
+
return curveDef[idx].y + yAdd;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
public static midPoint(src: Point2d, other: Point2d): Point2d {
|
|
301
|
+
return Ratchet2d.pointAtPercentDistance(src, other, 0.5);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
public static pointAtPercentDistance(src: Point2d, other: Point2d, percent: number): Point2d {
|
|
305
|
+
if (percent < 0 || percent > 1) {
|
|
306
|
+
throw ErrorRatchet.fErr('Percent must be between 0 and 1, but was %d', percent);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const deltaX: number = other.x - src.x;
|
|
310
|
+
const deltaY: number = other.y - src.y;
|
|
311
|
+
return { x: src.x + deltaX * percent, y: src.y + deltaY * percent };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
public static validatePoint(pt: Point2d): void {
|
|
315
|
+
RequireRatchet.notNullOrUndefined(pt, 'pt');
|
|
316
|
+
RequireRatchet.notNullOrUndefined(pt.x, 'pt.x');
|
|
317
|
+
RequireRatchet.notNullOrUndefined(pt.y, 'pt.y');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
public static validateMatrix(tx: TransformationMatrix, includeUV?: boolean): void {
|
|
321
|
+
RequireRatchet.notNullOrUndefined(tx, 'tx');
|
|
322
|
+
RequireRatchet.notNullOrUndefined(tx.a, 'tx.a');
|
|
323
|
+
RequireRatchet.notNullOrUndefined(tx.b, 'tx.b');
|
|
324
|
+
RequireRatchet.notNullOrUndefined(tx.c, 'tx.c');
|
|
325
|
+
RequireRatchet.notNullOrUndefined(tx.d, 'tx.d');
|
|
326
|
+
if (includeUV) {
|
|
327
|
+
RequireRatchet.notNullOrUndefined(tx.u, 'tx.u');
|
|
328
|
+
RequireRatchet.notNullOrUndefined(tx.v, 'tx.v');
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
public static applyTransform(pt: Point2d[], tx: TransformationMatrix): Point2d[] {
|
|
333
|
+
const rval: Point2d[] = pt?.length ? pt.map(p=>Ratchet2d.applyTransformSingle(p, tx)) : pt;
|
|
334
|
+
return rval;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
public static applyTransformSingle(pt: Point2d, tx: TransformationMatrix): Point2d {
|
|
338
|
+
Ratchet2d.validatePoint(pt);
|
|
339
|
+
Ratchet2d.validateMatrix(tx);
|
|
340
|
+
|
|
341
|
+
// Not a generalized matrix multiplier, although that's probably better. Use math.js for that :P
|
|
342
|
+
const rval : Point2d = {
|
|
343
|
+
x: (tx.a * pt.x) + (tx.b * pt.y) + (tx.u | 0),
|
|
344
|
+
y: (tx.c * pt.x) + (tx.d * pt.y) + (tx.v | 0)
|
|
345
|
+
}
|
|
346
|
+
return rval;
|
|
347
|
+
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
}
|