Skip to content

Instantly share code, notes, and snippets.

@svidgen
Last active April 20, 2025 20:51
Show Gist options
  • Save svidgen/d8136d4ec152677367582638a0ca6a92 to your computer and use it in GitHub Desktop.
Save svidgen/d8136d4ec152677367582638a0ca6a92 to your computer and use it in GitHub Desktop.
A Simple TypeScript Grid

A simple TypeScript Grid. Written by Jon Wire for articles on jonwire.com.

This may evolve over time for use in a variety of articles and play. If you'd like this in library form as an NPM package, let me know in the comments.

function isInitializer<T>(
init: T | ((row: number, col: number) => T)
): init is (row: number, col: number) => T {
return typeof init === 'function';
}
export class Grid<T> {
data: T[][];
constructor(
public rows: number,
public cols: number,
init?: T | ((row: number, col: number) => T)
) {
this.data = new Array(rows);
for (let r = 0; r < rows; r++) {
this.data[r] = new Array(cols);
for (let c = 0; c < cols; c++) {
if (init) {
this.data[r][c] =
isInitializer(init) ? init(r, c) : init;
}
}
}
}
get(row: number, col: number): T | undefined {
return this.data[row]?.[col];
}
set(row: number, col: number, value: T): void {
if (!this.data[row]) {
this.data[row] = [];
}
this.data[row][col] = value;
}
copy(): Grid<T> {
return this.map((value) => value);
}
map<NT>(
fn: (value: T, row: number, col: number, self: Grid<T>) => NT
): Grid<NT> {
const newGrid = new Grid<NT>(this.rows, this.cols);
for (let r = 0; r < this.rows; r++) {
for (let c = 0; c < this.cols; c++) {
newGrid.set(r, c, fn(this.get(r, c)!, r, c, this));
}
}
return newGrid;
}
/**
* A list of the 8 neighbor values of a cell in a grid.
*
* In order from top left (NW) to bottom right (SE):
*
* ```
* [
* NW, N, NE,
* W, E,
* SW, S, SE,
* ]
* ```
*
* @param row
* @param col
* @returns
*/
getNeighbors(
row: number,
col: number,
): T[] {
return [
this.get(row - 1, col - 1),
this.get(row - 1, col),
this.get(row - 1, col + 1),
this.get(row, col - 1),
this.get(row, col + 1),
this.get(row + 1, col - 1),
this.get(row + 1, col),
this.get(row + 1, col + 1)
].filter(v => v !== undefined) as T[];
}
toImageData<U>(
mapFn: (value: T, row: number, col: number, self: Grid<T>) => U
): ImageData {
const imageData = new ImageData(this.cols, this.rows);
const data = imageData.data;
for (let r = 0; r < this.rows; r++) {
for (let c = 0; c < this.cols; c++) {
const value = mapFn(this.get(r, c)!, r, c, this);
const index = (r * this.cols + c) * 4;
if (Array.isArray(value) && value.length === 4) {
// Assume value is [R, G, B, A]
data[index] = value[0]; // R
data[index + 1] = value[1]; // G
data[index + 2] = value[2]; // B
data[index + 3] = value[3]; // A
} else {
throw new Error(
'mapFn must return an array of [R, G, B, A] values'
);
}
}
}
return imageData;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment