mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-04-03 22:17:24 +02:00
refactor: replace deepCopy with structuredClone
This commit is contained in:
parent
7a49098425
commit
1116cc5e0f
9 changed files with 123 additions and 142 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -5,4 +5,8 @@
|
||||||
/dist
|
/dist
|
||||||
/coverage
|
/coverage
|
||||||
/playwright-report
|
/playwright-report
|
||||||
/test-results
|
/test-results
|
||||||
|
/_bmad
|
||||||
|
/_bmad-output
|
||||||
|
.github/agents/bmad-*
|
||||||
|
.github/prompts/bmad-*
|
||||||
|
|
@ -1141,7 +1141,6 @@ function reGraph() {
|
||||||
pack.cells = packCells;
|
pack.cells = packCells;
|
||||||
pack.cells.p = newCells.p;
|
pack.cells.p = newCells.p;
|
||||||
pack.cells.g = createTypedArray({maxValue: grid.points.length, from: newCells.g});
|
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.h = createTypedArray({maxValue: 100, from: newCells.h});
|
||||||
pack.cells.area = createTypedArray({maxValue: UINT16_MAX, length: packCells.i.length}).map((_, cellId) => {
|
pack.cells.area = createTypedArray({maxValue: UINT16_MAX, length: packCells.i.length}).map((_, cellId) => {
|
||||||
const area = Math.abs(d3.polygonArea(getPackPolygon(cellId)));
|
const area = Math.abs(d3.polygonArea(getPackPolygon(cellId)));
|
||||||
|
|
@ -1234,7 +1233,7 @@ function showStatistics() {
|
||||||
INFO && console.info(stats);
|
INFO && console.info(stats);
|
||||||
|
|
||||||
// Dispatch event for test automation and external integrations
|
// 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) {
|
const regenerateMap = debounce(async function (options) {
|
||||||
|
|
|
||||||
|
|
@ -260,7 +260,7 @@ function getName(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getGraph(currentGraph) {
|
function getGraph(currentGraph) {
|
||||||
const newGraph = shouldRegenerateGrid(currentGraph, seed) ? generateGrid() : deepCopy(currentGraph);
|
const newGraph = shouldRegenerateGrid(currentGraph, seed) ? generateGrid() : structuredClone(currentGraph);
|
||||||
delete newGraph.cells.h;
|
delete newGraph.cells.h;
|
||||||
return newGraph;
|
return newGraph;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ window.Resample = (function () {
|
||||||
scale: Number
|
scale: Number
|
||||||
*/
|
*/
|
||||||
function process({projection, inverse, scale}) {
|
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);
|
const riversData = saveRiversData(pack.rivers);
|
||||||
|
|
||||||
grid = generateGrid();
|
grid = generateGrid();
|
||||||
|
|
@ -28,7 +28,7 @@ window.Resample = (function () {
|
||||||
|
|
||||||
reGraph();
|
reGraph();
|
||||||
Features.markupPack();
|
Features.markupPack();
|
||||||
Ice.generate()
|
Ice.generate();
|
||||||
createDefaultRuler();
|
createDefaultRuler();
|
||||||
|
|
||||||
restoreCellData(parentMap, inverse, scale);
|
restoreCellData(parentMap, inverse, scale);
|
||||||
|
|
@ -51,9 +51,10 @@ window.Resample = (function () {
|
||||||
grid.cells.temp = new Int8Array(grid.points.length);
|
grid.cells.temp = new Int8Array(grid.points.length);
|
||||||
grid.cells.prec = new Uint8Array(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) => {
|
grid.points.forEach(([x, y], newGridCell) => {
|
||||||
const [parentX, parentY] = inverse(x, y);
|
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];
|
const parentGridCell = parentMap.pack.cells.g[parentPackCell];
|
||||||
|
|
||||||
grid.cells.h[newGridCell] = parentMap.grid.cells.h[parentGridCell];
|
grid.cells.h[newGridCell] = parentMap.grid.cells.h[parentGridCell];
|
||||||
|
|
@ -347,11 +348,12 @@ window.Resample = (function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function restoreFeatureDetails(parentMap, inverse) {
|
function restoreFeatureDetails(parentMap, inverse) {
|
||||||
|
const parentPackQ = d3.quadtree(parentMap.pack.cells.p.map(([x, y], i) => [x, y, i]));
|
||||||
pack.features.forEach(feature => {
|
pack.features.forEach(feature => {
|
||||||
if (!feature) return;
|
if (!feature) return;
|
||||||
const [x, y] = pack.cells.p[feature.firstCell];
|
const [x, y] = pack.cells.p[feature.firstCell];
|
||||||
const [parentX, parentY] = inverse(x, y);
|
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;
|
if (parentCell === undefined) return;
|
||||||
const parentFeature = parentMap.pack.features[parentMap.pack.cells.f[parentCell]];
|
const parentFeature = parentMap.pack.features[parentMap.pack.cells.f[parentCell]];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,8 @@ window.Submap = (function () {
|
||||||
const oldGrid = parentMap.grid;
|
const oldGrid = parentMap.grid;
|
||||||
// build cache old -> [newcelllist]
|
// build cache old -> [newcelllist]
|
||||||
const forwardGridMap = parentMap.grid.points.map(_ => []);
|
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];
|
const cid = parentMap.pack.cells.g[oldid];
|
||||||
grid.cells.h[id] = oldGrid.cells.h[cid];
|
grid.cells.h[id] = oldGrid.cells.h[cid];
|
||||||
grid.cells.temp[id] = oldGrid.cells.temp[cid];
|
grid.cells.temp[id] = oldGrid.cells.temp[cid];
|
||||||
|
|
@ -154,7 +155,7 @@ window.Submap = (function () {
|
||||||
// find replacement: closest water cell
|
// find replacement: closest water cell
|
||||||
const [ox, oy] = cells.p[id];
|
const [ox, oy] = cells.p[id];
|
||||||
const [tx, ty] = inverse(x, y);
|
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) {
|
if (!oldid) {
|
||||||
console.warn("Warning, no id found in quad", id, "parent", gridCellId);
|
console.warn("Warning, no id found in quad", id, "parent", gridCellId);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -16,46 +16,6 @@ export const unique = <T>(array: T[]): T[] => {
|
||||||
return [...new Set(array)];
|
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
|
* Get the appropriate typed array constructor based on the maximum value
|
||||||
* @param {number} maxValue - The maximum value that will be stored in the array
|
* @param {number} maxValue - The maximum value that will be stored in the array
|
||||||
|
|
@ -109,7 +69,6 @@ declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
last: typeof last;
|
last: typeof last;
|
||||||
unique: typeof unique;
|
unique: typeof unique;
|
||||||
deepCopy: typeof deepCopy;
|
|
||||||
getTypedArray: typeof getTypedArray;
|
getTypedArray: typeof getTypedArray;
|
||||||
createTypedArray: typeof createTypedArray;
|
createTypedArray: typeof createTypedArray;
|
||||||
INT8_MAX: number;
|
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 func - The function to debounce
|
||||||
* @param ms - The number of milliseconds to delay
|
* @param ms - The number of milliseconds to delay
|
||||||
* @returns The debounced function
|
* @returns The debounced function
|
||||||
|
|
@ -212,11 +212,14 @@ export const isCtrlClick = (event: MouseEvent | KeyboardEvent): boolean => {
|
||||||
* @returns Formatted date string
|
* @returns Formatted date string
|
||||||
*/
|
*/
|
||||||
export const generateDate = (from: number = 100, to: number = 1000): 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(
|
||||||
year: "numeric",
|
"en",
|
||||||
month: "long",
|
{
|
||||||
day: "numeric",
|
year: "numeric",
|
||||||
});
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import Alea from "alea";
|
import Alea from "alea";
|
||||||
import { color } from "d3";
|
import { color, quadtree } from "d3";
|
||||||
import Delaunator from "delaunator";
|
import Delaunator from "delaunator";
|
||||||
import {
|
import {
|
||||||
type Cells,
|
type Cells,
|
||||||
|
|
@ -266,6 +266,11 @@ export const findGridAll = (
|
||||||
return found;
|
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
|
* Returns the index of the packed cell containing the given x and y coordinates
|
||||||
* @param {number} x - The x coordinate
|
* @param {number} x - The x coordinate
|
||||||
|
|
@ -277,10 +282,16 @@ export const findClosestCell = (
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
radius = Infinity,
|
radius = Infinity,
|
||||||
packedGraph: any,
|
pack: { cells: { p: [number, number][] } },
|
||||||
): number | undefined => {
|
): number | undefined => {
|
||||||
if (!packedGraph.cells?.q) return;
|
if (!pack.cells?.p) throw new Error("Pack cells not found");
|
||||||
const found = packedGraph.cells.q.find(x, y, radius);
|
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;
|
return found ? found[2] : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -414,8 +425,13 @@ export const findAllCellsInRadius = (
|
||||||
radius: number,
|
radius: number,
|
||||||
packedGraph: any,
|
packedGraph: any,
|
||||||
): number[] => {
|
): number[] => {
|
||||||
// Use findAllInQuadtree directly instead of relying on prototype extension
|
const q = quadtree<[number, number, number]>(
|
||||||
const found = findAllInQuadtree(x, y, radius, packedGraph.cells.q);
|
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]);
|
return found.map((r: any) => r[2]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ window.list = list;
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createTypedArray,
|
createTypedArray,
|
||||||
deepCopy,
|
|
||||||
getTypedArray,
|
getTypedArray,
|
||||||
last,
|
last,
|
||||||
TYPED_ARRAY_MAX_VALUES,
|
TYPED_ARRAY_MAX_VALUES,
|
||||||
|
|
@ -35,7 +34,6 @@ import {
|
||||||
|
|
||||||
window.last = last;
|
window.last = last;
|
||||||
window.unique = unique;
|
window.unique = unique;
|
||||||
window.deepCopy = deepCopy;
|
|
||||||
window.getTypedArray = getTypedArray;
|
window.getTypedArray = getTypedArray;
|
||||||
window.createTypedArray = createTypedArray;
|
window.createTypedArray = createTypedArray;
|
||||||
window.INT8_MAX = TYPED_ARRAY_MAX_VALUES.INT8_MAX;
|
window.INT8_MAX = TYPED_ARRAY_MAX_VALUES.INT8_MAX;
|
||||||
|
|
@ -274,90 +272,89 @@ window.drawPoint = drawPoint;
|
||||||
window.drawPath = drawPath;
|
window.drawPath = drawPath;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
rn,
|
|
||||||
lim,
|
|
||||||
minmax,
|
|
||||||
normalize,
|
|
||||||
lerp,
|
|
||||||
isVowel,
|
|
||||||
trimVowels,
|
|
||||||
getAdjective,
|
|
||||||
nth,
|
|
||||||
abbreviate,
|
abbreviate,
|
||||||
list,
|
|
||||||
last,
|
|
||||||
unique,
|
|
||||||
deepCopy,
|
|
||||||
getTypedArray,
|
|
||||||
createTypedArray,
|
|
||||||
TYPED_ARRAY_MAX_VALUES,
|
|
||||||
rand,
|
|
||||||
P,
|
|
||||||
each,
|
|
||||||
gauss,
|
|
||||||
Pint,
|
|
||||||
biased,
|
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,
|
byId,
|
||||||
shouldRegenerateGrid,
|
C_12,
|
||||||
generateGrid,
|
|
||||||
findGridAll,
|
|
||||||
findGridCell,
|
|
||||||
findClosestCell,
|
|
||||||
calculateVoronoi,
|
calculateVoronoi,
|
||||||
findAllCellsInRadius,
|
capitalize,
|
||||||
getPackPolygon,
|
|
||||||
getGridPolygon,
|
|
||||||
poissonDiscSampler,
|
|
||||||
isLand,
|
|
||||||
isWater,
|
|
||||||
findAllInQuadtree,
|
|
||||||
drawHeights,
|
|
||||||
clipPoly,
|
clipPoly,
|
||||||
getSegmentId,
|
connectVertices,
|
||||||
|
convertTemperature,
|
||||||
|
createTypedArray,
|
||||||
debounce,
|
debounce,
|
||||||
throttle,
|
distanceSquared,
|
||||||
parseError,
|
|
||||||
getBase64,
|
|
||||||
openURL,
|
|
||||||
wiki,
|
|
||||||
link,
|
|
||||||
isCtrlClick,
|
|
||||||
generateDate,
|
|
||||||
getLongitude,
|
|
||||||
getLatitude,
|
|
||||||
getCoordinates,
|
|
||||||
initializePrompt,
|
|
||||||
drawCellsValue,
|
drawCellsValue,
|
||||||
|
drawHeights,
|
||||||
|
drawPath,
|
||||||
|
drawPoint,
|
||||||
drawPolygons,
|
drawPolygons,
|
||||||
drawRouteConnections,
|
drawRouteConnections,
|
||||||
drawPoint,
|
each,
|
||||||
drawPath,
|
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