🚀 Auto-deploy: melhoria no snap e medição AR em 22/05/2026 20:51:44

This commit is contained in:
2026-05-22 20:51:44 +00:00
parent d64d77dba6
commit d8ca2b2e6c
5 changed files with 145 additions and 20 deletions
+36 -4
View File
@@ -1,4 +1,4 @@
import { Suspense, useRef, useMemo, useEffect, type ReactNode } from 'react';
import { Suspense, useRef, useMemo, useEffect, type ReactNode, useState } from 'react';
import { Canvas, useThree, useFrame } from '@react-three/fiber';
import { OrbitControls, useGLTF, Grid, Html, Line } from '@react-three/drei';
import { Loader2 } from 'lucide-react';
@@ -6,6 +6,8 @@ import * as THREE from 'three';
import { useModelStore, type SceneModel } from '@/stores/useModelStore';
import { findNearestVertex, detectHoleAtFace, findNearestEdgeSegment, detectCircularEdgeAtPoint } from './SmartMeasure';
import { registerModelLocalGroup, unregisterModelLocalGroup } from '@/lib/modelTransforms';
import { parseIFCtoThree } from '@/lib/convertIFC';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
interface ModelViewerProps {
url?: string; // legacy, ignored — uses store.models
@@ -73,9 +75,36 @@ function LoadingFallback() {
}
function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive: boolean }) {
const { scene: rawScene } = useGLTF(sceneModel.url);
// Clone scene per instance so multiple GLBModels with same url don't share materials/transforms
const scene = useMemo(() => rawScene.clone(true), [rawScene]);
const [rawScene, setRawScene] = useState<THREE.Object3D | null>(null);
const scene = useMemo(() => rawScene ? rawScene.clone(true) : null, [rawScene]);
useEffect(() => {
let active = true;
const isIfc = sceneModel.fileName.toLowerCase().endsWith('.ifc');
if (isIfc) {
fetch(sceneModel.url)
.then((res) => res.arrayBuffer())
.then((buf) => parseIFCtoThree(buf))
.then((threeScene) => {
if (active) setRawScene(threeScene);
})
.catch((err) => console.error('[GLBModel] IFC parsing error', err));
} else {
const loader = new GLTFLoader();
loader.load(
sceneModel.url,
(gltf) => {
if (active) setRawScene(gltf.scene);
},
undefined,
(err) => console.error('[GLBModel] GLTF loading error', err)
);
}
return () => {
active = false;
};
}, [sceneModel.url, sceneModel.fileName]);
const ref = useRef<THREE.Group>(null);
const opacity = useModelStore((s) => s.opacity);
const renderMode = useModelStore((s) => s.renderMode);
@@ -88,6 +117,7 @@ function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive:
const fineTuning = sceneModel.fineTuning;
const modelInfo = useMemo(() => {
if (!scene) return { size: new THREE.Vector3(), center: new THREE.Vector3() };
const box = new THREE.Box3().setFromObject(scene);
const size = new THREE.Vector3();
const center = new THREE.Vector3();
@@ -99,6 +129,7 @@ function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive:
const originalColors = useRef<Map<THREE.Material, THREE.Color>>(new Map());
useEffect(() => {
if (!scene) return;
const hasRejected = checklist.some(i => i.status === 'rejected');
const allApproved = checklist.every(i => i.status === 'approved');
@@ -174,6 +205,7 @@ function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive:
const renderFactor = scaleRatio?.factor ?? 1;
if (!sceneModel.visible) return null;
if (!scene) return null;
// Determine dominant local axis of the model (longest bbox side) for axial roll
const dominantAxis: 'x' | 'y' | 'z' =