grid-neighbors-1d
Version:
Get the 8 closest neighbors of a cell in a 2d grid when flattened to a 1d array
150 lines • 5.47 kB
JavaScript
/**
* grid-neighbors-1d
* v2.1.0
*
* Get the 8 closest neighbors of a cell in a 2d grid when flattened to a 1d array
*
* Help me make this better:
* https://github.com/phugh/grid-neighbors-1d
*
* @module grid-neighbors-1d
* @copyright 2019-23 P. Hughes. All rights reserved.
* @license MIT
*/
export const Direction = {
NORTH: 0,
NORTH_EAST: 1,
EAST: 2,
SOUTH_EAST: 3,
SOUTH: 4,
SOUTH_WEST: 5,
WEST: 6,
NORTH_WEST: 7,
};
/**
* @function getNeighbors
* @description Get the 8 closest neighbors of a cell in a 2d grid when flattened to a 1d array
* @param cell the cell to get neighbors for
* @param width grid width
* @param height grid height
* @return an array of cell references [north, north-east, east, south-east, south, south-west, west, north-west]
* @throws {SyntaxError} if function arguments are undefined or not numbers
* @throws {RangeError} if total grid size is less than 9 cells or cell reference is out of bounds
* @example
* import { getNeighbors } from 'grid-neighbors-1d';
* const neighbors = getNeighbors(12,5,6); // i.e. get the neighbors of cell 12 in a grid 5 high by 6 wide
* console.log(neighbors); // [7, 8, 13, 18, 17, 16, 11, 6] - clockwise from north
* // i.e. [north, north-east, east, south-east, south, south-west, west, north-west]
*/
export const getNeighbors = (cell, width, height) => {
// Validate input
if (cell == null || isNaN(cell) || cell < 0) {
throw new SyntaxError("Expected cell to be a non-negative number.");
}
if (width == null || isNaN(width) || width < 0) {
throw new SyntaxError("Expected width to be a non-negative number.");
}
if (height == null || isNaN(height) || height < 0) {
throw new SyntaxError("Expected height to be a non-negative number.");
}
const CELL = Math.floor(cell);
const WIDTH = Math.floor(width);
const HEIGHT = Math.floor(height);
const SIZE = WIDTH * HEIGHT;
if (SIZE < 9) {
throw new RangeError(`Minimum grid size is 9 cells. Provided grid (${WIDTH} X ${HEIGHT}) is ${SIZE} cells.`);
}
else if (CELL >= SIZE) {
throw new RangeError(`Cell reference "${CELL}" out of bounds. Maximum is ${SIZE - 1}.`);
}
const ROW = Math.floor(CELL / WIDTH);
const COLUMN = CELL % WIDTH;
const LEFT_MOST_CELL = ROW * WIDTH;
const RIGHT_MOST_CELL = LEFT_MOST_CELL + WIDTH - 1;
const SIZE_MINUS_WIDTH = SIZE - WIDTH;
const CELL_MINUS_WIDTH = CELL - WIDTH;
const CELL_PLUS_WIDTH = CELL + WIDTH;
const TOP_RIGHT_CELL_IDX = WIDTH - 1;
const BOTTOM_RIGHT_CELL_IDX = SIZE - 1;
const north = CELL_MINUS_WIDTH < 0 ? SIZE_MINUS_WIDTH + CELL : CELL_MINUS_WIDTH;
const south = CELL_PLUS_WIDTH >= SIZE ? CELL - LEFT_MOST_CELL : CELL_PLUS_WIDTH;
let northEast;
let east;
let southEast;
let southWest;
let west;
let northWest;
// East, North-East, South-East
if (COLUMN === TOP_RIGHT_CELL_IDX) {
// RIGHT EDGE
east = LEFT_MOST_CELL;
if (CELL === TOP_RIGHT_CELL_IDX) {
// top right corner
northEast = SIZE_MINUS_WIDTH;
southEast = LEFT_MOST_CELL + WIDTH;
}
else if (CELL === BOTTOM_RIGHT_CELL_IDX) {
// bottom right corner
northEast = LEFT_MOST_CELL - WIDTH;
southEast = 0;
}
else {
northEast = LEFT_MOST_CELL - WIDTH;
southEast = LEFT_MOST_CELL + WIDTH;
}
}
else {
east = CELL + 1;
northEast = north + 1;
southEast = south + 1;
}
// West, North-West, South-West
if (COLUMN === 0) {
// LEFT EDGE
west = RIGHT_MOST_CELL;
if (CELL === SIZE_MINUS_WIDTH) {
// bottom left corner
northWest = CELL - 1;
southWest = TOP_RIGHT_CELL_IDX;
}
else if (CELL === 0) {
// top left corner
northWest = BOTTOM_RIGHT_CELL_IDX;
southWest = RIGHT_MOST_CELL + WIDTH;
}
else {
northWest = CELL - 1;
southWest = RIGHT_MOST_CELL + WIDTH;
}
}
else {
west = CELL - 1;
northWest = north - 1;
southWest = south - 1;
}
return [north, northEast, east, southEast, south, southWest, west, northWest];
};
export const getNeighbours = getNeighbors;
/**
* Pre-generate a lookup table for all cells in a grid
* @param width grid width
* @param height grid height
* @returns a function that takes a cell reference and returns an array of cell references [north, north-east,....]
* @throws {SyntaxError} if function arguments are undefined or not numbers
* @throws {RangeError} if total grid size is less than 9 cells
* @example
* import { generateNeighborLookup } from 'grid-neighbors-1d';
* const getNeighbors = generateNeighborLookup(5,6);
* const neighbors = getNeighbors(12); // i.e. get the neighbors of cell 12 in a grid 5 high by 6 wide
* console.log(neighbors); // [7, 8, 13, 18, 17, 16, 11, 6] - clockwise from north
*/
export const generateNeighborLookup = (width, height) => {
const neighborLookup = {};
const size = width * height;
for (let cell = 0; cell < size; cell++) {
neighborLookup[cell] = getNeighbors(cell, width, height);
}
return (cell) => neighborLookup[cell];
};
export const generateNeighbourLookup = generateNeighborLookup;
//# sourceMappingURL=index.js.map