refactor: replace deepCopy with structuredClone

This commit is contained in:
Azgaar 2026-03-09 23:05:27 +01:00
parent 7a49098425
commit 1116cc5e0f
9 changed files with 123 additions and 142 deletions

View file

@ -16,46 +16,6 @@ export const unique = <T>(array: T[]): T[] => {
return [...new Set(array)];
};
/**
* Deep copy an object or array
* @param {Object|Array} obj - The object or array to deep copy
* @returns A deep copy of the object or array
*/
export const deepCopy = <T>(obj: T): T => {
const id = (x: T): T => x;
const dcTArray = (a: T[]): T[] => a.map(id);
const dcObject = (x: object): object =>
Object.fromEntries(Object.entries(x).map(([k, d]) => [k, dcAny(d)]));
const dcAny = (x: any): any =>
x instanceof Object ? (cf.get(x.constructor) || id)(x) : x;
// don't map keys, probably this is what we would expect
const dcMapCore = (m: Map<any, any>): [any, any][] =>
[...m.entries()].map(([k, v]) => [k, dcAny(v)]);
const cf: Map<any, (x: any) => any> = new Map<any, (x: any) => any>([
[Int8Array, dcTArray],
[Uint8Array, dcTArray],
[Uint8ClampedArray, dcTArray],
[Int16Array, dcTArray],
[Uint16Array, dcTArray],
[Int32Array, dcTArray],
[Uint32Array, dcTArray],
[Float32Array, dcTArray],
[Float64Array, dcTArray],
[BigInt64Array, dcTArray],
[BigUint64Array, dcTArray],
[Map, (m) => new Map(dcMapCore(m))],
[WeakMap, (m) => new WeakMap(dcMapCore(m))],
[Array, (a) => a.map(dcAny)],
[Set, (s) => [...s.values()].map(dcAny)],
[Date, (d) => new Date(d.getTime())],
[Object, dcObject],
// ... extend here to implement their custom deep copy
]);
return dcAny(obj);
};
/**
* Get the appropriate typed array constructor based on the maximum value
* @param {number} maxValue - The maximum value that will be stored in the array
@ -109,7 +69,6 @@ declare global {
interface Window {
last: typeof last;
unique: typeof unique;
deepCopy: typeof deepCopy;
getTypedArray: typeof getTypedArray;
createTypedArray: typeof createTypedArray;
INT8_MAX: number;

View file

@ -67,7 +67,7 @@ export const getSegmentId = (
};
/**
* Creates a debounced function that delays invoking func until after ms milliseconds have elapsed
* Creates a debounced function that delays next func call until after ms milliseconds
* @param func - The function to debounce
* @param ms - The number of milliseconds to delay
* @returns The debounced function
@ -212,11 +212,14 @@ export const isCtrlClick = (event: MouseEvent | KeyboardEvent): boolean => {
* @returns Formatted date string
*/
export const generateDate = (from: number = 100, to: number = 1000): string => {
return new Date(rand(from, to), rand(12), rand(31)).toLocaleDateString("en", {
year: "numeric",
month: "long",
day: "numeric",
});
return new Date(rand(from, to), rand(11), rand(1, 28)).toLocaleDateString(
"en",
{
year: "numeric",
month: "long",
day: "numeric",
},
);
};
/**

View file

@ -1,5 +1,5 @@
import Alea from "alea";
import { color } from "d3";
import { color, quadtree } from "d3";
import Delaunator from "delaunator";
import {
type Cells,
@ -266,6 +266,11 @@ export const findGridAll = (
return found;
};
const quadtreeCache = new WeakMap<
object,
ReturnType<typeof quadtree<[number, number, number]>>
>();
/**
* Returns the index of the packed cell containing the given x and y coordinates
* @param {number} x - The x coordinate
@ -277,10 +282,16 @@ export const findClosestCell = (
x: number,
y: number,
radius = Infinity,
packedGraph: any,
pack: { cells: { p: [number, number][] } },
): number | undefined => {
if (!packedGraph.cells?.q) return;
const found = packedGraph.cells.q.find(x, y, radius);
if (!pack.cells?.p) throw new Error("Pack cells not found");
let qTree = quadtreeCache.get(pack.cells.p);
if (!qTree) {
qTree = quadtree(pack.cells.p.map(([px, py], i) => [px, py, i]));
if (!qTree) throw new Error("Failed to create quadtree");
quadtreeCache.set(pack.cells.p, qTree);
}
const found = qTree.find(x, y, radius);
return found ? found[2] : undefined;
};
@ -414,8 +425,13 @@ export const findAllCellsInRadius = (
radius: number,
packedGraph: any,
): number[] => {
// Use findAllInQuadtree directly instead of relying on prototype extension
const found = findAllInQuadtree(x, y, radius, packedGraph.cells.q);
const q = quadtree<[number, number, number]>(
packedGraph.cells.p.map(
([px, py]: [number, number], i: number) =>
[px, py, i] as [number, number, number],
),
);
const found = findAllInQuadtree(x, y, radius, q);
return found.map((r: any) => r[2]);
};

View file

@ -26,7 +26,6 @@ window.list = list;
import {
createTypedArray,
deepCopy,
getTypedArray,
last,
TYPED_ARRAY_MAX_VALUES,
@ -35,7 +34,6 @@ import {
window.last = last;
window.unique = unique;
window.deepCopy = deepCopy;
window.getTypedArray = getTypedArray;
window.createTypedArray = createTypedArray;
window.INT8_MAX = TYPED_ARRAY_MAX_VALUES.INT8_MAX;
@ -274,90 +272,89 @@ window.drawPoint = drawPoint;
window.drawPath = drawPath;
export {
rn,
lim,
minmax,
normalize,
lerp,
isVowel,
trimVowels,
getAdjective,
nth,
abbreviate,
list,
last,
unique,
deepCopy,
getTypedArray,
createTypedArray,
TYPED_ARRAY_MAX_VALUES,
rand,
P,
each,
gauss,
Pint,
biased,
generateSeed,
getNumberInRange,
ra,
rw,
convertTemperature,
si,
getIntegerFromSI,
toHEX,
getColors,
getRandomColor,
getMixedColor,
C_12,
getComposedPath,
getNextId,
rollups,
distanceSquared,
getIsolines,
getPolesOfInaccessibility,
connectVertices,
findPath,
getVertexPath,
round,
capitalize,
splitInTwo,
parseTransform,
isValidJSON,
safeParseJSON,
sanitizeId,
byId,
shouldRegenerateGrid,
generateGrid,
findGridAll,
findGridCell,
findClosestCell,
C_12,
calculateVoronoi,
findAllCellsInRadius,
getPackPolygon,
getGridPolygon,
poissonDiscSampler,
isLand,
isWater,
findAllInQuadtree,
drawHeights,
capitalize,
clipPoly,
getSegmentId,
connectVertices,
convertTemperature,
createTypedArray,
debounce,
throttle,
parseError,
getBase64,
openURL,
wiki,
link,
isCtrlClick,
generateDate,
getLongitude,
getLatitude,
getCoordinates,
initializePrompt,
distanceSquared,
drawCellsValue,
drawHeights,
drawPath,
drawPoint,
drawPolygons,
drawRouteConnections,
drawPoint,
drawPath,
each,
findAllCellsInRadius,
findAllInQuadtree,
findClosestCell,
findGridAll,
findGridCell,
findPath,
gauss,
generateDate,
generateGrid,
generateSeed,
getAdjective,
getBase64,
getColors,
getComposedPath,
getCoordinates,
getGridPolygon,
getIntegerFromSI,
getIsolines,
getLatitude,
getLongitude,
getMixedColor,
getNextId,
getNumberInRange,
getPackPolygon,
getPolesOfInaccessibility,
getRandomColor,
getSegmentId,
getTypedArray,
getVertexPath,
initializePrompt,
isCtrlClick,
isLand,
isValidJSON,
isVowel,
isWater,
last,
lerp,
lim,
link,
list,
minmax,
normalize,
nth,
openURL,
P,
parseError,
parseTransform,
Pint,
poissonDiscSampler,
ra,
rand,
rn,
rollups,
round,
rw,
safeParseJSON,
sanitizeId,
shouldRegenerateGrid,
si,
splitInTwo,
throttle,
toHEX,
trimVowels,
TYPED_ARRAY_MAX_VALUES,
unique,
wiki,
};