🚀 Auto-deploy: melhoria no snap e medição AR em 29/05/2026 20:32:41

This commit is contained in:
2026-05-29 20:32:41 +00:00
parent 1e4bd78128
commit 0f017d0e4b
4 changed files with 391 additions and 29 deletions
+96
View File
@@ -31,6 +31,9 @@ import {
pushXRModelFaceNormal,
computeXRCalibrationQuaternion,
subscribeXRCalibration,
pushXRRealPoint,
pushXRVirtualPoint,
computeVirtRealTransform,
} from '@/components/three/xrCalibrationBus';
// --- Diagnóstico XR ---
@@ -110,6 +113,7 @@ function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').
const ref = useRef<THREE.Group>(null);
const calGroupRef = useRef<THREE.Group>(null);
const localFrameRef = useRef<THREE.Group>(null);
const topGroupRef = useRef<THREE.Group>(null);
// Escuta mudanças de calibração para re-renderizar no AR
const [, setCalTick] = useState(0);
@@ -229,6 +233,7 @@ function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').
return (
<group
ref={topGroupRef}
scale={[renderFactor, renderFactor, renderFactor]}
onClick={(e) => {
e.stopPropagation();
@@ -248,6 +253,7 @@ function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').
// Calibração XR da Peça: captura normal da face do modelo em espaço de mundo.
if (
xrCalibration.modelId === sceneModel.id &&
xrCalibration.alignType === 'cube' &&
(xrCalibration.step === 'await-model-1' || xrCalibration.step === 'await-model-2' || xrCalibration.step === 'await-model-3') &&
e.face && e.object
) {
@@ -267,6 +273,48 @@ function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').
}
return;
}
// Calibração Virt/Real da Peça: captura os pontos virtuais.
if (
xrCalibration.modelId === sceneModel.id &&
xrCalibration.alignType === 'virt-real' &&
(xrCalibration.step === 'await-virtual-1' || xrCalibration.step === 'await-virtual-2' || xrCalibration.step === 'await-virtual-3') &&
localFrameRef.current
) {
const localPoint = localFrameRef.current.worldToLocal(e.point.clone());
const localPivotPoint = localPoint.clone().sub(modelInfo.center);
const stepNum = xrCalibration.step.endsWith('1') ? 1 : xrCalibration.step.endsWith('2') ? 2 : 3;
if (stepNum === 3 && xrCalibration.realPoints.length >= 3 && topGroupRef.current) {
const vPoints = [...xrCalibration.virtualPoints, localPivotPoint];
const realWorldPoints = xrCalibration.realPoints;
const parentPoints = realWorldPoints.map(rw =>
topGroupRef.current!.worldToLocal(rw.clone())
);
const res = computeVirtRealTransform(vPoints, parentPoints);
if (res) {
const { quaternion, position } = res;
useModelStore.getState().setCalibration(sceneModel.id, [quaternion.x, quaternion.y, quaternion.z, quaternion.w]);
useModelStore.getState().setFineTuning({
posX: position.x,
posY: position.y,
posZ: position.z,
rotX: 0,
rotY: 0,
rotZ: 0,
});
pushXRVirtualPoint(localPivotPoint);
toast.success("Peça virtual ajustada com sucesso na peça real!");
} else {
toast.error("Erro ao alinhar: pontos são colineares!");
}
} else {
pushXRVirtualPoint(localPivotPoint);
toast.success(`Ponto virtual ${stepNum} registrado!`);
}
return;
}
}}
>
{/* Translação */}
@@ -760,6 +808,53 @@ function XRSnapHandler() {
return null;
}
// ─── Desktop Aligner Fallback for Virt/Real Calibration ─
function XRVirtRealDesktopAligner() {
const { gl, camera, scene } = useThree();
useEffect(() => {
const isAligning = xrCalibration.alignType === 'virt-real' &&
(xrCalibration.step === 'await-real-1' ||
xrCalibration.step === 'await-real-2' ||
xrCalibration.step === 'await-real-3');
if (!isAligning) return;
if (gl.xr.isPresenting) return;
const mouse = new THREE.Vector2();
const raycaster = new THREE.Raycaster();
const onClick = (e: MouseEvent) => {
const rect = gl.domElement.getBoundingClientRect();
mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
const activeModelId = useModelStore.getState().activeModelId;
const hit = intersects.find(i => {
if (i.object.userData?.__edgeLine) return false;
let cur: THREE.Object3D | null = i.object;
while (cur) {
if (cur.userData?.modelId === activeModelId) return false;
cur = cur.parent;
}
return true;
});
const point = hit ? hit.point.clone() : new THREE.Vector3(0, 0, 0);
pushXRRealPoint(point);
const stepNum = xrCalibration.step.endsWith('1') ? 1 : xrCalibration.step.endsWith('2') ? 2 : 3;
toast.success(`Ponto real ${stepNum} registrado (Simulado)!`);
};
gl.domElement.addEventListener('click', onClick);
return () => gl.domElement.removeEventListener('click', onClick);
}, [gl, camera, scene, xrCalibration.step, xrCalibration.alignType]);
return null;
}
// ─── XR Grid ───────────────────────────────────────────
function XRGrid() {
const showGrid = useModelStore((s) => s.showGrid);
@@ -1025,6 +1120,7 @@ const XRSession = () => {
)}
<XRSnapHandler />
<XRVirtRealDesktopAligner />
<XRGrid />
<XRGridAutoFollower />
</XR>