This commit is contained in:
33333-33333 2026-05-28 15:48:42 +09:00
commit 860471f805
13 changed files with 1643 additions and 356 deletions

View file

@ -31,6 +31,61 @@ export function distanceToNearest(points, x, y, fallback = 999) {
return best;
}
export function createPointSpatialIndex(points, cellSize = 12) {
const buckets = new Map();
const normalized = (points || [])
.filter((p) => p && Number.isFinite(p.x) && Number.isFinite(p.y))
.map((p) => ({ ...p, x: Math.round(p.x), y: Math.round(p.y) }));
const keyOf = (cx, cy) => `${cx},${cy}`;
for (const p of normalized) {
const cx = Math.floor(p.x / cellSize);
const cy = Math.floor(p.y / cellSize);
const key = keyOf(cx, cy);
let bucket = buckets.get(key);
if (!bucket) {
bucket = [];
buckets.set(key, bucket);
}
bucket.push(p);
}
function nearestDistanceSq(x, y, maxDistance = Math.max(MAP_W, MAP_H)) {
if (!normalized.length) return maxDistance * maxDistance;
const cx = Math.floor(x / cellSize);
const cy = Math.floor(y / cellSize);
const maxRing = Number.isFinite(maxDistance) ? Math.ceil(maxDistance / cellSize) : Math.ceil(Math.max(MAP_W, MAP_H) / cellSize);
let best = maxDistance * maxDistance;
for (let ring = 0; ring <= maxRing; ring++) {
for (let by = cy - ring; by <= cy + ring; by++) {
for (let bx = cx - ring; bx <= cx + ring; bx++) {
if (ring > 0 && bx > cx - ring && bx < cx + ring && by > cy - ring && by < cy + ring) continue;
const bucket = buckets.get(keyOf(bx, by));
if (!bucket) continue;
for (const p of bucket) {
const dx = p.x - x;
const dy = p.y - y;
const d2 = dx * dx + dy * dy;
if (d2 < best) best = d2;
}
}
}
}
return best;
}
return {
points: normalized,
hasWithin(x, y, radius) {
return nearestDistanceSq(x, y, radius) < radius * radius;
},
distance(x, y, fallback = 999) {
const d2 = nearestDistanceSq(x, y, fallback);
return d2 < fallback * fallback ? Math.sqrt(d2) : fallback;
},
nearestDistanceSq,
};
}
export function aStar(start, goal, costAt) {
const startIndex = indexOf(start.x, start.y);
const goalIndex = indexOf(goal.x, goal.y);
@ -83,15 +138,18 @@ export function aStar(start, goal, costAt) {
export function influenceFromPaths(paths, radius) {
const grid = new Float32Array(SIZE);
const r = Math.ceil(radius);
const r2 = radius * radius;
for (const path of paths) {
for (const [x, y] of path) {
for (let dy = -radius; dy <= radius; dy++) {
for (let dx = -radius; dx <= radius; dx++) {
for (let dy = -r; dy <= r; dy++) {
for (let dx = -r; dx <= r; dx++) {
const nx = x + dx;
const ny = y + dy;
if (!inside(nx, ny)) continue;
const d = Math.hypot(dx, dy);
if (d > radius) continue;
const d2 = dx * dx + dy * dy;
if (d2 > r2) continue;
const d = Math.sqrt(d2);
const i = indexOf(nx, ny);
grid[i] = Math.max(grid[i], 1 / (1 + d));
}
@ -239,15 +297,18 @@ export function averagePathField(path, field) {
export function influenceFromPoints(points, radius, weightFn = () => 1) {
const grid = new Float32Array(SIZE);
const r = Math.ceil(radius);
const r2 = radius * radius;
for (const p of points) {
const weight = weightFn(p);
for (let dy = -radius; dy <= radius; dy++) {
for (let dx = -radius; dx <= radius; dx++) {
for (let dy = -r; dy <= r; dy++) {
for (let dx = -r; dx <= r; dx++) {
const nx = p.x + dx;
const ny = p.y + dy;
if (!inside(nx, ny)) continue;
const d = Math.hypot(dx, dy);
if (d > radius) continue;
const d2 = dx * dx + dy * dy;
if (d2 > r2) continue;
const d = Math.sqrt(d2);
const i = indexOf(nx, ny);
grid[i] = Math.max(grid[i], weight / (1 + d));
}