This commit is contained in:
33333-33333 2026-05-29 18:50:54 +09:00
commit 4a8aff90b2
7 changed files with 3806 additions and 83 deletions

View file

@ -1,4 +1,4 @@
import { CELL_SIZE, MAP_H, MAP_W, clamp } from "./mapUtils.js";
import { CELL_SIZE, MAP_H, MAP_W, clamp, valueNoise } from "./mapUtils.js";
const segmentVectorCache = new WeakMap();
@ -296,6 +296,38 @@ function isWaterSample(map, fx, fy) {
return seaCoverageSample(map, fx, fy) >= 0.50;
}
function terrainWorldPoint(map, fx, fy) {
return {
x: fx + (map?.worldCamera?.x || 0),
y: fy + (map?.worldCamera?.y || 0),
};
}
function waterVisualTone(map, fx, fy) {
const p = terrainWorldPoint(map, fx, fy);
const seed = (map?.seed || 0) >>> 0;
const broad = valueNoise(p.x, p.y, seed ^ 0x5d2a9b31, 74);
const mid = valueNoise(p.x, p.y, seed ^ 0x8f4c2d19, 29);
return clamp(broad * 0.72 + mid * 0.28);
}
function waterVisualColor(map, fx, fy, waterCoverage = null) {
const coverage = waterCoverage ?? seaCoverageSample(map, fx, fy);
const tone = waterVisualTone(map, fx, fy) - 0.5;
const offshore = clamp((coverage - 0.42) / 0.58);
const depth = clamp(0.48 + offshore * 0.22 + tone * 0.10);
return [
Math.round(174 - depth * 6),
Math.round(203 + depth * 2),
Math.round(224 + depth * 4),
];
}
function waterVisualShade(map, fx, fy) {
const tone = waterVisualTone(map, fx * 0.85 + 11.7, fy * 0.85 - 3.1) - 0.5;
return clamp(0.992 + tone * 0.018, 0.976, 1.012);
}
function mixRgb(a, b, t) {
return [
Math.round(a[0] + (b[0] - a[0]) * t),
@ -363,8 +395,10 @@ function terrainColorContinuous(map, fx, fy, mode) {
let color;
const waterCoverage = seaCoverageSample(map, fx, fy);
const depth = clamp(((map.seaLevel ?? 0.285) + 0.065 - fieldSample(map, map.elevation, fx, fy)) * 2.4);
const waterColor = [Math.round(174 - depth * 7), Math.round(203 + depth * 2), Math.round(224 + depth * 5)];
// Water must not inherit terrain DEM hill bands. Use a stable world-coordinate
// visual water tone instead of elevation-derived depth; otherwise patched sea
// areas expose rectangular/horizontal generation artefacts.
const waterColor = waterVisualColor(map, fx, fy, waterCoverage);
let landColor;
if (mode === "development") {
@ -405,6 +439,9 @@ function terrainColorContinuous(map, fx, fy, mode) {
}
function terrainShadeContinuous(map, fx, fy) {
const waterCoverage = seaCoverageSample(map, fx, fy);
if (waterCoverage >= 0.50) return waterVisualShade(map, fx, fy);
const step = 0.50;
const eC = fieldSample(map, map.elevation, fx, fy);
const eL = fieldSample(map, map.elevation, fx - step, fy);
@ -589,6 +626,14 @@ function getRasterBorderSegments(map, fieldName) {
return segments;
}
function getDisplayBorderSegments(map, fieldName, precomputedSegments, mode) {
// Raw raster borders expose every one-cell ID fragment and also reveal patch
// ownership seams as long rectangular prefecture/municipality lines. Keep
// that extractor debug-only; normal modes must use prepared vector layers.
if (mode === "borders-debug") return getRasterBorderSegments(map, fieldName);
return Array.isArray(precomputedSegments) ? precomputedSegments : [];
}
function drawRiverPath(ctx, map, path, color, widthFn, alpha = 1) {
if (!path || path.length < 2) return;
ctx.save();
@ -1067,8 +1112,8 @@ export function drawMap(canvas, map, options) {
const showPrefectureRegions = ["all", "admin", "borders-debug"].includes(mode);
if (showPrefectureRegions) drawPrefectureRegionFill(ctx, map, mode);
const adminBorderSegments = map.adminId ? getRasterBorderSegments(map, "adminId") : map.adminBorders;
const prefectureBorderSegments = map.prefectureRegionId ? getRasterBorderSegments(map, "prefectureRegionId") : map.regionalPrefectureBorders;
const adminBorderSegments = getDisplayBorderSegments(map, "adminId", map.adminBorders, mode);
const prefectureBorderSegments = getDisplayBorderSegments(map, "prefectureRegionId", map.regionalPrefectureBorders, mode);
if (showAdmin && adminBorderSegments?.length) {
drawVectorSegments(ctx, adminBorderSegments, "rgba(255, 255, 255, 0.8)", 2.8, false, { iterations: 1, tolerance: 0.05, offsetX: -0.5, offsetY: -0.5 });
drawVectorSegments(ctx, adminBorderSegments, "rgba(150, 140, 150, 0.9)", 1.2, true, { iterations: 1, tolerance: 0.05, offsetX: -0.5, offsetY: -0.5 });