🚀 Auto-deploy: melhoria no snap e medição AR em 01/06/2026 00:41:59

This commit is contained in:
2026-06-01 00:41:59 +00:00
parent ba33180a00
commit b47fb872b5
4 changed files with 271 additions and 7 deletions
+75 -3
View File
@@ -9,6 +9,19 @@ import { registerModelLocalGroup, unregisterModelLocalGroup, getModelWorldScaleF
import { parseIFCtoThree } from '@/lib/convertIFC';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { mainCameraRef, mainControlsRef, viewAnim, calibration, pushModelFaceNormal, computeCalibrationQuaternion, subscribeCalibration } from './viewCubeBus';
import { toast } from 'sonner';
function getColorForMaterialName(name: string): string {
if (!name) return '#a1a1aa';
let hash = 0;
for (let i = 0; i < name.length; i++) {
hash = name.charCodeAt(i) + ((hash << 5) - hash);
}
const h = Math.abs(hash) % 360;
const s = 65 + (Math.abs(hash >> 8) % 15);
const l = 45 + (Math.abs(hash >> 16) % 10);
return `hsl(${h}, ${s}%, ${l}%)`;
}
interface ModelViewerProps {
url?: string; // legacy, ignored — uses store.models
@@ -195,6 +208,7 @@ function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive:
const setActive = useModelStore((s) => s.setActiveModel);
const selectionMode = useModelStore((s) => s.selectionMode);
const measureMode = useModelStore((s) => s.measureMode);
const ifcMaterialColors = useModelStore((s) => s.ifcMaterialColors);
const fineTuning = sceneModel.fineTuning;
// Re-render when calibration state changes (so calQuat resets to identity during the flow).
@@ -261,8 +275,14 @@ function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive:
} else if (allApproved) {
mat.color.setHSL(0.38, 0.7, 0.45);
} else {
// Tint with the per-model color (subtle)
mat.color.set(sceneModel.color);
const root = findElementRoot(child);
const matName = root?.userData?.materialName;
if (ifcMaterialColors && matName) {
mat.color.set(getColorForMaterialName(matName));
} else {
// Tint with the per-model color (subtle)
mat.color.set(sceneModel.color);
}
}
}
mat.needsUpdate = true;
@@ -284,7 +304,7 @@ function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive:
}
}
});
}, [scene, opacity, renderMode, checklist, wireframeColor, wireframeThickness, edgeThresholdAngle, sceneModel.color, measureMode]);
}, [scene, opacity, renderMode, checklist, wireframeColor, wireframeThickness, edgeThresholdAngle, sceneModel.color, measureMode, ifcMaterialColors]);
const rotXRad = (fineTuning.rotX * Math.PI) / 180;
const rotYRad = (fineTuning.rotY * Math.PI) / 180;
@@ -716,6 +736,7 @@ function HoverDetector() {
const hoverTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const hideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const lastHitKey = useRef('');
const lastHitExpressId = useRef<number>(0);
useEffect(() => {
const clearTimers = () => {
@@ -741,11 +762,59 @@ function HoverDetector() {
if (!hit || !(hit.object instanceof THREE.Mesh)) {
lastHitKey.current = '';
lastHitExpressId.current = 0;
setHoverInfo(null);
clearTimers();
toast.dismiss('ifc-hover-info');
return;
}
// Detecção de propriedades do IFC de forma ágil baseada em mudança de elemento
const elementRoot = findElementRoot(hit.object);
const expressID = elementRoot?.userData?.ifcId ?? 0;
if (expressID !== lastHitExpressId.current) {
lastHitExpressId.current = expressID;
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',
});
}
} else {
toast.dismiss('ifc-hover-info');
}
}
// Stability check same approximate position for debounce (~3 mm)
const key = `${hit.point.x.toFixed(3)},${hit.point.y.toFixed(3)},${hit.point.z.toFixed(3)}`;
if (key === lastHitKey.current) return; // timer already running
@@ -808,8 +877,10 @@ function HoverDetector() {
const onLeave = () => {
lastHitKey.current = '';
lastHitExpressId.current = 0;
setHoverInfo(null);
clearTimers();
toast.dismiss('ifc-hover-info');
};
gl.domElement.addEventListener('pointermove', onMove);
@@ -818,6 +889,7 @@ function HoverDetector() {
gl.domElement.removeEventListener('pointermove', onMove);
gl.domElement.removeEventListener('pointerleave', onLeave);
clearTimers();
toast.dismiss('ifc-hover-info');
};
}, [camera, scene, gl, raycaster, mouse, setHoverInfo]);