@synnaxlabs/x 0.7.0 → 0.8.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.
- package/package.json +1 -1
- package/src/deep/external.ts +1 -0
- package/src/deep/memo.ts +15 -0
- package/src/spatial/box.ts +2 -0
- package/src/spatial/dimensions.ts +5 -0
- package/src/spatial/direction.ts +8 -8
- package/src/spatial/location.ts +11 -0
- package/src/spatial/xy.spec.ts +8 -0
- package/src/spatial/xy.ts +22 -7
- package/src/telem/series.ts +19 -3
- package/src/telem/telem.ts +5 -0
package/package.json
CHANGED
package/src/deep/external.ts
CHANGED
package/src/deep/memo.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { equal } from "@/deep/equal"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export const memo = <F extends (...args: any[]) => any>(func: F): F => {
|
|
5
|
+
let prevArgs: Parameters<F> = undefined as any
|
|
6
|
+
let prevResult: ReturnType<F> = undefined as any
|
|
7
|
+
const v = ((...args: Parameters<F>) => {
|
|
8
|
+
if (equal(prevArgs, args)) return prevResult
|
|
9
|
+
const result = func(...args)
|
|
10
|
+
prevArgs = args
|
|
11
|
+
prevResult = result
|
|
12
|
+
return result
|
|
13
|
+
})
|
|
14
|
+
return v as F;
|
|
15
|
+
}
|
package/src/spatial/box.ts
CHANGED
|
@@ -62,3 +62,8 @@ export const min = (crude: Crude[]): Dimensions => ({
|
|
|
62
62
|
width: Math.min(...crude.map((c) => construct(c).width)),
|
|
63
63
|
height: Math.min(...crude.map((c) => construct(c).height)),
|
|
64
64
|
});
|
|
65
|
+
|
|
66
|
+
export const scale = (ca: Crude, factor: number): Dimensions => {
|
|
67
|
+
const a = construct(ca);
|
|
68
|
+
return { width: a.width * factor, height: a.height * factor };
|
|
69
|
+
}
|
package/src/spatial/direction.ts
CHANGED
|
@@ -32,16 +32,16 @@ export const construct = (c: Crude): Direction => {
|
|
|
32
32
|
else return "x";
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
export const swap = (direction:
|
|
36
|
-
direction === "x" ? "y" : "x";
|
|
35
|
+
export const swap = (direction: CrudeDirection): Direction =>
|
|
36
|
+
construct(direction) === "x" ? "y" : "x";
|
|
37
37
|
|
|
38
|
-
export const dimension = (direction:
|
|
39
|
-
direction === "x" ? "width" : "height";
|
|
38
|
+
export const dimension = (direction: CrudeDirection): Dimension =>
|
|
39
|
+
construct(direction) === "x" ? "width" : "height";
|
|
40
40
|
|
|
41
|
-
export const location = (direction:
|
|
42
|
-
direction === "x" ? "left" : "top";
|
|
41
|
+
export const location = (direction: CrudeDirection): Location =>
|
|
42
|
+
construct(direction) === "x" ? "left" : "top";
|
|
43
43
|
|
|
44
44
|
export const isDirection = (c: unknown): c is Direction => crude.safeParse(c).success;
|
|
45
45
|
|
|
46
|
-
export const signedDimension = (direction:
|
|
47
|
-
direction === "x" ? "signedWidth" : "signedHeight";
|
|
46
|
+
export const signedDimension = (direction: CrudeDirection): SignedDimension =>
|
|
47
|
+
construct(direction) === "x" ? "signedWidth" : "signedHeight";
|
package/src/spatial/location.ts
CHANGED
|
@@ -52,6 +52,14 @@ const SWAPPED: Record<Location, Location> = {
|
|
|
52
52
|
center: "center",
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
const ROTATE_90: Record<Location, Location> = {
|
|
56
|
+
top: "left",
|
|
57
|
+
right: "top",
|
|
58
|
+
bottom: "right",
|
|
59
|
+
left: "bottom",
|
|
60
|
+
center: "center",
|
|
61
|
+
};
|
|
62
|
+
|
|
55
63
|
export const crude = crudeLocation;
|
|
56
64
|
|
|
57
65
|
export type Crude = CrudeLocation;
|
|
@@ -65,6 +73,8 @@ export const construct = (cl: Crude): Location => {
|
|
|
65
73
|
|
|
66
74
|
export const swap = (cl: Crude): Location => SWAPPED[construct(cl)];
|
|
67
75
|
|
|
76
|
+
export const rotate90 = (cl: Crude): Location => ROTATE_90[construct(cl)];
|
|
77
|
+
|
|
68
78
|
export const direction = (cl: Crude): Direction => {
|
|
69
79
|
const l = construct(cl);
|
|
70
80
|
if (l === "top" || l === "bottom") return "y";
|
|
@@ -76,6 +86,7 @@ export const corner = z.object({ x: xLocation, y: yLocation });
|
|
|
76
86
|
|
|
77
87
|
export type XY = z.infer<typeof xy>;
|
|
78
88
|
export type CornerXY = z.infer<typeof corner>;
|
|
89
|
+
export type CornerXYString = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
|
|
79
90
|
|
|
80
91
|
export const TOP_LEFT: CornerXY = { x: "left", y: "top" };
|
|
81
92
|
export const TOP_RIGHT: CornerXY = { x: "right", y: "top" };
|
package/src/spatial/xy.spec.ts
CHANGED
|
@@ -46,6 +46,14 @@ test("translate", () => {
|
|
|
46
46
|
expect(p.x).toEqual(6);
|
|
47
47
|
expect(p.y).toEqual(7);
|
|
48
48
|
});
|
|
49
|
+
|
|
50
|
+
test("translate multiple", () => {
|
|
51
|
+
let p = xy.construct([1, 2]);
|
|
52
|
+
p = xy.translate(p, [5, 5], [2, 2]);
|
|
53
|
+
expect(p.x).toEqual(8);
|
|
54
|
+
expect(p.y).toEqual(9);
|
|
55
|
+
});
|
|
56
|
+
|
|
49
57
|
describe("equals", () => {
|
|
50
58
|
const TESTS: Array<[xy.Crude, xy.Crude, boolean]> = [
|
|
51
59
|
[[1, 1], { x: 1, y: 1 }, true],
|
package/src/spatial/xy.ts
CHANGED
|
@@ -25,6 +25,7 @@ export { clientXY, xy, type ClientXY as Client, type XY };
|
|
|
25
25
|
|
|
26
26
|
/** A crude representation of a {@link XY} coordinate as a zod schema. */
|
|
27
27
|
export const crudeZ = z.union([
|
|
28
|
+
z.number(),
|
|
28
29
|
xy,
|
|
29
30
|
numberCouple,
|
|
30
31
|
dimensions,
|
|
@@ -42,7 +43,7 @@ export type Crude = z.infer<typeof crudeZ>;
|
|
|
42
43
|
* @param y - If x is a number, the y coordinate. If x is a number and this argument is
|
|
43
44
|
* not given, the y coordinate is assumed to be the same as the x coordinate.
|
|
44
45
|
*/
|
|
45
|
-
export const construct = (x: Crude
|
|
46
|
+
export const construct = (x: Crude, y?: number): XY => {
|
|
46
47
|
if (typeof x === "number") return { x, y: y ?? x };
|
|
47
48
|
if (Array.isArray(x)) return { x: x[0], y: x[1] };
|
|
48
49
|
if ("signedWidth" in x) return { x: x.signedWidth, y: x.signedHeight };
|
|
@@ -63,11 +64,12 @@ export const INFINITY = { x: Infinity, y: Infinity };
|
|
|
63
64
|
/** An x and y coordinate of NaN */
|
|
64
65
|
export const NAN = { x: NaN, y: NaN };
|
|
65
66
|
|
|
66
|
-
/** @returns true if the two XY coordinates are
|
|
67
|
-
export const equals = (a: Crude, b: Crude): boolean => {
|
|
67
|
+
/** @returns true if the two XY coordinates are semantically equal. */
|
|
68
|
+
export const equals = (a: Crude, b: Crude, threshold: number = 0): boolean => {
|
|
68
69
|
const a_ = construct(a);
|
|
69
70
|
const b_ = construct(b);
|
|
70
|
-
return a_.x === b_.x && a_.y === b_.y;
|
|
71
|
+
if (threshold === 0) return a_.x === b_.x && a_.y === b_.y;
|
|
72
|
+
return Math.abs(a_.x - b_.x) <= threshold && Math.abs(a_.y - b_.y) <= threshold;
|
|
71
73
|
};
|
|
72
74
|
|
|
73
75
|
/** Is zero is true if the XY coordinate has a semantic x and y value of zero. */
|
|
@@ -94,15 +96,28 @@ export const translateY = (c: Crude, y: number): XY => {
|
|
|
94
96
|
return { x: p.x, y: p.y + y };
|
|
95
97
|
};
|
|
96
98
|
|
|
99
|
+
type TranslateOverloadOne = (a: Crude, b: Crude, ...cb: Crude[]) => XY;
|
|
100
|
+
type TranslateOverloadTwo = (a: Crude, direction: Direction, value: number) => XY;
|
|
101
|
+
|
|
97
102
|
/**
|
|
98
103
|
* @returns the given coordinate translated by an arbitrary number of translation
|
|
99
104
|
* coordinates.
|
|
100
105
|
*/
|
|
101
|
-
export const translate
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
export const translate: TranslateOverloadOne & TranslateOverloadTwo = (
|
|
107
|
+
a,
|
|
108
|
+
b,
|
|
109
|
+
v,
|
|
110
|
+
...cb
|
|
111
|
+
): XY => {
|
|
112
|
+
if (typeof b === "string" && typeof v === "number") {
|
|
113
|
+
if (b === "x") return translateX(a, v);
|
|
114
|
+
return translateY(a, v);
|
|
115
|
+
}
|
|
116
|
+
return [a, b, v ?? ZERO, ...cb].reduce((p: XY, c) => {
|
|
117
|
+
const xy = construct(c as Crude);
|
|
104
118
|
return { x: p.x + xy.x, y: p.y + xy.y };
|
|
105
119
|
}, ZERO);
|
|
120
|
+
};
|
|
106
121
|
|
|
107
122
|
/**
|
|
108
123
|
* @returns the given coordinate the given direction set to the given value.
|
package/src/telem/series.ts
CHANGED
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import { type z } from "zod";
|
|
10
|
+
import { date, type z } from "zod";
|
|
11
11
|
|
|
12
|
+
import { compare } from "@/compare";
|
|
12
13
|
import { bounds } from "@/spatial";
|
|
13
14
|
import { type GLBufferController, type GLBufferUsage } from "@/telem/gl";
|
|
14
15
|
import {
|
|
@@ -21,7 +22,6 @@ import {
|
|
|
21
22
|
type TimeStamp,
|
|
22
23
|
type CrudeDataType,
|
|
23
24
|
} from "@/telem/telem";
|
|
24
|
-
import { compare } from "@/compare";
|
|
25
25
|
|
|
26
26
|
export type SampleValue = number | bigint;
|
|
27
27
|
|
|
@@ -204,6 +204,22 @@ export class Series {
|
|
|
204
204
|
return new TextDecoder().decode(this.buffer).split("\n").slice(0, -1);
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
toUUIDs(): string[] {
|
|
208
|
+
if (!this.dataType.equals(DataType.UUID))
|
|
209
|
+
throw new Error("cannot convert non-uuid series to uuids");
|
|
210
|
+
const den = DataType.UUID.density.valueOf();
|
|
211
|
+
const r = Array(this.length);
|
|
212
|
+
|
|
213
|
+
for (let i = 0; i < this.length; i++) {
|
|
214
|
+
const v = this.buffer.slice(i * den, (i + 1) * den);
|
|
215
|
+
const id = Array.from(new Uint8Array(v), (b) => b.toString(16).padStart(2, "0"))
|
|
216
|
+
.join("")
|
|
217
|
+
.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, "$1-$2-$3-$4-$5");
|
|
218
|
+
r[i] = id;
|
|
219
|
+
}
|
|
220
|
+
return r;
|
|
221
|
+
}
|
|
222
|
+
|
|
207
223
|
parseJSON<Z extends z.ZodTypeAny>(schema: Z): Array<z.output<Z>> {
|
|
208
224
|
if (!this.dataType.equals(DataType.JSON))
|
|
209
225
|
throw new Error("cannot convert non-string series to strings");
|
|
@@ -217,7 +233,7 @@ export class Series {
|
|
|
217
233
|
/** @returns the time range of this array. */
|
|
218
234
|
get timeRange(): TimeRange {
|
|
219
235
|
validateFieldNotNull("timeRange", this._timeRange);
|
|
220
|
-
return this._timeRange
|
|
236
|
+
return this._timeRange!;
|
|
221
237
|
}
|
|
222
238
|
|
|
223
239
|
/** @returns the capacity of the underlying buffer in bytes. */
|
package/src/telem/telem.ts
CHANGED
|
@@ -328,6 +328,11 @@ export class TimeStamp extends Number implements Stringer {
|
|
|
328
328
|
return remainder(this, divisor);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
/** @returns true if the day portion TimeStamp is today, false otherwise. */
|
|
332
|
+
get isToday(): boolean {
|
|
333
|
+
return this.truncate(TimeSpan.DAY).equals(TimeStamp.now().truncate(TimeSpan.DAY));
|
|
334
|
+
}
|
|
335
|
+
|
|
331
336
|
truncate(span: TimeSpan | TimeStamp): TimeStamp {
|
|
332
337
|
return this.sub(this.remainder(span));
|
|
333
338
|
}
|