🚀 Auto-deploy: melhoria no snap e medição AR em 11/06/2026 14:55:19
This commit is contained in:
@@ -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
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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 });
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user