This commit is contained in:
33333-33333 2026-05-20 20:55:43 +09:00
commit 4bf36b1f59
6 changed files with 157 additions and 43 deletions

View file

@ -25,7 +25,7 @@ function neighbors4(x, y) {
return out;
}
export function generateAdminRegions(centers, prefectureMask, sea, elevation, slope, river, ridgeField, valleyField, populationDensity, landuse) {
export function generateAdminRegions(centers, prefectureMask, sea, elevation, slope, river, ridgeField, valleyField, crestCrossingScore, populationDensity, landuse) {
const adminId = new Int16Array(SIZE);
adminId.fill(-1);
const dist = new Float32Array(SIZE);
@ -55,14 +55,16 @@ export function generateAdminRegions(centers, prefectureMask, sea, elevation, sl
const ridgeBarrier = Math.max(ridgeField[ni], ridgeField[curIndex]);
const riverBarrier = Math.max(river[ni], river[curIndex]);
const highDivide = Math.max(elevation[ni], elevation[curIndex]);
const crest = Math.max(crestCrossingScore?.[ni] || 0, crestCrossingScore?.[curIndex] || 0);
const watershedBarrier = ridgeBarrier * (27.0 + Math.max(0, highDivide - 0.46) * 46.0);
const crestPenalty = crest * (18.0 + Math.max(0, highDivide - 0.48) * 38.0) * (1 + Math.max(0, highDivide - 0.56) * 1.9);
const ridgePenalty = Math.max(0, highDivide - 0.36) * 16.0 + Math.abs(elevation[ni] - elevation[curIndex]) * 12.4 + watershedBarrier;
const slopePenalty = slope[ni] * 10.6;
const valleyBarrier = valleyField[ni] > 0.50 ? valleyField[ni] * (riverBarrier > 0.16 ? 7.2 : 2.6) : 0;
const riverPenalty = riverBarrier > 0.7 ? 22.0 : riverBarrier > 0.42 ? 14.8 : riverBarrier > 0.22 ? 7.4 : riverBarrier > 0.12 ? 2.2 : 0;
const urbanContinuityBonus = (landuse[ni] >= 2 && landuse[ni] <= 4 && populationDensity[ni] > 0.20) ? 1.65 : 0;
const valleyLocalityBonus = valleyField[ni] * 0.16;
const stepCost = Math.max(0.25, 0.72 + ridgePenalty + slopePenalty + riverPenalty + valleyBarrier - valleyLocalityBonus - urbanContinuityBonus) * step;
const stepCost = Math.max(0.25, 0.72 + ridgePenalty + crestPenalty + slopePenalty + riverPenalty + valleyBarrier - valleyLocalityBonus - urbanContinuityBonus) * step;
const nextDist = dist[curIndex] + stepCost;
if (nextDist < dist[ni]) {
@ -266,15 +268,16 @@ export function removeMunicipalExclaves(adminId, prefectureMask, sea, adminCente
}
}
export function terrainBoundaryTargetScore(i, elevation, slope, river, ridgeField, valleyField, flowAccum, populationDensity, landuse) {
export function terrainBoundaryTargetScore(i, elevation, slope, river, ridgeField, valleyField, flowAccum, crestCrossingScore, populationDensity, landuse) {
const urbanPenalty = urbanBoundaryPenalty(i, populationDensity, landuse);
const crest = crestCrossingScore?.[i] || 0;
const majorRiver = clamp(Math.max(river[i] - 0.32, 0) * 1.9 + Math.max(flowAccum[i] - 0.38, 0) * 0.75);
const minorStream = clamp(river[i] * 0.34 + flowAccum[i] * 0.18);
const ridgeDivide = clamp(ridgeField[i] * 1.55 + Math.max(0, elevation[i] - 0.54) * ridgeField[i] * 0.95);
const slopeBreak = clamp(slope[i] * 0.58 + Math.max(0, slope[i] - 0.32) * 0.68);
const highGround = Math.max(0, elevation[i] - 0.56) * 0.22;
const valleyFloorPenalty = valleyField[i] * (majorRiver > 0.34 ? -0.10 : -0.62);
return clamp(ridgeDivide + majorRiver * 0.88 + minorStream * 0.22 + slopeBreak + highGround + valleyFloorPenalty - urbanPenalty * 0.72);
return clamp(ridgeDivide + crest * 1.38 + majorRiver * 0.88 + minorStream * 0.22 + slopeBreak + highGround + valleyFloorPenalty - urbanPenalty * 0.72);
}
function urbanBoundaryPenalty(i, populationDensity, landuse) {
@ -344,7 +347,7 @@ function buildAdminProtectedMask(adminId, prefectureMask, sea, adminCenters = []
function localBoundaryEnergy(labels, i, candidateId, targetScore, centerDist, populationDensity, landuse, river, valleyField) {
const [x, y] = xyOf(i);
const oldId = labels[i];
let energy = centerDist[candidateId]?.[i] ?? 0;
let energy = (centerDist[candidateId]?.[i] ?? 0) * 0.035;
let same4 = 0;
let diff4 = 0;
let diagDiff = 0;
@ -375,7 +378,7 @@ function localBoundaryEnergy(labels, i, candidateId, targetScore, centerDist, po
if (diagDiff >= 3 && diff4 >= 2 && targetScore[i] < 0.50) energy += 0.42;
if (candidateId !== oldId && centerDist[candidateId] && centerDist[oldId]) {
const drift = centerDist[candidateId][i] - centerDist[oldId][i];
if (drift > 0) energy += Math.min(0.9, drift * 0.012);
if (drift > 0) energy += Math.min(0.55, drift * 0.006);
}
return energy;
}
@ -467,7 +470,7 @@ function classifyLandscapeCell(i, elevation, slope, river, ridgeField, valleyFie
return 11;
}
function buildLandscapeUnits(prefectureMask, sea, elevation, slope, river, ridgeField, valleyField, basinField, coastalLowland, flowAccum, plain, agriculture, populationDensity, landuse) {
function buildLandscapeUnits(prefectureMask, sea, elevation, slope, river, ridgeField, valleyField, basinField, coastalLowland, flowAccum, crestCrossingScore, plain, agriculture, populationDensity, landuse) {
const unitId = new Int32Array(SIZE);
unitId.fill(-1);
const cellClass = new Int16Array(SIZE);
@ -504,7 +507,7 @@ function buildLandscapeUnits(prefectureMask, sea, elevation, slope, river, ridge
}
const targetScore = new Float32Array(SIZE);
for (let i = 0; i < SIZE; i++) if (cellClass[i] >= 0) targetScore[i] = terrainBoundaryTargetScore(i, elevation, slope, river, ridgeField, valleyField, flowAccum, populationDensity, landuse);
for (let i = 0; i < SIZE; i++) if (cellClass[i] >= 0) targetScore[i] = terrainBoundaryTargetScore(i, elevation, slope, river, ridgeField, valleyField, flowAccum, crestCrossingScore, populationDensity, landuse);
rebuildLandscapeUnitAdjacency(unitId, units, targetScore, prefectureMask, sea);
mergeTinyLandscapeUnits(unitId, units, 10);
rebuildLandscapeUnitAdjacency(unitId, units, targetScore, prefectureMask, sea);
@ -569,8 +572,8 @@ function landscapeTransitionCost(a, b, edge) {
return Math.max(0.18, 0.70 + boundaryTarget * 4.2 + (a.classId === b.classId ? 0 : 0.75) + ((a.classId === 8 || b.classId === 8) ? 1.2 : 0) - urbanContinuity - (bothCorridor ? 0.55 : 0));
}
export function applyLandscapeUnitAdminPartition(adminId, prefectureMask, sea, elevation, slope, river, ridgeField, valleyField, basinField, coastalLowland, flowAccum, plain, agriculture, populationDensity, landuse, adminCenters = []) {
const { unitId, units, targetScore } = buildLandscapeUnits(prefectureMask, sea, elevation, slope, river, ridgeField, valleyField, basinField, coastalLowland, flowAccum, plain, agriculture, populationDensity, landuse);
export function applyLandscapeUnitAdminPartition(adminId, prefectureMask, sea, elevation, slope, river, ridgeField, valleyField, basinField, coastalLowland, flowAccum, crestCrossingScore, plain, agriculture, populationDensity, landuse, adminCenters = []) {
const { unitId, units, targetScore } = buildLandscapeUnits(prefectureMask, sea, elevation, slope, river, ridgeField, valleyField, basinField, coastalLowland, flowAccum, crestCrossingScore, plain, agriculture, populationDensity, landuse);
if (units.length === 0) return;
for (let id = 0; id < adminCenters.length; id++) {
const center = adminCenters[id];
@ -621,11 +624,11 @@ export function applyLandscapeUnitAdminPartition(adminId, prefectureMask, sea, e
repairAdminTopology(adminId, prefectureMask, sea, adminCenters, targetScore, populationDensity, landuse);
}
export function snapAdminBoundariesToTerrain(adminId, prefectureMask, sea, elevation, slope, river, ridgeField, valleyField, flowAccum, populationDensity, landuse, adminCenters = [], protectedPoints = [], passes = 6) {
export function snapAdminBoundariesToTerrain(adminId, prefectureMask, sea, elevation, slope, river, ridgeField, valleyField, flowAccum, crestCrossingScore, populationDensity, landuse, adminCenters = [], protectedPoints = [], passes = 6) {
const targetScore = new Float32Array(SIZE);
for (let i = 0; i < SIZE; i++) if (prefectureMask[i] && !sea[i]) targetScore[i] = terrainBoundaryTargetScore(i, elevation, slope, river, ridgeField, valleyField, flowAccum, populationDensity, landuse);
for (let i = 0; i < SIZE; i++) if (prefectureMask[i] && !sea[i]) targetScore[i] = terrainBoundaryTargetScore(i, elevation, slope, river, ridgeField, valleyField, flowAccum, crestCrossingScore, populationDensity, landuse);
const protectedMask = buildAdminProtectedMask(adminId, prefectureMask, sea, adminCenters, protectedPoints, populationDensity, landuse);
const band = buildBoundaryBand(adminId, prefectureMask, sea, 5);
const band = buildBoundaryBand(adminId, prefectureMask, sea, 8);
const adminIds = [...new Set([...adminId].filter((id) => id >= 0))];
const centerDist = buildCenterDistanceFields(adminIds, adminCenters, prefectureMask, sea);
let current = new Int16Array(adminId);