road update
This commit is contained in:
parent
71b58cf033
commit
a6e66f2838
9 changed files with 2174 additions and 258 deletions
106
renderer.js
106
renderer.js
|
|
@ -314,7 +314,8 @@ function terrainColorContinuous(map, fx, fy, mode) {
|
|||
|
||||
if (isWaterSample(map, fx, fy)) {
|
||||
const depth = clamp(((map.seaLevel ?? 0.285) + 0.065 - fieldSample(map.elevation, fx, fy)) * 2.4);
|
||||
color = [Math.round(170 + depth * 5), Math.round(218 + depth * 10), Math.round(255 - depth * 5)];
|
||||
// Calm sky-blue water; less saturated than the previous bright cyan.
|
||||
color = [Math.round(174 - depth * 7), Math.round(203 + depth * 2), Math.round(224 + depth * 5)];
|
||||
} else if (mode === "development") {
|
||||
const dCity = distToNearest(map.modernCities, fx, fy);
|
||||
const urban = clamp(1 - dCity / 25);
|
||||
|
|
@ -644,6 +645,36 @@ function drawDebugCells(ctx, map, field, color) {
|
|||
ctx.restore();
|
||||
}
|
||||
|
||||
function drawTransportDebug(ctx, map) {
|
||||
const layers = map.transportDebug?.layers;
|
||||
if (!layers) return;
|
||||
drawDebugCells(ctx, map, layers.expresswayPotential, (v) => v < 0.16 ? "rgba(0,0,0,0)" : `rgba(80, 190, 110, ${0.035 + v * 0.13})`);
|
||||
drawDebugCells(ctx, map, layers.railPotential, (v) => v < 0.16 ? "rgba(0,0,0,0)" : `rgba(70, 120, 230, ${0.035 + v * 0.13})`);
|
||||
drawDebugCells(ctx, map, layers.nationalRoadPotential, (v) => v < 0.16 ? "rgba(0,0,0,0)" : `rgba(245, 205, 65, ${0.030 + v * 0.12})`);
|
||||
drawDebugCells(ctx, map, layers.slopeSeaPenalty, (v) => v < 0.45 ? "rgba(0,0,0,0)" : `rgba(80, 30, 30, ${0.025 + v * 0.10})`);
|
||||
|
||||
const componentColors = {
|
||||
expressway: "rgba(60, 165, 80, 0.42)",
|
||||
rail: "rgba(65, 95, 210, 0.42)",
|
||||
national: "rgba(210, 155, 20, 0.42)",
|
||||
};
|
||||
ctx.save();
|
||||
for (const comp of layers.components || []) {
|
||||
ctx.fillStyle = componentColors[comp.mode] || "rgba(150,150,150,0.35)";
|
||||
for (const [x, y] of comp.cells || []) ctx.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
|
||||
}
|
||||
ctx.restore();
|
||||
|
||||
for (const repair of layers.repairedSegments || []) {
|
||||
const color = repair.mode === "expressway" ? "rgba(0, 115, 40, 0.95)"
|
||||
: repair.mode === "rail" ? "rgba(35, 70, 210, 0.95)"
|
||||
: repair.mode === "national" ? "rgba(180, 120, 0, 0.95)"
|
||||
: "rgba(210, 35, 155, 0.92)";
|
||||
drawPath(ctx, repair.path, "rgba(255,255,255,0.92)", 5.0);
|
||||
drawPath(ctx, repair.path, color, 2.4);
|
||||
}
|
||||
}
|
||||
|
||||
function prefectureRegionColor(id) {
|
||||
const palette = [
|
||||
[234, 220, 214], [218, 232, 218], [218, 224, 238], [238, 232, 208],
|
||||
|
|
@ -653,10 +684,10 @@ function prefectureRegionColor(id) {
|
|||
}
|
||||
|
||||
function drawPrefectureRegionFill(ctx, map, mode) {
|
||||
if (!["all", "admin", "admin-debug", "borders-debug"].includes(mode)) return;
|
||||
if (!["all", "admin", "borders-debug"].includes(mode)) return;
|
||||
const ids = map.prefectureRegionId;
|
||||
if (!ids) return;
|
||||
const alpha = mode === "borders-debug" ? 0.34 : mode === "admin-debug" ? 0.26 : 0.18;
|
||||
const alpha = mode === "borders-debug" ? 0.34 : 0.18;
|
||||
ctx.save();
|
||||
ctx.globalAlpha = alpha;
|
||||
for (let y = 0; y < MAP_H; y++) {
|
||||
|
|
@ -688,30 +719,38 @@ function boxesOverlap(a, b, pad = 3) {
|
|||
|
||||
function labelWithCollision(ctx, p, occupied) {
|
||||
if (!p.name) return false;
|
||||
const isPrefectureLabel = p.kind === "Prefecture" || p.kind === "Current Prefecture" || p.isPrefectureLabel;
|
||||
ctx.save();
|
||||
ctx.font = "600 11.5px ui-sans-serif, system-ui, -apple-system, sans-serif";
|
||||
ctx.font = isPrefectureLabel
|
||||
? "900 20px ui-sans-serif, system-ui, -apple-system, sans-serif"
|
||||
: "600 11.5px ui-sans-serif, system-ui, -apple-system, sans-serif";
|
||||
const baseX = p.x * CELL_SIZE + CELL_SIZE / 2;
|
||||
const baseY = p.y * CELL_SIZE + CELL_SIZE / 2;
|
||||
const textW = ctx.measureText(p.name).width;
|
||||
const textH = 12;
|
||||
const candidates = [
|
||||
[7, -4], [7, 13], [-textW - 7, -4], [-textW - 7, 13],
|
||||
[-textW / 2, -12], [-textW / 2, 20], [12, 4], [-textW - 12, 4],
|
||||
];
|
||||
const textH = isPrefectureLabel ? 22 : 12;
|
||||
const candidates = isPrefectureLabel
|
||||
? [
|
||||
[-textW / 2, 6], [-textW / 2, -12], [-textW / 2, 24],
|
||||
[10, 6], [-textW - 10, 6],
|
||||
]
|
||||
: [
|
||||
[7, -4], [7, 13], [-textW - 7, -4], [-textW - 7, 13],
|
||||
[-textW / 2, -12], [-textW / 2, 20], [12, 4], [-textW - 12, 4],
|
||||
];
|
||||
|
||||
for (const [ox, oy] of candidates) {
|
||||
const x = baseX + ox;
|
||||
const y = baseY + oy;
|
||||
const box = { x1: x - 2, y1: y - textH, x2: x + textW + 2, y2: y + 4 };
|
||||
const box = { x1: x - 3, y1: y - textH, x2: x + textW + 3, y2: y + 5 };
|
||||
if (box.x1 < 0 || box.y1 < 0 || box.x2 > MAP_W * CELL_SIZE || box.y2 > MAP_H * CELL_SIZE) continue;
|
||||
if (occupied.some((b) => boxesOverlap(box, b))) continue;
|
||||
|
||||
if (occupied.some((b) => boxesOverlap(box, b, isPrefectureLabel ? 5 : 3))) continue;
|
||||
|
||||
ctx.lineJoin = "round";
|
||||
ctx.lineWidth = 3.5;
|
||||
ctx.strokeStyle = "rgba(255, 255, 255, 0.95)";
|
||||
ctx.lineWidth = isPrefectureLabel ? 6.2 : 3.5;
|
||||
ctx.strokeStyle = isPrefectureLabel ? "rgba(255, 255, 255, 0.98)" : "rgba(255, 255, 255, 0.95)";
|
||||
ctx.strokeText(p.name, x, y);
|
||||
|
||||
ctx.fillStyle = p.isPrefecturalCapital ? "#111111" : "#333333";
|
||||
|
||||
ctx.fillStyle = isPrefectureLabel ? "rgba(62, 38, 112, 0.98)" : p.isPrefecturalCapital ? "#111111" : "#333333";
|
||||
ctx.fillText(p.name, x, y);
|
||||
occupied.push(box);
|
||||
ctx.restore();
|
||||
|
|
@ -787,8 +826,8 @@ export function drawMap(canvas, map, options) {
|
|||
drawVectorSegments(ctx, coastSegments, "rgba(248, 250, 242, 0.68)", 1.1, false, { iterations: 2, tolerance: 0.06, offsetX: -0.5, offsetY: -0.5 });
|
||||
|
||||
// 2. Rivers
|
||||
const waterBlue = "rgba(160, 205, 240, 1)";
|
||||
const mediumBlue = "rgba(160, 205, 240, 0.88)";
|
||||
const waterBlue = "rgba(116, 165, 202, 0.92)";
|
||||
const mediumBlue = "rgba(132, 184, 220, 0.78)";
|
||||
const riverStrengthForPath = (path) => {
|
||||
if (!path || path.length === 0) return 0;
|
||||
let peak = 0;
|
||||
|
|
@ -813,7 +852,7 @@ export function drawMap(canvas, map, options) {
|
|||
for (const path of map.smallStreams || []) {
|
||||
const strength = riverStrengthForPath(path);
|
||||
if ((path?.length || 0) < 5 || strength < 0.045) continue;
|
||||
drawRiverPath(ctx, map, path, "rgba(150, 198, 235, 1)", (s) => s > 0.45 ? 0.58 : s > 0.22 ? 0.48 : 0.36, 0.34);
|
||||
drawRiverPath(ctx, map, path, "rgba(140, 190, 224, 0.82)", (s) => s > 0.45 ? 0.52 : s > 0.22 ? 0.42 : 0.32, 0.28);
|
||||
}
|
||||
for (const path of map.tributaryRivers || []) {
|
||||
const strength = riverStrengthForPath(path);
|
||||
|
|
@ -837,26 +876,28 @@ export function drawMap(canvas, map, options) {
|
|||
}
|
||||
|
||||
const showHistory = ["history", "all", "terrain"].includes(mode);
|
||||
const showModern = ["modern", "all", "development", "landuse", "admin-debug", "borders-debug"].includes(mode);
|
||||
const showRoads = ["all", "development"].includes(mode);
|
||||
const showMinorRoads = ["all", "modern", "development"].includes(mode);
|
||||
const showAdmin = ["admin", "all", "admin-debug", "borders-debug"].includes(mode);
|
||||
const showTransportDebug = mode === "transport-debug";
|
||||
const showModern = ["modern", "all", "development", "landuse", "borders-debug", "transport-debug"].includes(mode);
|
||||
const showRoads = ["all", "development", "transport-debug"].includes(mode);
|
||||
const showMinorRoads = ["all", "modern", "development", "transport-debug"].includes(mode);
|
||||
const showAdmin = ["admin", "all", "borders-debug"].includes(mode);
|
||||
|
||||
// 3. Borders
|
||||
const showPrefectureRegions = ["all", "admin", "admin-debug", "borders-debug"].includes(mode);
|
||||
const showPrefectureRegions = ["all", "admin", "borders-debug"].includes(mode);
|
||||
if (showPrefectureRegions) drawPrefectureRegionFill(ctx, map, mode);
|
||||
|
||||
if (showAdmin && map.adminBorders) {
|
||||
drawVectorSegments(ctx, map.adminBorders, "rgba(255, 255, 255, 0.8)", 2.8, false, { iterations: 1, tolerance: 0.05, offsetX: -0.5, offsetY: -0.5 });
|
||||
drawVectorSegments(ctx, map.adminBorders, "rgba(150, 140, 150, 0.9)", 1.2, true, { iterations: 1, tolerance: 0.05, offsetX: -0.5, offsetY: -0.5 });
|
||||
}
|
||||
if (mode === "admin-debug" || mode === "borders-debug") {
|
||||
if (mode === "borders-debug") {
|
||||
// Keep the natural barrier heatmap subtle. A dense cell fill can look like
|
||||
// artificial horizontal hatching, so only strong terrain dividers are shown.
|
||||
drawDebugCells(ctx, map, map.naturalBarrierScore, (v) => v < 0.42 ? "rgba(0,0,0,0)" : `rgba(255, 120, 40, ${0.025 + v * 0.075})`);
|
||||
if (map.adminDebug?.compartmentBorders) drawSegments(ctx, map.adminDebug.compartmentBorders, "rgba(45, 95, 160, 0.72)", 1.0, true);
|
||||
for (const p of map.adminDebug?.lowlandAdminSeeds || []) dot(ctx, p, 3.2, "rgba(255,255,255,0.9)", "rgba(40,150,95,0.95)");
|
||||
}
|
||||
if (showTransportDebug) drawTransportDebug(ctx, map);
|
||||
|
||||
if (showPrefectureRegions && map.regionalPrefectureBorders) {
|
||||
drawVectorSegments(ctx, map.regionalPrefectureBorders, "rgba(255, 255, 255, 0.90)", 4.2, false, { iterations: 2, tolerance: 0.06, offsetX: -0.5, offsetY: -0.5 });
|
||||
|
|
@ -916,7 +957,7 @@ export function drawMap(canvas, map, options) {
|
|||
}
|
||||
|
||||
// 6. Icons & Labels
|
||||
if (["admin", "admin-debug", "borders-debug"].includes(mode)) {
|
||||
if (["admin", "borders-debug"].includes(mode)) {
|
||||
for (const p of map.adminCenters || []) dot(ctx, p, 2.8, "rgba(255,255,255,0.96)", "rgba(75,60,90,0.95)");
|
||||
}
|
||||
|
||||
|
|
@ -937,20 +978,25 @@ export function drawMap(canvas, map, options) {
|
|||
}
|
||||
for (const p of map.interchanges || []) dot(ctx, p, 2.7, "rgba(250, 250, 245, 0.95)", "rgba(95, 125, 95, 0.95)");
|
||||
for (const p of map.logisticsParks || []) dot(ctx, p, 2.4, "rgba(235, 238, 230, 0.95)", "rgba(105, 125, 105, 0.88)");
|
||||
if (mode === "admin-debug" || mode === "borders-debug") {
|
||||
if (mode === "borders-debug") {
|
||||
for (const p of map.externalGateways || []) dot(ctx, p, 5.5, "rgba(255,255,255,0.95)", "rgba(40,80,170,0.95)");
|
||||
for (const p of map.transportDebug?.missingRequiredNodes || []) dot(ctx, p, 6.5, "rgba(255,80,80,0.95)", "rgba(80,20,20,0.95)");
|
||||
}
|
||||
if (showTransportDebug) {
|
||||
for (const p of map.transportDebug?.layers?.unservedSettlements || []) {
|
||||
dot(ctx, p, p.repaired ? 3.4 : 4.8, p.repaired ? "rgba(255,255,255,0.92)" : "rgba(255,80,140,0.95)", "rgba(125,35,105,0.95)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showLabels) {
|
||||
const prefectureLabels = showPrefectureRegions ? (map.prefectureRegions || []).map((p) => ({ ...p, labelPriorityBase: p.labelPriorityBase || 900 })) : [];
|
||||
const prefectureLabels = (map.prefectureRegions || []).map((p) => ({ ...p, isPrefectureLabel: true, labelPriorityBase: p.labelPriorityBase || 1700 }));
|
||||
if (mode === "admin") {
|
||||
drawLabels(ctx, [...prefectureLabels, ...(map.adminCenters || [])], Infinity);
|
||||
drawScaleBar(ctx);
|
||||
return;
|
||||
}
|
||||
if (mode === "admin-debug" || mode === "borders-debug") {
|
||||
if (mode === "borders-debug") {
|
||||
drawLabels(ctx, [...prefectureLabels, ...(map.adminCenters || [])], Infinity);
|
||||
drawScaleBar(ctx);
|
||||
return;
|
||||
|
|
@ -970,7 +1016,7 @@ export function drawMap(canvas, map, options) {
|
|||
...(map.logisticsParks || []).map((p) => ({ ...p, labelPriorityBase: 60 })),
|
||||
...(map.satelliteCities || []),
|
||||
...allLayerTowns,
|
||||
].filter((p) => p.forceAllLayerTownLabel || p.insidePrefecture || p.isRegionalCapital || p.kind === "External Gateway" || (p.population || 0) >= 5000);
|
||||
].filter((p) => p.isPrefectureLabel || p.forceAllLayerTownLabel || p.insidePrefecture || p.isRegionalCapital || p.kind === "External Gateway" || (p.population || 0) >= 5000);
|
||||
drawLabels(ctx, important, mode === "all" ? 85 : 60);
|
||||
}
|
||||
drawScaleBar(ctx);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue