🚀 Auto-deploy: melhoria no snap e medição AR em 01/06/2026 01:00:37
This commit is contained in:
@@ -731,6 +731,7 @@ function SmartSnapHandler() {
|
||||
function HoverDetector() {
|
||||
const { camera, scene, gl } = useThree();
|
||||
const setHoverInfo = useModelStore((s) => s.setHoverInfo);
|
||||
const setHoverIfcProps = useModelStore((s) => s.setHoverIfcProps);
|
||||
const raycaster = useMemo(() => new THREE.Raycaster(), []);
|
||||
const mouse = useMemo(() => new THREE.Vector2(), []);
|
||||
const hoverTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
@@ -764,8 +765,8 @@ function HoverDetector() {
|
||||
lastHitKey.current = '';
|
||||
lastHitExpressId.current = 0;
|
||||
setHoverInfo(null);
|
||||
setHoverIfcProps(null);
|
||||
clearTimers();
|
||||
toast.dismiss('ifc-hover-info');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -778,40 +779,9 @@ function HoverDetector() {
|
||||
const props = elementRoot?.userData?.properties;
|
||||
|
||||
if (props && (props.name || props.material || props.tag)) {
|
||||
const infoLines: string[] = [];
|
||||
if (props.tag || props.name) {
|
||||
infoLines.push(`Peça: ${props.tag || props.name}`);
|
||||
}
|
||||
if (props.material) {
|
||||
infoLines.push(`Material: ${props.material}`);
|
||||
}
|
||||
|
||||
const lengthKey = Object.keys(props).find(k => {
|
||||
const kl = k.toLowerCase();
|
||||
return kl.includes('length') || kl.includes('comprimento') || kl.includes('lengthvalue');
|
||||
});
|
||||
|
||||
if (lengthKey && props[lengthKey]) {
|
||||
infoLines.push(`Comprimento: ${props[lengthKey]}`);
|
||||
} else {
|
||||
const commonLengthKeys = ['Length', 'Comprimento', 'LengthValue', 'height', 'comprimento'];
|
||||
for (const key of commonLengthKeys) {
|
||||
if (props[key]) {
|
||||
infoLines.push(`Comprimento: ${props[key]}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (infoLines.length > 0) {
|
||||
toast(infoLines.join(' | '), {
|
||||
id: 'ifc-hover-info',
|
||||
duration: 8000,
|
||||
position: 'bottom-center',
|
||||
});
|
||||
}
|
||||
setHoverIfcProps(props);
|
||||
} else {
|
||||
toast.dismiss('ifc-hover-info');
|
||||
setHoverIfcProps(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -879,8 +849,8 @@ function HoverDetector() {
|
||||
lastHitKey.current = '';
|
||||
lastHitExpressId.current = 0;
|
||||
setHoverInfo(null);
|
||||
setHoverIfcProps(null);
|
||||
clearTimers();
|
||||
toast.dismiss('ifc-hover-info');
|
||||
};
|
||||
|
||||
gl.domElement.addEventListener('pointermove', onMove);
|
||||
@@ -889,9 +859,9 @@ function HoverDetector() {
|
||||
gl.domElement.removeEventListener('pointermove', onMove);
|
||||
gl.domElement.removeEventListener('pointerleave', onLeave);
|
||||
clearTimers();
|
||||
toast.dismiss('ifc-hover-info');
|
||||
setHoverIfcProps(null);
|
||||
};
|
||||
}, [camera, scene, gl, raycaster, mouse, setHoverInfo]);
|
||||
}, [camera, scene, gl, raycaster, mouse, setHoverInfo, setHoverIfcProps]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
+33
-1
@@ -35,7 +35,8 @@ const Viewer = () => {
|
||||
fineTuning,
|
||||
sectionEnabled,
|
||||
sectionInvert,
|
||||
sectionLevel
|
||||
sectionLevel,
|
||||
hoverIfcProps
|
||||
} = useModelStore();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [converting, setConverting] = useState(false);
|
||||
@@ -366,6 +367,37 @@ const Viewer = () => {
|
||||
{/* 3D Canvas with floating controls */}
|
||||
<div className="relative flex-1 min-w-0" id="canvas-container">
|
||||
<ModelViewerCanvas />
|
||||
{hoverIfcProps && (
|
||||
<div
|
||||
className={`absolute left-4 z-40 w-72 rounded-xl border border-primary/20 bg-background/80 p-3.5 shadow-2xl backdrop-blur-md transition-all duration-300 pointer-events-none select-none font-mono text-[11px] ${
|
||||
isFullscreen ? 'top-16' : 'top-4'
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-col gap-2.5">
|
||||
<div className="flex items-center gap-1.5 border-b border-primary/10 pb-1.5">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-primary animate-pulse" />
|
||||
<span className="font-semibold text-primary uppercase text-[9px] tracking-wider font-sans">Propriedades IFC</span>
|
||||
</div>
|
||||
|
||||
{Object.entries(hoverIfcProps).map(([key, value]) => {
|
||||
if (!value) return null;
|
||||
let displayKey = key;
|
||||
if (key === 'name') displayKey = 'Nome';
|
||||
else if (key === 'tag') displayKey = 'Marca';
|
||||
else if (key === 'material') displayKey = 'Material';
|
||||
else if (key === 'objectType') displayKey = 'Tipo';
|
||||
else if (key === 'description') displayKey = 'Descrição';
|
||||
|
||||
return (
|
||||
<div key={key} className="flex flex-col gap-0.5">
|
||||
<span className="text-[8px] text-muted-foreground uppercase tracking-widest">{displayKey}</span>
|
||||
<span className="text-foreground font-semibold truncate text-[11px] max-w-[250px]" title={value}>{value}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isFullscreen ? (
|
||||
<div className="absolute top-4 left-4 z-50 flex gap-2">
|
||||
<Button
|
||||
|
||||
@@ -357,6 +357,9 @@ interface ModelStore {
|
||||
hoverInfo: HoverInfo | null;
|
||||
setHoverInfo: (info: HoverInfo | null) => void;
|
||||
|
||||
hoverIfcProps: Record<string, string> | null;
|
||||
setHoverIfcProps: (props: Record<string, string> | null) => void;
|
||||
|
||||
screenshots: string[];
|
||||
addScreenshot: (dataUrl: string) => void;
|
||||
removeScreenshot: (index: number) => void;
|
||||
@@ -782,6 +785,9 @@ export const useModelStore = create<ModelStore>((set, get) => ({
|
||||
hoverInfo: null,
|
||||
setHoverInfo: (hoverInfo) => set({ hoverInfo }),
|
||||
|
||||
hoverIfcProps: null,
|
||||
setHoverIfcProps: (hoverIfcProps) => set({ hoverIfcProps }),
|
||||
|
||||
screenshots: [],
|
||||
addScreenshot: (dataUrl) => set((state) => ({ screenshots: [...state.screenshots, dataUrl] })),
|
||||
removeScreenshot: (index) => set((state) => ({
|
||||
|
||||
Reference in New Issue
Block a user