@latticexyz/utils 2.0.0-alpha.1.21 → 2.0.0-alpha.1.211
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/dist/index.js +2 -995
- package/dist/index.js.map +1 -1
- package/package.json +20 -33
- package/src/CoordMap.spec.ts +57 -0
- package/src/CoordMap.ts +106 -0
- package/src/VoxelCoordMap.ts +76 -0
- package/src/area.ts +15 -0
- package/{dist/arrays.d.ts → src/arrays.ts} +8 -2
- package/src/bytes.spec.ts +77 -0
- package/src/bytes.ts +85 -0
- package/src/console.ts +62 -0
- package/src/cubic.spec.ts +14 -0
- package/src/cubic.ts +109 -0
- package/src/deferred.ts +14 -0
- package/src/distance.ts +11 -0
- package/src/enums.ts +13 -0
- package/src/eth.ts +42 -0
- package/src/guards.ts +10 -0
- package/src/hash.ts +20 -0
- package/{dist/index.d.ts → src/index.ts} +13 -1
- package/src/iterable.spec.ts +56 -0
- package/src/iterable.ts +59 -0
- package/{dist/math.d.ts → src/math.ts} +4 -1
- package/src/mobx.ts +26 -0
- package/src/objects.ts +16 -0
- package/src/pack.spec.ts +37 -0
- package/src/pack.ts +61 -0
- package/src/promise.ts +46 -0
- package/src/proxy.spec.ts +186 -0
- package/src/proxy.ts +101 -0
- package/{dist/random.d.ts → src/random.ts} +7 -2
- package/src/rx.spec.ts +45 -0
- package/src/rx.ts +124 -0
- package/src/sleep.ts +3 -0
- package/src/types.ts +48 -0
- package/src/uuid.ts +70 -0
- package/src/v2/TableId.ts +49 -0
- package/src/v2/arrayToHex.ts +3 -0
- package/src/v2/bytesToString.ts +1 -0
- package/src/v2/getTableIds.ts +10 -0
- package/src/v2/hexToArray.ts +12 -0
- package/{dist/v2/index.d.ts → src/v2/index.ts} +3 -1
- package/src/v2/isDefined.ts +3 -0
- package/src/v2/isHex.ts +6 -0
- package/src/v2/isNotNull.ts +3 -0
- package/src/v2/stringToBytes.ts +9 -0
- package/src/worker.ts +16 -0
- package/dist/CoordMap.d.ts +0 -26
- package/dist/VoxelCoordMap.d.ts +0 -23
- package/dist/area.d.ts +0 -3
- package/dist/bytes.d.ts +0 -10
- package/dist/console.d.ts +0 -52
- package/dist/cubic.d.ts +0 -32
- package/dist/deferred.d.ts +0 -5
- package/dist/distance.d.ts +0 -8
- package/dist/enums.d.ts +0 -5
- package/dist/eth.d.ts +0 -22
- package/dist/guards.d.ts +0 -3
- package/dist/hash.d.ts +0 -8
- package/dist/iterable.d.ts +0 -10
- package/dist/mobx.d.ts +0 -6
- package/dist/objects.d.ts +0 -11
- package/dist/pack.d.ts +0 -16
- package/dist/promise.d.ts +0 -4
- package/dist/proxy.d.ts +0 -8
- package/dist/rx.d.ts +0 -30
- package/dist/sleep.d.ts +0 -1
- package/dist/types.d.ts +0 -40
- package/dist/uuid.d.ts +0 -19
- package/dist/v2/TableId.d.ts +0 -10
- package/dist/v2/arrayToHex.d.ts +0 -1
- package/dist/v2/bytesToString.d.ts +0 -1
- package/dist/v2/hexToArray.d.ts +0 -1
- package/dist/v2/isDefined.d.ts +0 -1
- package/dist/v2/isHex.d.ts +0 -1
- package/dist/v2/stringToBytes16.d.ts +0 -1
- package/dist/worker.d.ts +0 -6
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatHex,
|
|
3
|
+
hexStringToUint8Array,
|
|
4
|
+
Uint8ArrayToHexString,
|
|
5
|
+
concatUint8Arrays,
|
|
6
|
+
splitUint8Arrays,
|
|
7
|
+
Int32ArrayToUint8Array,
|
|
8
|
+
Uint8ArrayToInt32Array,
|
|
9
|
+
ethAddressToUint8Array,
|
|
10
|
+
} from "./bytes";
|
|
11
|
+
import { random } from "./random";
|
|
12
|
+
|
|
13
|
+
const NUM_TESTS = 1000;
|
|
14
|
+
|
|
15
|
+
describe("bytes", () => {
|
|
16
|
+
describe("Uint8ArrayToHexString", () => {
|
|
17
|
+
it("should be the inverse of hexStringToUint8Array", () => {
|
|
18
|
+
for (let i = 0; i < NUM_TESTS; i++) {
|
|
19
|
+
const hex = formatHex(random(Number.MAX_SAFE_INTEGER).toString(16));
|
|
20
|
+
expect(Uint8ArrayToHexString(hexStringToUint8Array(hex))).toBe(hex);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("hexStringToUint8Array", () => {
|
|
26
|
+
it("should be the inverse of Uint8ArrayToHexString", () => {
|
|
27
|
+
for (let i = 0; i < NUM_TESTS; i++) {
|
|
28
|
+
const uint8arr = hexStringToUint8Array(formatHex(random(Number.MAX_SAFE_INTEGER).toString(16)));
|
|
29
|
+
expect(hexStringToUint8Array(Uint8ArrayToHexString(uint8arr))).toEqual(uint8arr);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("concatUint8Arrays", () => {
|
|
35
|
+
it("should concat Uint8Arrays", () => {
|
|
36
|
+
const arr1 = [123, 456, -134];
|
|
37
|
+
const arr2 = [763, 99887, 0, 0];
|
|
38
|
+
const result = [...arr1, ...arr2];
|
|
39
|
+
expect(concatUint8Arrays(Uint8Array.from(arr1), Uint8Array.from(arr2))).toEqual(Uint8Array.from(result));
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("splitUint8Arrays", () => {
|
|
44
|
+
it("should split Uint8Arrays", () => {
|
|
45
|
+
const arr1 = [123, 456, -134];
|
|
46
|
+
const arr2 = [763, 99887, 0, 0];
|
|
47
|
+
const result = [...arr1, ...arr2];
|
|
48
|
+
expect(splitUint8Arrays(Uint8Array.from(result), [3, 4])).toEqual([Uint8Array.from(arr1), Uint8Array.from(arr2)]);
|
|
49
|
+
expect(splitUint8Arrays(Uint8Array.from(result), [4, 3])).toEqual([
|
|
50
|
+
Uint8Array.from([123, 456, -134, 763]),
|
|
51
|
+
Uint8Array.from([99887, 0, 0]),
|
|
52
|
+
]);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("Uint8ArrayToInt32Array", () => {
|
|
57
|
+
it("should be the inverse of Int32ArrayToUint8Array", () => {
|
|
58
|
+
const input = [123, -456, 0, 2 ** 31 - 1, -1 * 2 ** 31];
|
|
59
|
+
const bytes = Int32ArrayToUint8Array(input);
|
|
60
|
+
expect(Uint8ArrayToInt32Array(bytes)).toEqual(input);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("Int32ArrayToUint8Array", () => {
|
|
65
|
+
it("should be the inverse of Uint8ArrayToInt32Array", () => {
|
|
66
|
+
const input = [123, -456, 0, 2 ** 31 - 1, -1 * 2 ** 31];
|
|
67
|
+
const bytes = Int32ArrayToUint8Array(input);
|
|
68
|
+
expect(Int32ArrayToUint8Array(Uint8ArrayToInt32Array(bytes))).toEqual(bytes);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("ethAddressToUint8Array", () => {
|
|
73
|
+
it("should produce a 160bit Uint8Array", () => {
|
|
74
|
+
expect(ethAddressToUint8Array("0x00").length).toBe(20);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
package/src/bytes.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { toEthAddress } from "./eth";
|
|
2
|
+
import { hexToArray } from "./v2";
|
|
3
|
+
|
|
4
|
+
export function formatHex(hex: string): string {
|
|
5
|
+
if (hex.substring(0, 2) == "0x") hex = hex.substring(2);
|
|
6
|
+
const prefix = hex.length % 2 !== 0 ? "0x0" : "0x";
|
|
7
|
+
return prefix + hex;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function hexStringToUint8Array(hexString: string): Uint8Array {
|
|
11
|
+
return hexToArray(hexString);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function Uint8ArrayToHexString(data: Uint8Array): string {
|
|
15
|
+
if (data.length === 0) return "0x00";
|
|
16
|
+
return formatHex(data.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function concatUint8Arrays(...arrays: Uint8Array[]): Uint8Array {
|
|
20
|
+
return Uint8Array.from(
|
|
21
|
+
arrays.reduce<number[]>((acc, curr) => {
|
|
22
|
+
return [...acc, ...curr];
|
|
23
|
+
}, [])
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function splitUint8Arrays(data: Uint8Array, byteLengths: number[]): Uint8Array[] {
|
|
28
|
+
const arrays: Uint8Array[] = [];
|
|
29
|
+
let i = 0;
|
|
30
|
+
for (const length of byteLengths) {
|
|
31
|
+
const array = new Uint8Array(length);
|
|
32
|
+
arrays.push(array);
|
|
33
|
+
for (let j = 0; j < length; j++) {
|
|
34
|
+
array[j] = data[i];
|
|
35
|
+
i++;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return arrays;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function Int32ArrayToUint8Array(input: number[]): Uint8Array {
|
|
42
|
+
const buffer = new ArrayBuffer(input.length * 4);
|
|
43
|
+
const int32arr = new Int32Array(buffer);
|
|
44
|
+
for (let i = 0; i < input.length; i++) {
|
|
45
|
+
int32arr[i] = input[i];
|
|
46
|
+
}
|
|
47
|
+
return new Uint8Array(buffer);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function Uint8ArrayToInt32Array(input: Uint8Array): number[] {
|
|
51
|
+
return [...new Int32Array(input.buffer)];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function ethAddressToUint8Array(address: string): Uint8Array {
|
|
55
|
+
return hexStringToUint8Array(toEthAddress(address));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// https://stackoverflow.com/a/55330424
|
|
59
|
+
export function createToInt(size: number) {
|
|
60
|
+
if (size < 2) {
|
|
61
|
+
throw new Error("Minimum size is 2");
|
|
62
|
+
} else if (size > 64) {
|
|
63
|
+
throw new Error("Maximum size is 64");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Determine value range
|
|
67
|
+
const maxValue = 2 ** (size - 1) - 1;
|
|
68
|
+
const minValue = -maxValue - 1;
|
|
69
|
+
|
|
70
|
+
return (value: number) => {
|
|
71
|
+
value = value << 0;
|
|
72
|
+
if (value > maxValue || value < minValue) {
|
|
73
|
+
console.log("value", value, maxValue, minValue, value > maxValue, value < minValue);
|
|
74
|
+
throw new Error(`Int${size} overflow`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (value < 0) {
|
|
78
|
+
return 2 ** size + value;
|
|
79
|
+
} else {
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const toInt32 = createToInt(32);
|
package/src/console.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const TOPICS_KEY = "mud-logger-topics";
|
|
2
|
+
|
|
3
|
+
export function enableLogger() {
|
|
4
|
+
const windowConsole = window.console;
|
|
5
|
+
let filtersActive = Boolean(localStorage.getItem(TOPICS_KEY));
|
|
6
|
+
|
|
7
|
+
const topicsString = localStorage.getItem(TOPICS_KEY);
|
|
8
|
+
let topics: string[] = topicsString ? JSON.parse(topicsString) : [];
|
|
9
|
+
|
|
10
|
+
function log(...logs: string[]) {
|
|
11
|
+
if (filtersActive) return;
|
|
12
|
+
windowConsole.log(...logs);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function logWithTopic(topic: string, ...logs: string[]) {
|
|
16
|
+
if (!filtersActive || topics.includes(topic)) {
|
|
17
|
+
windowConsole.log(`--- BETTER CONSOLE / TOPIC ${topic} ---`);
|
|
18
|
+
windowConsole.log(...logs);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function enableFilters() {
|
|
23
|
+
localStorage.setItem(TOPICS_KEY, JSON.stringify([]));
|
|
24
|
+
filtersActive = true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function disableFilters() {
|
|
28
|
+
localStorage.removeItem(TOPICS_KEY);
|
|
29
|
+
filtersActive = false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function addTopic(topic: string) {
|
|
33
|
+
topics.push(topic);
|
|
34
|
+
localStorage.setItem(TOPICS_KEY, JSON.stringify(topics));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function removeTopic(topic: string) {
|
|
38
|
+
topics = topics.filter((t) => t !== topic);
|
|
39
|
+
localStorage.setItem(TOPICS_KEY, JSON.stringify(topics));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function resetTopics() {
|
|
43
|
+
topics = [];
|
|
44
|
+
localStorage.setItem(TOPICS_KEY, JSON.stringify(topics));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const logger = {
|
|
48
|
+
...windowConsole,
|
|
49
|
+
log,
|
|
50
|
+
logWithTopic,
|
|
51
|
+
enableFilters,
|
|
52
|
+
disableFilters,
|
|
53
|
+
addTopic,
|
|
54
|
+
removeTopic,
|
|
55
|
+
resetTopics,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
59
|
+
(window as any).logger = logger;
|
|
60
|
+
window.console = logger;
|
|
61
|
+
return logger;
|
|
62
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { cubicNoiseSample2, cubicNoiseConfig } from "./cubic";
|
|
2
|
+
|
|
3
|
+
describe("Cubic", () => {
|
|
4
|
+
it("computes noise", () => {
|
|
5
|
+
const coords: [number, number][] = [];
|
|
6
|
+
for (let i = 0; i < 100; i++) {
|
|
7
|
+
coords.push([Math.floor((Math.random() - 0.5) * 1000), Math.floor((Math.random() - 0.5) * 1000)]);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const noises = coords.map((c) => cubicNoiseSample2(cubicNoiseConfig(1000, 64, 16, 256, 256), c[0], c[1]));
|
|
11
|
+
console.log("coords", JSON.stringify(coords));
|
|
12
|
+
console.log("noises", JSON.stringify(noises));
|
|
13
|
+
});
|
|
14
|
+
});
|
package/src/cubic.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
const RND_A = 134775813;
|
|
2
|
+
const RND_B = 1103515245;
|
|
3
|
+
const ACCURACY = 1000;
|
|
4
|
+
|
|
5
|
+
export function randomize(seed: number, x: number, y: number) {
|
|
6
|
+
return (((((x ^ y) * RND_A) ^ (seed + x)) * (((RND_B * x) << 16) ^ (RND_B * y - RND_A))) >>> 0) / 4294967295;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function tile(coordinate: number, period: number) {
|
|
10
|
+
if (coordinate < 0) while (coordinate < 0) coordinate += period;
|
|
11
|
+
return coordinate % period;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function interpolate(a: number, b: number, c: number, d: number, x: number, s: number, scale: number) {
|
|
15
|
+
const p = d - c - (a - b);
|
|
16
|
+
return (b * Math.pow(s, 3) + x * (c * Math.pow(s, 2) + a * s * (-s + x) + x * (-(b + p) * s + p * x))) * scale;
|
|
17
|
+
|
|
18
|
+
// return (x) * ((x ) * ((x ) * p + (a - b - p)) + (c - a)) + b;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Config a cubic noise.
|
|
23
|
+
* @param {Number} seed A seed in the range [0, 1].
|
|
24
|
+
* @param {Number} [periodX] The number of units after which the x coordinate repeats.
|
|
25
|
+
* @param {Number} [periodY] The number of units after which the y coordinate repeats.
|
|
26
|
+
* @returns {Object} A configuration object used by noise functions.
|
|
27
|
+
*/
|
|
28
|
+
export function cubicNoiseConfig(
|
|
29
|
+
seed: number,
|
|
30
|
+
octave: number,
|
|
31
|
+
scale: number,
|
|
32
|
+
periodX = Number.MAX_SAFE_INTEGER,
|
|
33
|
+
periodY = Number.MAX_SAFE_INTEGER
|
|
34
|
+
) {
|
|
35
|
+
return {
|
|
36
|
+
seed: Math.floor(seed * Number.MAX_SAFE_INTEGER),
|
|
37
|
+
periodX: periodX,
|
|
38
|
+
periodY: periodY,
|
|
39
|
+
octave,
|
|
40
|
+
scale,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Sample 1D cubic noise.
|
|
46
|
+
* @param {Object} config A valid noise configuration.
|
|
47
|
+
* @param {Number} x The X position to sample at.
|
|
48
|
+
* @returns {Number} A noise value in the range [0, 1].
|
|
49
|
+
*/
|
|
50
|
+
export function cubicNoiseSample1(config: ReturnType<typeof cubicNoiseConfig>, x: number) {
|
|
51
|
+
const xi = Math.floor(x);
|
|
52
|
+
const lerp = x - xi;
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
interpolate(
|
|
56
|
+
randomize(config.seed, tile(xi - 1, config.periodX), 0),
|
|
57
|
+
randomize(config.seed, tile(xi, config.periodX), 0),
|
|
58
|
+
randomize(config.seed, tile(xi + 1, config.periodX), 0),
|
|
59
|
+
randomize(config.seed, tile(xi + 2, config.periodX), 0),
|
|
60
|
+
lerp,
|
|
61
|
+
1,
|
|
62
|
+
1
|
|
63
|
+
) *
|
|
64
|
+
0.666666 +
|
|
65
|
+
0.166666
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Sample 2D cubic noise.
|
|
71
|
+
* @param {Object} config A valid noise configuration.
|
|
72
|
+
* @param {Number} x The X position to sample at.
|
|
73
|
+
* @param {Number} y The Y position to sample at.
|
|
74
|
+
* @returns {Number} A noise value in the range [0, 1].
|
|
75
|
+
*/
|
|
76
|
+
export function cubicNoiseSample2(
|
|
77
|
+
{ octave, periodX, periodY, seed, scale }: ReturnType<typeof cubicNoiseConfig>,
|
|
78
|
+
x: number,
|
|
79
|
+
y: number
|
|
80
|
+
) {
|
|
81
|
+
const xi = Math.floor(x / octave);
|
|
82
|
+
const lerpX = Math.floor((x * ACCURACY) / octave) - xi * ACCURACY;
|
|
83
|
+
const yi = Math.floor(y / octave);
|
|
84
|
+
const lerpY = Math.floor((y * ACCURACY) / octave) - yi * ACCURACY;
|
|
85
|
+
const x0 = tile(xi - 1, periodX);
|
|
86
|
+
const x1 = tile(xi, periodX);
|
|
87
|
+
const x2 = tile(xi + 1, periodX);
|
|
88
|
+
const x3 = tile(xi + 2, periodX);
|
|
89
|
+
|
|
90
|
+
const xSamples = new Array(4);
|
|
91
|
+
|
|
92
|
+
for (let i = 0; i < 4; ++i) {
|
|
93
|
+
const y = tile(yi - 1 + i, periodY);
|
|
94
|
+
|
|
95
|
+
xSamples[i] = interpolate(
|
|
96
|
+
randomize(seed, x0, y),
|
|
97
|
+
randomize(seed, x1, y),
|
|
98
|
+
randomize(seed, x2, y),
|
|
99
|
+
randomize(seed, x3, y),
|
|
100
|
+
lerpX,
|
|
101
|
+
ACCURACY,
|
|
102
|
+
1
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return Math.floor(
|
|
107
|
+
interpolate(xSamples[0], xSamples[1], xSamples[2], xSamples[3], lerpY, ACCURACY, scale) / Math.pow(ACCURACY, 6)
|
|
108
|
+
);
|
|
109
|
+
}
|
package/src/deferred.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A convenient way to create a promise with resolve and reject functions.
|
|
3
|
+
* @returns Tuple with resolve function, reject function and promise.
|
|
4
|
+
*/
|
|
5
|
+
export function deferred<T>(): [(t: T) => void, (t: Error) => void, Promise<T>] {
|
|
6
|
+
let resolve: ((t: T) => void) | null = null;
|
|
7
|
+
let reject: ((t: Error) => void) | null = null;
|
|
8
|
+
const promise = new Promise<T>((r, rj) => {
|
|
9
|
+
resolve = (t: T) => r(t);
|
|
10
|
+
reject = (e: Error) => rj(e);
|
|
11
|
+
});
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
return [resolve as any, reject as any, promise];
|
|
14
|
+
}
|
package/src/distance.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute the Euclidean distance between two points
|
|
3
|
+
* https://en.wikipedia.org/wiki/Euclidean_distance
|
|
4
|
+
* @param a
|
|
5
|
+
* @param b
|
|
6
|
+
* @returns Euclidian distance between a and b
|
|
7
|
+
*/
|
|
8
|
+
export function euclidean(a: number[], b: number[]): number {
|
|
9
|
+
if (a.length !== b.length) throw new Error("points must have same dimension");
|
|
10
|
+
return Math.sqrt(a.reduce((acc, _, i) => acc + Math.pow(a[i] - b[i], 2), 0));
|
|
11
|
+
}
|
package/src/enums.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param enm Numeric enum
|
|
3
|
+
* @returns Number array containing the enum values
|
|
4
|
+
*/
|
|
5
|
+
export function numValues(enm: object): number[] {
|
|
6
|
+
const nums: number[] = [];
|
|
7
|
+
for (const val of Object.values(enm)) {
|
|
8
|
+
if (!isNaN(Number(val))) {
|
|
9
|
+
nums.push(Number(val));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return nums;
|
|
13
|
+
}
|
package/src/eth.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pads start of a hex string with 0 to create a bit string of the given length
|
|
3
|
+
* @param input Hex string
|
|
4
|
+
* @param bits Number of bits in the output hex string
|
|
5
|
+
* @returns Hex string of specified length
|
|
6
|
+
*/
|
|
7
|
+
export function padToBitLength(input: string, bits: number) {
|
|
8
|
+
// Cut off 0x prefix
|
|
9
|
+
if (input.substring(0, 2) == "0x") input = input.substring(2);
|
|
10
|
+
// Pad start with 0 to get desired bit length
|
|
11
|
+
const length = bits / 4;
|
|
12
|
+
input = input.padStart(length, "0");
|
|
13
|
+
input = input.substring(input.length - length);
|
|
14
|
+
// Prefix with 0x
|
|
15
|
+
return `0x${input}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Pads start of a hex string with 0 to create a 160 bit hex string
|
|
20
|
+
* which can be used as an Ethereum address
|
|
21
|
+
* @param input Hex string
|
|
22
|
+
* @returns 160 bit hex string
|
|
23
|
+
*/
|
|
24
|
+
export function toEthAddress(input: string) {
|
|
25
|
+
return padToBitLength(input, 160);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Pads start of a hex string with 0 to create a 256bit hex string
|
|
30
|
+
* which can be used as an Ethereum address
|
|
31
|
+
* @param input Hex string
|
|
32
|
+
* @returns 256 bit hex string
|
|
33
|
+
*/
|
|
34
|
+
export function to256BitString(input: string) {
|
|
35
|
+
return padToBitLength(input, 256);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function extractEncodedArguments(input: string) {
|
|
39
|
+
// Cutting off the first 4 bytes, which represent the function selector
|
|
40
|
+
if (input[0] !== "0" && input[1] !== "x") throw new Error("Invalid hex string");
|
|
41
|
+
return "0x" + input.substring(10);
|
|
42
|
+
}
|
package/src/guards.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { Func } from "./types";
|
|
3
|
+
|
|
4
|
+
export function isObject(c: unknown): c is Record<string, any> {
|
|
5
|
+
return typeof c === "object" && !Array.isArray(c) && c !== null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function isFunction(c: unknown): c is Func<any, any> {
|
|
9
|
+
return c instanceof Function;
|
|
10
|
+
}
|
package/src/hash.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BigNumber } from "ethers";
|
|
2
|
+
import { keccak256 as keccak256Bytes, toUtf8Bytes } from "ethers/lib/utils.js";
|
|
3
|
+
import { Coord } from "./types";
|
|
4
|
+
|
|
5
|
+
import { defaultAbiCoder as abi } from "ethers/lib/utils.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Compute keccak256 hash from given string and remove padding from the resulting hex string
|
|
9
|
+
* @param data String to be hashed
|
|
10
|
+
* @returns Hash of the given string as hex string without padding
|
|
11
|
+
*/
|
|
12
|
+
export function keccak256(data: string) {
|
|
13
|
+
return BigNumber.from(keccak256Bytes(toUtf8Bytes(data))).toHexString();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function keccak256Coord(coord: Coord): string {
|
|
17
|
+
// TODO: make faster by implementing in wasm
|
|
18
|
+
const bytes = abi.encode(["int32", "int32"], [coord.x, coord.y]);
|
|
19
|
+
return keccak256Bytes(bytes);
|
|
20
|
+
}
|
|
@@ -24,4 +24,16 @@ export * from "./distance";
|
|
|
24
24
|
export * from "./math";
|
|
25
25
|
export * from "./bytes";
|
|
26
26
|
export * from "./v2";
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
export type {
|
|
29
|
+
Cached,
|
|
30
|
+
CachedValue,
|
|
31
|
+
Func,
|
|
32
|
+
AsyncFunc,
|
|
33
|
+
PromiseValue,
|
|
34
|
+
ValueOf,
|
|
35
|
+
Area,
|
|
36
|
+
Coord,
|
|
37
|
+
VoxelCoord,
|
|
38
|
+
Logger,
|
|
39
|
+
} from "./types";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { arrayToIterator, mergeIterators } from "./iterable";
|
|
2
|
+
|
|
3
|
+
describe("arrayToIterator", () => {
|
|
4
|
+
it("should return an iterable iterator with the same content as the array", () => {
|
|
5
|
+
const array = ["a", "b", "c", 1, 2, 3];
|
|
6
|
+
const iterator = arrayToIterator(array);
|
|
7
|
+
expect([...iterator]).toEqual(array);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("should not return a next value if the array is empty", () => {
|
|
11
|
+
const array: string[] = [];
|
|
12
|
+
const iterator = arrayToIterator(array);
|
|
13
|
+
expect([...iterator]).toEqual(array);
|
|
14
|
+
|
|
15
|
+
const mock = jest.fn();
|
|
16
|
+
for (const item of iterator) {
|
|
17
|
+
mock(item);
|
|
18
|
+
}
|
|
19
|
+
expect(mock).not.toHaveBeenCalled();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should not be possible to iterate over an iterator multiple times", () => {
|
|
23
|
+
const array = ["a", "b", "c", 1, 2, 3];
|
|
24
|
+
const iterator = arrayToIterator(array);
|
|
25
|
+
expect([...iterator]).toEqual(array);
|
|
26
|
+
expect([...iterator]).toEqual([]);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("mergeIterators", () => {
|
|
31
|
+
it("should return a merged iterator", () => {
|
|
32
|
+
const a = arrayToIterator(["a", "b", "c"]);
|
|
33
|
+
const b = arrayToIterator([1, 2, 3]);
|
|
34
|
+
expect([...mergeIterators(a, b)]).toEqual([
|
|
35
|
+
["a", 1],
|
|
36
|
+
["b", 2],
|
|
37
|
+
["c", 3],
|
|
38
|
+
]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should work with iterators of unequal length", () => {
|
|
42
|
+
const a = arrayToIterator(["a"]);
|
|
43
|
+
const b = arrayToIterator([1, 2, 3]);
|
|
44
|
+
expect([...mergeIterators(a, b)]).toEqual([
|
|
45
|
+
["a", 1],
|
|
46
|
+
[null, 2],
|
|
47
|
+
[null, 3],
|
|
48
|
+
]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should return an empty iterator if both inputs are empty", () => {
|
|
52
|
+
const a = arrayToIterator([]);
|
|
53
|
+
const b = arrayToIterator([]);
|
|
54
|
+
expect([...mergeIterators(a, b)]).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
});
|
package/src/iterable.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export function makeIterable<T>(iterator: Iterator<T>): IterableIterator<T> {
|
|
2
|
+
const iterable: IterableIterator<T> = {
|
|
3
|
+
...iterator,
|
|
4
|
+
[Symbol.iterator]() {
|
|
5
|
+
return this;
|
|
6
|
+
},
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
return iterable;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function concatIterators<T>(first: Iterator<T>, second?: Iterator<T>): IterableIterator<T> {
|
|
13
|
+
if (!second) return makeIterable(first);
|
|
14
|
+
return makeIterable({
|
|
15
|
+
next() {
|
|
16
|
+
const next = first.next();
|
|
17
|
+
if (!next.done) return next;
|
|
18
|
+
return second.next();
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function mergeIterators<A, B>(iteratorA: Iterator<A>, iteratorB: Iterator<B>): IterableIterator<[A, B]> {
|
|
24
|
+
const iterator: Iterator<[A, B]> = {
|
|
25
|
+
next() {
|
|
26
|
+
const nextA = iteratorA.next();
|
|
27
|
+
const nextB = iteratorB.next();
|
|
28
|
+
if (nextA.done && nextB.done) return { done: true, value: null };
|
|
29
|
+
return { value: [nextA.value, nextB.value] };
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
return makeIterable(iterator);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function transformIterator<A, B>(iterator: Iterator<A>, transform: (value: A) => B): IterableIterator<B> {
|
|
36
|
+
return makeIterable({
|
|
37
|
+
next() {
|
|
38
|
+
const { done, value } = iterator.next();
|
|
39
|
+
return { done, value: done ? value : transform(value) };
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Turns an array into an iterator. NOTE: an iterator can only be iterated once.
|
|
46
|
+
* @param array Array to be turned into an iterator
|
|
47
|
+
* @returns Iterator to iterate through the array
|
|
48
|
+
*/
|
|
49
|
+
export function arrayToIterator<T>(array: T[]): IterableIterator<T> {
|
|
50
|
+
let i = 0;
|
|
51
|
+
const iterator: Iterator<T> = {
|
|
52
|
+
next() {
|
|
53
|
+
const done = i >= array.length;
|
|
54
|
+
if (done) return { done, value: null };
|
|
55
|
+
return { value: array[i++] };
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
return makeIterable(iterator);
|
|
59
|
+
}
|
|
@@ -5,4 +5,7 @@
|
|
|
5
5
|
* @param x A numeric expression.
|
|
6
6
|
* @returns Input rounded towards zero.
|
|
7
7
|
*/
|
|
8
|
-
export
|
|
8
|
+
export function roundTowardsZero(x: number) {
|
|
9
|
+
const sign = x < 0 ? -1 : 1;
|
|
10
|
+
return sign * Math.floor(Math.abs(x));
|
|
11
|
+
}
|
package/src/mobx.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { IComputedValue, IObservableValue, reaction } from "mobx";
|
|
2
|
+
import { deferred } from "./deferred";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param comp Computed/Observable value that is either defined or undefined
|
|
6
|
+
* @returns promise that resolves with the first truthy computed value
|
|
7
|
+
*/
|
|
8
|
+
export async function awaitValue<T>(comp: IComputedValue<T | undefined> | IObservableValue<T | undefined>): Promise<T> {
|
|
9
|
+
const [resolve, , promise] = deferred<T>();
|
|
10
|
+
|
|
11
|
+
const dispose = reaction(
|
|
12
|
+
() => comp.get(),
|
|
13
|
+
(value) => {
|
|
14
|
+
if (value) {
|
|
15
|
+
resolve(value);
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
{ fireImmediately: true }
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const value = await promise;
|
|
22
|
+
// Dispose the reaction once the promise is resolved
|
|
23
|
+
dispose();
|
|
24
|
+
|
|
25
|
+
return value;
|
|
26
|
+
}
|
package/src/objects.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility function to map a source object to an object with the same keys but mapped values
|
|
3
|
+
* @param source Source object to be mapped
|
|
4
|
+
* @param valueMap Mapping values of the source object to values of the target object
|
|
5
|
+
* @returns An object with the same keys as the source object but mapped values
|
|
6
|
+
*/
|
|
7
|
+
export function mapObject<S extends { [key: string]: unknown }, T extends { [key in keyof S]: unknown }>(
|
|
8
|
+
source: S,
|
|
9
|
+
valueMap: (value: S[keyof S], key: keyof S) => T[keyof S]
|
|
10
|
+
): T {
|
|
11
|
+
const target: Partial<{ [key in keyof typeof source]: T[keyof S] }> = {};
|
|
12
|
+
for (const key in source) {
|
|
13
|
+
target[key] = valueMap(source[key], key);
|
|
14
|
+
}
|
|
15
|
+
return target as T;
|
|
16
|
+
}
|