// Пиксель-арт утилиты: матрицы (массивы строк, символ → цвет палитры;
// символ без цвета в палитре — прозрачный пиксель) → canvas/dataURL.
// Файл НЕ импортирует pixi — используется и в DOM (чипы инвентаря),
// и в рендерере (textures.ts), чтобы pixi оставался в async-чанке.
export type Palette = Record<string, string>
export type Matrix = string[]
export function drawMatrix(rows: Matrix, palette: Palette, px: number): HTMLCanvasElement {
const h = rows.length
const w = rows[0]?.length ?? 0
const cv = document.createElement('canvas')
cv.width = w * px
cv.height = h * px
const ctx = cv.getContext('2d')!
ctx.imageSmoothingEnabled = false
for (let y = 0; y < h; y++) {
const row = rows[y]!
for (let x = 0; x < w; x++) {
const color = palette[row[x]!]
if (!color) continue
ctx.fillStyle = color
ctx.fillRect(x * px, y * px, px, px)
}
}
return cv
}
/** Data URL пиксель-превью (чипы инвентаря, drag-ghost). px — размер «пикселя». */
export function dataUrlFor(rows: Matrix, palette: Palette, px = 4): string {
return drawMatrix(rows, palette, px).toDataURL()
}
/** Впечатать patch в base по координатам (сборка больших спрайтов из частей). */
export function stamp(base: Matrix, patch: Matrix, x: number, y: number): Matrix {
const out = base.map((row) => row.split(''))
patch.forEach((pr, py) => {
const target = out[y + py]
if (!target) return
pr.split('').forEach((ch, px2) => {
if (px2 + x < target.length && ch !== ' ' && ch !== '.') target[px2 + x] = ch
})
})
return out.map((row) => row.join(''))
}
/** Пустая матрица w×h (все пиксели прозрачны). */
export function blank(w: number, h: number): Matrix {
return Array.from({ length: h }, () => ' '.repeat(w))
}
/** Зеркалирование по горизонтали. */
export function flipX(rows: Matrix): Matrix {
return rows.map((row) => row.split('').reverse().join(''))
}
/** Строка из повторов символа (удобно для стен/полос). */
export function line(ch: string, n: number): string {
return ch.repeat(n)
}