tweak
This commit is contained in:
parent
47af930e18
commit
e1bb10ff8a
12 changed files with 1336 additions and 526 deletions
94
renderer.js
94
renderer.js
|
|
@ -1,8 +1,5 @@
|
|||
import { MAP_W, MAP_H, CELL_SIZE, indexOf } from "./mapGenerator.js";
|
||||
|
||||
function clamp(v, a = 0, b = 1) {
|
||||
return Math.max(a, Math.min(b, v));
|
||||
}
|
||||
import { clamp } from "./grid.js";
|
||||
|
||||
function distToNearest(points, x, y, fallback = 999) {
|
||||
let best = fallback;
|
||||
|
|
@ -197,12 +194,73 @@ function drawPath(ctx, path, color, width, dashed = false) {
|
|||
ctx.restore();
|
||||
}
|
||||
|
||||
function drawSegments(ctx, segments, color, width, dashed = false) {
|
||||
function segmentPointKey(p) {
|
||||
return `${p[0]},${p[1]}`;
|
||||
}
|
||||
|
||||
function chainSegments(segments) {
|
||||
const unused = segments.map((seg) => [seg[0], seg[1]]);
|
||||
const chains = [];
|
||||
while (unused.length) {
|
||||
const chain = unused.pop();
|
||||
let grew = true;
|
||||
while (grew) {
|
||||
grew = false;
|
||||
const head = segmentPointKey(chain[0]);
|
||||
const tail = segmentPointKey(chain[chain.length - 1]);
|
||||
for (let i = unused.length - 1; i >= 0; i--) {
|
||||
const [a, b] = unused[i];
|
||||
const ak = segmentPointKey(a);
|
||||
const bk = segmentPointKey(b);
|
||||
if (ak === tail) { chain.push(b); unused.splice(i, 1); grew = true; break; }
|
||||
if (bk === tail) { chain.push(a); unused.splice(i, 1); grew = true; break; }
|
||||
if (bk === head) { chain.unshift(a); unused.splice(i, 1); grew = true; break; }
|
||||
if (ak === head) { chain.unshift(b); unused.splice(i, 1); grew = true; break; }
|
||||
}
|
||||
}
|
||||
chains.push(chain);
|
||||
}
|
||||
return chains;
|
||||
}
|
||||
|
||||
function chaikin(points, passes = 1) {
|
||||
let out = points;
|
||||
for (let pass = 0; pass < passes; pass++) {
|
||||
if (out.length < 3) return out;
|
||||
const next = [out[0]];
|
||||
for (let i = 0; i < out.length - 1; i++) {
|
||||
const a = out[i];
|
||||
const b = out[i + 1];
|
||||
next.push([a[0] * 0.75 + b[0] * 0.25, a[1] * 0.75 + b[1] * 0.25]);
|
||||
next.push([a[0] * 0.25 + b[0] * 0.75, a[1] * 0.25 + b[1] * 0.75]);
|
||||
}
|
||||
next.push(out[out.length - 1]);
|
||||
out = next;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function drawSegments(ctx, segments, color, width, dashed = false, smooth = false) {
|
||||
ctx.save();
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = width;
|
||||
ctx.lineCap = "round";
|
||||
ctx.lineJoin = "round";
|
||||
if (dashed) ctx.setLineDash([4, 4]);
|
||||
|
||||
if (smooth) {
|
||||
for (const chain of chainSegments(segments)) {
|
||||
const points = chaikin(chain, 1);
|
||||
if (points.length < 2) continue;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(points[0][0] * CELL_SIZE, points[0][1] * CELL_SIZE);
|
||||
for (let i = 1; i < points.length; i++) ctx.lineTo(points[i][0] * CELL_SIZE, points[i][1] * CELL_SIZE);
|
||||
ctx.stroke();
|
||||
}
|
||||
ctx.restore();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const seg of segments) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(seg[0][0] * CELL_SIZE, seg[0][1] * CELL_SIZE);
|
||||
|
|
@ -375,13 +433,13 @@ function labelWithCollision(ctx, p, occupied) {
|
|||
return false;
|
||||
}
|
||||
|
||||
function drawLabels(ctx, points) {
|
||||
function drawLabels(ctx, points, limit = Infinity) {
|
||||
const occupied = [];
|
||||
const prioritized = points
|
||||
.filter((p) => p?.name)
|
||||
.map((p) => ({ ...p, labelPriority: (p.isPrefecturalCapital ? 1000 : 0) + (p.population || 0) / 1000 + (p.kind === "Major Port" ? 160 : 0) + (p.kind === "Market Town" ? 55 : 0) + (p.kind?.includes("Castle") ? 45 : 0) }))
|
||||
.map((p) => ({ ...p, labelPriority: (p.isPrefecturalCapital ? 1000 : 0) + (p.population || 0) / 900 + (p.portClass === "major" ? 170 : p.portClass === "regional" ? 95 : 0) + (p.kind === "Market Town" ? 48 : 0) + (p.kind?.includes("Castle") ? 70 : 0) + (p.kind === "External Gateway" ? 60 : 0) }))
|
||||
.sort((a, b) => b.labelPriority - a.labelPriority);
|
||||
for (const p of prioritized) labelWithCollision(ctx, p, occupied);
|
||||
for (const p of prioritized.slice(0, limit)) labelWithCollision(ctx, p, occupied);
|
||||
}
|
||||
|
||||
export function drawMap(canvas, map, options) {
|
||||
|
|
@ -408,9 +466,9 @@ export function drawMap(canvas, map, options) {
|
|||
for (const path of map.mainRivers) drawPath(ctx, path, waterBlue, 3.2);
|
||||
drawHarborWorks(ctx, map);
|
||||
|
||||
if (map.regionalPrefectureBorders) drawSegments(ctx, map.regionalPrefectureBorders, "rgba(95,95,95,0.30)", 1.0);
|
||||
drawSegments(ctx, map.prefectureBorder, "rgba(30,30,30,0.82)", 2.4);
|
||||
drawSegments(ctx, map.prefectureBorder, "rgba(255,255,255,0.74)", 1.05);
|
||||
if (map.regionalPrefectureBorders) drawSegments(ctx, map.regionalPrefectureBorders, mode === "all" ? "rgba(95,95,95,0.18)" : "rgba(95,95,95,0.30)", 1.0, false, mode === "all");
|
||||
drawSegments(ctx, map.prefectureBorder, "rgba(30,30,30,0.82)", 2.4, false, true);
|
||||
drawSegments(ctx, map.prefectureBorder, "rgba(255,255,255,0.74)", 1.05, false, true);
|
||||
|
||||
if (!showFeatures) return;
|
||||
|
||||
|
|
@ -419,11 +477,11 @@ export function drawMap(canvas, map, options) {
|
|||
const showRoads = ["roads", "all", "development", "landuse"].includes(mode);
|
||||
const showAdmin = ["admin", "all"].includes(mode);
|
||||
|
||||
if (showAdmin) drawSegments(ctx, map.adminBorders, "rgba(120,120,120,0.65)", 1.3);
|
||||
if (showAdmin) drawSegments(ctx, map.adminBorders, mode === "all" ? "rgba(120,120,120,0.28)" : "rgba(120,120,120,0.65)", mode === "all" ? 0.9 : 1.3);
|
||||
|
||||
if (showHistory) {
|
||||
for (const path of map.premodernRoads) drawPath(ctx, path, "rgba(150, 120, 90, 0.55)", 1.45, true);
|
||||
for (const path of map.minorRoads) drawPath(ctx, path, "rgba(180, 150, 120, 0.62)", 1.05);
|
||||
for (const path of map.premodernRoads) drawPath(ctx, path, mode === "all" ? "rgba(150, 120, 90, 0.34)" : "rgba(150, 120, 90, 0.55)", mode === "all" ? 1.15 : 1.45, true);
|
||||
for (const path of map.minorRoads) drawPath(ctx, path, mode === "all" ? "rgba(180, 150, 120, 0.28)" : "rgba(180, 150, 120, 0.62)", mode === "all" ? 0.8 : 1.05);
|
||||
}
|
||||
|
||||
if (showModern) {
|
||||
|
|
@ -445,7 +503,7 @@ export function drawMap(canvas, map, options) {
|
|||
}
|
||||
|
||||
if (showHistory) {
|
||||
for (const p of map.villages) dot(ctx, p, 1.9, "rgba(120, 100, 80, 0.72)");
|
||||
for (const p of map.villages) dot(ctx, p, mode === "all" ? 1.35 : 1.9, mode === "all" ? "rgba(120, 100, 80, 0.42)" : "rgba(120, 100, 80, 0.72)");
|
||||
for (const p of map.markets) dot(ctx, p, 4.8, "rgba(200, 130, 80, 0.95)");
|
||||
for (const p of map.ports) {
|
||||
const color = p.portClass === "major" ? "rgba(40, 105, 190, 0.98)" : p.portClass === "regional" ? "rgba(70, 130, 200, 0.95)" : p.portClass === "lake" ? "rgba(80, 155, 180, 0.92)" : "rgba(95, 150, 195, 0.82)";
|
||||
|
|
@ -480,13 +538,13 @@ export function drawMap(canvas, map, options) {
|
|||
const important = [
|
||||
...map.modernCities,
|
||||
...map.ports,
|
||||
...map.markets.slice(0, 10),
|
||||
...map.castles.slice(0, 8),
|
||||
...map.markets.slice(0, mode === "all" ? 6 : 10),
|
||||
...map.castles.slice(0, mode === "all" ? 5 : 8),
|
||||
...(map.satelliteCities || []),
|
||||
...map.newTowns,
|
||||
...map.externalGateways,
|
||||
].filter((p) => p.insidePrefecture || p.kind === "External Gateway");
|
||||
|
||||
drawLabels(ctx, important);
|
||||
drawLabels(ctx, important, mode === "all" ? 28 : Infinity);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue