This commit is contained in:
33333-33333 2026-05-29 14:31:42 +09:00
commit f2d0306d96
13 changed files with 1434 additions and 516 deletions

View file

@ -68,8 +68,8 @@ function copyViewportField(name, source, world, camera, viewWidth, viewHeight) {
return out;
}
function inViewportPoint(p, margin = 0) {
return p && p.x >= -margin && p.y >= -margin && p.x < MAP_W + margin && p.y < MAP_H + margin;
function inViewportPoint(p, margin = 0, viewWidth = MAP_W, viewHeight = MAP_H) {
return p && p.x >= -margin && p.y >= -margin && p.x < viewWidth + margin && p.y < viewHeight + margin;
}
function transformPointObject(point, camera, originX, originY) {
@ -83,9 +83,9 @@ function transformPointObject(point, camera, originX, originY) {
};
}
function transformPointArray(items, camera, originX, originY, margin = 36, preserveIndexes = false) {
function transformPointArray(items, camera, originX, originY, margin = 36, preserveIndexes = false, viewWidth = MAP_W, viewHeight = MAP_H) {
const mapped = (items || []).map((item) => transformPointObject(item, camera, originX, originY));
return preserveIndexes ? mapped : mapped.filter((item) => inViewportPoint(item, margin));
return preserveIndexes ? mapped : mapped.filter((item) => inViewportPoint(item, margin, viewWidth, viewHeight));
}
function transformTuple(tuple, camera, originX, originY) {
@ -93,16 +93,16 @@ function transformTuple(tuple, camera, originX, originY) {
return [tuple[0] + originX - camera.x, tuple[1] + originY - camera.y];
}
function tupleInside(tuple, margin = 0) {
return tuple && tuple[0] >= -margin && tuple[1] >= -margin && tuple[0] < MAP_W + margin && tuple[1] < MAP_H + margin;
function tupleInside(tuple, margin = 0, viewWidth = MAP_W, viewHeight = MAP_H) {
return tuple && tuple[0] >= -margin && tuple[1] >= -margin && tuple[0] < viewWidth + margin && tuple[1] < viewHeight + margin;
}
function splitTransformedPath(path, camera, originX, originY, margin = 0) {
function splitTransformedPath(path, camera, originX, originY, margin = 0, viewWidth = MAP_W, viewHeight = MAP_H) {
const chunks = [];
let current = [];
for (const tuple of path || []) {
const p = transformTuple(tuple, camera, originX, originY);
const inside = tupleInside(p, margin);
const inside = tupleInside(p, margin, viewWidth, viewHeight);
if (inside) {
current.push([Math.round(p[0]), Math.round(p[1])]);
} else if (current.length >= 2) {
@ -116,23 +116,23 @@ function splitTransformedPath(path, camera, originX, originY, margin = 0) {
return chunks;
}
function transformPaths(paths, camera, originX, originY) {
function transformPaths(paths, camera, originX, originY, viewWidth = MAP_W, viewHeight = MAP_H) {
const out = [];
for (const path of paths || []) out.push(...splitTransformedPath(path, camera, originX, originY));
for (const path of paths || []) out.push(...splitTransformedPath(path, camera, originX, originY, 0, viewWidth, viewHeight));
return out;
}
function segmentIntersectsViewport(seg, margin = 4) {
function segmentIntersectsViewport(seg, margin = 4, viewWidth = MAP_W, viewHeight = MAP_H) {
if (!seg || seg.length < 2) return false;
const xs = [seg[0][0], seg[1][0]];
const ys = [seg[0][1], seg[1][1]];
return Math.max(...xs) >= -margin && Math.min(...xs) <= MAP_W + margin && Math.max(...ys) >= -margin && Math.min(...ys) <= MAP_H + margin;
return Math.max(...xs) >= -margin && Math.min(...xs) <= viewWidth + margin && Math.max(...ys) >= -margin && Math.min(...ys) <= viewHeight + margin;
}
function transformSegments(segments, camera, originX, originY) {
function transformSegments(segments, camera, originX, originY, viewWidth = MAP_W, viewHeight = MAP_H) {
return (segments || [])
.map((seg) => [transformTuple(seg?.[0], camera, originX, originY), transformTuple(seg?.[1], camera, originX, originY)])
.filter(segmentIntersectsViewport);
.filter((seg) => segmentIntersectsViewport(seg, 4, viewWidth, viewHeight));
}
@ -151,18 +151,18 @@ function copySourceMapViewportField(source, camera, originX, originY, viewWidth,
return out;
}
function transformTransportDebug(debug, camera, originX, originY, viewport = null) {
function transformTransportDebug(debug, camera, originX, originY, viewport = null, viewWidth = MAP_W, viewHeight = MAP_H) {
if (!debug?.layers) return debug;
const layers = { ...debug.layers };
for (const [key, value] of Object.entries(layers)) {
if (ArrayBuffer.isView(value) && value.length === SIZE) layers[key] = copySourceMapViewportField(value, camera, originX, originY, MAP_W, MAP_H);
if (ArrayBuffer.isView(value) && value.length === SIZE) layers[key] = copySourceMapViewportField(value, camera, originX, originY, viewWidth, viewHeight);
}
// The original transport-debug potential layers are fixed-map arrays. Once the
// viewport pans into patched world cells, synthesize equivalent viewport-sized
// debug fields from the current world-backed fields so the color overlay moves
// with the terrain instead of staying tied to the initial source map.
if (viewport) {
const n = MAP_W * MAP_H;
const n = viewWidth * viewHeight;
const make = (fn) => {
const out = new Float32Array(n);
for (let i = 0; i < n; i++) out[i] = fn(i);
@ -184,14 +184,14 @@ function transformTransportDebug(debug, camera, originX, originY, viewport = nul
...component,
cells: (component.cells || [])
.map((cell) => transformTuple(cell, camera, originX, originY))
.filter((cell) => tupleInside(cell, 0))
.filter((cell) => tupleInside(cell, 0, viewWidth, viewHeight))
.map(([x, y]) => [Math.round(x), Math.round(y)]),
})).filter((component) => component.cells.length);
}
if (Array.isArray(layers.repairedSegments)) {
layers.repairedSegments = layers.repairedSegments.flatMap((repair) => transformPaths([repair.path || []], camera, originX, originY).map((path) => ({ ...repair, path })));
layers.repairedSegments = layers.repairedSegments.flatMap((repair) => transformPaths([repair.path || []], camera, originX, originY, viewWidth, viewHeight).map((path) => ({ ...repair, path })));
}
if (Array.isArray(layers.unservedSettlements)) layers.unservedSettlements = transformPointArray(layers.unservedSettlements, camera, originX, originY);
if (Array.isArray(layers.unservedSettlements)) layers.unservedSettlements = transformPointArray(layers.unservedSettlements, camera, originX, originY, 36, false, viewWidth, viewHeight);
return { ...debug, layers };
}
@ -206,7 +206,7 @@ function buildEmptyViewportFromSource(sourceMap, world, camera, viewWidth, viewH
return viewport;
}
export function getViewportMap(world, camera, viewWidth = MAP_W, viewHeight = MAP_H) {
export function getViewportMap(world, camera, viewWidth = MAP_W, viewHeight = MAP_H, options = {}) {
const sourceMap = world?.sourceMap || {};
const normalizedCamera = {
x: Math.round(camera?.x || 0),
@ -218,29 +218,31 @@ export function getViewportMap(world, camera, viewWidth = MAP_W, viewHeight = MA
viewport[name] = copyViewportField(name, value, world, normalizedCamera, viewWidth, viewHeight);
}
if (options.light) return viewport;
const originX = world?.originX || 0;
const originY = world?.originY || 0;
for (const key of POINT_ARRAY_KEYS) {
if (!Array.isArray(sourceMap[key])) continue;
viewport[key] = transformPointArray(sourceMap[key], normalizedCamera, originX, originY, 36, key === "adminCenters");
viewport[key] = transformPointArray(sourceMap[key], normalizedCamera, originX, originY, 36, key === "adminCenters", viewWidth, viewHeight);
}
for (const key of PATH_ARRAY_KEYS) if (Array.isArray(sourceMap[key])) viewport[key] = transformPaths(sourceMap[key], normalizedCamera, originX, originY);
for (const key of SEGMENT_ARRAY_KEYS) if (Array.isArray(sourceMap[key])) viewport[key] = transformSegments(sourceMap[key], normalizedCamera, originX, originY);
for (const key of PATH_ARRAY_KEYS) if (Array.isArray(sourceMap[key])) viewport[key] = transformPaths(sourceMap[key], normalizedCamera, originX, originY, viewWidth, viewHeight);
for (const key of SEGMENT_ARRAY_KEYS) if (Array.isArray(sourceMap[key])) viewport[key] = transformSegments(sourceMap[key], normalizedCamera, originX, originY, viewWidth, viewHeight);
if (sourceMap.adminDebug) {
viewport.adminDebug = {
...sourceMap.adminDebug,
compartmentBorders: transformSegments(sourceMap.adminDebug.compartmentBorders || [], normalizedCamera, originX, originY),
lowlandAdminSeeds: transformPointArray(sourceMap.adminDebug.lowlandAdminSeeds || [], normalizedCamera, originX, originY),
compartmentBorders: transformSegments(sourceMap.adminDebug.compartmentBorders || [], normalizedCamera, originX, originY, viewWidth, viewHeight),
lowlandAdminSeeds: transformPointArray(sourceMap.adminDebug.lowlandAdminSeeds || [], normalizedCamera, originX, originY, 36, false, viewWidth, viewHeight),
};
}
if (sourceMap.transportDebug) viewport.transportDebug = transformTransportDebug(sourceMap.transportDebug, normalizedCamera, originX, originY, viewport);
if (sourceMap.transportDebug) viewport.transportDebug = transformTransportDebug(sourceMap.transportDebug, normalizedCamera, originX, originY, viewport, viewWidth, viewHeight);
if (sourceMap.neighborPrefectureDetails) {
viewport.neighborPrefectureDetails = {
...sourceMap.neighborPrefectureDetails,
cities: transformPointArray(sourceMap.neighborPrefectureDetails.cities || [], normalizedCamera, originX, originY),
adminCenters: transformPointArray(sourceMap.neighborPrefectureDetails.adminCenters || [], normalizedCamera, originX, originY),
roads: transformPaths(sourceMap.neighborPrefectureDetails.roads || [], normalizedCamera, originX, originY),
cities: transformPointArray(sourceMap.neighborPrefectureDetails.cities || [], normalizedCamera, originX, originY, 36, false, viewWidth, viewHeight),
adminCenters: transformPointArray(sourceMap.neighborPrefectureDetails.adminCenters || [], normalizedCamera, originX, originY, 36, false, viewWidth, viewHeight),
roads: transformPaths(sourceMap.neighborPrefectureDetails.roads || [], normalizedCamera, originX, originY, viewWidth, viewHeight),
};
}