🚀 Auto-deploy: melhoria no snap e medição AR em 01/06/2026 00:41:59
This commit is contained in:
@@ -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]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user