🚀 Auto-deploy: melhoria no snap e medição AR em 11/06/2026 14:55:19

This commit is contained in:
2026-06-11 14:55:19 +00:00
parent fc10c77346
commit 7bd45e473d
4 changed files with 90 additions and 19 deletions
+7 -4
View File
@@ -11,7 +11,7 @@ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { mainCameraRef, mainControlsRef, viewAnim, calibration, pushModelFaceNormal, computeCalibrationQuaternion, subscribeCalibration } from './viewCubeBus'; import { mainCameraRef, mainControlsRef, viewAnim, calibration, pushModelFaceNormal, computeCalibrationQuaternion, subscribeCalibration } from './viewCubeBus';
import { toast } from 'sonner'; import { toast } from 'sonner';
function getColorForMaterialName(name: string): string { export function getColorForMaterialName(name: string): string {
if (!name) return '#a1a1aa'; if (!name) return '#a1a1aa';
let hash = 0; let hash = 0;
for (let i = 0; i < name.length; i++) { for (let i = 0; i < name.length; i++) {
@@ -208,7 +208,7 @@ function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive:
const setActive = useModelStore((s) => s.setActiveModel); const setActive = useModelStore((s) => s.setActiveModel);
const selectionMode = useModelStore((s) => s.selectionMode); const selectionMode = useModelStore((s) => s.selectionMode);
const measureMode = useModelStore((s) => s.measureMode); const measureMode = useModelStore((s) => s.measureMode);
const ifcMaterialColors = useModelStore((s) => s.ifcMaterialColors); const ifcColorMode = useModelStore((s) => s.ifcColorMode);
const fineTuning = sceneModel.fineTuning; const fineTuning = sceneModel.fineTuning;
// Re-render when calibration state changes (so calQuat resets to identity during the flow). // Re-render when calibration state changes (so calQuat resets to identity during the flow).
@@ -277,8 +277,11 @@ function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive:
} else { } else {
const root = findElementRoot(child); const root = findElementRoot(child);
const matName = root?.userData?.materialName; const matName = root?.userData?.materialName;
if (ifcMaterialColors && matName) { const descName = root?.userData?.properties?.description || root?.userData?.description;
if (ifcColorMode === 'material' && matName) {
mat.color.set(getColorForMaterialName(matName)); mat.color.set(getColorForMaterialName(matName));
} else if (ifcColorMode === 'description' && descName) {
mat.color.set(getColorForMaterialName(descName));
} else { } else {
// Tint with the per-model color (subtle) // Tint with the per-model color (subtle)
mat.color.set(sceneModel.color); mat.color.set(sceneModel.color);
@@ -304,7 +307,7 @@ function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive:
} }
} }
}); });
}, [scene, opacity, renderMode, checklist, wireframeColor, wireframeThickness, edgeThresholdAngle, sceneModel.color, measureMode, ifcMaterialColors]); }, [scene, opacity, renderMode, checklist, wireframeColor, wireframeThickness, edgeThresholdAngle, sceneModel.color, measureMode, ifcColorMode]);
const rotXRad = (fineTuning.rotX * Math.PI) / 180; const rotXRad = (fineTuning.rotX * Math.PI) / 180;
const rotYRad = (fineTuning.rotY * Math.PI) / 180; const rotYRad = (fineTuning.rotY * Math.PI) / 180;
+38 -11
View File
@@ -24,7 +24,7 @@ const Index = () => {
const [showLanding, setShowLanding] = useState(false); const [showLanding, setShowLanding] = useState(false);
const [converting, setConverting] = useState(false); const [converting, setConverting] = useState(false);
const { model, addModel, models, maxModels, xrSupported, setXrSupported, ifcMaterialColors, setIfcMaterialColors } = useModelStore(); const { model, addModel, models, maxModels, xrSupported, setXrSupported, ifcColorMode, setIfcColorMode } = useModelStore();
// --- Painel de Logs Remotos em Tempo Real --- // --- Painel de Logs Remotos em Tempo Real ---
const [debugLogs, setDebugLogs] = useState<{ level: 'info' | 'warn' | 'error'; message: string; timestamp: string; data?: unknown }[]>([]); const [debugLogs, setDebugLogs] = useState<{ level: 'info' | 'warn' | 'error'; message: string; timestamp: string; data?: unknown }[]>([]);
@@ -265,21 +265,48 @@ const Index = () => {
/> />
</div> </div>
{/* Toggle IFC Material Colors */} {/* Escolha do Filtro de Cores (IFC) */}
<div className="flex items-center justify-between rounded-lg border border-border bg-card/50 p-3.5"> <div className="flex flex-col gap-2 rounded-lg border border-border bg-card/50 p-3.5">
<div className="flex flex-col gap-0.5"> <div className="flex flex-col gap-0.5">
<Label htmlFor="ifc-mat-colors-toggle" className="text-xs font-semibold cursor-pointer text-foreground"> <Label className="text-xs font-semibold text-foreground">
Cores por Material (IFC) Modo de Cores (IFC)
</Label> </Label>
<p className="text-[10px] text-muted-foreground"> <p className="text-[10px] text-muted-foreground">
{ifcMaterialColors ? 'Cores baseadas no material do IFC' : 'Cores padrão da cena'} Escolha como aplicar cores aos elementos do modelo
</p> </p>
</div> </div>
<Switch <div className="grid grid-cols-3 gap-1 bg-slate-950/40 p-1 rounded-md border border-border/40 mt-1">
id="ifc-mat-colors-toggle" <button
checked={ifcMaterialColors} onClick={() => setIfcColorMode('none')}
onCheckedChange={setIfcMaterialColors} className={`py-1.5 px-2 text-[10px] font-medium rounded transition-all ${
/> ifcColorMode === 'none'
? 'bg-primary text-primary-foreground shadow-sm'
: 'text-muted-foreground hover:bg-slate-900/60 hover:text-foreground'
}`}
>
Cor Única
</button>
<button
onClick={() => setIfcColorMode('material')}
className={`py-1.5 px-2 text-[10px] font-medium rounded transition-all ${
ifcColorMode === 'material'
? 'bg-primary text-primary-foreground shadow-sm'
: 'text-muted-foreground hover:bg-slate-900/60 hover:text-foreground'
}`}
>
Material
</button>
<button
onClick={() => setIfcColorMode('description')}
className={`py-1.5 px-2 text-[10px] font-medium rounded transition-all ${
ifcColorMode === 'description'
? 'bg-primary text-primary-foreground shadow-sm'
: 'text-muted-foreground hover:bg-slate-900/60 hover:text-foreground'
}`}
>
Descrição
</button>
</div>
</div> </div>
{/* Demo buttons */} {/* Demo buttons */}
+12 -2
View File
@@ -23,7 +23,7 @@ import { ControllerLocomotion } from '@/components/three/ControllerLocomotion';
import { useDevKit } from '@/devkit/useDevKit'; import { useDevKit } from '@/devkit/useDevKit';
import { DevPanel } from '@/devkit/DevPanel'; import { DevPanel } from '@/devkit/DevPanel';
import { FakeControllers } from '@/devkit/FakeControllers'; import { FakeControllers } from '@/devkit/FakeControllers';
import { VisibilityApplier } from '@/components/three/ModelViewer'; import { VisibilityApplier, findElementRoot, getColorForMaterialName } from '@/components/three/ModelViewer';
import { registerModelLocalGroup, unregisterModelLocalGroup } from '@/lib/modelTransforms'; import { registerModelLocalGroup, unregisterModelLocalGroup } from '@/lib/modelTransforms';
import { parseIFCtoThree } from '@/lib/convertIFC'; import { parseIFCtoThree } from '@/lib/convertIFC';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
@@ -104,6 +104,7 @@ function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').
const edgeThresholdAngle = useModelStore((s) => s.edgeThresholdAngle); const edgeThresholdAngle = useModelStore((s) => s.edgeThresholdAngle);
const checklist = useModelStore((s) => s.checklist); const checklist = useModelStore((s) => s.checklist);
const measureMode = useModelStore((s) => s.measureMode); const measureMode = useModelStore((s) => s.measureMode);
const ifcColorMode = useModelStore((s) => s.ifcColorMode);
const modelInfo = useMemo(() => { const modelInfo = useMemo(() => {
if (!scene) return { size: new THREE.Vector3(), center: new THREE.Vector3() }; if (!scene) return { size: new THREE.Vector3(), center: new THREE.Vector3() };
@@ -158,10 +159,19 @@ function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').
mat.color.setHSL(0, 0.7, 0.5); mat.color.setHSL(0, 0.7, 0.5);
} else if (allApproved) { } else if (allApproved) {
mat.color.setHSL(0.38, 0.7, 0.45); mat.color.setHSL(0.38, 0.7, 0.45);
} else {
const root = findElementRoot(child);
const matName = root?.userData?.materialName;
const descName = root?.userData?.properties?.description || root?.userData?.description;
if (ifcColorMode === 'material' && matName) {
mat.color.set(getColorForMaterialName(matName));
} else if (ifcColorMode === 'description' && descName) {
mat.color.set(getColorForMaterialName(descName));
} else { } else {
mat.color.set(sceneModel.color); mat.color.set(sceneModel.color);
} }
} }
}
mat.needsUpdate = true; mat.needsUpdate = true;
} }
}); });
@@ -181,7 +191,7 @@ function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore').
} }
} }
}); });
}, [scene, opacity, renderMode, checklist, wireframeColor, wireframeThickness, edgeThresholdAngle, sceneModel.color, measureMode]); }, [scene, opacity, renderMode, checklist, wireframeColor, wireframeThickness, edgeThresholdAngle, sceneModel.color, measureMode, ifcColorMode]);
const rotXRad = (fineTuning.rotX * Math.PI) / 180; const rotXRad = (fineTuning.rotX * Math.PI) / 180;
const rotYRad = (fineTuning.rotY * Math.PI) / 180; const rotYRad = (fineTuning.rotY * Math.PI) / 180;
+32 -1
View File
@@ -95,6 +95,9 @@ function loadVisibility(fileName: string): { hidden: Set<string>; isolated: Set<
} }
const IFC_MAT_COLORS_KEY = 'tsxr_ifc_mat_colors_v1'; const IFC_MAT_COLORS_KEY = 'tsxr_ifc_mat_colors_v1';
const IFC_COLOR_MODE_KEY = 'tsxr_ifc_color_mode_v1';
export type IFCColorMode = 'none' | 'material' | 'description';
function loadIfcMaterialColors(): boolean { function loadIfcMaterialColors(): boolean {
try { try {
const raw = localStorage.getItem(IFC_MAT_COLORS_KEY); const raw = localStorage.getItem(IFC_MAT_COLORS_KEY);
@@ -106,6 +109,21 @@ function saveIfcMaterialColors(enabled: boolean) {
try { localStorage.setItem(IFC_MAT_COLORS_KEY, String(enabled)); } catch (e) { /* ignore */ } try { localStorage.setItem(IFC_MAT_COLORS_KEY, String(enabled)); } catch (e) { /* ignore */ }
} }
function loadIfcColorMode(): IFCColorMode {
try {
const raw = localStorage.getItem(IFC_COLOR_MODE_KEY);
if (raw === 'none' || raw === 'material' || raw === 'description') {
return raw as IFCColorMode;
}
const legacy = localStorage.getItem(IFC_MAT_COLORS_KEY);
if (legacy === 'false') return 'none';
} catch (e) { /* ignore */ }
return 'material';
}
function saveIfcColorMode(mode: IFCColorMode) {
try { localStorage.setItem(IFC_COLOR_MODE_KEY, mode); } catch (e) { /* ignore */ }
}
export interface ScaleRatio { export interface ScaleRatio {
@@ -372,6 +390,8 @@ interface ModelStore {
ifcMaterialColors: boolean; ifcMaterialColors: boolean;
setIfcMaterialColors: (enabled: boolean) => void; setIfcMaterialColors: (enabled: boolean) => void;
ifcColorMode: IFCColorMode;
setIfcColorMode: (mode: IFCColorMode) => void;
xrRig: THREE.Group | null; xrRig: THREE.Group | null;
setXRRig: (rig: THREE.Group | null) => void; setXRRig: (rig: THREE.Group | null) => void;
@@ -815,7 +835,18 @@ export const useModelStore = create<ModelStore>((set, get) => ({
ifcMaterialColors: loadIfcMaterialColors(), ifcMaterialColors: loadIfcMaterialColors(),
setIfcMaterialColors: (enabled: boolean) => { setIfcMaterialColors: (enabled: boolean) => {
saveIfcMaterialColors(enabled); saveIfcMaterialColors(enabled);
set({ ifcMaterialColors: enabled }); set((state) => {
const mode = enabled ? 'material' : 'none';
saveIfcColorMode(mode);
return { ifcMaterialColors: enabled, ifcColorMode: mode };
});
},
ifcColorMode: loadIfcColorMode(),
setIfcColorMode: (mode: IFCColorMode) => {
saveIfcColorMode(mode);
const enabled = mode === 'material';
saveIfcMaterialColors(enabled);
set({ ifcColorMode: mode, ifcMaterialColors: enabled });
}, },
})); }));