hmm
This commit is contained in:
parent
112e6bf86b
commit
f2d0306d96
13 changed files with 1434 additions and 516 deletions
130
mapPipeline.js
130
mapPipeline.js
|
|
@ -1,5 +1,5 @@
|
|||
import { CELL_SIZE, MAP_H, MAP_W, indexOf } from "./mapUtils.js";
|
||||
import { generateInitialTerrainRect, generateTerrainAndRivers } from "./mapTerrain.js";
|
||||
import { generateTerrainAndRivers } from "./mapTerrain.js";
|
||||
import { generateMapFeatures } from "./mapFeatures.js";
|
||||
import { finishMapOutput } from "./mapOutput.js";
|
||||
import { generateAdminLayout } from "./mapAdminStage.js";
|
||||
|
|
@ -8,19 +8,91 @@ import { finalizeAdminAwareTransport } from "./mapPostAdminTransport.js";
|
|||
|
||||
export { CELL_SIZE, MAP_H, MAP_W, indexOf } from "./mapUtils.js";
|
||||
|
||||
function normalizeRectLike(rect) {
|
||||
if (!rect) return null;
|
||||
const x0 = Math.floor(Math.min(Number(rect.x0), Number(rect.x1)));
|
||||
const y0 = Math.floor(Math.min(Number(rect.y0), Number(rect.y1)));
|
||||
const x1 = Math.ceil(Math.max(Number(rect.x0), Number(rect.x1)));
|
||||
const y1 = Math.ceil(Math.max(Number(rect.y0), Number(rect.y1)));
|
||||
if (![x0, y0, x1, y1].every(Number.isFinite)) return null;
|
||||
return { x0, y0, x1, y1 };
|
||||
}
|
||||
|
||||
function normalizeGenerationContext(options = {}) {
|
||||
const originX = Math.floor(Number.isFinite(options.originX) ? options.originX : 0);
|
||||
const originY = Math.floor(Number.isFinite(options.originY) ? options.originY : 0);
|
||||
const width = Math.max(1, Math.floor(Number.isFinite(options.width) ? options.width : MAP_W));
|
||||
const height = Math.max(1, Math.floor(Number.isFinite(options.height) ? options.height : MAP_H));
|
||||
const variant = Math.max(0, Math.floor(Number.isFinite(options.variant) ? options.variant : 0)) >>> 0;
|
||||
const contextRect = normalizeRectLike(options.contextRect);
|
||||
return {
|
||||
worldNative: options.worldNative === true,
|
||||
legacyTerrain: options.legacyTerrain !== false,
|
||||
originX,
|
||||
originY,
|
||||
width,
|
||||
height,
|
||||
variant,
|
||||
contextRect,
|
||||
hasBoundaryWorld: !!options.boundaryWorld,
|
||||
};
|
||||
}
|
||||
|
||||
function mixUint(h, value) {
|
||||
h = Math.imul((h ^ (value >>> 0)) >>> 0, 2246822519) >>> 0;
|
||||
h ^= h >>> 13;
|
||||
return Math.imul(h, 3266489917) >>> 0;
|
||||
}
|
||||
|
||||
function mixString(h, value) {
|
||||
const text = String(value ?? "");
|
||||
for (let i = 0; i < text.length; i++) h = mixUint(h, text.charCodeAt(i));
|
||||
return h >>> 0;
|
||||
}
|
||||
|
||||
function contextualSeed(seed, context, options = {}) {
|
||||
let h = seed >>> 0;
|
||||
if (!context.worldNative && !context.variant && !context.originX && !context.originY) return h;
|
||||
h = mixString(h, options.terrainType || options.generationType || "auto");
|
||||
h = mixUint(h, context.variant);
|
||||
h = mixUint(h, context.originX | 0);
|
||||
h = mixUint(h, context.originY | 0);
|
||||
h = mixUint(h, context.width);
|
||||
h = mixUint(h, context.height);
|
||||
if (context.contextRect) {
|
||||
h = mixUint(h, context.contextRect.x0 | 0);
|
||||
h = mixUint(h, context.contextRect.y0 | 0);
|
||||
h = mixUint(h, context.contextRect.x1 | 0);
|
||||
h = mixUint(h, context.contextRect.y1 | 0);
|
||||
}
|
||||
return h >>> 0;
|
||||
}
|
||||
|
||||
function makeRuntimeOptions(options, baseSeed) {
|
||||
const generationContext = normalizeGenerationContext(options);
|
||||
const effectiveSeed = contextualSeed(baseSeed, generationContext, options);
|
||||
return {
|
||||
...options,
|
||||
generationContext,
|
||||
baseSeed,
|
||||
effectiveSeed,
|
||||
originX: generationContext.originX,
|
||||
originY: generationContext.originY,
|
||||
width: generationContext.width,
|
||||
height: generationContext.height,
|
||||
variant: generationContext.variant,
|
||||
};
|
||||
}
|
||||
|
||||
function generateInitialTerrain(seed, options = {}) {
|
||||
// The original high-detail terrain system remains the default for full-map
|
||||
// generation. Rect-native terrain is available as an explicit option and is
|
||||
// used by patch generation, but the historical noise/natural-compartment
|
||||
// pipeline is still the visual baseline for ordinary maps.
|
||||
if (options?.rectNativeInitial === true) return generateInitialTerrainRect(seed, options);
|
||||
// Production generation is intentionally pinned to the legacy high-detail
|
||||
// terrain/natural-compartment pipeline. Rect-native terrain helpers may remain
|
||||
// in the codebase for experiments, but they are not reachable from generateMap.
|
||||
return generateTerrainAndRivers(seed, options);
|
||||
}
|
||||
|
||||
function terrainStageLabel(options = {}) {
|
||||
return options?.rectNativeInitial === true
|
||||
? "Rect-native terrain, rivers, and natural compartments"
|
||||
: "Terrain, rivers, and natural compartments";
|
||||
function terrainStageLabel() {
|
||||
return "Terrain, rivers, and natural compartments";
|
||||
}
|
||||
|
||||
function nowMs() {
|
||||
|
|
@ -60,7 +132,9 @@ async function timedStageAsync(timings, options, key, label, fn) {
|
|||
|
||||
|
||||
export function generateMap(seedInput = 114514, options = {}) {
|
||||
const seed = Number(seedInput) >>> 0;
|
||||
const baseSeed = Number(seedInput) >>> 0;
|
||||
options = makeRuntimeOptions(options, baseSeed);
|
||||
const seed = options.effectiveSeed;
|
||||
if (typeof options.onProgress !== "function") options = { ...options, onProgress: () => {} };
|
||||
|
||||
const generationTimings = [];
|
||||
|
|
@ -86,10 +160,10 @@ export function generateMap(seedInput = 114514, options = {}) {
|
|||
naturalCompartments,
|
||||
} = terrain;
|
||||
|
||||
const geographyBasis = stage("geography", "Unified geographic basis", () => buildGeographicBasis(seed, terrain));
|
||||
const terrainWithGeography = { ...terrain, geography: geographyBasis };
|
||||
const geographyBasis = stage("geography", "Unified geographic basis", () => buildGeographicBasis(seed, terrain, options));
|
||||
const terrainWithGeography = { ...terrain, geography: geographyBasis, generationContext: options.generationContext };
|
||||
|
||||
const features = stage("settlements", "Settlements, towns, ports, and land-use demand", () => generateMapFeatures(seed, terrainWithGeography));
|
||||
const features = stage("settlements", "Settlements, towns, ports, and land-use demand", () => generateMapFeatures(seed, terrainWithGeography, options));
|
||||
const {
|
||||
settlementScore,
|
||||
villages,
|
||||
|
|
@ -109,10 +183,10 @@ export function generateMap(seedInput = 114514, options = {}) {
|
|||
villageInfluence,
|
||||
} = features;
|
||||
|
||||
const geography = stage("geographyFinal", "Unified accessibility and centrality fields", () => finalizeGeographicBasis(seed, terrain, features, geographyBasis));
|
||||
const geography = stage("geographyFinal", "Unified accessibility and centrality fields", () => finalizeGeographicBasis(seed, terrain, features, geographyBasis, options));
|
||||
|
||||
const admin = stage("admin", "Municipal and prefectural administration", () => generateAdminLayout({
|
||||
seed, prefectureMask: landMask || prefectureMask, focusedPrefectureMask: prefectureMask, naturalCompartmentId, naturalCompartments, sea, elevation, slope, river, ridgeField, naturalBarrierScore, valleyField, basinField, coastalLowland, flowAccum, plain, agriculture,
|
||||
seed, generationContext: options.generationContext, prefectureMask: landMask || prefectureMask, focusedPrefectureMask: prefectureMask, naturalCompartmentId, naturalCompartments, sea, elevation, slope, river, ridgeField, naturalBarrierScore, valleyField, basinField, coastalLowland, flowAccum, plain, agriculture,
|
||||
geography, habitability: geography.habitability, accessibility: geography.accessibility, centrality: geography.centrality, geographicBarrier: geography.geographicBarrier, geographicBarrierCost: geography.geographicBarrierCost, adminBoundaryPreference: geography.adminBoundaryPreference, boundaryAvoidance: geography.boundaryAvoidance,
|
||||
settlementScore, populationDensity, stationInfluence, roadInfluence, railInfluence2, villageInfluence, landuse, modernCities, satelliteCities, newTowns, markets, villages, ports, stations, industrialZones, logisticsParks,
|
||||
adminProgress: (event) => options?.onProgress?.({
|
||||
|
|
@ -127,7 +201,7 @@ export function generateMap(seedInput = 114514, options = {}) {
|
|||
}),
|
||||
}));
|
||||
|
||||
stage("transport", "Administrative-aware final transport network", () => finalizeAdminAwareTransport({ seed, terrain, features, admin, geography }));
|
||||
stage("transport", "Administrative-aware final transport network", () => finalizeAdminAwareTransport({ seed, terrain, features, admin, geography, generationContext: options.generationContext }));
|
||||
|
||||
const output = stage("output", "Names, population totals, and final packaging", () => finishMapOutput({
|
||||
seed,
|
||||
|
|
@ -139,11 +213,16 @@ export function generateMap(seedInput = 114514, options = {}) {
|
|||
}));
|
||||
output.generationTimings = generationTimings;
|
||||
output.generationTotalMs = generationTimings.reduce((sum, row) => sum + row.ms, 0);
|
||||
output.baseSeed = options.baseSeed;
|
||||
output.effectiveSeed = seed;
|
||||
output.generationContext = { ...options.generationContext };
|
||||
return output;
|
||||
}
|
||||
|
||||
export async function generateMapAsync(seedInput = 114514, options = {}) {
|
||||
const seed = Number(seedInput) >>> 0;
|
||||
const baseSeed = Number(seedInput) >>> 0;
|
||||
options = makeRuntimeOptions(options, baseSeed);
|
||||
const seed = options.effectiveSeed;
|
||||
if (typeof options.onProgress !== "function") options = { ...options, onProgress: () => {} };
|
||||
|
||||
const generationTimings = [];
|
||||
|
|
@ -169,10 +248,10 @@ export async function generateMapAsync(seedInput = 114514, options = {}) {
|
|||
naturalCompartments,
|
||||
} = terrain;
|
||||
|
||||
const geographyBasis = await stage("geography", "Unified geographic basis", () => buildGeographicBasis(seed, terrain));
|
||||
const terrainWithGeography = { ...terrain, geography: geographyBasis };
|
||||
const geographyBasis = await stage("geography", "Unified geographic basis", () => buildGeographicBasis(seed, terrain, options));
|
||||
const terrainWithGeography = { ...terrain, geography: geographyBasis, generationContext: options.generationContext };
|
||||
|
||||
const features = await stage("settlements", "Settlements, towns, ports, and land-use demand", () => generateMapFeatures(seed, terrainWithGeography));
|
||||
const features = await stage("settlements", "Settlements, towns, ports, and land-use demand", () => generateMapFeatures(seed, terrainWithGeography, options));
|
||||
const {
|
||||
settlementScore,
|
||||
villages,
|
||||
|
|
@ -192,10 +271,10 @@ export async function generateMapAsync(seedInput = 114514, options = {}) {
|
|||
villageInfluence,
|
||||
} = features;
|
||||
|
||||
const geography = await stage("geographyFinal", "Unified accessibility and centrality fields", () => finalizeGeographicBasis(seed, terrain, features, geographyBasis));
|
||||
const geography = await stage("geographyFinal", "Unified accessibility and centrality fields", () => finalizeGeographicBasis(seed, terrain, features, geographyBasis, options));
|
||||
|
||||
const admin = await stage("admin", "Municipal and prefectural administration", () => generateAdminLayout({
|
||||
seed, prefectureMask: landMask || prefectureMask, focusedPrefectureMask: prefectureMask, naturalCompartmentId, naturalCompartments, sea, elevation, slope, river, ridgeField, naturalBarrierScore, valleyField, basinField, coastalLowland, flowAccum, plain, agriculture,
|
||||
seed, generationContext: options.generationContext, prefectureMask: landMask || prefectureMask, focusedPrefectureMask: prefectureMask, naturalCompartmentId, naturalCompartments, sea, elevation, slope, river, ridgeField, naturalBarrierScore, valleyField, basinField, coastalLowland, flowAccum, plain, agriculture,
|
||||
geography, habitability: geography.habitability, accessibility: geography.accessibility, centrality: geography.centrality, geographicBarrier: geography.geographicBarrier, geographicBarrierCost: geography.geographicBarrierCost, adminBoundaryPreference: geography.adminBoundaryPreference, boundaryAvoidance: geography.boundaryAvoidance,
|
||||
settlementScore, populationDensity, stationInfluence, roadInfluence, railInfluence2, villageInfluence, landuse, modernCities, satelliteCities, newTowns, markets, villages, ports, stations, industrialZones, logisticsParks,
|
||||
adminProgress: (event) => options?.onProgress?.({
|
||||
|
|
@ -210,7 +289,7 @@ export async function generateMapAsync(seedInput = 114514, options = {}) {
|
|||
}),
|
||||
}));
|
||||
|
||||
await stage("transport", "Administrative-aware final transport network", () => finalizeAdminAwareTransport({ seed, terrain, features, admin, geography }));
|
||||
await stage("transport", "Administrative-aware final transport network", () => finalizeAdminAwareTransport({ seed, terrain, features, admin, geography, generationContext: options.generationContext }));
|
||||
|
||||
const output = await stage("output", "Names, population totals, and final packaging", () => finishMapOutput({
|
||||
seed,
|
||||
|
|
@ -222,5 +301,8 @@ export async function generateMapAsync(seedInput = 114514, options = {}) {
|
|||
}));
|
||||
output.generationTimings = generationTimings;
|
||||
output.generationTotalMs = generationTimings.reduce((sum, row) => sum + row.ms, 0);
|
||||
output.baseSeed = options.baseSeed;
|
||||
output.effectiveSeed = seed;
|
||||
output.generationContext = { ...options.generationContext };
|
||||
return output;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue