// iso: o motor 3D do kit. Projeção de câmera real (elevação de 32°, giro livre em torno do eixo vertical), cubóides com 3 faces
// sombreadas, sombra no chão, ordenação por profundidade. Unidades do mundo: 1 = um bloco. `S` = pixels por unidade.
import React from 'react';
export const ELEV = (32 * Math.PI) / 180;
export type Cubo = {x: number; y: number; z?: number; w: number; d: number; h: number; cor: string; /** quadro em que começa a cair */ cai?: number};
export const proj = (x: number, y: number, z: number, ang: number, S: number) => {
const c = Math.cos(ang), s = Math.sin(ang), rx = x * c - y * s, ry = x * s + y * c;
return {x: rx * S, y: (ry * Math.sin(ELEV) - z * Math.cos(ELEV)) * S, d: ry};
};
const hex = (h: string) => {const n = parseInt(h.slice(1), 16); return [(n >> 16) & 255, (n >> 8) & 255, n & 255];};
/** multiplica o brilho de uma cor hex */
export const tom = (h: string, f: number) => `rgb(${hex(h).map((v) => Math.max(0, Math.min(255, Math.round(v * f)))).join(',')})`;
/** as faces visíveis de um cubóide, já projetadas e sombreadas (luz vindo de cima à esquerda) */
export const faces = (b: Cubo, ang: number, S: number, dz = 0) => {
const {x, y, w, d, h, cor} = b, z = (b.z ?? 0) + dz, hw = w / 2, hd = d / 2;
const P = (px: number, py: number, pz: number) => proj(px, py, pz, ang, S);
const lista: {pts: {x: number; y: number}[]; n: [number, number]; topo?: boolean}[] = [
{topo: true, n: [0, 0], pts: [P(x - hw, y - hd, z + h), P(x + hw, y - hd, z + h), P(x + hw, y + hd, z + h), P(x - hw, y + hd, z + h)]},
{n: [1, 0], pts: [P(x + hw, y - hd, z), P(x + hw, y + hd, z), P(x + hw, y + hd, z + h), P(x + hw, y - hd, z + h)]},
{n: [-1, 0], pts: [P(x - hw, y + hd, z), P(x - hw, y - hd, z), P(x - hw, y - hd, z + h), P(x - hw, y + hd, z + h)]},
{n: [0, 1], pts: [P(x + hw, y + hd, z), P(x - hw, y + hd, z), P(x - hw, y + hd, z + h), P(x + hw, y + hd, z + h)]},
{n: [0, -1], pts: [P(x - hw, y - hd, z), P(x + hw, y - hd, z), P(x + hw, y - hd, z + h), P(x - hw, y - hd, z + h)]},
];
const c = Math.cos(ang), s = Math.sin(ang);
return lista.map((f) => {
const nx = f.n[0] * c - f.n[1] * s, ny = f.n[0] * s + f.n[1] * c;
if (!f.topo && ny <= 0.02) return null;
const luz = f.topo ? 1.12 : 0.78 + (nx * -0.6 + ny * 0.3) * 0.38;
return {d: f.pts.map((p) => `${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(' '), cor: tom(cor, luz)};
}).filter(Boolean) as {d: string; cor: string}[];
};
/** um cubóide como grupo SVG */
export const CuboSVG: React.FC<{b: Cubo; ang: number; S: number; dz?: number; opacidade?: number}> = ({b, ang, S, dz = 0, opacidade = 1}) =>
{faces(b, ang, S, dz).map((f, i) => )};
/** sombra achatada no chão (um pouco deslocada pela luz) */
export const SombraSVG: React.FC<{b: Cubo; ang: number; S: number; escala?: number}> = ({b, ang, S, escala = 1}) => {
const hw = (b.w / 2) * escala, hd = (b.d / 2) * escala, o = b.h * 0.35 + (b.z ?? 0) * 0.25;
const pts = [[-hw, -hd], [hw, -hd], [hw, hd], [-hw, hd]].map(([px, py]) => proj(b.x + px + o, b.y + py + o * 0.6, 0, ang, S));
return `${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(' ')} fill="rgba(27,43,75,0.16)" />;
};
/** profundidade do centro de um cubóide (menor = mais longe) */
export const profundidade = (b: Cubo, ang: number) => proj(b.x, b.y, 0, ang, 1).d + (b.z ?? 0) * 0.01;