109 lines
3.7 KiB
TypeScript
109 lines
3.7 KiB
TypeScript
import { useAtlasStore, type ClipAxis } from "../store/useAtlasStore.js";
|
|
|
|
const AXES: { value: ClipAxis; label: string }[] = [
|
|
{ value: "sagittal", label: "Sagital (esq./dir.)" },
|
|
{ value: "coronal", label: "Coronal / Frontal (ant./post.)" },
|
|
{ value: "transverse", label: "Transversal (sup./inf.)" },
|
|
];
|
|
|
|
export function ClippingControls() {
|
|
const clipping = useAtlasStore((s) => s.clipping);
|
|
const setClippingEnabled = useAtlasStore((s) => s.setClippingEnabled);
|
|
const setClipAxis = useAtlasStore((s) => s.setClipAxis);
|
|
const setClipPosition = useAtlasStore((s) => s.setClipPosition);
|
|
const setClipTilt = useAtlasStore((s) => s.setClipTilt);
|
|
|
|
return (
|
|
<div className="space-y-2 border-t border-white/10 pt-3">
|
|
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
|
<input
|
|
type="checkbox"
|
|
checked={clipping.enabled}
|
|
onChange={(e) => setClippingEnabled(e.target.checked)}
|
|
className="accent-accent"
|
|
/>
|
|
Dissecção virtual (plano de corte)
|
|
</label>
|
|
|
|
{clipping.enabled && (
|
|
<div className="space-y-3 pl-1">
|
|
<div className="space-y-1">
|
|
<label className="text-xs text-slate-400">Orientação de base</label>
|
|
<select
|
|
value={clipping.axis}
|
|
onChange={(e) => setClipAxis(e.target.value as ClipAxis)}
|
|
className="w-full text-sm bg-white/5 border border-white/10 rounded px-2 py-1"
|
|
>
|
|
{AXES.map((a) => (
|
|
<option key={a.value} value={a.value}>
|
|
{a.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="space-y-1">
|
|
<label className="text-xs text-slate-400">
|
|
Posição do corte no corpo ({clipping.position.toFixed(2)})
|
|
</label>
|
|
<input
|
|
type="range"
|
|
min={-1}
|
|
max={1}
|
|
step={0.01}
|
|
value={clipping.position}
|
|
onChange={(e) => setClipPosition(Number(e.target.value))}
|
|
className="w-full accent-accent"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-1">
|
|
<label className="text-xs text-slate-400">
|
|
Inclinação oblíqua — eixo 1 ({clipping.tiltX.toFixed(0)}°)
|
|
</label>
|
|
<input
|
|
type="range"
|
|
min={-89}
|
|
max={89}
|
|
step={1}
|
|
value={clipping.tiltX}
|
|
onChange={(e) => setClipTilt(Number(e.target.value), clipping.tiltZ)}
|
|
className="w-full accent-accent"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-1">
|
|
<label className="text-xs text-slate-400">
|
|
Inclinação oblíqua — eixo 2 ({clipping.tiltZ.toFixed(0)}°)
|
|
</label>
|
|
<input
|
|
type="range"
|
|
min={-89}
|
|
max={89}
|
|
step={1}
|
|
value={clipping.tiltZ}
|
|
onChange={(e) => setClipTilt(clipping.tiltX, Number(e.target.value))}
|
|
className="w-full accent-accent"
|
|
/>
|
|
</div>
|
|
|
|
{(clipping.tiltX !== 0 || clipping.tiltZ !== 0) && (
|
|
<button
|
|
onClick={() => setClipTilt(0, 0)}
|
|
className="text-xs text-accent hover:underline"
|
|
>
|
|
Zerar inclinação (voltar ao corte puro)
|
|
</button>
|
|
)}
|
|
|
|
<p className="text-[11px] text-slate-500 leading-snug">
|
|
A posição desliza o corte por todo o corpo carregado (não só pelo
|
|
centro) e a inclinação permite cortes oblíquos a partir de
|
|
qualquer um dos 3 planos anatômicos de base.
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|