Cell filters & automata
The CellFilter algebra — composable per-cell scalar functions — plus cellular automata rules and distance fields over grid layers.
A CellFilter is a function (grid, x, y, rotation) → number. Every filter in the library extends this base class, has a static .from(...) factory, and must be initialize(grid, seed) before use. Filters compose — the output of one becomes the input of the next — so complex terrain masks, density fields, and probability functions are all built from the same small set of primitives.
Source: @woosh/meep-engine/src/generation/filtering/
Filter lifecycle
import { CellFilter } from "@woosh/meep-engine/src/generation/filtering/CellFilter.js";
// All filters share this interface:
filter.initialize(grid, seed); // called once; sets __initialized = true
const v = filter.execute(grid, x, y, rotation); // called per cell
filter.finalize(); // resets __initialized for re-use
ensureInitialized(grid, seed) is a safe guard that calls initialize only if not yet initialized; composable filters call this on their children before executing.
Complete filter catalog
The library groups into seven families. The map below is a quick index; the tables that follow give every class and its import path.
Constants and layer reads
| Class | Import path (under filtering/) | Description |
|---|---|---|
CellFilterLiteralFloat | numeric/CellFilterLiteralFloat.js | Returns a constant. CellFilterLiteralFloat.ONE and .ZERO are pre-built singletons. |
CellFilterSampleLayerLinear | numeric/sampling/CellFilterSampleLayerLinear.js | Bilinear sample of a named GridDataLayer. |
CellFilterSampleLayerCubic | numeric/sampling/CellFilterSampleLayerCubic.js | Bicubic sample of a named GridDataLayer. |
CellFilterLiteralBoolean | boolean/CellFilterLiteralBoolean.js | Returns a constant boolean (as 0/1). |
Arithmetic
| Class | Import path | Operation |
|---|---|---|
CellFilterAdd | numeric/math/algebra/CellFilterAdd.js | left + right |
CellFilterSubtract | numeric/math/algebra/CellFilterSubtract.js | left - right |
CellFilterMultiply | numeric/math/algebra/CellFilterMultiply.js | left * right |
CellFilterDivide | numeric/math/algebra/CellFilterDivide.js | left / right |
CellFilterNegate | numeric/math/algebra/CellFilterNegate.js | -value |
CellFilterAbsolute | numeric/math/CellFilterAbsolute.js | Math.abs(v) |
CellFilterPower | numeric/math/CellFilterPower.js | Math.pow(value, power) |
CellFilterMax2 | numeric/math/CellFilterMax2.js | max(a, b) |
CellFilterMin2 | numeric/math/CellFilterMin2.js | min(a, b) |
CellFilterOneMinus | numeric/math/CellFilterOneMinus.js | 1 - v |
Range and interpolation
| Class | Import path | Description |
|---|---|---|
CellFilterClamp | numeric/math/CellFilterClamp.js | clamp(value, min, max) |
CellFilterSaturate | numeric/math/CellFilterSaturate.js | clamp(v, 0, 1) |
CellFilterStep | numeric/math/CellFilterStep.js | Returns 0 if x < edge, else 1 |
CellFilterLerp | numeric/math/CellFilterLerp.js | lerp(a, b, f) — linear interpolation |
CellFilterInverseLerp | numeric/math/CellFilterInverseLerp.js | Inverse of lerp: given a, b, value, returns f |
CellFilterSmoothStep | numeric/math/CellFilterSmoothStep.js | GLSL smoothstep(edge0, edge1, x) — smooth Hermite interpolation |
CellFilterMembershipGeneralizedBell | numeric/math/CellFilterMembershipGeneralizedBell.js | Generalized bell membership function `1 / (1 + |
CellFilterCubicFunction | numeric/math/poly/CellFilterCubicFunction.js | Evaluates a cubic polynomial |
Spatial / convolution
| Class | Import path | Description |
|---|---|---|
CellFilterDilate | numeric/complex/CellFilterDilate.js | 3×3 morphological dilation — returns max of the 3×3 neighborhood |
CellFilterSobel | numeric/complex/CellFilterSobel.js | Sobel edge-detection: magnitude of the gradient vector from the 3×3 Sobel kernel |
CellFilterGaussianBlur | numeric/complex/CellFilterGaussianBlur.js | Gaussian blur with configurable sigma and sample count |
CellFilterFXAA | numeric/complex/CellFilterFXAA.js | FXAA anti-aliasing filter adapted from the NVIDIA algorithm |
CellFilterCurvature | numeric/complex/CellFilterCurvature.js | Approximate surface curvature at a cell, based on cross-products of neighbouring normals |
CellFilterAngleToNormal | numeric/complex/CellFilterAngleToNormal.js | Builds the surface normal of another filter and returns the angle to a fixed 3D reference vector |
CellFilterDisplaced | numeric/util/CellFilterDisplaced.js | Samples another filter at a position offset by two displacement filters |
Noise
| Class | Import path | Description |
|---|---|---|
CellFilterSimplexNoise | numeric/complex/CellFilterSimplexNoise.js | 2D simplex noise in [0, 1], seeded. .fractal(octaves, scale, seed, persistence, lacunarity) builds an fBm chain automatically. |
Lookup and gradient
| Class | Import path | Description |
|---|---|---|
CellFilterLookupTable | numeric/complex/CellFilterLookupTable.js | Remaps the source filter through a ParameterLookupTable (1-channel LUT) |
computeCellFilterGradient | numeric/process/computeCellFilterGradient.js | Utility function: computes the normalized gradient vector [dx, dy] at (x, y) using the Sobel kernel; returns false when magnitude is zero |
Boolean / logic filters
| Class | Import path | Description |
|---|---|---|
CellFilterAnd | boolean/logic/CellFilterAnd.js | left && right (Boolean data type) |
Composing filters
All binary filters have left / right children; ternary filters have a, b, c; unary filters have source. Use the static factories — they assert isCellFilter on every argument:
import { CellFilterSimplexNoise } from "@woosh/meep-engine/src/generation/filtering/numeric/complex/CellFilterSimplexNoise.js";
import { CellFilterSaturate } from "@woosh/meep-engine/src/generation/filtering/numeric/math/CellFilterSaturate.js";
import { CellFilterLerp } from "@woosh/meep-engine/src/generation/filtering/numeric/math/CellFilterLerp.js";
import { CellFilterLiteralFloat } from "@woosh/meep-engine/src/generation/filtering/numeric/CellFilterLiteralFloat.js";
import { CellFilterGaussianBlur } from "@woosh/meep-engine/src/generation/filtering/numeric/complex/CellFilterGaussianBlur.js";
// Fractal noise, blurred, saturated — a smooth terrain mask
const noise = CellFilterSimplexNoise.fractal(4, 32);
const blurred = CellFilterGaussianBlur.from(noise, 3, 3);
const mask = CellFilterSaturate.from(blurred);
// Blend two noise fields
const noiseLow = CellFilterSimplexNoise.from(64, 64);
const noiseHigh = CellFilterSimplexNoise.from(8, 8);
const blend = CellFilterLerp.from(noiseLow, noiseHigh, mask);
Cellular automata
CellularAutomata is the base class; subclasses override step(data, width, height) where data is a Uint8Array of the grid cells. The supplied concrete implementation is CaveGeneratorCellularAutomata:
import { CaveGeneratorCellularAutomata } from "@woosh/meep-engine/src/generation/automata/CaveGeneratorCellularAutomata.js";
const ca = new CaveGeneratorCellularAutomata();
ca.random = seededRandom(42);
// data is a Uint8Array of width*height cells, pre-filled with 0/1
for (let i = 0; i < 5; i++) {
ca.step(data, width, height);
}
CaveGeneratorCellularAutomata applies a survival/birth rule: a live cell dies if it has fewer than 4 live neighbours (8-connected); a dead cell is born if it has more than 5 live neighbours. Running 4–6 iterations on randomly seeded data produces cave-like open regions. The GridTaskCellularAutomata generator wraps this as a task stage.
Distance fields
AreaMask computes a per-pixel unsigned distance field over a binary Sampler2D:
import { AreaMask } from "@woosh/meep-engine/src/generation/theme/AreaMask.js";
const mask = new AreaMask();
mask.resize(128, 128);
// fill mask.mask.data with 0/1 painted regions …
mask.updateDistanceField(); // fills mask.distanceField via computeUnsignedDistanceField
mask.updateBounds(); // computes the AABB2 of all set pixels
ThemeEngine uses the distance fields on AreaTheme masks to blend terrain weights at biome boundaries: when multiple themes overlap a cell, each theme’s influence is proportional to its distance-field value divided by the overlap count. See Themes, matchers & rules.
Caching
CellFilterCache wraps any filter and memoizes its output at every (x, y) for the lifetime of one initialization. Use it around expensive multi-sample filters (Gaussian blur, simplex noise chains) that are evaluated many times per cell from multiple downstream consumers.