/**
* Штамповка модели в мир: (0,0,0) модели → заданная мировая точка.
* Клиппинг: воксели вне вертикали мира и вне опционального прямоугольника
* (half-open [min, max)) пропускаются. Возвращает число поставленных
* вокселей; dirty-трекинг мира обычный (remeshDirty демки подхватит).
*/
import { VoxelWorld } from '../world/world';
import { decodeModel } from './format';
import type { VoxelModel } from './format';
export interface StampClip {
/** half-open границы клиппинга в мировых координатах. */
minX?: number; minY?: number; minZ?: number;
maxX?: number; maxY?: number; maxZ?: number;
}
export function stampModel(
world: VoxelWorld, model: VoxelModel, x: number, y: number, z: number,
clip: StampClip = {},
): number {
const grid = decodeModel(model);
const inClip = (wx: number, wy: number, wz: number): boolean =>
(clip.minX === undefined || wx >= clip.minX) && (clip.maxX === undefined || wx < clip.maxX) &&
(clip.minY === undefined || wy >= clip.minY) && (clip.maxY === undefined || wy < clip.maxY) &&
(clip.minZ === undefined || wz >= clip.minZ) && (clip.maxZ === undefined || wz < clip.maxZ);
let placed = 0;
for (let ly = 0; ly < grid.sy; ly++)
for (let lz = 0; lz < grid.sz; lz++)
for (let lx = 0; lx < grid.sx; lx++) {
const slot = grid.get(lx, ly, lz);
if (slot === 0) continue;
const wx = x + lx, wy = y + ly, wz = z + lz;
if (!inClip(wx, wy, wz)) continue;
if (world.set(wx, wy, wz, slot)) placed++;
}
return placed;
}