2026-05-21 13:20:19 +09:00
import { CELL _SIZE , MAP _H , MAP _W , indexOf } from "./mapUtils.js" ;
import { generateTerrainAndRivers } from "./mapTerrain.js" ;
2026-05-26 00:45:01 +09:00
import { generateMapFeatures } from "./mapFeatures.js" ;
2026-05-21 13:20:19 +09:00
import { finishMapOutput } from "./mapOutput.js" ;
import { generateAdminLayout } from "./mapAdminStage.js" ;
2026-05-28 00:30:09 +09:00
import { buildGeographicBasis , finalizeGeographicBasis } from "./mapGeography.js" ;
import { finalizeAdminAwareTransport } from "./mapPostAdminTransport.js" ;
2026-05-21 13:20:19 +09:00
export { CELL _SIZE , MAP _H , MAP _W , indexOf } from "./mapUtils.js" ;
2026-05-24 19:33:09 +09:00
function nowMs ( ) {
return typeof performance !== "undefined" && performance . now ? performance . now ( ) : Date . now ( ) ;
}
function timedStage ( timings , options , key , label , fn ) {
2026-05-24 22:52:22 +09:00
options ? . onProgress ? . ( { status : "start" , key , label , timings : timings . slice ( ) , startedAt : nowMs ( ) } ) ;
2026-05-24 19:33:09 +09:00
const t0 = nowMs ( ) ;
const value = fn ( ) ;
const ms = Math . round ( ( nowMs ( ) - t0 ) * 10 ) / 10 ;
const entry = { key , label , ms } ;
timings . push ( entry ) ;
options ? . onProgress ? . ( { status : "done" , key , label , ms , timings : timings . slice ( ) } ) ;
return value ;
}
2026-05-24 22:52:22 +09:00
function yieldToBrowser ( ) {
if ( typeof requestAnimationFrame === "function" ) {
return new Promise ( ( resolve ) => requestAnimationFrame ( ( ) => resolve ( ) ) ) ;
}
return new Promise ( ( resolve ) => setTimeout ( resolve , 0 ) ) ;
}
async function timedStageAsync ( timings , options , key , label , fn ) {
options ? . onProgress ? . ( { status : "start" , key , label , timings : timings . slice ( ) , startedAt : nowMs ( ) } ) ;
await yieldToBrowser ( ) ;
const t0 = nowMs ( ) ;
const value = fn ( ) ;
const ms = Math . round ( ( nowMs ( ) - t0 ) * 10 ) / 10 ;
const entry = { key , label , ms } ;
timings . push ( entry ) ;
options ? . onProgress ? . ( { status : "done" , key , label , ms , timings : timings . slice ( ) } ) ;
await yieldToBrowser ( ) ;
return value ;
}
2026-05-21 13:20:19 +09:00
export function generateMap ( seedInput = 114514 , options = { } ) {
const seed = Number ( seedInput ) >>> 0 ;
2026-05-24 19:33:09 +09:00
if ( typeof options . onProgress !== "function" ) options = { ... options , onProgress : ( ) => { } } ;
const generationTimings = [ ] ;
const stage = ( key , label , fn ) => timedStage ( generationTimings , options , key , label , fn ) ;
2026-05-21 13:20:19 +09:00
2026-05-26 15:32:27 +09:00
const terrain = stage ( "terrain" , "Terrain, rivers, and natural compartments" , ( ) => generateTerrainAndRivers ( seed ) ) ;
2026-05-21 13:20:19 +09:00
const {
elevation ,
slope ,
sea ,
river ,
plain ,
agriculture ,
ridgeField ,
valleyField ,
basinField ,
coastalLowland ,
flowAccum ,
2026-05-21 22:03:14 +09:00
naturalBarrierScore ,
2026-05-21 13:20:19 +09:00
prefectureMask ,
2026-05-26 15:32:27 +09:00
landMask ,
naturalCompartmentId ,
naturalCompartments ,
2026-05-21 13:20:19 +09:00
} = terrain ;
2026-05-28 00:30:09 +09:00
const geographyBasis = stage ( "geography" , "Unified geographic basis" , ( ) => buildGeographicBasis ( seed , terrain ) ) ;
const terrainWithGeography = { ... terrain , geography : geographyBasis } ;
const features = stage ( "settlements" , "Settlements, towns, ports, and land-use demand" , ( ) => generateMapFeatures ( seed , terrainWithGeography ) ) ;
2026-05-21 13:20:19 +09:00
const {
2026-05-24 21:21:17 +09:00
settlementScore ,
villages ,
markets ,
modernCities ,
populationDensity ,
stations ,
industrialZones ,
logisticsParks ,
satelliteCities ,
newTowns ,
ports ,
landuse ,
stationInfluence ,
roadInfluence ,
railInfluence2 ,
villageInfluence ,
2026-05-21 13:20:19 +09:00
} = features ;
2026-05-28 00:30:09 +09:00
const geography = stage ( "geographyFinal" , "Unified accessibility and centrality fields" , ( ) => finalizeGeographicBasis ( seed , terrain , features , geographyBasis ) ) ;
2026-05-24 21:21:17 +09:00
const admin = stage ( "admin" , "Municipal and prefectural administration" , ( ) => generateAdminLayout ( {
2026-05-26 15:32:27 +09:00
seed , prefectureMask : landMask || prefectureMask , focusedPrefectureMask : prefectureMask , naturalCompartmentId , naturalCompartments , sea , elevation , slope , river , ridgeField , naturalBarrierScore , valleyField , basinField , coastalLowland , flowAccum , plain , agriculture ,
2026-05-28 00:30:09 +09:00
geography , habitability : geography . habitability , accessibility : geography . accessibility , centrality : geography . centrality , geographicBarrier : geography . geographicBarrier , geographicBarrierCost : geography . geographicBarrierCost , adminBoundaryPreference : geography . adminBoundaryPreference , boundaryAvoidance : geography . boundaryAvoidance ,
2026-05-21 13:20:19 +09:00
settlementScore , populationDensity , stationInfluence , roadInfluence , railInfluence2 , villageInfluence , landuse , modernCities , satelliteCities , newTowns , markets , villages , ports , stations , industrialZones , logisticsParks ,
2026-05-24 19:33:09 +09:00
adminProgress : ( event ) => options ? . onProgress ? . ( {
... event ,
key : "admin" ,
label : event . status === "region-done"
? ` Admin region ${ event . regionId } done `
: event . status === "admin-step"
? ` Admin region ${ event . regionId } : ${ event . step } `
: ` Admin region ${ event . regionId } ` ,
timings : generationTimings . slice ( ) ,
} ) ,
} ) ) ;
2026-05-21 13:20:19 +09:00
2026-05-28 00:30:09 +09:00
stage ( "transport" , "Administrative-aware final transport network" , ( ) => finalizeAdminAwareTransport ( { seed , terrain , features , admin , geography } ) ) ;
2026-05-24 19:33:09 +09:00
const output = stage ( "output" , "Names, population totals, and final packaging" , ( ) => finishMapOutput ( {
2026-05-24 21:21:17 +09:00
seed ,
options ,
terrain ,
features ,
admin ,
2026-05-28 00:30:09 +09:00
geography ,
2026-05-24 19:33:09 +09:00
} ) ) ;
output . generationTimings = generationTimings ;
output . generationTotalMs = generationTimings . reduce ( ( sum , row ) => sum + row . ms , 0 ) ;
return output ;
2026-05-21 13:20:19 +09:00
}
2026-05-24 22:52:22 +09:00
export async function generateMapAsync ( seedInput = 114514 , options = { } ) {
const seed = Number ( seedInput ) >>> 0 ;
if ( typeof options . onProgress !== "function" ) options = { ... options , onProgress : ( ) => { } } ;
const generationTimings = [ ] ;
const stage = ( key , label , fn ) => timedStageAsync ( generationTimings , options , key , label , fn ) ;
2026-05-26 15:32:27 +09:00
const terrain = await stage ( "terrain" , "Terrain, rivers, and natural compartments" , ( ) => generateTerrainAndRivers ( seed ) ) ;
2026-05-24 22:52:22 +09:00
const {
elevation ,
slope ,
sea ,
river ,
plain ,
agriculture ,
ridgeField ,
valleyField ,
basinField ,
coastalLowland ,
flowAccum ,
naturalBarrierScore ,
prefectureMask ,
2026-05-26 15:32:27 +09:00
landMask ,
naturalCompartmentId ,
naturalCompartments ,
2026-05-24 22:52:22 +09:00
} = terrain ;
2026-05-28 00:30:09 +09:00
const geographyBasis = await stage ( "geography" , "Unified geographic basis" , ( ) => buildGeographicBasis ( seed , terrain ) ) ;
const terrainWithGeography = { ... terrain , geography : geographyBasis } ;
const features = await stage ( "settlements" , "Settlements, towns, ports, and land-use demand" , ( ) => generateMapFeatures ( seed , terrainWithGeography ) ) ;
2026-05-24 22:52:22 +09:00
const {
settlementScore ,
villages ,
markets ,
modernCities ,
populationDensity ,
stations ,
industrialZones ,
logisticsParks ,
satelliteCities ,
newTowns ,
ports ,
landuse ,
stationInfluence ,
roadInfluence ,
railInfluence2 ,
villageInfluence ,
} = features ;
2026-05-28 00:30:09 +09:00
const geography = await stage ( "geographyFinal" , "Unified accessibility and centrality fields" , ( ) => finalizeGeographicBasis ( seed , terrain , features , geographyBasis ) ) ;
2026-05-24 22:52:22 +09:00
const admin = await stage ( "admin" , "Municipal and prefectural administration" , ( ) => generateAdminLayout ( {
2026-05-26 15:32:27 +09:00
seed , prefectureMask : landMask || prefectureMask , focusedPrefectureMask : prefectureMask , naturalCompartmentId , naturalCompartments , sea , elevation , slope , river , ridgeField , naturalBarrierScore , valleyField , basinField , coastalLowland , flowAccum , plain , agriculture ,
2026-05-28 00:30:09 +09:00
geography , habitability : geography . habitability , accessibility : geography . accessibility , centrality : geography . centrality , geographicBarrier : geography . geographicBarrier , geographicBarrierCost : geography . geographicBarrierCost , adminBoundaryPreference : geography . adminBoundaryPreference , boundaryAvoidance : geography . boundaryAvoidance ,
2026-05-24 22:52:22 +09:00
settlementScore , populationDensity , stationInfluence , roadInfluence , railInfluence2 , villageInfluence , landuse , modernCities , satelliteCities , newTowns , markets , villages , ports , stations , industrialZones , logisticsParks ,
adminProgress : ( event ) => options ? . onProgress ? . ( {
... event ,
key : "admin" ,
label : event . status === "region-done"
? ` Admin region ${ event . regionId } done `
: event . status === "admin-step"
? ` Admin region ${ event . regionId } : ${ event . step } `
: ` Admin region ${ event . regionId } ` ,
timings : generationTimings . slice ( ) ,
} ) ,
} ) ) ;
2026-05-28 00:30:09 +09:00
await stage ( "transport" , "Administrative-aware final transport network" , ( ) => finalizeAdminAwareTransport ( { seed , terrain , features , admin , geography } ) ) ;
2026-05-24 22:52:22 +09:00
const output = await stage ( "output" , "Names, population totals, and final packaging" , ( ) => finishMapOutput ( {
seed ,
options ,
terrain ,
features ,
admin ,
2026-05-28 00:30:09 +09:00
geography ,
2026-05-24 22:52:22 +09:00
} ) ) ;
output . generationTimings = generationTimings ;
output . generationTotalMs = generationTimings . reduce ( ( sum , row ) => sum + row . ms , 0 ) ;
return output ;
}