import { useMemo } from 'react';
import { Grid, Environment, Text } from '@react-three/drei';
import { ViewerOrbitControls as OrbitControls } from './ViewerOrbitControls';
import * as THREE from 'three';
import SceneCanvas from '../SceneCanvas';
import FallbackDiagram from '../FallbackDiagram';
import { useCanvasTheme } from '@/lib/theme';
export interface Dome3DInput {
diameter: number;
rise: number;
wallHeight: number;
cpi: number;
cpeBarlavento: number;
cpeTopo: number;
cpeLateral: number;
}
function domeColor(cpe: number, cpi: number, isDark: boolean): THREE.Color {
const p = cpe - cpi;
const intensity = Math.min(1, Math.abs(p) / 1.5);
const l = isDark ? (65 - intensity * 15) : (60 - intensity * 25);
if (p > 0) {
return new THREE.Color(`hsl(${215 - intensity * 10}, ${70 + intensity * 25}%, ${Math.max(35, l)}%)`);
}
return new THREE.Color(`hsl(0, ${70 + intensity * 25}%, ${Math.max(40, l + 5)}%)`);
}
function DomeModel({ diameter, rise, wallHeight, cpi, cpeBarlavento, cpeTopo, cpeLateral }: Dome3DInput) {
const theme = useCanvasTheme();
const isDark = theme === 'dark';
const radius = diameter / 2;
const segments = 64;
// Cúpula (casca esférica) — gerada por segmentos de 0° a 360° para fechar o domo
const domeGeoms = useMemo(() => {
const arr: { startTheta: number; endTheta: number; color: THREE.Color }[] = [];
// Divide a circunferência completa (360°) em 6 zonas (simétricas)
const zones = [
{ fromDeg: 0, toDeg: 60, cpe: cpeBarlavento },
{ fromDeg: 60, toDeg: 120, cpe: cpeTopo },
{ fromDeg: 120, toDeg: 180, cpe: cpeLateral },
{ fromDeg: 180, toDeg: 240, cpe: cpeLateral },
{ fromDeg: 240, toDeg: 300, cpe: cpeTopo },
{ fromDeg: 300, toDeg: 360, cpe: cpeBarlavento },
];
for (const z of zones) {
arr.push({
startTheta: (z.fromDeg * Math.PI) / 180,
endTheta: (z.toDeg * Math.PI) / 180,
color: domeColor(z.cpe, cpi, isDark),
});
}
return arr;
}, [cpeBarlavento, cpeTopo, cpeLateral, cpi, isDark]);
// Raio da esfera da calota esférica baseada na flecha (rise) e raio da base (radius)
const rSphere = useMemo(() => {
return (radius * radius + rise * rise) / (2 * rise);
}, [radius, rise]);
return (
{/* Parede cilíndrica inferior */}
{/* Detalhes de anéis metálicos nas bordas */}
{/* Cúpula de cobertura (Spherical Cap) segmentada */}
{domeGeoms.map((zone, idx) => {
const phiSteps = 16;
const segments2 = 16;
const vertices: number[] = [];
const normals: number[] = [];
const indices: number[] = [];
const phiStart = zone.startTheta;
const phiRange = zone.endTheta - zone.startTheta;
for (let i = 0; i <= phiSteps; i++) {
const phi = phiStart + (i / phiSteps) * phiRange;
for (let j = 0; j <= segments2; j++) {
const t = j / segments2;
const y = t * rise;
// Equação da esfera da calota
const yLocal = (rSphere - rise) + y;
const r = Math.sqrt(Math.max(0, rSphere * rSphere - yLocal * yLocal));
const x = r * Math.cos(phi);
const z = r * Math.sin(phi);
vertices.push(x, wallHeight + y, z);
// Normal analítica perfeita da esfera
normals.push(x / rSphere, yLocal / rSphere, z / rSphere);
}
}
for (let i = 0; i < phiSteps; i++) {
for (let j = 0; j < segments2; j++) {
const a = i * (segments2 + 1) + j;
const b = (i + 1) * (segments2 + 1) + j;
const c = (i + 1) * (segments2 + 1) + (j + 1);
const d = i * (segments2 + 1) + (j + 1);
indices.push(a, b, c, a, c, d);
}
}
return (
);
})}
{/* Seta indicativa de direção do vento */}
Vento
{/* Texto informativo */}
Diâm = {diameter}m | Flecha = {rise}m
);
}
export default function Dome3DViewer(props: Dome3DInput) {
const theme = useCanvasTheme();
const isDark = theme === 'dark';
const fallback = (
);
const maxDim = Math.max(props.diameter, props.wallHeight + props.rise);
return (
);
}