Files
flying-jib/lib/border.js
T
2026-07-30 23:43:12 -04:00

126 lines
3.2 KiB
JavaScript

'use strict'
/**
* Border / neighbor helpers for mesh regions (ADR-0008).
* Pure functions — no I/O. Used by CLI and future Squid plugin.
*/
/**
* Distance to nearest edge of bounds (positive inside, negative outside).
* @param {{ minX:number, maxX:number, minZ:number, maxZ:number }} bounds
* @param {number} x
* @param {number} z
*/
function distanceToEdge(bounds, x, z) {
const dx = Math.min(x - bounds.minX, bounds.maxX - x)
const dz = Math.min(z - bounds.minZ, bounds.maxZ - z)
return Math.min(dx, dz)
}
/**
* @param {object} bounds
* @param {number} x
* @param {number} z
* @param {number} [margin=16]
*/
function nearBorder(bounds, x, z, margin = 16) {
if (!bounds) return false
if (x < bounds.minX || x > bounds.maxX || z < bounds.minZ || z > bounds.maxZ) {
return true
}
return distanceToEdge(bounds, x, z) <= margin
}
/**
* Pick a neighboring region for a position leaving `current`.
* Prefers region containing (x,z); else nearest by center distance.
* @param {object[]} regions
* @param {object} current current region record
* @param {number} x
* @param {number} z
*/
function findNeighbor(regions, current, x, z) {
const others = regions.filter((r) => r.regionId !== current.regionId)
const containing = others.find(
(r) =>
r.bounds &&
x >= r.bounds.minX &&
x <= r.bounds.maxX &&
z >= r.bounds.minZ &&
z <= r.bounds.maxZ
)
if (containing) return containing
// Nearest center among regions
let best = null
let bestD = Infinity
for (const r of others) {
if (!r.bounds) continue
const cx = (r.bounds.minX + r.bounds.maxX) / 2
const cz = (r.bounds.minZ + r.bounds.maxZ) / 2
const d = (cx - x) * (cx - x) + (cz - z) * (cz - z)
if (d < bestD) {
bestD = d
best = r
}
}
return best
}
/**
* Map position into neighbor local space using offsets.
* @param {object} fromRegion
* @param {object} toRegion
* @param {number} x
* @param {number} y
* @param {number} z
*/
function mapPosition(fromRegion, toRegion, x, y, z) {
const fo = fromRegion.offset || { x: 0, z: 0 }
const to = toRegion.offset || { x: 0, z: 0 }
// Global ≈ local + offset; remap: localTo = global - to.offset
const gx = x + fo.x
const gz = z + fo.z
return {
x: gx - to.x,
y,
z: gz - to.z
}
}
/**
* Suggest portal policy action.
* @returns {{ action: 'none'|'warn'|'migrate', neighbor: object|null, margin: number }}
*/
function borderAction(regions, current, x, z, margin = 16) {
if (!current || !current.bounds) {
return { action: 'none', neighbor: null, margin }
}
if (!nearBorder(current.bounds, x, z, margin)) {
return { action: 'none', neighbor: null, margin }
}
const outside =
x < current.bounds.minX ||
x > current.bounds.maxX ||
z < current.bounds.minZ ||
z > current.bounds.maxZ
const neighbor = findNeighbor(regions, current, x, z)
if (!neighbor) {
return { action: 'warn', neighbor: null, margin }
}
return {
action: outside ? 'migrate' : 'warn',
neighbor,
margin,
mapped: outside ? mapPosition(current, neighbor, x, 64, z) : null
}
}
module.exports = {
distanceToEdge,
nearBorder,
findNeighbor,
mapPosition,
borderAction
}