From f1bd573ea13306a0a6834c94f5a61d11ac8ffe4c Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Sat, 2 May 2026 04:09:55 +0200 Subject: [PATCH] Add utilities for managing and sampling terrain foliage masks --- src/document/terrains.ts | 66 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/document/terrains.ts b/src/document/terrains.ts index 0ae716e9..46866570 100644 --- a/src/document/terrains.ts +++ b/src/document/terrains.ts @@ -856,6 +856,72 @@ function getStoredTerrainPaintWeightAtSample( return terrain.paintWeights[offset + layerOffset] ?? 0; } +export function getTerrainFoliageMaskSampleIndex( + mask: Pick, + sampleX: number, + sampleZ: number +): number { + if ( + !Number.isInteger(sampleX) || + sampleX < 0 || + sampleX >= mask.resolutionX + ) { + throw new Error(`Terrain foliage mask sampleX ${sampleX} is out of range.`); + } + + if ( + !Number.isInteger(sampleZ) || + sampleZ < 0 || + sampleZ >= mask.resolutionZ + ) { + throw new Error(`Terrain foliage mask sampleZ ${sampleZ} is out of range.`); + } + + return sampleZ * mask.resolutionX + sampleX; +} + +export function getTerrainFoliageMaskValueAtSample( + mask: TerrainFoliageMask, + sampleX: number, + sampleZ: number +): number { + return ( + mask.values[getTerrainFoliageMaskSampleIndex(mask, sampleX, sampleZ)] ?? 0 + ); +} + +export function getTerrainFoliageMask( + terrain: Pick, + layerId: string +): TerrainFoliageMask | null { + return terrain.foliageMasks[layerId] ?? null; +} + +export function getOrCreateTerrainFoliageMask( + terrain: Terrain, + layerId: string +): TerrainFoliageMask { + const normalizedLayerId = normalizeTerrainFoliageLayerId( + layerId, + "Terrain foliage mask layerId" + ); + const currentMask = terrain.foliageMasks[normalizedLayerId]; + + if (currentMask !== undefined) { + return currentMask; + } + + const nextMask = createEmptyTerrainFoliageMask(terrain, normalizedLayerId); + terrain.foliageMasks[normalizedLayerId] = nextMask; + return nextMask; +} + +export function isTerrainFoliageMaskEmpty( + mask: TerrainFoliageMask +): boolean { + return mask.values.every((value) => value === 0); +} + function sampleTerrainPaintWeightAtGridCoordinate( terrain: Terrain, sampleX: number,