mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-03-22 07:07:24 +01:00
refactor: replace deepCopy with structuredClone
This commit is contained in:
parent
7a49098425
commit
1116cc5e0f
9 changed files with 123 additions and 142 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -6,3 +6,7 @@
|
|||
/coverage
|
||||
/playwright-report
|
||||
/test-results
|
||||
/_bmad
|
||||
/_bmad-output
|
||||
.github/agents/bmad-*
|
||||
.github/prompts/bmad-*
|
||||
|
|
@ -1141,7 +1141,6 @@ function reGraph() {
|
|||
pack.cells = packCells;
|
||||
pack.cells.p = newCells.p;
|
||||
pack.cells.g = createTypedArray({maxValue: grid.points.length, from: newCells.g});
|
||||
pack.cells.q = d3.quadtree(newCells.p.map(([x, y], i) => [x, y, i]));
|
||||
pack.cells.h = createTypedArray({maxValue: 100, from: newCells.h});
|
||||
pack.cells.area = createTypedArray({maxValue: UINT16_MAX, length: packCells.i.length}).map((_, cellId) => {
|
||||
const area = Math.abs(d3.polygonArea(getPackPolygon(cellId)));
|
||||
|
|
@ -1234,7 +1233,7 @@ function showStatistics() {
|
|||
INFO && console.info(stats);
|
||||
|
||||
// Dispatch event for test automation and external integrations
|
||||
window.dispatchEvent(new CustomEvent('map:generated', { detail: { seed, mapId } }));
|
||||
window.dispatchEvent(new CustomEvent("map:generated", {detail: {seed, mapId}}));
|
||||
}
|
||||
|
||||
const regenerateMap = debounce(async function (options) {
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ function getName(id) {
|
|||
}
|
||||
|
||||
function getGraph(currentGraph) {
|
||||
const newGraph = shouldRegenerateGrid(currentGraph, seed) ? generateGrid() : deepCopy(currentGraph);
|
||||
const newGraph = shouldRegenerateGrid(currentGraph, seed) ? generateGrid() : structuredClone(currentGraph);
|
||||
delete newGraph.cells.h;
|
||||
return newGraph;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ window.Resample = (function () {
|
|||
scale: Number
|
||||
*/
|
||||
function process({projection, inverse, scale}) {
|
||||
const parentMap = {grid: deepCopy(grid), pack: deepCopy(pack), notes: deepCopy(notes)};
|
||||
const parentMap = {grid: structuredClone(grid), pack: structuredClone(pack), notes: structuredClone(notes)};
|
||||
const riversData = saveRiversData(pack.rivers);
|
||||
|
||||
grid = generateGrid();
|
||||
|
|
@ -28,7 +28,7 @@ window.Resample = (function () {
|
|||
|
||||
reGraph();
|
||||
Features.markupPack();
|
||||
Ice.generate()
|
||||
Ice.generate();
|
||||
createDefaultRuler();
|
||||
|
||||
restoreCellData(parentMap, inverse, scale);
|
||||
|
|
@ -51,9 +51,10 @@ window.Resample = (function () {
|
|||
grid.cells.temp = new Int8Array(grid.points.length);
|
||||
grid.cells.prec = new Uint8Array(grid.points.length);
|
||||
|
||||
const parentPackQ = d3.quadtree(parentMap.pack.cells.p.map(([x, y], i) => [x, y, i]));
|
||||
grid.points.forEach(([x, y], newGridCell) => {
|
||||
const [parentX, parentY] = inverse(x, y);
|
||||
const parentPackCell = parentMap.pack.cells.q.find(parentX, parentY, Infinity)[2];
|
||||
const parentPackCell = parentPackQ.find(parentX, parentY, Infinity)[2];
|
||||
const parentGridCell = parentMap.pack.cells.g[parentPackCell];
|
||||
|
||||
grid.cells.h[newGridCell] = parentMap.grid.cells.h[parentGridCell];
|
||||
|
|
@ -347,11 +348,12 @@ window.Resample = (function () {
|
|||
}
|
||||
|
||||
function restoreFeatureDetails(parentMap, inverse) {
|
||||
const parentPackQ = d3.quadtree(parentMap.pack.cells.p.map(([x, y], i) => [x, y, i]));
|
||||
pack.features.forEach(feature => {
|
||||
if (!feature) return;
|
||||
const [x, y] = pack.cells.p[feature.firstCell];
|
||||
const [parentX, parentY] = inverse(x, y);
|
||||
const parentCell = parentMap.pack.cells.q.find(parentX, parentY, Infinity)[2];
|
||||
const parentCell = parentPackQ.find(parentX, parentY, Infinity)[2];
|
||||
if (parentCell === undefined) return;
|
||||
const parentFeature = parentMap.pack.features[parentMap.pack.cells.f[parentCell]];
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ window.Submap = (function () {
|
|||
const oldGrid = parentMap.grid;
|
||||
// build cache old -> [newcelllist]
|
||||
const forwardGridMap = parentMap.grid.points.map(_ => []);
|
||||
resampler(grid.points, parentMap.pack.cells.q, (id, oldid) => {
|
||||
const parentPackQ = d3.quadtree(parentMap.pack.cells.p.map(([x, y], i) => [x, y, i]));
|
||||
resampler(grid.points, parentPackQ, (id, oldid) => {
|
||||
const cid = parentMap.pack.cells.g[oldid];
|
||||
grid.cells.h[id] = oldGrid.cells.h[cid];
|
||||
grid.cells.temp[id] = oldGrid.cells.temp[cid];
|
||||
|
|
@ -154,7 +155,7 @@ window.Submap = (function () {
|
|||
// find replacement: closest water cell
|
||||
const [ox, oy] = cells.p[id];
|
||||
const [tx, ty] = inverse(x, y);
|
||||
oldid = oldCells.q.find(tx, ty, Infinity)[2];
|
||||
oldid = d3.quadtree(oldCells.p.map(([px, py], i) => [px, py, i])).find(tx, ty, Infinity)[2];
|
||||
if (!oldid) {
|
||||
console.warn("Warning, no id found in quad", id, "parent", gridCellId);
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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", {
|
||||
return new Date(rand(from, to), rand(11), rand(1, 28)).toLocaleDateString(
|
||||
"en",
|
||||
{
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue