/**
* Изометрия 2:1 (классический «ромб»).
*
* Сетка — целочисленные координаты тайлов (tx, ty).
* Экран — виртуальные пиксели. Тайл рисуется ромбом шириной tileW и высотой tileH (2:1).
*
* Формулы:
* screenX = (tx - ty) * (tileW / 2)
* screenY = (tx + ty) * (tileH / 2)
* Обратная:
* tx = (screenX / (tileW/2) + screenY / (tileH/2)) / 2
* ty = (screenY / (tileH/2) - screenX / (tileW/2)) / 2
*/
export interface IsoLayout {
/** Ширина ромба тайла (пиксели, чётное). */
tileW: number;
/** Высота ромба тайла (пиксели, tileW / 2). */
tileH: number;
/** Экранные координаты верхнего левого угла нулевого тайла. */
originX: number;
originY: number;
}
/** Стандартный тайл 32x16. */
export const DEFAULT_ISO: IsoLayout = { tileW: 32, tileH: 16, originX: 0, originY: 0 };
export function isoToScreen(tx: number, ty: number, iso: IsoLayout = DEFAULT_ISO): { x: number; y: number } {
return {
x: Math.round((tx - ty) * (iso.tileW / 2) + iso.originX),
y: Math.round((tx + ty) * (iso.tileH / 2) + iso.originY)
};
}
export function screenToIso(sx: number, sy: number, iso: IsoLayout = DEFAULT_ISO): { x: number; y: number } {
const halfW = iso.tileW / 2;
const halfH = iso.tileH / 2;
const dx = (sx - iso.originX) / halfW;
const dy = (sy - iso.originY) / halfH;
return { x: Math.floor((dx + dy) / 2), y: Math.floor((dy - dx) / 2) };
}
/**
* Точное попадание внутрь ромба тайла (floor может дать соседний тайл на границе).
* Возвращает координаты тайла, ромб которого содержит точку, или null.
*/
export function screenToIsoExact(
sx: number,
sy: number,
mapW: number,
mapH: number,
iso: IsoLayout = DEFAULT_ISO
): { x: number; y: number } | null {
const cand = screenToIso(sx, sy, iso);
for (let dy = 0; dy <= 1; dy++) {
for (let dx = 0; dx <= 1; dx++) {
const tx = cand.x + dx;
const ty = cand.y + dy;
const c = isoToScreen(tx, ty, iso);
const rx = sx - c.x;
const ry = sy - c.y;
// Ромб: |x/halfW| + |y/halfH| <= 1
if (Math.abs(rx) / (iso.tileW / 2) + Math.abs(ry) / (iso.tileH / 2) <= 1) {
if (tx >= 0 && ty >= 0 && tx < mapW && ty < mapH) {
return { x: tx, y: ty };
}
return null;
}
}
}
return null;
}