road update
This commit is contained in:
parent
71b58cf033
commit
a6e66f2838
9 changed files with 2174 additions and 258 deletions
|
|
@ -703,7 +703,7 @@ function naturalGroupKey(unit) {
|
|||
if (unit.classId <= 3) return `urban:${Math.round(unit.x / 10)}:${Math.round(unit.y / 10)}`;
|
||||
if (unit.classId === 5) return `coast:${Math.round(unit.y / 8)}`;
|
||||
if (unit.classId === 6) return `basin:${Math.round(unit.x / 12)}:${Math.round(unit.y / 12)}`;
|
||||
if (unit.classId === 7) return `valley:${Math.round((unit.x + unit.y) / 12)}`;
|
||||
if (unit.classId === 7) return `valley:${Math.round(unit.x / 11)}:${Math.round(unit.y / 11)}`;
|
||||
if (unit.classId === 8 || unit.classId === 9) return `mountain:${Math.round(unit.x / 14)}:${Math.round(unit.y / 14)}`;
|
||||
return `plain:${Math.round(unit.x / 14)}:${Math.round(unit.y / 14)}`;
|
||||
}
|
||||
|
|
@ -1030,6 +1030,7 @@ function buildSeededNaturalCompartments(prefectureMask, sea, elevation, slope, r
|
|||
let compartments = buildUnitsFromAssignment(compartmentId, cellClass, prefectureMask, sea, fields);
|
||||
rebuildLandscapeUnitAdjacency(compartmentId, compartments, naturalBarrierScore, prefectureMask, sea);
|
||||
mergeTinyLandscapeUnits(compartmentId, compartments, 9);
|
||||
mergeWeakArtificialLandscapeUnits(compartmentId, compartments, fields, prefectureMask, sea, Math.max(42, Math.round(landArea / Math.max(1, targetCount) * 2.15)), 5);
|
||||
progress?.(`natural post-split units: ${compartments.filter((unit) => unit && unit.area > 0).length}`);
|
||||
splitDisconnectedCompartments(compartmentId, compartments, prefectureMask, sea);
|
||||
compartments = renumberCompartments(compartmentId, compartments, prefectureMask, sea);
|
||||
|
|
@ -1067,6 +1068,7 @@ function buildSeededNaturalCompartments(prefectureMask, sea, elevation, slope, r
|
|||
}
|
||||
}
|
||||
|
||||
mergeWeakArtificialLandscapeUnits(compartmentId, compartments, fields, prefectureMask, sea, Math.max(48, Math.round(landArea / Math.max(1, targetCount) * 2.05)), 4);
|
||||
splitDisconnectedCompartments(compartmentId, compartments, prefectureMask, sea);
|
||||
compartments = renumberCompartments(compartmentId, compartments, prefectureMask, sea);
|
||||
refreshAllCompartmentStats(compartments, fields);
|
||||
|
|
@ -1137,10 +1139,11 @@ function rebuildLandscapeUnitAdjacency(unitId, units, targetScore, prefectureMas
|
|||
const b = unitId[ni];
|
||||
if (b < 0 || b === a || !units[b] || units[b].area === 0) continue;
|
||||
const v = (targetScore[i] + targetScore[ni]) * 0.5;
|
||||
const keyA = units[a].adjacent.get(b) || { count: 0, target: 0 };
|
||||
keyA.count++; keyA.target += v; units[a].adjacent.set(b, keyA);
|
||||
const keyB = units[b].adjacent.get(a) || { count: 0, target: 0 };
|
||||
keyB.count++; keyB.target += v; units[b].adjacent.set(a, keyB);
|
||||
const vertical = nx !== x ? 1 : 0;
|
||||
const keyA = units[a].adjacent.get(b) || { count: 0, target: 0, vertical: 0, horizontal: 0 };
|
||||
keyA.count++; keyA.target += v; if (vertical) keyA.vertical++; else keyA.horizontal++; units[a].adjacent.set(b, keyA);
|
||||
const keyB = units[b].adjacent.get(a) || { count: 0, target: 0, vertical: 0, horizontal: 0 };
|
||||
keyB.count++; keyB.target += v; if (vertical) keyB.vertical++; else keyB.horizontal++; units[b].adjacent.set(a, keyB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1172,6 +1175,65 @@ function mergeTinyLandscapeUnits(unitId, units, minArea = 10) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function mergeUnitInto(unitId, units, fromId, toId, fields) {
|
||||
const from = units[fromId];
|
||||
const to = units[toId];
|
||||
if (!from || !to || from.area === 0 || to.area === 0 || fromId === toId) return false;
|
||||
for (const ci of from.cells) { unitId[ci] = toId; to.cells.push(ci); }
|
||||
from.area = 0;
|
||||
from.cells = [];
|
||||
refreshCompartmentStats(to, fields.elevation, fields.slope, fields.ridgeField, fields.valleyField, fields.basinField, fields.coastalLowland, fields.plain, fields.agriculture, fields.populationDensity, fields.landuse);
|
||||
return true;
|
||||
}
|
||||
|
||||
function mergeWeakArtificialLandscapeUnits(unitId, units, fields, prefectureMask, sea, maxMergedArea = 84, passes = 5) {
|
||||
// Seeded graph growth can create diagonal stair-step borders in uniform plains
|
||||
// and gentle hills. If the shared edge is weak, balanced H/V, and the two
|
||||
// sides are the same natural group, merge it instead of preserving an
|
||||
// artificial Voronoi-like cut.
|
||||
for (let pass = 0; pass < passes; pass++) {
|
||||
rebuildLandscapeUnitAdjacency(unitId, units, fields.naturalBarrierScore, prefectureMask, sea);
|
||||
let best = null;
|
||||
let bestScore = 0.0;
|
||||
for (const unit of units) {
|
||||
if (!unit || unit.area === 0) continue;
|
||||
for (const [otherId, edge] of unit.adjacent) {
|
||||
if (otherId <= unit.id) continue;
|
||||
const other = units[otherId];
|
||||
if (!other || other.area === 0) continue;
|
||||
const avgBarrier = edge.target / Math.max(1, edge.count);
|
||||
const sameClass = unit.classId === other.classId;
|
||||
const sameGroup = naturalGroupKey(unit) === naturalGroupKey(other);
|
||||
const combinedArea = unit.area + other.area;
|
||||
if (!sameClass && !sameGroup) continue;
|
||||
if (combinedArea > maxMergedArea && unit.area > 18 && other.area > 18) continue;
|
||||
const h = edge.horizontal || 0;
|
||||
const v = edge.vertical || 0;
|
||||
const balancedStair = Math.min(h, v) / Math.max(1, h + v);
|
||||
const lowlandContinuity = Math.min(unit.lowlandFitness || 0, other.lowlandFitness || 0);
|
||||
const mountainContinuity = Math.min(unit.mountainFitness || 0, other.mountainFitness || 0);
|
||||
const urbanGuard = Math.max(unit.urbanWeight || 0, other.urbanWeight || 0);
|
||||
const weakDivider = avgBarrier < (sameClass ? 0.46 : 0.36);
|
||||
if (!weakDivider) continue;
|
||||
const score =
|
||||
(sameClass ? 1.7 : 0) +
|
||||
(sameGroup ? 1.2 : 0) +
|
||||
balancedStair * 1.4 +
|
||||
edge.count * 0.018 +
|
||||
lowlandContinuity * 0.85 +
|
||||
mountainContinuity * 0.35 -
|
||||
avgBarrier * 4.8 -
|
||||
Math.max(0, combinedArea - maxMergedArea) * 0.020 -
|
||||
urbanGuard * 0.20;
|
||||
if (score > bestScore) best = { fromId: unit.area <= other.area ? unit.id : otherId, toId: unit.area <= other.area ? otherId : unit.id }, bestScore = score;
|
||||
}
|
||||
}
|
||||
if (!best) break;
|
||||
mergeUnitInto(unitId, units, best.fromId, best.toId, fields);
|
||||
}
|
||||
}
|
||||
|
||||
function naturalOwnershipAffinity(unit, neighbor, edge) {
|
||||
const boundaryTarget = edge.target / Math.max(1, edge.count);
|
||||
const sameClass = unit.classId === neighbor.classId ? 1.0 : 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue