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`; 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\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 = number extends T['length'] ? false : T extends readonly [] ? false : true; export type SwizzleProps = IsValid 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 {}; `.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`, }); 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 {\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);