improoooved
This commit is contained in:
parent
440dba2f09
commit
0990b98436
5 changed files with 358 additions and 2795 deletions
258
mapPatch.js
258
mapPatch.js
|
|
@ -3,6 +3,7 @@ import { generateMap } from "./mapPipeline.js";
|
|||
import { generateTerrainRect } from "./mapTerrain.js";
|
||||
import { LANDUSE } from "./landuseCodes.js";
|
||||
import { reconcileMunicipalMetadata, refreshPrefectureRegionsMetadata } from "./mapMunicipalCoherence.js";
|
||||
import { createPatchContext } from "./mapPatchContext.js";
|
||||
|
||||
export const PATCH_MIN_WIDTH = 48;
|
||||
export const PATCH_MIN_HEIGHT = 48;
|
||||
|
|
@ -1358,8 +1359,8 @@ function repairDiscreteSeamOwnership(world, rects, oldFields, seed = 0) {
|
|||
return { adminSeamCellsResolved, prefectureSeamCellsResolved };
|
||||
}
|
||||
|
||||
function copyFullPipelineFields(world, candidate, rects, seed, sourceMap = null, seaLevel = 0.30) {
|
||||
const window = sourceWindowForRects(rects);
|
||||
function copyFullPipelineFields(world, candidate, rects, seed, sourceMap = null, seaLevel = 0.30, patchContext = null) {
|
||||
const window = patchContext?.candidateWindow || sourceWindowForRects(rects);
|
||||
const elevationAdjustment = computeElevationCandidateAdjustment(world, candidate, rects, window, seed);
|
||||
const oldSea = world.fields.sea ? new Uint8Array(world.fields.sea) : null;
|
||||
const oldLanduse = world.fields.landuse ? new world.fields.landuse.constructor(world.fields.landuse) : null;
|
||||
|
|
@ -1381,6 +1382,7 @@ function copyFullPipelineFields(world, candidate, rects, seed, sourceMap = null,
|
|||
let adminCellsReassigned = 0;
|
||||
let landUseCellsUpdated = 0;
|
||||
|
||||
const fieldEntries = [];
|
||||
for (const [name, source] of Object.entries(candidate || {})) {
|
||||
if (SKIP_CELL_FIELDS.has(name) || !isCandidateCellField(candidate, source, window)) continue;
|
||||
const dest = ensureWorldField(world, name, source);
|
||||
|
|
@ -1388,7 +1390,11 @@ function copyFullPipelineFields(world, candidate, rects, seed, sourceMap = null,
|
|||
const isFloat = source.constructor === Float32Array || source.constructor === Float64Array;
|
||||
const isDiscrete = DISCRETE_FIELD_NAMES.has(name) || !isFloat;
|
||||
const idOffset = fieldIdOffset(name, seed);
|
||||
fieldEntries.push({ name, source, dest, isDiscrete, idOffset });
|
||||
}
|
||||
|
||||
const cells = patchContext?.writeCells?.length ? patchContext.writeCells : (() => {
|
||||
const out = [];
|
||||
for (let y = rects.writeRect.y0; y < rects.writeRect.y1; y++) {
|
||||
for (let x = rects.writeRect.x0; x < rects.writeRect.x1; x++) {
|
||||
const wi = worldIndex(world, x, y);
|
||||
|
|
@ -1396,33 +1402,39 @@ function copyFullPipelineFields(world, candidate, rects, seed, sourceMap = null,
|
|||
const si = sourceIndexForWorld(rects, window, x, y);
|
||||
if (si < 0) continue;
|
||||
const alpha = patchAlpha(x, y, rects, seed);
|
||||
if (alpha <= 0.005) continue;
|
||||
if (alpha > 0.005) out.push({ x, y, wi, si, alpha });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
})();
|
||||
|
||||
if (isDiscrete) {
|
||||
const threshold = continuityReplaceThreshold(name, x, y, rects, seed);
|
||||
if (alpha >= threshold) {
|
||||
const raw = source[si];
|
||||
const mapped = remapAdminCandidateValue(name, raw, adminIdMapping);
|
||||
const value = (name === "adminId" || name === "municipalityId" || name === "prefectureRegionId")
|
||||
? mapped
|
||||
: idOffset && raw >= 0 ? raw + idOffset : raw;
|
||||
if (name === "sea" && oldSea && dest[wi] !== value) coastCellsChanged++;
|
||||
if ((name === "naturalCompartmentId" || name === "watershedId" || name === "regionId") && dest[wi] !== value) naturalRegionsUpdated++;
|
||||
if ((name === "adminId" || name === "municipalityId") && dest[wi] !== value) adminCellsReassigned++;
|
||||
if (name === "landuse" && dest[wi] !== value) landUseCellsUpdated++;
|
||||
dest[wi] = value;
|
||||
}
|
||||
} else {
|
||||
const before = dest[wi] || 0;
|
||||
let candidateValue = source[si] || 0;
|
||||
if (name === "elevation") candidateValue = adjustedCandidateElevation(candidateValue, x, y, elevationAdjustment);
|
||||
dest[wi] = lerp(before, candidateValue, alpha);
|
||||
}
|
||||
for (const cell of cells) {
|
||||
const { x, y, wi, si, alpha } = cell;
|
||||
for (const entry of fieldEntries) {
|
||||
const { name, source, dest, isDiscrete, idOffset } = entry;
|
||||
if (isDiscrete) {
|
||||
const threshold = continuityReplaceThreshold(name, x, y, rects, seed);
|
||||
if (alpha < threshold) continue;
|
||||
const raw = source[si];
|
||||
const mapped = remapAdminCandidateValue(name, raw, adminIdMapping);
|
||||
const value = (name === "adminId" || name === "municipalityId" || name === "prefectureRegionId")
|
||||
? mapped
|
||||
: idOffset && raw >= 0 ? raw + idOffset : raw;
|
||||
if (name === "sea" && oldSea && dest[wi] !== value) coastCellsChanged++;
|
||||
if ((name === "naturalCompartmentId" || name === "watershedId" || name === "regionId") && dest[wi] !== value) naturalRegionsUpdated++;
|
||||
if ((name === "adminId" || name === "municipalityId") && dest[wi] !== value) adminCellsReassigned++;
|
||||
if (name === "landuse" && dest[wi] !== value) landUseCellsUpdated++;
|
||||
dest[wi] = value;
|
||||
} else {
|
||||
const before = dest[wi] || 0;
|
||||
let candidateValue = source[si] || 0;
|
||||
if (name === "elevation") candidateValue = adjustedCandidateElevation(candidateValue, x, y, elevationAdjustment);
|
||||
dest[wi] = lerp(before, candidateValue, alpha);
|
||||
}
|
||||
|
||||
if (name === "elevation") {
|
||||
updatedCells++;
|
||||
if (alpha > 0.94) terrainCellsFullyReplaced++;
|
||||
}
|
||||
if (name === "elevation") {
|
||||
updatedCells++;
|
||||
if (alpha > 0.94) terrainCellsFullyReplaced++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3212,7 +3224,6 @@ function reconnectTransportGraph(world, sourceMap, rects, seed, mode, graphRect,
|
|||
const rejectedPairs = new Set();
|
||||
let attemptsRemaining = options.maxAttempts ?? (mode === "rail" ? 4 : 8);
|
||||
for (let pass = 0; pass < maxAdds && attemptsRemaining > 0; pass++) {
|
||||
snapshot = buildTransportGraphSnapshot(world, sourceMap, mode, graphRect, rects, seed);
|
||||
const dirtyCount = snapshot.comps.filter(eligible).length;
|
||||
if (dirtyCount <= 1) break;
|
||||
const comps = snapshot.comps.filter(contextual);
|
||||
|
|
@ -3285,13 +3296,13 @@ function reconnectTransportGraph(world, sourceMap, rects, seed, mode, graphRect,
|
|||
rejectedPairs.add(transportPairSignature(b, a, mode));
|
||||
continue;
|
||||
}
|
||||
snapshot = checkSnapshot;
|
||||
debug[`${mode}GraphConnectorsAdded`] += writeResult.wrote;
|
||||
accepted = true;
|
||||
break;
|
||||
}
|
||||
if (!accepted) break;
|
||||
}
|
||||
snapshot = buildTransportGraphSnapshot(world, sourceMap, mode, graphRect, rects, seed);
|
||||
debug[`${mode}GraphAfterComponents`] = snapshot.comps.filter(eligible).length;
|
||||
return debug;
|
||||
}
|
||||
|
|
@ -3438,7 +3449,6 @@ function mergePathLayers(world, sourceMap, candidate, rects, window, seed) {
|
|||
// repair is intentionally allowed to operate in a much broader neighborhood,
|
||||
// because clipping graph repairs to the selected polygon leaves implausible
|
||||
// dangling regional networks just outside the patch.
|
||||
const connectorPatchOptions = null;
|
||||
const externalRoadAnchors = collectExternalNetworkAnchors(world, sourceMap, ["nationalRoads", "minorRoads", "premodernRoads", "externalRoads"], rects.writeRect, transportRect, "road");
|
||||
const externalRailAnchors = collectExternalNetworkAnchors(world, sourceMap, ["railways", "branchRailways", "externalRailways"], rects.writeRect, transportRect, "rail");
|
||||
roadAnchors = roadAnchors.concat(externalRoadAnchors);
|
||||
|
|
@ -3447,9 +3457,6 @@ function mergePathLayers(world, sourceMap, candidate, rects, window, seed) {
|
|||
// visible fragments that did not reduce disconnected components. Keep the
|
||||
// anchor collection for diagnostics, but route all actual repair through the
|
||||
// graph reconnection pass below, which rolls back failed candidates.
|
||||
const roadConn = { connectors: 0, disconnected: roadAnchors.length, skippedConnectorAnchors: 0, connectorAttempts: 0 };
|
||||
const railConn = { connectors: 0, disconnected: railAnchors.length, skippedConnectorAnchors: 0, connectorAttempts: 0 };
|
||||
const settlementRoadConnectors = { connectors: 0, skippedServedSettlements: 0, checkedSettlementCoverage: 0 };
|
||||
const graphRect = strictMask
|
||||
? transportRect
|
||||
: transportRect;
|
||||
|
|
@ -3459,14 +3466,14 @@ function mergePathLayers(world, sourceMap, candidate, rects, window, seed) {
|
|||
roadsClipped,
|
||||
railsClipped,
|
||||
regeneratedPaths,
|
||||
roadConnectorsCreated: roadConn.connectors + settlementRoadConnectors.connectors + (roadGraph.roadGraphConnectorsAdded || 0),
|
||||
railwayConnectorsCreated: railConn.connectors + (railGraph.railGraphConnectorsAdded || 0),
|
||||
disconnectedRoadComponents: roadConn.disconnected,
|
||||
disconnectedRailComponents: railConn.disconnected,
|
||||
skippedConnectorAnchors: (roadConn.skippedConnectorAnchors || 0) + (railConn.skippedConnectorAnchors || 0),
|
||||
connectorAttempts: (roadConn.connectorAttempts || 0) + (railConn.connectorAttempts || 0),
|
||||
skippedServedSettlements: settlementRoadConnectors.skippedServedSettlements || 0,
|
||||
checkedSettlementCoverage: settlementRoadConnectors.checkedSettlementCoverage || 0,
|
||||
roadConnectorsCreated: roadGraph.roadGraphConnectorsAdded || 0,
|
||||
railwayConnectorsCreated: railGraph.railGraphConnectorsAdded || 0,
|
||||
disconnectedRoadComponents: roadAnchors.length,
|
||||
disconnectedRailComponents: railAnchors.length,
|
||||
skippedConnectorAnchors: 0,
|
||||
connectorAttempts: (roadGraph.roadGraphCandidatesConsidered || 0) + (railGraph.railGraphCandidatesConsidered || 0),
|
||||
skippedServedSettlements: 0,
|
||||
checkedSettlementCoverage: 0,
|
||||
externalRoadAnchors: externalRoadAnchors.length,
|
||||
externalRailAnchors: externalRailAnchors.length,
|
||||
...roadGraph,
|
||||
|
|
@ -4208,6 +4215,82 @@ function addInvalidatedRect(world, rect) {
|
|||
if (!list.some((r) => rectKey(r) === key)) list.push({ ...normalized });
|
||||
}
|
||||
|
||||
function repairPatchTerrain(world, rects, seed, seaLevel) {
|
||||
const terrainSeamDebug = featherTerrainSeam(world, rects, seed);
|
||||
const elevationCliffDebug = smoothExtremeElevationSeams(world, rects, seed, seaLevel);
|
||||
const waterDebug = smoothWaterTopology(world, rects.writeRect, seaLevel, rects, seed);
|
||||
const waterComponentDebug = repairWaterComponentTopology(world, rects, seaLevel, seed);
|
||||
const residualSeaDebug = fillTinyResidualSeas(world, rects, seaLevel, seed, { aggressive: true });
|
||||
const waterElevationDebug = smoothPatchedWaterElevation(world, rects, seaLevel, seed);
|
||||
const maskDebug = repairDisplayMasks(world, rects, seed);
|
||||
recomputeSlopeAndWaterDependentFields(world, rects.repairRect, seaLevel);
|
||||
return {
|
||||
terrainSeamDebug,
|
||||
elevationCliffDebug,
|
||||
waterDebug,
|
||||
waterComponentDebug,
|
||||
residualSeaDebug,
|
||||
waterElevationDebug,
|
||||
maskDebug,
|
||||
};
|
||||
}
|
||||
|
||||
function repairPatchAdministration(world, sourceMap, rects, seed, adminIdMapping, strictFieldSnapshot) {
|
||||
const landDebug = repairLanduseAndPopulation(world, rects);
|
||||
const finalAdminCoverageDebug = repairAdminCoverage(world, sourceMap, rects, adminIdMapping, seed);
|
||||
const adminTopologyDebug = repairPatchAdministrativeTopology(world, rects);
|
||||
const strictMaskDebugPreCoherence = restoreOutsideStrictSelectionFields(world, rects, strictFieldSnapshot, seed);
|
||||
const sourceAdminMetadataUpdated = updateSourceAdminMetadata(sourceMap, adminIdMapping);
|
||||
const municipalCoherence = reconcileMunicipalMetadata({
|
||||
adminId: world.fields.adminId,
|
||||
municipalityId: world.fields.municipalityId,
|
||||
prefectureRegionId: world.fields.prefectureRegionId,
|
||||
sea: world.fields.sea,
|
||||
adminCenters: sourceMap.adminCenters || [],
|
||||
municipalityToPrefectureId: sourceMap.municipalityToPrefectureId,
|
||||
fields: world.fields,
|
||||
width: world.width,
|
||||
height: world.height,
|
||||
pointOffsetX: world.originX || 0,
|
||||
pointOffsetY: world.originY || 0,
|
||||
seed,
|
||||
});
|
||||
sourceMap.adminCenters = municipalCoherence.adminCenters;
|
||||
sourceMap.municipalityToPrefectureId = municipalCoherence.municipalityToPrefectureId;
|
||||
const prefectureCoherence = refreshPrefectureRegionsMetadata({
|
||||
prefectureRegionId: world.fields.prefectureRegionId,
|
||||
sea: world.fields.sea,
|
||||
existing: sourceMap.prefectureRegions || [],
|
||||
adminCenters: sourceMap.adminCenters || [],
|
||||
fields: world.fields,
|
||||
width: world.width,
|
||||
height: world.height,
|
||||
pointOffsetX: world.originX || 0,
|
||||
pointOffsetY: world.originY || 0,
|
||||
});
|
||||
sourceMap.prefectureRegions = prefectureCoherence.prefectureRegions;
|
||||
const strictMaskDebugPostCoherence = restoreOutsideStrictSelectionFields(world, rects, strictFieldSnapshot, seed);
|
||||
sourceMap.adminDebug = {
|
||||
...(sourceMap.adminDebug || {}),
|
||||
municipalCoherence: municipalCoherence.debug,
|
||||
prefectureMetadataCoherence: prefectureCoherence.debug,
|
||||
};
|
||||
return {
|
||||
landDebug,
|
||||
finalAdminCoverageDebug,
|
||||
adminTopologyDebug,
|
||||
strictMaskDebugPreCoherence,
|
||||
strictMaskDebugPostCoherence,
|
||||
sourceAdminMetadataUpdated,
|
||||
municipalCoherence,
|
||||
prefectureCoherence,
|
||||
};
|
||||
}
|
||||
|
||||
function repairPatchTransport(world, sourceMap, candidate, rects, window, seed) {
|
||||
return mergePathLayers(world, sourceMap, candidate, rects, window, seed);
|
||||
}
|
||||
|
||||
export function generatePatch(world, userRectInput, options = {}) {
|
||||
const validation = validatePatchRect(userRectInput, world);
|
||||
if (!validation.ok) return { ok: false, ...validation };
|
||||
|
|
@ -4258,85 +4341,80 @@ export function generatePatch(world, userRectInput, options = {}) {
|
|||
: (candidateWindow.variable ? "Variable candidate generation" : "Full candidate generation"));
|
||||
getPatchAlphaCache(rects, seed);
|
||||
getPatchSourceIndexCache(rects, candidateWindow);
|
||||
const patchContext = createPatchContext({
|
||||
world,
|
||||
rects,
|
||||
candidateWindow,
|
||||
seed,
|
||||
patchAlpha,
|
||||
sourceIndexForWorld,
|
||||
worldIndex,
|
||||
});
|
||||
const sourceMap = world.sourceMap || (world.sourceMap = {});
|
||||
const strictMetadataSnapshot = captureStrictMetadataSnapshot(world, sourceMap, rects, seed);
|
||||
const logisticsLabelsMigrated = sanitizeExistingLogistics(sourceMap);
|
||||
|
||||
const seaLevel = candidate.seaLevel || world.sourceMap?.seaLevel || 0.30;
|
||||
const fieldDebug = copyFullPipelineFields(world, candidate, rects, seed, sourceMap, seaLevel);
|
||||
const fieldDebug = copyFullPipelineFields(world, candidate, rects, seed, sourceMap, seaLevel, patchContext);
|
||||
patchTimer.mark("fields", "Field copy and alpha blend");
|
||||
const terrainSeamDebug = featherTerrainSeam(world, rects, seed);
|
||||
const elevationCliffDebug = smoothExtremeElevationSeams(world, rects, seed, seaLevel);
|
||||
const waterDebug = smoothWaterTopology(world, rects.writeRect, seaLevel, rects, seed);
|
||||
const waterComponentDebug = repairWaterComponentTopology(world, rects, seaLevel, seed);
|
||||
const residualSeaDebug = fillTinyResidualSeas(world, rects, seaLevel, seed, { aggressive: true });
|
||||
const waterElevationDebug = smoothPatchedWaterElevation(world, rects, seaLevel, seed);
|
||||
const maskDebug = repairDisplayMasks(world, rects, seed);
|
||||
recomputeSlopeAndWaterDependentFields(world, rects.repairRect, seaLevel);
|
||||
const terrainDebug = repairPatchTerrain(world, rects, seed, seaLevel);
|
||||
patchTimer.mark("terrainRepair", "Water, masks, and terrain repair");
|
||||
const pointDebug = mergePointLayers(world, sourceMap, candidate, rects, fieldDebug.window, seed, fieldDebug.adminIdMapping);
|
||||
patchTimer.mark("points", "Point merge");
|
||||
const pathDebug = mergePathLayers(world, sourceMap, candidate, rects, fieldDebug.window, seed);
|
||||
const pathDebug = repairPatchTransport(world, sourceMap, candidate, rects, fieldDebug.window, seed);
|
||||
patchTimer.mark("paths", "Path merge and connector repair");
|
||||
const influenceDebug = refreshPatchInfluenceFields(world, sourceMap, rects);
|
||||
patchTimer.mark("influence", "Influence refresh");
|
||||
const landDebug = repairLanduseAndPopulation(world, rects);
|
||||
const finalAdminCoverageDebug = repairAdminCoverage(world, sourceMap, rects, fieldDebug.adminIdMapping, seed);
|
||||
const adminTopologyDebug = repairPatchAdministrativeTopology(world, rects);
|
||||
const strictMaskDebugPreCoherence = restoreOutsideStrictSelectionFields(world, rects, strictFieldSnapshot, seed);
|
||||
const sourceAdminMetadataUpdated = updateSourceAdminMetadata(sourceMap, fieldDebug.adminIdMapping);
|
||||
const municipalCoherence = reconcileMunicipalMetadata({
|
||||
adminId: world.fields.adminId,
|
||||
municipalityId: world.fields.municipalityId,
|
||||
prefectureRegionId: world.fields.prefectureRegionId,
|
||||
sea: world.fields.sea,
|
||||
adminCenters: sourceMap.adminCenters || [],
|
||||
municipalityToPrefectureId: sourceMap.municipalityToPrefectureId,
|
||||
fields: world.fields,
|
||||
width: world.width,
|
||||
height: world.height,
|
||||
pointOffsetX: world.originX || 0,
|
||||
pointOffsetY: world.originY || 0,
|
||||
seed,
|
||||
});
|
||||
sourceMap.adminCenters = municipalCoherence.adminCenters;
|
||||
sourceMap.municipalityToPrefectureId = municipalCoherence.municipalityToPrefectureId;
|
||||
const prefectureCoherence = refreshPrefectureRegionsMetadata({
|
||||
prefectureRegionId: world.fields.prefectureRegionId,
|
||||
sea: world.fields.sea,
|
||||
existing: sourceMap.prefectureRegions || [],
|
||||
adminCenters: sourceMap.adminCenters || [],
|
||||
fields: world.fields,
|
||||
width: world.width,
|
||||
height: world.height,
|
||||
pointOffsetX: world.originX || 0,
|
||||
pointOffsetY: world.originY || 0,
|
||||
});
|
||||
sourceMap.prefectureRegions = prefectureCoherence.prefectureRegions;
|
||||
const strictMaskDebugPostCoherence = restoreOutsideStrictSelectionFields(world, rects, strictFieldSnapshot, seed);
|
||||
const adminDebug = repairPatchAdministration(world, sourceMap, rects, seed, fieldDebug.adminIdMapping, strictFieldSnapshot);
|
||||
const residualSeaStrictDebug = fillTinyResidualSeas(world, rects, seaLevel, seed, { aggressive: true, preservePockets: true });
|
||||
if ((residualSeaStrictDebug.residualSeaCellsFilled || 0) > 0) {
|
||||
repairDisplayMasks(world, rects, seed);
|
||||
recomputeSlopeAndWaterDependentFields(world, rects.repairRect, seaLevel);
|
||||
}
|
||||
const strictMetadataDebug = restoreOutsideStrictMetadata(world, sourceMap, rects, strictMetadataSnapshot, seed);
|
||||
sourceMap.adminDebug = {
|
||||
...(sourceMap.adminDebug || {}),
|
||||
municipalCoherence: municipalCoherence.debug,
|
||||
prefectureMetadataCoherence: prefectureCoherence.debug,
|
||||
};
|
||||
const segmentDebug = mergeSegmentLayers(world, sourceMap, rects, seed, candidate, fieldDebug.window);
|
||||
const candidateCompartmentSegmentsAdded = mergeCandidateCompartmentDebugSegments(world, sourceMap, candidate, rects, fieldDebug.window, seed);
|
||||
patchTimer.mark("segments", "Boundary and debug segment merge");
|
||||
sanitizeExistingLogistics(sourceMap);
|
||||
patchTimer.mark("cleanup", "Land-use, admin, and label cleanup");
|
||||
const patchTimings = patchTimer.timings;
|
||||
const {
|
||||
terrainSeamDebug,
|
||||
elevationCliffDebug,
|
||||
waterDebug,
|
||||
waterComponentDebug,
|
||||
residualSeaDebug,
|
||||
waterElevationDebug,
|
||||
maskDebug,
|
||||
} = terrainDebug;
|
||||
const {
|
||||
landDebug,
|
||||
finalAdminCoverageDebug,
|
||||
adminTopologyDebug,
|
||||
strictMaskDebugPreCoherence,
|
||||
strictMaskDebugPostCoherence,
|
||||
sourceAdminMetadataUpdated,
|
||||
municipalCoherence,
|
||||
prefectureCoherence,
|
||||
} = adminDebug;
|
||||
|
||||
const seaStats = countSea(world, rects.coreRect, rects, seed);
|
||||
const label = terrainLabel(candidate, terrainType);
|
||||
const id = terrainId(candidate, terrainType);
|
||||
const patchStageDebug = {
|
||||
candidate: { cacheHit, cacheSize, patchGenerationMode, candidateAreaRatio: candidateWindow.areaRatio ?? 1 },
|
||||
fields: fieldDebug,
|
||||
terrain: terrainDebug,
|
||||
points: pointDebug,
|
||||
transport: pathDebug,
|
||||
influence: influenceDebug,
|
||||
admin: adminDebug,
|
||||
segments: { ...segmentDebug, candidateCompartmentSegmentsAdded },
|
||||
cleanup: { logisticsLabelsMigrated, strictMetadataDebug, residualSeaStrictDebug },
|
||||
};
|
||||
const humanGeography = {
|
||||
ok: true,
|
||||
patchStages: patchStageDebug,
|
||||
modernCities: (sourceMap.modernCities || []).filter((p) => insideRect(pointWorldX(world, p), pointWorldY(world, p), rects.writeRect)).length,
|
||||
ports: (sourceMap.ports || []).filter((p) => insideRect(pointWorldX(world, p), pointWorldY(world, p), rects.writeRect)).length,
|
||||
villages: (sourceMap.villages || []).filter((p) => insideRect(pointWorldX(world, p), pointWorldY(world, p), rects.writeRect)).length,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue