fix bugs
This commit is contained in:
1 parent
1e1d2a9473
commit
157993d755
3 files changed
+89
-28
No files matched your search
@@ -32,6 +32,12 @@ export function QuickRegionNav() {
|
|||||||
const focusOnRegion = useAtlasStore((s) => s.focusOnRegion);
|
const focusOnRegion = useAtlasStore((s) => s.focusOnRegion);
|
||||||
const focusOnViewAngle = useAtlasStore((s) => s.focusOnViewAngle);
|
const focusOnViewAngle = useAtlasStore((s) => s.focusOnViewAngle);
|
||||||
const [pos, setPos] = useState<{ x: number; y: number } | null>(() => loadStoredPos());
|
const [pos, setPos] = useState<{ x: number; y: number } | null>(() => loadStoredPos());
|
||||||
|
// Só pra realçar visualmente o último botão clicado em cada grupo — o
|
||||||
|
// enquadramento inicial já é "Corpo Inteiro" + "Anterior" (ver Scene.tsx),
|
||||||
|
// então começamos os dois já marcados como ativos pra bater com o que a
|
||||||
|
// câmera mostra ao abrir.
|
||||||
|
const [activeRegion, setActiveRegion] = useState<BodyRegion>("corpo_inteiro");
|
||||||
|
const [activeAngle, setActiveAngle] = useState<"anterior" | "posterior">("anterior");
|
||||||
const elRef = useRef<HTMLDivElement>(null);
|
const elRef = useRef<HTMLDivElement>(null);
|
||||||
const dragRef = useRef<{ startX: number; startY: number; origX: number; origY: number } | null>(null);
|
const dragRef = useRef<{ startX: number; startY: number; origX: number; origY: number } | null>(null);
|
||||||
|
|
||||||
@@ -111,39 +117,84 @@ export function QuickRegionNav() {
|
|||||||
onPointerDown={onGripPointerDown}
|
onPointerDown={onGripPointerDown}
|
||||||
onPointerMove={onGripPointerMove}
|
onPointerMove={onGripPointerMove}
|
||||||
onPointerUp={onGripPointerUp}
|
onPointerUp={onGripPointerUp}
|
||||||
className="shrink-0 cursor-move select-none text-slate-500 hover:text-slate-300 px-1 touch-none"
|
className="shrink-0 cursor-move select-none text-slate-500 hover:text-slate-300 px-1 touch-none self-stretch flex items-center"
|
||||||
title="Arrastar"
|
title="Arrastar"
|
||||||
aria-label="Arrastar menu de regiões"
|
aria-label="Arrastar menu de regiões"
|
||||||
>
|
>
|
||||||
⠿
|
⠿
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1.5 overflow-x-auto md:flex-wrap">
|
|
||||||
{REGIONS.map((r) => (
|
{/* Duas linhas em vez de um único flex-wrap: região e ângulo de vista
|
||||||
<button
|
são decisões diferentes (onde olhar x de que lado olhar), e
|
||||||
key={r.region}
|
misturadas numa fileira só quebravam de um jeito confuso (ex:
|
||||||
onClick={() => focusOnRegion(r.region)}
|
"Corpo Inteiro" ficava orfão numa segunda linha, e Anterior/
|
||||||
className="shrink-0 text-xs px-3 py-1.5 rounded-full bg-white/5 hover:bg-accent hover:text-white text-slate-300 transition-colors"
|
Posterior se perdiam no meio dos pills de região). */}
|
||||||
>
|
<div className="flex flex-col gap-1.5 min-w-0">
|
||||||
{r.label}
|
<div className="flex items-center gap-1.5">
|
||||||
</button>
|
<span className="shrink-0 text-[10px] font-medium uppercase tracking-wide text-slate-500 w-14">
|
||||||
))}
|
Região
|
||||||
</div>
|
</span>
|
||||||
<div className="shrink-0 h-6 w-px bg-white/10" aria-hidden="true" />
|
<div className="flex gap-1.5 overflow-x-auto md:flex-wrap md:max-w-[40vw]">
|
||||||
<div className="flex gap-1.5 shrink-0">
|
{REGIONS.map((r) => (
|
||||||
<button
|
<button
|
||||||
onClick={() => focusOnViewAngle("anterior")}
|
key={r.region}
|
||||||
title="Vista anterior (de frente)"
|
onClick={() => {
|
||||||
className="shrink-0 text-xs px-3 py-1.5 rounded-full bg-white/5 hover:bg-accent hover:text-white text-slate-300 transition-colors"
|
setActiveRegion(r.region);
|
||||||
>
|
focusOnRegion(r.region);
|
||||||
Anterior
|
}}
|
||||||
</button>
|
className={
|
||||||
<button
|
"shrink-0 text-xs px-3 py-1.5 rounded-full transition-colors " +
|
||||||
onClick={() => focusOnViewAngle("posterior")}
|
(activeRegion === r.region
|
||||||
title="Vista posterior (de costas)"
|
? "bg-accent text-white"
|
||||||
className="shrink-0 text-xs px-3 py-1.5 rounded-full bg-white/5 hover:bg-accent hover:text-white text-slate-300 transition-colors"
|
: "bg-white/5 hover:bg-accent hover:text-white text-slate-300")
|
||||||
>
|
}
|
||||||
Posterior
|
>
|
||||||
</button>
|
{r.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<span className="shrink-0 text-[10px] font-medium uppercase tracking-wide text-slate-500 w-14">
|
||||||
|
Vista
|
||||||
|
</span>
|
||||||
|
{/* Toggle segmentado (2 opções, uma cápsula só) em vez de dois
|
||||||
|
pills soltos — deixa claro que é uma escolha binária (frente
|
||||||
|
OU costas), não mais um item de uma lista como as regiões. */}
|
||||||
|
<div className="inline-flex shrink-0 rounded-full bg-white/5 p-0.5">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setActiveAngle("anterior");
|
||||||
|
focusOnViewAngle("anterior");
|
||||||
|
}}
|
||||||
|
title="Vista anterior (de frente)"
|
||||||
|
className={
|
||||||
|
"text-xs px-3 py-1 rounded-full transition-colors " +
|
||||||
|
(activeAngle === "anterior"
|
||||||
|
? "bg-accent text-white"
|
||||||
|
: "text-slate-300 hover:text-white")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Anterior
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setActiveAngle("posterior");
|
||||||
|
focusOnViewAngle("posterior");
|
||||||
|
}}
|
||||||
|
title="Vista posterior (de costas)"
|
||||||
|
className={
|
||||||
|
"text-xs px-3 py-1 rounded-full transition-colors " +
|
||||||
|
(activeAngle === "posterior"
|
||||||
|
? "bg-accent text-white"
|
||||||
|
: "text-slate-300 hover:text-white")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Posterior
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -382,6 +382,7 @@ export function Scene({
|
|||||||
sectionColor="#334155"
|
sectionColor="#334155"
|
||||||
fadeDistance={6}
|
fadeDistance={6}
|
||||||
infiniteGrid
|
infiniteGrid
|
||||||
|
userData={{ excludeFromFit: true }}
|
||||||
/>
|
/>
|
||||||
<CameraController modelFiles={modelFiles} />
|
<CameraController modelFiles={modelFiles} />
|
||||||
<IntercostalLabels bySlug={bySlug} />
|
<IntercostalLabels bySlug={bySlug} />
|
||||||
|
|||||||
@@ -15,12 +15,21 @@ function isFiniteVec3(v: THREE.Vector3): boolean {
|
|||||||
// vértices degenerados/NaN, e Box3.setFromObject(scene) ingenuamente
|
// vértices degenerados/NaN, e Box3.setFromObject(scene) ingenuamente
|
||||||
// incluiria isso, distorcendo o enquadramento (ou escondendo tudo o mais
|
// incluiria isso, distorcendo o enquadramento (ou escondendo tudo o mais
|
||||||
// fora do "zoom" resultante).
|
// fora do "zoom" resultante).
|
||||||
|
//
|
||||||
|
// Também pulamos objetos marcados com userData.excludeFromFit — o <Grid>
|
||||||
|
// decorativo do chão (ver Scene.tsx) é uma malha de verdade (um
|
||||||
|
// <planeGeometry args={[4,4]}/> por baixo do shader que faz ele PARECER
|
||||||
|
// infinito), então sem essa exclusão ele entrava na conta do
|
||||||
|
// computeVisibleBoundingBox(scene) e distorcia o enquadramento inicial da
|
||||||
|
// câmera (cálculo de distância/aspecto erradOS, câmera ficando perto
|
||||||
|
// demais ou longe demais do corpo de verdade).
|
||||||
export function computeVisibleBoundingBox(root: THREE.Object3D): THREE.Box3 {
|
export function computeVisibleBoundingBox(root: THREE.Object3D): THREE.Box3 {
|
||||||
const union = new THREE.Box3();
|
const union = new THREE.Box3();
|
||||||
const tmp = new THREE.Box3();
|
const tmp = new THREE.Box3();
|
||||||
root.traverse((obj) => {
|
root.traverse((obj) => {
|
||||||
const mesh = obj as THREE.Mesh;
|
const mesh = obj as THREE.Mesh;
|
||||||
if (!mesh.isMesh || !mesh.visible) return;
|
if (!mesh.isMesh || !mesh.visible) return;
|
||||||
|
if (mesh.userData?.excludeFromFit) return;
|
||||||
tmp.setFromObject(mesh);
|
tmp.setFromObject(mesh);
|
||||||
if (tmp.isEmpty()) return;
|
if (tmp.isEmpty()) return;
|
||||||
if (!isFiniteVec3(tmp.min) || !isFiniteVec3(tmp.max)) return;
|
if (!isFiniteVec3(tmp.min) || !isFiniteVec3(tmp.max)) return;
|
||||||
|
|||||||
Reference in new issue
Block a user