diff --git a/src/components/FineTuningControls.tsx b/src/components/FineTuningControls.tsx
new file mode 100644
index 0000000..bfaa134
--- /dev/null
+++ b/src/components/FineTuningControls.tsx
@@ -0,0 +1,116 @@
+import { Move, RotateCw, RefreshCw } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { useModelStore } from '@/stores/useModelStore';
+
+const POSITION_STEP = 0.001; // 1mm
+const ROTATION_STEP = 0.1; // 0.1°
+
+function StepButton({ label, onClick }: { label: string; onClick: () => void }) {
+ return (
+
+ );
+}
+
+function AxisControl({
+ axis,
+ value,
+ unit,
+ onIncrement,
+ onDecrement,
+ displayValue,
+}: {
+ axis: string;
+ value: number;
+ unit: string;
+ onIncrement: () => void;
+ onDecrement: () => void;
+ displayValue: string;
+}) {
+ return (
+
+ {axis}
+
+ {displayValue}{unit}
+
+
+ );
+}
+
+export function FineTuningControls() {
+ const { fineTuning, setFineTuning, resetFineTuning } = useModelStore();
+
+ return (
+
+
+
+ Ajuste Fino
+
+
+
+
+ {/* Position */}
+
+
+
+ Posição (±1mm)
+
+
setFineTuning({ posX: fineTuning.posX + POSITION_STEP })}
+ onDecrement={() => setFineTuning({ posX: fineTuning.posX - POSITION_STEP })}
+ />
+ setFineTuning({ posY: fineTuning.posY + POSITION_STEP })}
+ onDecrement={() => setFineTuning({ posY: fineTuning.posY - POSITION_STEP })}
+ />
+ setFineTuning({ posZ: fineTuning.posZ + POSITION_STEP })}
+ onDecrement={() => setFineTuning({ posZ: fineTuning.posZ - POSITION_STEP })}
+ />
+
+
+ {/* Rotation */}
+
+
+
+ Rotação Y (±0.1°)
+
+
setFineTuning({ rotY: fineTuning.rotY + ROTATION_STEP })}
+ onDecrement={() => setFineTuning({ rotY: fineTuning.rotY - ROTATION_STEP })}
+ />
+
+
+
+
+ No Quest 3: Grip + Joystick esq. = X/Y, Joystick dir. = Z/Rotação
+
+
+
+ );
+}
diff --git a/src/components/three/ModelViewer.tsx b/src/components/three/ModelViewer.tsx
index ea013f4..b9e73ab 100644
--- a/src/components/three/ModelViewer.tsx
+++ b/src/components/three/ModelViewer.tsx
@@ -26,6 +26,7 @@ function GLBModel({ url }: { url: string }) {
const opacity = useModelStore((s) => s.opacity);
const renderMode = useModelStore((s) => s.renderMode);
const checklist = useModelStore((s) => s.checklist);
+ const fineTuning = useModelStore((s) => s.fineTuning);
const modelInfo = useMemo(() => {
const box = new THREE.Box3().setFromObject(scene);
@@ -71,8 +72,18 @@ function GLBModel({ url }: { url: string }) {
});
}, [scene, opacity, renderMode, checklist]);
+ const rotYRad = (fineTuning.rotY * Math.PI) / 180;
+
return (
-
+
);
diff --git a/src/pages/Viewer.tsx b/src/pages/Viewer.tsx
index 0cbd362..0ecd83c 100644
--- a/src/pages/Viewer.tsx
+++ b/src/pages/Viewer.tsx
@@ -5,6 +5,7 @@ import { useModelStore } from '@/stores/useModelStore';
import { ModelViewerCanvas } from '@/components/three/ModelViewer';
import { ViewerControls } from '@/components/ViewerControls';
import { InspectionChecklist } from '@/components/InspectionChecklist';
+import { FineTuningControls } from '@/components/FineTuningControls';
import { useEffect } from 'react';
import { toast } from 'sonner';
import { ScrollArea } from '@/components/ui/scroll-area';
@@ -84,6 +85,10 @@ const Viewer = () => {
+
+
+
+