chore: documentation and optimization
What a rhyme.
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
import * as esbuild from "esbuild";
|
||||||
|
|
||||||
|
const result = esbuild.buildSync({
|
||||||
|
entryPoints: ["src/index.ts"],
|
||||||
|
bundle: true,
|
||||||
|
minify: true,
|
||||||
|
outdir: "dist",
|
||||||
|
define: {
|
||||||
|
"import.meta.vitest": "undefined",
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
import { writeFileSync } from "fs";
|
||||||
|
import { relative } from "path";
|
||||||
|
|
||||||
|
function capitalize(/** @type {string} */ s) {
|
||||||
|
return s.slice(0, 1).toUpperCase() + s.slice(1).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
const script = import.meta.filename;
|
||||||
|
|
||||||
|
const path = "./src/mixin/math/swizzlePermutations.d.ts";
|
||||||
|
const axis = ["x", "y", "z", "w", "v"];
|
||||||
|
|
||||||
|
const property = ["first", "second", "third", "fourth", "fifth"];
|
||||||
|
|
||||||
|
const mixin = "./src/mixin/base/mixin";
|
||||||
|
|
||||||
|
let conditional = "";
|
||||||
|
|
||||||
|
for (let n = 0; n < axis.length; n++) {
|
||||||
|
if (n === axis.length - 1) {
|
||||||
|
conditional += `Swizzle${n + 1}D<T, t>`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const type = new Array(n + 1).fill("number").join(", ");
|
||||||
|
const indent = "\t".repeat(n + 1);
|
||||||
|
conditional += `T extends readonly [${type}]\n`;
|
||||||
|
conditional += `${indent}? Swizzle${n + 1}D<T, t>\n`;
|
||||||
|
conditional += `${indent}: `;
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = new Array(axis.length).fill("number").join(", ");
|
||||||
|
|
||||||
|
let src = `
|
||||||
|
// .d.ts file auto-generated by ${relative(path, script)}
|
||||||
|
// Do not edit
|
||||||
|
|
||||||
|
import type { Props, Return } from '${relative(path, mixin)}';
|
||||||
|
|
||||||
|
type IsValid<T extends readonly number[]> = number extends T['length']
|
||||||
|
? false
|
||||||
|
: T extends readonly []
|
||||||
|
? false
|
||||||
|
: true;
|
||||||
|
|
||||||
|
export type SwizzleProps<T extends readonly number[], t extends Props> =
|
||||||
|
IsValid<T> extends false
|
||||||
|
? {}
|
||||||
|
: ${conditional
|
||||||
|
.split("\n")
|
||||||
|
.map((line) => "\t\t" + line)
|
||||||
|
.join("\n")
|
||||||
|
.trimStart()};
|
||||||
|
|
||||||
|
export interface ISwizzle<
|
||||||
|
T extends readonly [${type}] =
|
||||||
|
readonly [${type}],
|
||||||
|
t extends Props = Props
|
||||||
|
> extends Swizzle${axis.length}D<T, t> {};
|
||||||
|
|
||||||
|
`.trimStart();
|
||||||
|
|
||||||
|
for (let n = 0; n < axis.length; n++) {
|
||||||
|
const state = new Array(axis.length).fill(-1);
|
||||||
|
|
||||||
|
const props = [];
|
||||||
|
|
||||||
|
outer: do {
|
||||||
|
for (let i = state.length - 1; i >= 0; i--)
|
||||||
|
if (++state[i] > n) {
|
||||||
|
state[i] = -1;
|
||||||
|
} else break;
|
||||||
|
|
||||||
|
let nulled = false;
|
||||||
|
for (const item of state) {
|
||||||
|
if (item === -1) {
|
||||||
|
nulled = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (nulled) continue outer;
|
||||||
|
}
|
||||||
|
if (state.every((x) => x === -1)) break;
|
||||||
|
|
||||||
|
const key = state
|
||||||
|
.filter((x) => x !== -1)
|
||||||
|
.map((x) => axis[x])
|
||||||
|
.join("");
|
||||||
|
if (state.slice(1).every((x) => x === -1)) {
|
||||||
|
const arr = [5, 3, 8, 2, 1].map((x) =>
|
||||||
|
Math.abs(x - n - state[0]),
|
||||||
|
);
|
||||||
|
props.push({
|
||||||
|
doc: `
|
||||||
|
/**
|
||||||
|
* Vector-style accessor, ${property[state[0]]} property of the value
|
||||||
|
* @from {@link Math \`Math\`}
|
||||||
|
* @example
|
||||||
|
* \`\`\`ts
|
||||||
|
* const vec = [${arr
|
||||||
|
.slice(0, Math.max(n + 1, 2))
|
||||||
|
.map((n) => n.toString())
|
||||||
|
.join(", ")}] as const;
|
||||||
|
*
|
||||||
|
* const ${axis[state[0]]} = $(vec).${axis[state[0]]}.value;
|
||||||
|
* expect(${axis[state[0]]}).toBe(${arr[state[0]]})
|
||||||
|
* \`\`\`
|
||||||
|
*/`.trimStart(),
|
||||||
|
property: `readonly ${key}: Return<T[${state[0]}], t>`,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
if (state.every((x) => x === count++)) continue;
|
||||||
|
|
||||||
|
const arr = [6, 2, 3, 5, 4, 7].map((x, idx) =>
|
||||||
|
Math.abs(x - state[idx]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const type = state
|
||||||
|
.filter((x) => x !== -1)
|
||||||
|
.map((x) => `T[${x}]`)
|
||||||
|
.join(", ");
|
||||||
|
props.push({
|
||||||
|
doc: `
|
||||||
|
/**
|
||||||
|
* Vector-style swizzle accessor, rearannges the elements of the value
|
||||||
|
*
|
||||||
|
* Equivalent to \`[${state
|
||||||
|
.filter((x) => x !== -1)
|
||||||
|
.map((x) => `vec[${x}]`)
|
||||||
|
.join(", ")}]\`
|
||||||
|
* @from {@link Math \`Math\`}
|
||||||
|
* @example
|
||||||
|
* \`\`\`ts
|
||||||
|
* const vec = [${arr.slice(0, n + 1).join(", ")}] as const;
|
||||||
|
*
|
||||||
|
* const ${key} = $(vec).${key}().value;
|
||||||
|
* expect(${key}).toMatchObject([${state
|
||||||
|
.filter((x) => x !== -1)
|
||||||
|
.map((x) => arr[x])
|
||||||
|
.join(", ")}])
|
||||||
|
* \`\`\`
|
||||||
|
*/
|
||||||
|
`.trimStart(),
|
||||||
|
property: `readonly ${key}: () => Return<[${type}], t>`,
|
||||||
|
});
|
||||||
|
} while (state.some((x) => x !== -1));
|
||||||
|
|
||||||
|
props.sort((a, b) => a.property.length - b.property.length);
|
||||||
|
|
||||||
|
src += `interface Swizzle${n + 1}D<T extends readonly [${new Array(n + 1).fill("number").join(", ")}], t extends Props> {\n`;
|
||||||
|
src += `${props
|
||||||
|
.map((x) =>
|
||||||
|
[x.doc, x.property]
|
||||||
|
.join("\n")
|
||||||
|
.split("\n")
|
||||||
|
.map((line) => "\t" + line)
|
||||||
|
.join("\n"),
|
||||||
|
)
|
||||||
|
.join("\n")}\n`;
|
||||||
|
src += "}\n";
|
||||||
|
src += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(path, src);
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
src/**/*.d.ts
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { defineConfig } from "gen-doctests/config";
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
include: ["src/**/*.{js,ts}"],
|
include: ["src/**/*.{js,ts}"],
|
||||||
|
exclude: ["src/**/*.d.ts"],
|
||||||
outDir: "tests/generated",
|
outDir: "tests/generated",
|
||||||
templateHeader: [
|
templateHeader: [
|
||||||
"import { test, expect, expectTypeOf, describe, vi } from 'vitest'",
|
"import { test, expect, expectTypeOf, describe, vi } from 'vitest'",
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ import tseslint from "typescript-eslint";
|
|||||||
import { defineConfig, globalIgnores } from "eslint/config";
|
import { defineConfig, globalIgnores } from "eslint/config";
|
||||||
|
|
||||||
export default defineConfig([
|
export default defineConfig([
|
||||||
globalIgnores(["dist", "tests/generated", "coverage"]),
|
globalIgnores(["dist", "tests/generated", "coverage", "*.d.ts"]),
|
||||||
{
|
{
|
||||||
files: ["**/*.{ts,tsx}"],
|
files: ["**/*.{ts,tsx}"],
|
||||||
extends: [
|
extends: [
|
||||||
|
|||||||
+7
-4
@@ -4,11 +4,14 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "gen-doctests && vitest --run --reporter=tree --coverage --typecheck",
|
"test": "gen-doctests && vitest --run --reporter=tree --typecheck --cache",
|
||||||
"build": "tsc -b && esbuild --minify --bundle src/index.ts --outdir=dist --define:import.meta.vitest=undefined",
|
"coverage": "gen-doctests && vitest --run --reporter=dot --coverage --cache",
|
||||||
|
"build:generate": "node .config/generateSwizzle.js",
|
||||||
|
"build": "tsc -b && node .config/build.js",
|
||||||
"fmt": "prettier --write .",
|
"fmt": "prettier --write .",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview"
|
"prepublish": "pnpm build:generate",
|
||||||
|
"prepack": "pnpm test && pnpm build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
@@ -21,7 +24,7 @@
|
|||||||
"jiti": "^2.7.0",
|
"jiti": "^2.7.0",
|
||||||
"madge": "^8.0.0",
|
"madge": "^8.0.0",
|
||||||
"prettier": "^3.8.4",
|
"prettier": "^3.8.4",
|
||||||
"typescript": "6",
|
"typescript": "^6.0.3",
|
||||||
"typescript-eslint": "^8.59.2",
|
"typescript-eslint": "^8.59.2",
|
||||||
"vitest": "^4.1.9"
|
"vitest": "^4.1.9"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+1
-1
@@ -39,7 +39,7 @@ importers:
|
|||||||
specifier: ^3.8.4
|
specifier: ^3.8.4
|
||||||
version: 3.8.4
|
version: 3.8.4
|
||||||
typescript:
|
typescript:
|
||||||
specifier: '6'
|
specifier: ^6.0.3
|
||||||
version: 6.0.3
|
version: 6.0.3
|
||||||
typescript-eslint:
|
typescript-eslint:
|
||||||
specifier: ^8.59.2
|
specifier: ^8.59.2
|
||||||
|
|||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
import type { Fluent } from ".";
|
import type { Fluent } from ".";
|
||||||
import { never } from "../internal";
|
import { phantom } from "../internal";
|
||||||
import type { Registry } from "../registry";
|
import type { Registry } from "../registry";
|
||||||
import type { RecPartial } from "../utility";
|
import type { RecPartial } from "../utility";
|
||||||
import type { HKT } from "./hkt";
|
import type { HKT } from "./hkt";
|
||||||
@@ -29,8 +29,8 @@ export function Mixin<I, T extends MixinHKT<I>>(
|
|||||||
fn: MixinFn<I>,
|
fn: MixinFn<I>,
|
||||||
): Mixin<I, T> {
|
): Mixin<I, T> {
|
||||||
return {
|
return {
|
||||||
interface: never,
|
interface: phantom,
|
||||||
hkt: never,
|
hkt: phantom,
|
||||||
fn,
|
fn,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ export interface Identity extends HKT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||||
export const never = undefined as never;
|
export const phantom = undefined as never;
|
||||||
|
|
||||||
export function isArray(v: unknown): v is unknown[] {
|
export function isArray(v: unknown): v is unknown[] {
|
||||||
return typeof v === "object" && v !== null && Array.isArray(v);
|
return typeof v === "object" && v !== null && Array.isArray(v);
|
||||||
|
|||||||
+21
-3
@@ -6,8 +6,9 @@ import {
|
|||||||
type Input,
|
type Input,
|
||||||
type Props,
|
type Props,
|
||||||
type Return,
|
type Return,
|
||||||
|
type Shim,
|
||||||
} from "../base/mixin";
|
} from "../base/mixin";
|
||||||
import { assert, never } from "../internal";
|
import { assert, phantom } from "../internal";
|
||||||
import { Base } from "./base";
|
import { Base } from "./base";
|
||||||
|
|
||||||
interface AwaitedIdentitity extends HKT {
|
interface AwaitedIdentitity extends HKT {
|
||||||
@@ -21,7 +22,7 @@ class Awaited<T extends Promise<unknown>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get [shim]() {
|
public get [shim]() {
|
||||||
return never as {
|
return phantom as {
|
||||||
input: T extends Promise<infer U> ? U : never;
|
input: T extends Promise<infer U> ? U : never;
|
||||||
output: AwaitedIdentitity;
|
output: AwaitedIdentitity;
|
||||||
value: T;
|
value: T;
|
||||||
@@ -114,11 +115,28 @@ export const AsyncMixin = Mixin<IAsync, AsyncMixin>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (import.meta.vitest) {
|
if (import.meta.vitest) {
|
||||||
const { test, expect } = import.meta.vitest;
|
const { test, expect, expectTypeOf, describe } = import.meta
|
||||||
|
.vitest;
|
||||||
|
|
||||||
const registry = [Base, AsyncMixin] as const;
|
const registry = [Base, AsyncMixin] as const;
|
||||||
const $ = makeFluent(registry);
|
const $ = makeFluent(registry);
|
||||||
|
|
||||||
|
describe("Awaited<T> class", () => {
|
||||||
|
test("constructor", () => {
|
||||||
|
const promise = new Promise(() => void {});
|
||||||
|
const awaited = new Awaited(promise);
|
||||||
|
|
||||||
|
expect(awaited.value).toBe(promise);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("shim", () => {
|
||||||
|
const awaited = new Awaited(new Promise(() => void {}));
|
||||||
|
|
||||||
|
expect(awaited[shim]).toBeUndefined();
|
||||||
|
expectTypeOf(awaited[shim]).toExtend<Shim>();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test(".awaited", async () => {
|
test(".awaited", async () => {
|
||||||
const value = 10 as const;
|
const value = 10 as const;
|
||||||
const promise = new Promise<number>((r) => {
|
const promise = new Promise<number>((r) => {
|
||||||
|
|||||||
+57
-12
@@ -1,5 +1,14 @@
|
|||||||
import type { Inc, Vec } from "../../utility";
|
import type { Inc, Vec } from "../../utility";
|
||||||
|
|
||||||
|
export type Truncate<T extends number> =
|
||||||
|
`${T}` extends `${infer N extends number}.${number}` ? N : T;
|
||||||
|
export type Decimal<T extends number> =
|
||||||
|
`${T}` extends `${infer Sign extends "-" | ""}${number}.${infer N extends number}`
|
||||||
|
? `${Sign}0.${N}` extends `${infer O extends number}`
|
||||||
|
? O
|
||||||
|
: never
|
||||||
|
: 0;
|
||||||
|
|
||||||
export type Floor<T extends number> =
|
export type Floor<T extends number> =
|
||||||
`${T}` extends `${infer N extends number}.${number}` ? N : T;
|
`${T}` extends `${infer N extends number}.${number}` ? N : T;
|
||||||
export type Ceil<T extends number> =
|
export type Ceil<T extends number> =
|
||||||
@@ -36,110 +45,144 @@ type Fn<
|
|||||||
interface IBase {
|
interface IBase {
|
||||||
/**
|
/**
|
||||||
* `x + y`
|
* `x + y`
|
||||||
* @param other The second term of the addition (`y`)
|
* @param other Addend term of the addition (`y`)
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
add: Fn<1>;
|
add: Fn<1>;
|
||||||
/**
|
/**
|
||||||
* `x - y`
|
* `x - y`
|
||||||
* @param other The second term of the subtraction (`y`)
|
* @param other Subtrahend term of the subtraction (`y`)
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
subtract: Fn<1>;
|
subtract: Fn<1>;
|
||||||
/**
|
/**
|
||||||
* `x * y`
|
* `x * y`
|
||||||
* @param factor Factor term of the multiplication (`y`)
|
* @param factor Factor term of the multiplication (`y`)
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
multiply: Fn<1>;
|
multiply: Fn<1>;
|
||||||
/**
|
/**
|
||||||
* `x ** y`
|
* `x ** y`
|
||||||
* @param exponent Exponent term of the power (`y`)
|
* @param exponent Exponent term of the power (`y`)
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
pow: Fn<1, true>;
|
pow: Fn<1, true>;
|
||||||
/**
|
/**
|
||||||
* `Math.sqrt(x)` (or `Math.log2(x)`)
|
* `Math.sqrt(x)` (or `Math.log2(x)`)
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
sqrt: Fn<0, true>;
|
sqrt: Fn<0, true>;
|
||||||
/**
|
/**
|
||||||
* `x / y`
|
* `x / y`
|
||||||
* @param divisor Divisor term of the division (`y`)
|
* @param divisor Divisor term of the division (`y`)
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
divide: Fn<1, true>;
|
divide: Fn<1, true>;
|
||||||
/**
|
/**
|
||||||
* `x mod y`, not to be confused with `x % y` (remainder operation)
|
* `x mod y` modulo operation (not to be confused with `x % y` (remainder
|
||||||
|
* operation))
|
||||||
* @param divisor Divisor term of the modulo operation (`y`)
|
* @param divisor Divisor term of the modulo operation (`y`)
|
||||||
* @see `.rem()` for the remainder operation
|
* @see `.rem()` for the remainder operation
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
mod: Fn<1, true>;
|
mod: Fn<1, true>;
|
||||||
/**
|
/**
|
||||||
* `x % y` remainder operation, not to be confused with `x mod y` (modulo)
|
* `x % y` remainder operation (not to be confused with `x mod y` (modulo
|
||||||
|
* operation))
|
||||||
* @param divisor Divisor term of the remainder operation (`y`)
|
* @param divisor Divisor term of the remainder operation (`y`)
|
||||||
* @see `.mod()` for the true modulo operation
|
* @see `.mod()` for the true modulo operation
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
rem: Fn<1, true>;
|
rem: Fn<1, true>;
|
||||||
/**
|
/**
|
||||||
* `ln(x)`, `log(x, base)`, or alternatively `Math.log(x) / Math.log(base)`
|
* `ln(x)`, `log(x, base)`, or alternatively `Math.log(x) / Math.log(base)`
|
||||||
* @param base Base of the logarithm (`base`). Defaults to `Math.E` for a natural logarithm function
|
* @param base Base of the logarithm (`base`). Defaults to `Math.E` for a natural logarithm function
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
log: Fn<0, true>;
|
log: Fn<0, true>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Math.trunc(x)` or `x - (x % 1)`, remove the decimal part of the value
|
||||||
|
* @from {@link Math `Math`}
|
||||||
|
*/
|
||||||
|
truncate: Fn<0, true>;
|
||||||
|
/**
|
||||||
|
* `x % 1`, remove the whole part of the value, leaving you with the decimals
|
||||||
|
* @from {@link Math `Math`}
|
||||||
|
*/
|
||||||
|
decimal: Fn<0, true>;
|
||||||
/**
|
/**
|
||||||
* `Math.floor(x)`
|
* `Math.floor(x)`
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
floor: Fn<0, true>;
|
floor: Fn<0, true>;
|
||||||
/**
|
/**
|
||||||
* `Math.ceil(x)`
|
* `Math.ceil(x)`
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
ceil: Fn<0, true>;
|
ceil: Fn<0, true>;
|
||||||
/**
|
/**
|
||||||
* `Math.round(x)` or `Math.round(x / multiple) * multiple` to round to the
|
* `Math.round(x)` or `Math.round(x / multiple) * multiple`, round to the
|
||||||
* specified multiple
|
* specified multiple
|
||||||
* @param multiple Multiple to round to, 1 if unspecified
|
* @param multiple Multiple to round to, 1 if unspecified
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
round: Fn<1, true>;
|
round: Fn<1, true>;
|
||||||
/**
|
/**
|
||||||
* `Math.fround(x)`, round to the nearest 32-bit float
|
* `Math.fround(x)`, round to the nearest 32-bit float
|
||||||
* approximation of value
|
* approximation of value
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
fround: Fn<0, true>;
|
fround: Fn<0, true>;
|
||||||
/**
|
/**
|
||||||
* `Math.f16round(x)`, round to the nearest 16-bit float
|
* `Math.f16round(x)`, round to the nearest 16-bit float
|
||||||
* approximation of value
|
* approximation of value
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
ffround: Fn<0, true>;
|
ffround: Fn<0, true>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `Math.abs(x)`, calculate the absolute value (distance
|
* `Math.abs(x)`, calculate the absolute value (distance
|
||||||
* from 0).
|
* from 0)
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
abs: Fn<0, true>;
|
abs: Fn<0, true>;
|
||||||
/**
|
/**
|
||||||
* `Math.sign(x)`, returns the sign (1 or -1) of the value or zero
|
* `Math.sign(x)`, returns the sign (1 or -1) of the value or zero
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
sign: Fn<0, true>;
|
sign: Fn<0, true>;
|
||||||
/**
|
/**
|
||||||
* `-x` or alternatively `x * -1`
|
* `-x` or alternatively `x * -1`
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
negate: Fn<0, true>;
|
negate: Fn<0, true>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `Math.min(x, ...values)`
|
* `Math.min(x, ...values)`, return the smallest of the provided values
|
||||||
* @param values Other canidates
|
* @param values Other canidates
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
min: Fn<0>;
|
min: Fn<0>;
|
||||||
/**
|
/**
|
||||||
* `Math.min(x, ...values)`
|
* `Math.min(x, ...values)`, return the largest of the provided values
|
||||||
* @param values Other canidates
|
* @param values Other canidates
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
max: Fn<0>;
|
max: Fn<0>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `Math.min(Math.max(x, min), max)`
|
* `Math.min(Math.max(x, min), max)`, clamp value to the specified range
|
||||||
* @param min Smallest allowed value to clamp to
|
* [min, max]
|
||||||
* @param max Largest allowed value to clamp to
|
* @param min Minimum value of the range
|
||||||
|
* @param max Maximum value of the range
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
clamp: Fn<2, true>;
|
clamp: Fn<2, true>;
|
||||||
/**
|
/**
|
||||||
* Clamps value into the range [0.0, 1.0]
|
* Clamp value into the range [0.0, 1.0]
|
||||||
* @see `.clamp()` for a customizable range
|
* @see `.clamp()` for a customizable range
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
saturate: Fn<0, true>;
|
saturate: Fn<0, true>;
|
||||||
|
|
||||||
@@ -156,6 +199,7 @@ interface IBase {
|
|||||||
* @param b Target value to interpolate to
|
* @param b Target value to interpolate to
|
||||||
* @param t Interpolation factor, the point on the line where 0.0 is `a` and 1.0 is `b`
|
* @param t Interpolation factor, the point on the line where 0.0 is `a` and 1.0 is `b`
|
||||||
* @see `.inverseLerp()` for the inverse operation (to calculate `t`)
|
* @see `.inverseLerp()` for the inverse operation (to calculate `t`)
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
lerp: Fn<2, true>;
|
lerp: Fn<2, true>;
|
||||||
/**
|
/**
|
||||||
@@ -170,6 +214,7 @@ interface IBase {
|
|||||||
* @param a The 0.0 point of the range
|
* @param a The 0.0 point of the range
|
||||||
* @param b The 1.0 point of the range
|
* @param b The 1.0 point of the range
|
||||||
* @see `.lerp()` for the inverse operation
|
* @see `.lerp()` for the inverse operation
|
||||||
|
* @from {@link Math `Math`}
|
||||||
*/
|
*/
|
||||||
inverseLerp: Fn<2, true>;
|
inverseLerp: Fn<2, true>;
|
||||||
}
|
}
|
||||||
|
|||||||
+597
-37
File diff suppressed because it is too large
Load Diff
+57
-32
@@ -7,16 +7,17 @@ import {
|
|||||||
type Abs,
|
type Abs,
|
||||||
type Base,
|
type Base,
|
||||||
type Ceil,
|
type Ceil,
|
||||||
|
type Decimal,
|
||||||
type Floor,
|
type Floor,
|
||||||
type Negate,
|
type Negate,
|
||||||
type Round,
|
type Round,
|
||||||
type Sign,
|
type Sign,
|
||||||
|
type Truncate,
|
||||||
} from "./base";
|
} from "./base";
|
||||||
|
|
||||||
namespace INumber {
|
namespace INumber {
|
||||||
export type BaseImpl<T extends number, t extends Props> = Base<{
|
export type BaseImpl<T extends number, t extends Props> = Base<{
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = $(12).add(8).value;
|
* const v = $(12).add(8).value;
|
||||||
@@ -24,11 +25,10 @@ namespace INumber {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
add: (
|
add: (
|
||||||
/** Term to add */
|
/** Addend term of the addition (`y`) */
|
||||||
other: number,
|
other: number,
|
||||||
) => Return<number, t>;
|
) => Return<number, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = $(12).subtract(8).value;
|
* const v = $(12).subtract(8).value;
|
||||||
@@ -36,11 +36,10 @@ namespace INumber {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
subtract: (
|
subtract: (
|
||||||
/** Term to subtract by */
|
/** Subtrahend term of the subtraction (`y`) */
|
||||||
other: number,
|
other: number,
|
||||||
) => Return<number, t>;
|
) => Return<number, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = $(12).multiply(8).value;
|
* const v = $(12).multiply(8).value;
|
||||||
@@ -48,11 +47,10 @@ namespace INumber {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
multiply: (
|
multiply: (
|
||||||
/** Factor to multiply by */
|
/** Factor term of the multiplication (`y`) */
|
||||||
factor: number,
|
factor: number,
|
||||||
) => Return<number, t>;
|
) => Return<number, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = $(12).pow(2).value;
|
* const v = $(12).pow(2).value;
|
||||||
@@ -60,11 +58,10 @@ namespace INumber {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
pow: (
|
pow: (
|
||||||
/** Exponent to raise to */
|
/** Exponent term of the power (`y`) */
|
||||||
exponent: number,
|
exponent: number,
|
||||||
) => Return<number, t>;
|
) => Return<number, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = $(144).sqrt().value;
|
* const v = $(144).sqrt().value;
|
||||||
@@ -73,7 +70,6 @@ namespace INumber {
|
|||||||
*/
|
*/
|
||||||
sqrt: () => Return<number, t>;
|
sqrt: () => Return<number, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = $(12).divide(8).value;
|
* const v = $(12).divide(8).value;
|
||||||
@@ -81,11 +77,10 @@ namespace INumber {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
divide: (
|
divide: (
|
||||||
/** Divisor to divide by */
|
/** Divisor term of the division (`y`) */
|
||||||
divisor: number,
|
divisor: number,
|
||||||
) => Return<number, t>;
|
) => Return<number, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = $(12).mod(8).value;
|
* const v = $(12).mod(8).value;
|
||||||
@@ -93,11 +88,10 @@ namespace INumber {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
mod: (
|
mod: (
|
||||||
/** Divisor to divide by */
|
/** Divisor term of the modulo operation (`y`) */
|
||||||
divisor: number,
|
divisor: number,
|
||||||
) => Return<number, t>;
|
) => Return<number, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = $(12).rem(8).value;
|
* const v = $(12).rem(8).value;
|
||||||
@@ -105,11 +99,10 @@ namespace INumber {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
rem: (
|
rem: (
|
||||||
/** Divisor to divide by */
|
/** Divisor term of the remainder operation (`y`) */
|
||||||
divisor: number,
|
divisor: number,
|
||||||
) => Return<number, t>;
|
) => Return<number, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const a = $(Math.E).log().value;
|
* const a = $(Math.E).log().value;
|
||||||
@@ -120,12 +113,31 @@ namespace INumber {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
log: (
|
log: (
|
||||||
/** The base of the logarithm */
|
/**
|
||||||
|
* Base term of the logarithm (`base`)
|
||||||
|
* @default Math.E
|
||||||
|
*/
|
||||||
base?: number,
|
base?: number,
|
||||||
) => Return<number, t>;
|
) => Return<number, t>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const v = $(-1.8).truncate().value;
|
||||||
|
* expect(v).toBe(-1);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
truncate: () => Return<Truncate<T>, t>;
|
||||||
|
/**
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const v = $(4.25).decimal().value;
|
||||||
|
* expect(v).toBeCloseTo(0.25);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
decimal: () => Return<Decimal<T>, t>;
|
||||||
|
|
||||||
|
/**
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = $(1.7).floor().value;
|
* const v = $(1.7).floor().value;
|
||||||
@@ -134,7 +146,6 @@ namespace INumber {
|
|||||||
*/
|
*/
|
||||||
floor: () => Return<Floor<T>, t>;
|
floor: () => Return<Floor<T>, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = $(1.3).ceil().value;
|
* const v = $(1.3).ceil().value;
|
||||||
@@ -143,7 +154,6 @@ namespace INumber {
|
|||||||
*/
|
*/
|
||||||
ceil: () => Return<Ceil<T>, t>;
|
ceil: () => Return<Ceil<T>, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const a = $(1.3).round().value;
|
* const a = $(1.3).round().value;
|
||||||
@@ -167,7 +177,6 @@ namespace INumber {
|
|||||||
t
|
t
|
||||||
>;
|
>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = $(0.99999999).fround().value;
|
* const v = $(0.99999999).fround().value;
|
||||||
@@ -176,7 +185,6 @@ namespace INumber {
|
|||||||
*/
|
*/
|
||||||
fround: () => Return<number, t>;
|
fround: () => Return<number, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = $(0.9999).ffround().value;
|
* const v = $(0.9999).ffround().value;
|
||||||
@@ -186,7 +194,6 @@ namespace INumber {
|
|||||||
ffround: () => Return<number, t>;
|
ffround: () => Return<number, t>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = 4 as const;
|
* const v = 4 as const;
|
||||||
@@ -200,7 +207,6 @@ namespace INumber {
|
|||||||
*/
|
*/
|
||||||
abs: () => Return<Abs<T>, t>;
|
abs: () => Return<Abs<T>, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const v = 10 as const;
|
* const v = 10 as const;
|
||||||
@@ -214,7 +220,6 @@ namespace INumber {
|
|||||||
*/
|
*/
|
||||||
negate: () => Return<Negate<T>, t>;
|
negate: () => Return<Negate<T>, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const a = $(-42).sign().value;
|
* const a = $(-42).sign().value;
|
||||||
@@ -229,7 +234,6 @@ namespace INumber {
|
|||||||
sign: () => Return<Sign<T>, t>;
|
sign: () => Return<Sign<T>, t>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const maximum = 10;
|
* const maximum = 10;
|
||||||
@@ -246,7 +250,6 @@ namespace INumber {
|
|||||||
...values: U
|
...values: U
|
||||||
) => Return<T | U[number], t>;
|
) => Return<T | U[number], t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const minimum = 0;
|
* const minimum = 0;
|
||||||
@@ -263,7 +266,6 @@ namespace INumber {
|
|||||||
...values: U
|
...values: U
|
||||||
) => Return<T | U[number], t>;
|
) => Return<T | U[number], t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const min = 0;
|
* const min = 0;
|
||||||
@@ -279,13 +281,12 @@ namespace INumber {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
clamp: <Min extends number, Max extends number>(
|
clamp: <Min extends number, Max extends number>(
|
||||||
/** The minimum (smallest) value of the range */
|
/** Minimum value of the range */
|
||||||
min: Min,
|
min: Min,
|
||||||
/** The maximum (largest) value of the range */
|
/** Maximum value of the range */
|
||||||
max: Max,
|
max: Max,
|
||||||
) => Return<T | Min | Max, t>;
|
) => Return<T | Min | Max, t>;
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const a = $(-2).saturate().value;
|
* const a = $(-2).saturate().value;
|
||||||
@@ -300,7 +301,6 @@ namespace INumber {
|
|||||||
saturate: () => Return<number, t>;
|
saturate: () => Return<number, t>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @from {@link Math `Math`}
|
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const from = 12;
|
* const from = 12;
|
||||||
@@ -327,6 +327,27 @@ namespace INumber {
|
|||||||
/** Interpolation factor, 0.0 corresponds to `a` and 1.0 corresponds to `b` */
|
/** Interpolation factor, 0.0 corresponds to `a` and 1.0 corresponds to `b` */
|
||||||
t: number,
|
t: number,
|
||||||
) => Return<number, t>;
|
) => Return<number, t>;
|
||||||
|
/**
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const from = 5;
|
||||||
|
* const to = 10;
|
||||||
|
*
|
||||||
|
* const a = $(5).inverseLerp(from, to).value;
|
||||||
|
* const b = $(7.5).inverseLerp(from, to).value;
|
||||||
|
* const c = $(10).inverseLerp(from, to).value;
|
||||||
|
*
|
||||||
|
* expect(a).toBe(0.0);
|
||||||
|
* expect(b).toBe(0.5);
|
||||||
|
* expect(c).toBe(1.0);
|
||||||
|
*
|
||||||
|
* const d = $(15).inverseLerp(from, to).value;
|
||||||
|
* const e = $(0).inverseLerp(from, to).value;
|
||||||
|
*
|
||||||
|
* expect(d).toBe(2);
|
||||||
|
* expect(e).toBe(-1);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
inverseLerp: (
|
inverseLerp: (
|
||||||
/** 0.0 point of the range */
|
/** 0.0 point of the range */
|
||||||
a: number,
|
a: number,
|
||||||
@@ -483,6 +504,8 @@ export const applyNumber = Mixin.partial<INumber, number>(
|
|||||||
return fluent(Math.abs(value - other) < delta);
|
return fluent(Math.abs(value - other) < delta);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$.truncate = () => fluent(Math.trunc(value));
|
||||||
|
$.decimal = () => fluent(value % 1);
|
||||||
$.floor = () => fluent(Math.floor(value));
|
$.floor = () => fluent(Math.floor(value));
|
||||||
$.ceil = () => fluent(Math.ceil(value));
|
$.ceil = () => fluent(Math.ceil(value));
|
||||||
$.round = (multiple?: number) =>
|
$.round = (multiple?: number) =>
|
||||||
@@ -575,6 +598,8 @@ if (import.meta.vitest) {
|
|||||||
).toBe(false);
|
).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t("truncate", -6.4, -6);
|
||||||
|
t("decimal", -6.4, -6.4 % 1);
|
||||||
test("floor()", () => {
|
test("floor()", () => {
|
||||||
expect($(1).floor().value).toBe(1);
|
expect($(1).floor().value).toBe(1);
|
||||||
expect($(1.7).floor().value).toBe(1);
|
expect($(1.7).floor().value).toBe(1);
|
||||||
|
|||||||
+57
-246
@@ -1,230 +1,65 @@
|
|||||||
import { makeFluent } from "../../base";
|
import { makeFluent } from "../../base";
|
||||||
import { Mixin, type Props, type Return } from "../../base/mixin";
|
import { Mixin } from "../../base/mixin";
|
||||||
import { Math } from "../math";
|
import { Math } from "../math";
|
||||||
import type { Vec, Dec, Inc as __Inc } from "../../utility";
|
import type { ISwizzle, SwizzleProps } from "./swizzlePermutations";
|
||||||
|
|
||||||
type Axis = typeof axis;
|
const axis = ["x", "y", "z", "w", "v"] as const;
|
||||||
|
|
||||||
type Inc<N extends number | undefined> = N extends number
|
export type { ISwizzle, SwizzleProps as Swizzle };
|
||||||
? __Inc<N>
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
type Next<
|
|
||||||
N extends number,
|
|
||||||
State extends readonly (number | undefined)[],
|
|
||||||
TOut extends (number | undefined)[] = [],
|
|
||||||
> = State extends readonly [
|
|
||||||
infer Current extends number | undefined,
|
|
||||||
...infer Rest extends (number | undefined)[],
|
|
||||||
]
|
|
||||||
? Inc<Current> extends infer TInc
|
|
||||||
? TInc extends N
|
|
||||||
? Next<N, Rest, [...TOut, undefined]>
|
|
||||||
: [...TOut, TInc, ...Rest]
|
|
||||||
: never
|
|
||||||
: null;
|
|
||||||
|
|
||||||
type Key<
|
|
||||||
State extends (number | undefined)[],
|
|
||||||
TOut extends string = "",
|
|
||||||
> = State extends readonly [infer Only extends number, ...undefined[]]
|
|
||||||
? `${TOut}${Axis[Only]}`
|
|
||||||
: State extends readonly [
|
|
||||||
infer Current extends number,
|
|
||||||
...infer Rest extends (number | undefined)[],
|
|
||||||
]
|
|
||||||
? Key<Rest, `${TOut}${Axis[Current]}`>
|
|
||||||
: never;
|
|
||||||
|
|
||||||
type IsAscending<
|
|
||||||
State extends (number | undefined)[],
|
|
||||||
Prev extends number | undefined = undefined,
|
|
||||||
> = State extends readonly [Inc<Prev>, ...infer Rest extends number[]]
|
|
||||||
? IsAscending<Rest, Inc<Prev>>
|
|
||||||
: State extends readonly []
|
|
||||||
? true
|
|
||||||
: false;
|
|
||||||
|
|
||||||
type Pretty<T> = { [K in keyof T]: T[K] };
|
|
||||||
|
|
||||||
type SwizzlePermutations<
|
|
||||||
N extends number,
|
|
||||||
State extends Vec<number | undefined, N> = Vec<undefined, N>,
|
|
||||||
TOut extends Record<string, readonly number[]> = Record<
|
|
||||||
never,
|
|
||||||
never
|
|
||||||
>,
|
|
||||||
> =
|
|
||||||
Next<N, State> extends infer TNext
|
|
||||||
? TNext extends Vec<number | undefined, N>
|
|
||||||
? SwizzlePermutations<
|
|
||||||
N,
|
|
||||||
TNext,
|
|
||||||
TOut &
|
|
||||||
(IsAscending<State> extends true
|
|
||||||
? unknown
|
|
||||||
: Record<Key<State>, State>)
|
|
||||||
>
|
|
||||||
: Pretty<TOut & Record<Key<State>, State>>
|
|
||||||
: never;
|
|
||||||
|
|
||||||
type Sequence<
|
|
||||||
T extends readonly unknown[],
|
|
||||||
Indexes extends (number | undefined)[],
|
|
||||||
TOut extends T[number][] = [],
|
|
||||||
> = Indexes extends readonly [
|
|
||||||
infer Current extends number,
|
|
||||||
...infer Rest extends (number | undefined)[],
|
|
||||||
]
|
|
||||||
? Sequence<T, Rest, [...TOut, T[Current]]>
|
|
||||||
: TOut;
|
|
||||||
|
|
||||||
type Primative<
|
|
||||||
T extends readonly unknown[],
|
|
||||||
t extends Props,
|
|
||||||
Axes extends readonly string[] = Axis,
|
|
||||||
Acc = unknown,
|
|
||||||
TOut extends unknown[] = [],
|
|
||||||
> = Axes extends readonly [
|
|
||||||
infer Current extends string,
|
|
||||||
...infer Rest extends string[],
|
|
||||||
]
|
|
||||||
? Acc &
|
|
||||||
Record<
|
|
||||||
Current,
|
|
||||||
Return<T[TOut["length"]], t>
|
|
||||||
> extends infer TAcc
|
|
||||||
? Primative<T, t, Rest, TAcc, [...TOut, TAcc]>
|
|
||||||
: never
|
|
||||||
: TOut;
|
|
||||||
|
|
||||||
interface Swizzle4D extends SwizzlePermutations<4> {}
|
|
||||||
|
|
||||||
type SwizzleCache = [
|
|
||||||
{ x: [0] },
|
|
||||||
SwizzlePermutations<2>,
|
|
||||||
SwizzlePermutations<3>,
|
|
||||||
Swizzle4D,
|
|
||||||
];
|
|
||||||
|
|
||||||
export type Swizzle<
|
|
||||||
T extends readonly unknown[],
|
|
||||||
t extends Props,
|
|
||||||
> = number extends T["length"]
|
|
||||||
? unknown
|
|
||||||
: T extends readonly []
|
|
||||||
? unknown
|
|
||||||
: (
|
|
||||||
Dec<T["length"]> extends infer K extends
|
|
||||||
| 0
|
|
||||||
| 1
|
|
||||||
| 2
|
|
||||||
| 3
|
|
||||||
? { k: K; c: SwizzleCache[K] }
|
|
||||||
: SwizzleCache extends [
|
|
||||||
...unknown[],
|
|
||||||
infer Last,
|
|
||||||
]
|
|
||||||
? {
|
|
||||||
k: Dec<SwizzleCache["length"]>;
|
|
||||||
c: Last;
|
|
||||||
}
|
|
||||||
: never
|
|
||||||
) extends {
|
|
||||||
k: infer K extends number;
|
|
||||||
c: infer C extends Record<
|
|
||||||
PropertyKey,
|
|
||||||
(number | undefined)[]
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
? Omit<
|
|
||||||
{
|
|
||||||
readonly [K in keyof C]: Return<
|
|
||||||
Sequence<T, C[K]>,
|
|
||||||
t
|
|
||||||
>;
|
|
||||||
},
|
|
||||||
Axis[number]
|
|
||||||
> &
|
|
||||||
Primative<T, t>[K]
|
|
||||||
: never;
|
|
||||||
|
|
||||||
export type ISwizzle<
|
|
||||||
T extends readonly unknown[] = readonly unknown[],
|
|
||||||
t extends Props = Props,
|
|
||||||
> = {
|
|
||||||
readonly [K in keyof Swizzle4D]: Return<
|
|
||||||
Sequence<T, Swizzle4D[K]>,
|
|
||||||
t
|
|
||||||
>;
|
|
||||||
} & Primative<T, t>[3];
|
|
||||||
|
|
||||||
const axis = ["x", "y", "z", "w"] as const;
|
|
||||||
|
|
||||||
export const applySwizzle = Mixin.partial<
|
export const applySwizzle = Mixin.partial<
|
||||||
ISwizzle,
|
ISwizzle,
|
||||||
readonly unknown[]
|
readonly unknown[]
|
||||||
>((value, $, fluent) => {
|
>((value, $, fluent) => {
|
||||||
const length = globalThis.Math.min(value.length, axis.length);
|
const length = globalThis.Math.min(value.length, axis.length);
|
||||||
const state = new Array<number>(length).fill(-1);
|
const state = new Array<number>(axis.length).fill(-1);
|
||||||
|
|
||||||
main: do {
|
outer: do {
|
||||||
for (let i = 0; i < state.length; i++) {
|
for (let i = 0; i < state.length; i++)
|
||||||
state[i] += 1;
|
if (++state[i] >= length) {
|
||||||
if (state[i] >= length) state[i] = -1;
|
state[i] = -1;
|
||||||
else break;
|
} else break;
|
||||||
}
|
|
||||||
|
|
||||||
let reachedEmpty = false;
|
let nulled = false;
|
||||||
for (const item of state) {
|
for (const item of state) {
|
||||||
if (item === -1) {
|
if (item === -1) {
|
||||||
reachedEmpty = true;
|
nulled = true;
|
||||||
} else if (reachedEmpty) {
|
continue;
|
||||||
continue main;
|
|
||||||
}
|
}
|
||||||
|
if (nulled) continue outer;
|
||||||
}
|
}
|
||||||
|
if (state.every((x) => x === -1)) break;
|
||||||
|
|
||||||
const key = state
|
const key = state
|
||||||
.filter((x) => x !== -1)
|
.filter((x) => x !== -1)
|
||||||
.map((x) => axis[x])
|
.map((x) => axis[x])
|
||||||
.join("");
|
.join("");
|
||||||
if (key.length <= 1 || key in $) continue;
|
if (state.slice(1).every((x) => x === -1)) {
|
||||||
|
const v = value[state[0]];
|
||||||
|
Object.defineProperty($, key, {
|
||||||
|
get: () => {
|
||||||
|
return fluent(v);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
if (state.every((x) => x === count++)) continue;
|
||||||
|
|
||||||
const permutation = state
|
const permutation = state
|
||||||
.filter((x) => x !== -1)
|
.filter((x) => x !== -1)
|
||||||
.map((x) => value[x]);
|
.map((x) => value[x]);
|
||||||
Object.defineProperty($, key, {
|
Object.defineProperty($, key, {
|
||||||
get: () => {
|
value: () => {
|
||||||
// OPTIMIZE? Reduce amount of arrays allocated and held for the lambda..?
|
|
||||||
return fluent(permutation);
|
return fluent(permutation);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} while (state.some((x) => x !== -1));
|
} while (state.some((x) => x !== -1));
|
||||||
|
|
||||||
Object.defineProperty($, "x", {
|
|
||||||
get: () => {
|
|
||||||
return fluent(value[0]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Object.defineProperty($, "y", {
|
|
||||||
get: () => {
|
|
||||||
return fluent(value[1]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Object.defineProperty($, "z", {
|
|
||||||
get: () => {
|
|
||||||
return fluent(value[2]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Object.defineProperty($, "w", {
|
|
||||||
get: () => {
|
|
||||||
return fluent(value[3]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (import.meta.vitest) {
|
if (import.meta.vitest) {
|
||||||
const { test, describe, expect, expectTypeOf } = import.meta
|
const { test, expect, expectTypeOf } = import.meta.vitest;
|
||||||
.vitest;
|
|
||||||
|
|
||||||
const registry = [Math] as const;
|
const registry = [Math] as const;
|
||||||
const $ = makeFluent(registry);
|
const $ = makeFluent(registry);
|
||||||
@@ -235,20 +70,33 @@ if (import.meta.vitest) {
|
|||||||
|
|
||||||
const vec = [x, y] as const;
|
const vec = [x, y] as const;
|
||||||
|
|
||||||
expect($(vec).xx.value).toMatchObject([x, x]);
|
expect($(vec).xx().value).toMatchObject([x, x]);
|
||||||
expect($(vec).yy.value).toMatchObject([y, y]);
|
expect($(vec).yy().value).toMatchObject([y, y]);
|
||||||
|
|
||||||
const yx = $(vec).yx.value;
|
expect($(vec).xyxy().value).toMatchObject([x, y, x, y]);
|
||||||
|
|
||||||
|
const yx = $(vec).yx().value;
|
||||||
expect(yx).toMatchObject([y, x]);
|
expect(yx).toMatchObject([y, x]);
|
||||||
expectTypeOf(yx).toEqualTypeOf<[typeof y, typeof x]>();
|
expectTypeOf(yx).toEqualTypeOf<[typeof y, typeof x]>();
|
||||||
|
|
||||||
const large = [x, y, x, y] as const;
|
const large = [x, y, x, y] as const;
|
||||||
|
|
||||||
expect($(large).wzyx.value).toMatchObject([y, x, y, x]);
|
expect($(large).wzyx().value).toMatchObject([y, x, y, x]);
|
||||||
expect($(large).xzyw.value).toMatchObject([x, x, y, y]);
|
expect($(large).xzyw().value).toMatchObject([x, x, y, y]);
|
||||||
expect($(large).ywxz.value).toMatchObject([y, y, x, x]);
|
expect($(large).ywxz().value).toMatchObject([y, y, x, x]);
|
||||||
expect($(large).xxxx.value).toMatchObject([x, x, x, x]);
|
expect($(large).xxxx().value).toMatchObject([x, x, x, x]);
|
||||||
expect($(large).wwww.value).toMatchObject([y, y, y, y]);
|
expect($(large).wwww().value).toMatchObject([y, y, y, y]);
|
||||||
|
|
||||||
|
const v = 3 as const;
|
||||||
|
const gigantic = [x, y, v, y, x] as const;
|
||||||
|
|
||||||
|
expect($(gigantic).vwzyx().value).toMatchObject([
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
v,
|
||||||
|
y,
|
||||||
|
x,
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".x", () => {
|
test(".x", () => {
|
||||||
@@ -287,49 +135,12 @@ if (import.meta.vitest) {
|
|||||||
expectTypeOf(v).toEqualTypeOf(w);
|
expectTypeOf(v).toEqualTypeOf(w);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("types", () => {
|
test(".v", () => {
|
||||||
test("Next", () => {
|
const v = 0 as const;
|
||||||
type N = 3;
|
const arr = [v, v, v, v, v] as const;
|
||||||
|
|
||||||
expectTypeOf<
|
const value = $(arr).v.value;
|
||||||
Next<N, [undefined, undefined, undefined]>
|
expect(value).toBe(v);
|
||||||
>().toEqualTypeOf<[0, undefined, undefined]>();
|
expectTypeOf(value).toEqualTypeOf(v);
|
||||||
|
|
||||||
expectTypeOf<
|
|
||||||
Next<N, [2, undefined, undefined]>
|
|
||||||
>().toEqualTypeOf<[undefined, 0, undefined]>();
|
|
||||||
|
|
||||||
expectTypeOf<Next<N, [2, 2, 2]>>().toEqualTypeOf<null>();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Key", () => {
|
|
||||||
expectTypeOf<
|
|
||||||
Key<[1, 2, undefined]>
|
|
||||||
>().toEqualTypeOf<"yz">();
|
|
||||||
expectTypeOf<Key<[1, 1, 1]>>().toEqualTypeOf<"yyy">();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Sequence", () => {
|
|
||||||
type In = [1, 2, 3, 4, 5];
|
|
||||||
type Out = [5, 4, 3, 2, 1];
|
|
||||||
type Reverse = [4, 3, 2, 1, 0];
|
|
||||||
|
|
||||||
expectTypeOf<
|
|
||||||
Sequence<In, Reverse>
|
|
||||||
>().toEqualTypeOf<Out>();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Primative", () => {
|
|
||||||
interface Props {
|
|
||||||
value: [number, number];
|
|
||||||
meta: { registry: [] };
|
|
||||||
}
|
|
||||||
|
|
||||||
type V = Primative<[number, number], Props>[1];
|
|
||||||
|
|
||||||
expectTypeOf<V>().toHaveProperty("x");
|
|
||||||
expectTypeOf<V>().toHaveProperty("y");
|
|
||||||
expectTypeOf<V>().not.toHaveProperty("z");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user