import { Eye, Grid3X3, Ruler, Trash2, Camera, LayoutGrid, Palette, Box, Move3D, Undo2, MousePointerClick, EyeOff, Focus, Download } from 'lucide-react'; import { Slider } from '@/components/ui/slider'; import { Button } from '@/components/ui/button'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { useModelStore } from '@/stores/useModelStore'; import { ScaleSelector } from '@/components/ScaleSelector'; import { useCallback } from 'react'; import { toast } from 'sonner'; import { exportObjectsAsGLB } from '@/lib/exportSelectionGLB'; import * as THREE from 'three'; import { elementKey, sceneRef } from '@/components/three/ModelViewer'; const WIRE_COLORS = [ { label: 'Ciano (radiante)', value: '#00f3ff' }, { label: 'Vermelho', value: '#ef4444' }, { label: 'Verde', value: '#22c55e' }, { label: 'Azul', value: '#3b82f6' }, { label: 'Amarelo', value: '#eab308' }, { label: 'Laranja', value: '#f97316' }, { label: 'Branco', value: '#e2e8f0' }, { label: 'Cinza', value: '#8899aa' }, ]; export function ViewerControls() { const { opacity, setOpacity, renderMode, setRenderMode, measureMode, setMeasureMode, measurements, clearMeasurements, undoLastMeasurement, measurePoints, addScreenshot, showGrid, setShowGrid, wireframeColor, setWireframeColor, wireframeThickness, setWireframeThickness, edgeThresholdAngle, setEdgeThresholdAngle, positionMode, setPositionMode, activeModelId, models, selectionMode, setSelectionMode, selectedElementKeys, hiddenElementKeys, isolatedElementKeys, hideSelectedElements, isolateSelectedElements, showAllElements, clearElementSelection, } = useModelStore(); const activeModel = models.find(m => m.id === activeModelId); const hasSelection = selectedElementKeys.size > 0; const hasHiddenOrIsolated = hiddenElementKeys.size > 0 || isolatedElementKeys !== null; const handleExportSelection = useCallback(async () => { if (!activeModel || selectedElementKeys.size === 0) return; const scene = sceneRef.current; if (!scene) { toast.error('Cena 3D não disponível'); return; } const objects: THREE.Object3D[] = []; const collect = (root: THREE.Object3D) => { const modelId = root.userData?.modelId as string | undefined; if (!modelId) { root.children.forEach(collect); return; } root.traverse((el) => { if (!el.userData?.ifcElement) return; if (selectedElementKeys.has(elementKey(modelId, el))) objects.push(el); }); }; collect(scene); if (objects.length === 0) { toast.error('Não foi possível localizar elementos selecionados. Use IFC para seleção persistente.'); return; } const base = activeModel.fileName.replace(/\.(glb|gltf|ifc|obj|stl)$/i, ''); await exportObjectsAsGLB(objects, `${base}_selecao_${objects.length}elem.glb`); toast.success(`GLB exportado com ${objects.length} elemento(s)`); }, [activeModel, selectedElementKeys]); const handleScreenshot = useCallback(() => { const canvas = document.querySelector('canvas'); if (!canvas) { toast.error('Canvas não encontrado'); return; } const dataUrl = canvas.toDataURL('image/png'); addScreenshot(dataUrl); toast.success('Screenshot capturado!'); }, [addScreenshot]); return (
{/* Opacity slider */}
Opacidade
{Math.round(opacity * 100)}%
setOpacity(v / 100)} />
{/* Grid toggle */} {/* Scale presets */} {/* Edges color/thickness/threshold popover */} {renderMode === 'edges' && (
Cor das Linhas
{WIRE_COLORS.map((c) => (
Espessura {wireframeThickness.toFixed(1)}
setWireframeThickness(v)} />
{renderMode === 'edges' && (
Ângulo Limite {edgeThresholdAngle}°
setEdgeThresholdAngle(v)} />
)}
)} {/* Posicionar (manual mouse placement) */} {/* Measure tool */}
{(measurePoints.length > 0 || measurements.length > 0) && ( )} {measurements.length > 0 && ( )}
{/* Selection cluster */}
{hasSelection && ( <> )} {hasHiddenOrIsolated && ( )}
{/* Screenshot */}
); }