🚀 Auto-deploy: melhoria no snap e medição AR em 24/05/2026 02:00:33

This commit is contained in:
2026-05-24 02:00:33 +00:00
parent c839653a97
commit c8d51e0e1b
7 changed files with 170 additions and 26 deletions
+28 -13
View File
@@ -1,6 +1,6 @@
import { useEffect, useRef, useMemo, useState, useCallback } from 'react';
import { useFrame, useThree } from '@react-three/fiber';
import { Grid } from '@react-three/drei';
import { Grid, Billboard } from '@react-three/drei';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import * as THREE from 'three';
import { useModelStore, type SceneModel } from '@/stores/useModelStore';
@@ -8,6 +8,15 @@ import { findNearestVertex } from './SmartMeasure';
import { registerModelLocalGroup, unregisterModelLocalGroup } from '@/lib/modelTransforms';
import { parseIFCtoThree } from '@/lib/convertIFC';
function findModelId(obj: THREE.Object3D): string | undefined {
let cur: THREE.Object3D | null = obj;
while (cur) {
if (cur.userData?.modelId) return cur.userData.modelId as string;
cur = cur.parent;
}
return undefined;
}
// ─── XRModel ───────────────────────────────────────────
export function XRModel({ sceneModel }: { sceneModel: SceneModel }) {
const [rawScene, setRawScene] = useState<THREE.Object3D | null>(null);
@@ -282,15 +291,17 @@ function XRMeasurementText({ text, color }: { text: string; color: string }) {
}, [text, color]);
return (
<mesh renderOrder={999}>
<planeGeometry args={[0.08, 0.02]} />
<meshBasicMaterial
map={texture}
transparent
depthTest={false}
side={THREE.DoubleSide}
/>
</mesh>
<Billboard follow={true}>
<mesh renderOrder={999}>
<planeGeometry args={[0.08, 0.02]} />
<meshBasicMaterial
map={texture}
transparent
depthTest={false}
side={THREE.DoubleSide}
/>
</mesh>
</Billboard>
);
}
@@ -444,7 +455,8 @@ export function XRSnapHandler() {
if (hit && hit.object instanceof THREE.Mesh) {
const canvas = gl.domElement;
const snap = findNearestVertex(hit.object, hit.point, camera, { width: canvas.clientWidth, height: canvas.clientHeight }, 10);
setSnapPoint(snap ? { x: snap.x, y: snap.y, z: snap.z } : null);
const hitModelId = findModelId(hit.object);
setSnapPoint(snap ? { x: snap.x, y: snap.y, z: snap.z, modelId: hitModelId } : null);
} else {
setSnapPoint(null);
}
@@ -455,7 +467,7 @@ export function XRSnapHandler() {
if (gl.xr.isPresenting) return;
const onClick = (e: MouseEvent) => {
if (snapPoint) {
addMeasurePoint({ x: snapPoint.x, y: snapPoint.y, z: snapPoint.z });
addMeasurePoint({ x: snapPoint.x, y: snapPoint.y, z: snapPoint.z, modelId: snapPoint.modelId });
return;
}
const rect = gl.domElement.getBoundingClientRect();
@@ -464,7 +476,10 @@ export function XRSnapHandler() {
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
const hit = intersects.find(i => i.object instanceof THREE.Mesh && !i.object.userData.__edgeLine);
if (hit) addMeasurePoint({ x: hit.point.x, y: hit.point.y, z: hit.point.z });
if (hit) {
const hitModelId = findModelId(hit.object);
addMeasurePoint({ x: hit.point.x, y: hit.point.y, z: hit.point.z, modelId: hitModelId });
}
};
gl.domElement.addEventListener('click', onClick);
return () => gl.domElement.removeEventListener('click', onClick);