troca do manequim
Signed-off-by: Rodrigo Bertoldo de Sousa Martins <rodrigo@martinsevieira.adv.br>
This commit is contained in:
1 parent
19bc92356f
commit
270cdbb965
2 files changed
+132
-26
No files matched your search
@@ -1,38 +1,134 @@
|
|||||||
// Boneco esquemático simples (mesmas proporções/posições usadas em
|
import { useMemo } from "react";
|
||||||
// PlanesPage) — extraído pra um componente próprio pra ser reaproveitado
|
import * as THREE from "three";
|
||||||
// também pelo módulo de Exame Físico, que precisa das MESMAS coordenadas
|
|
||||||
// de tórax/abdome/membros pra posicionar os pontos de exame corretamente.
|
const SKIN = "#8fa3c8";
|
||||||
export function Mannequin() {
|
|
||||||
const skin = "#8fa3c8";
|
// Perfil do tronco (raio, altura) de baixo pra cima — vira uma superfície
|
||||||
|
// lisa e arredondada ao ser revolvido (LatheGeometry), bem mais parecido
|
||||||
|
// com um corpo de verdade do que uma caixa. Não é uma malha esculpida (não
|
||||||
|
// dá forma às costelas, escápulas etc.) — é só um contorno correto o
|
||||||
|
// bastante pra indicar plano/direção sobre uma região reconhecível, e por
|
||||||
|
// ser 100% procedural (nenhum arquivo importado) continua leve e sem
|
||||||
|
// nenhuma questão de licença, ao contrário de baixar um basemesh pronto.
|
||||||
|
const TORSO_PROFILE: [radius: number, y: number][] = [
|
||||||
|
[0.13, 0.55], // base da pelve
|
||||||
|
[0.165, 0.63], // quadril (mais largo)
|
||||||
|
[0.145, 0.75],
|
||||||
|
[0.12, 0.9], // cintura (mais estreito)
|
||||||
|
[0.135, 1.0],
|
||||||
|
[0.16, 1.12],
|
||||||
|
[0.19, 1.25], // tórax/ombros (mais largo)
|
||||||
|
[0.175, 1.36],
|
||||||
|
[0.11, 1.46], // base do pescoço
|
||||||
|
[0.065, 1.52],
|
||||||
|
];
|
||||||
|
|
||||||
|
function useTorsoGeometry() {
|
||||||
|
return useMemo(() => {
|
||||||
|
const points = TORSO_PROFILE.map(([r, y]) => new THREE.Vector2(r, y));
|
||||||
|
const geo = new THREE.LatheGeometry(points, 20);
|
||||||
|
// O tronco humano é mais largo (ombro a ombro) do que profundo
|
||||||
|
// (peito a costas) — achatando o eixo Z do sólido de revolução (que
|
||||||
|
// sai circular) a gente sai de "cilindro" pra uma seção elíptica bem
|
||||||
|
// mais realista, sem precisar de um perfil 2D por eixo.
|
||||||
|
geo.scale(1, 1, 0.62);
|
||||||
|
return geo;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Limb({
|
||||||
|
x,
|
||||||
|
topY,
|
||||||
|
midY,
|
||||||
|
bottomY,
|
||||||
|
topRadius,
|
||||||
|
bottomRadius,
|
||||||
|
footZ,
|
||||||
|
}: {
|
||||||
|
x: number;
|
||||||
|
topY: number;
|
||||||
|
midY: number;
|
||||||
|
bottomY: number;
|
||||||
|
topRadius: number;
|
||||||
|
bottomRadius: number;
|
||||||
|
footZ?: number;
|
||||||
|
}) {
|
||||||
|
const upperLength = topY - midY;
|
||||||
|
const lowerLength = midY - bottomY;
|
||||||
return (
|
return (
|
||||||
<group>
|
<group>
|
||||||
|
{/* segmento superior (braço/coxa) */}
|
||||||
|
<mesh position={[x, midY + upperLength / 2, 0]}>
|
||||||
|
<capsuleGeometry args={[topRadius, Math.max(upperLength - topRadius * 1.6, 0.02), 4, 8]} />
|
||||||
|
<meshStandardMaterial color={SKIN} />
|
||||||
|
</mesh>
|
||||||
|
{/* segmento inferior (antebraço/perna), mais fino */}
|
||||||
|
<mesh position={[x, bottomY + lowerLength / 2, 0]}>
|
||||||
|
<capsuleGeometry args={[bottomRadius, Math.max(lowerLength - bottomRadius * 1.6, 0.02), 4, 8]} />
|
||||||
|
<meshStandardMaterial color={SKIN} />
|
||||||
|
</mesh>
|
||||||
|
{/* mão/pé — só uma cápsula achatada na ponta, o bastante pra não
|
||||||
|
"sumir" o membro no ar */}
|
||||||
|
<mesh
|
||||||
|
position={[x, bottomY - 0.03, footZ ?? 0]}
|
||||||
|
rotation={footZ ? [Math.PI / 2, 0, 0] : [0, 0, 0]}
|
||||||
|
>
|
||||||
|
<capsuleGeometry args={[bottomRadius * 0.85, footZ ? 0.09 : 0.05, 3, 6]} />
|
||||||
|
<meshStandardMaterial color={SKIN} />
|
||||||
|
</mesh>
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boneco esquemático usado só pela página de Planos Anatômicos, pra ter
|
||||||
|
// uma silhueta humana reconhecível (tronco arredondado + membros afunilados)
|
||||||
|
// sobre a qual desenhar planos de corte e setas de direção — não precisa
|
||||||
|
// (nem deveria, pelo tamanho) da fidelidade do dataset real do Z-Anatomy,
|
||||||
|
// que é usado no Atlas e no Exame Físico via <Scene>.
|
||||||
|
export function Mannequin() {
|
||||||
|
const torsoGeometry = useTorsoGeometry();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group>
|
||||||
|
{/* cabeça */}
|
||||||
<mesh position={[0, 1.65, 0]}>
|
<mesh position={[0, 1.65, 0]}>
|
||||||
<sphereGeometry args={[0.12]} />
|
<sphereGeometry args={[0.115, 20, 16]} />
|
||||||
<meshStandardMaterial color={skin} />
|
<meshStandardMaterial color={SKIN} />
|
||||||
</mesh>
|
</mesh>
|
||||||
<mesh position={[0, 1.2, 0]}>
|
{/* pescoço */}
|
||||||
<boxGeometry args={[0.4, 0.55, 0.22]} />
|
<mesh position={[0, 1.555, 0]}>
|
||||||
<meshStandardMaterial color={skin} />
|
<capsuleGeometry args={[0.055, 0.03, 3, 8]} />
|
||||||
|
<meshStandardMaterial color={SKIN} />
|
||||||
</mesh>
|
</mesh>
|
||||||
<mesh position={[0, 0.75, 0]}>
|
|
||||||
<boxGeometry args={[0.36, 0.3, 0.2]} />
|
{/* tronco (revolução do perfil acima) */}
|
||||||
<meshStandardMaterial color={skin} />
|
<mesh geometry={torsoGeometry}>
|
||||||
|
<meshStandardMaterial color={SKIN} />
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|
||||||
{[1, -1].map((side) => (
|
{[1, -1].map((side) => (
|
||||||
<group key={`arm-${side}`}>
|
<Limb
|
||||||
<mesh position={[side * 0.28, 1.1, 0]}>
|
key={`arm-${side}`}
|
||||||
<cylinderGeometry args={[0.05, 0.045, 0.55, 10]} />
|
x={side * 0.235}
|
||||||
<meshStandardMaterial color={skin} />
|
topY={1.42}
|
||||||
</mesh>
|
midY={1.08}
|
||||||
</group>
|
bottomY={0.83}
|
||||||
|
topRadius={0.052}
|
||||||
|
bottomRadius={0.042}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{[1, -1].map((side) => (
|
{[1, -1].map((side) => (
|
||||||
<group key={`leg-${side}`}>
|
<Limb
|
||||||
<mesh position={[side * 0.11, 0.3, 0]}>
|
key={`leg-${side}`}
|
||||||
<cylinderGeometry args={[0.065, 0.05, 0.65, 10]} />
|
x={side * 0.09}
|
||||||
<meshStandardMaterial color={skin} />
|
topY={0.58}
|
||||||
</mesh>
|
midY={0.3}
|
||||||
</group>
|
bottomY={0.02}
|
||||||
|
topRadius={0.075}
|
||||||
|
bottomRadius={0.055}
|
||||||
|
footZ={0.09}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -51,6 +51,15 @@ const AXIS_GROUPS: AxisGroup[] = [
|
|||||||
{ label: "Esquerda (do paciente)", from: [0, 0.9, 0], to: [0.7, 0.9, 0], color: "#a78bfa" },
|
{ label: "Esquerda (do paciente)", from: [0, 0.9, 0], to: [0.7, 0.9, 0], color: "#a78bfa" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "cranial-caudal",
|
||||||
|
label: "Cranial / Caudal (na coluna)",
|
||||||
|
hint: "Mesmo sentido de Superior/Inferior, mas usado sobretudo pra estruturas ao longo do eixo da cabeça à cauda (coluna, tronco, SNC)",
|
||||||
|
axes: [
|
||||||
|
{ label: "Cranial", from: [0, 0.9, -0.12], to: [0, 1.65, -0.12], color: "#c084fc" },
|
||||||
|
{ label: "Caudal", from: [0, 0.9, -0.12], to: [0, 0.05, -0.12], color: "#c084fc" },
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "medial-lateral",
|
key: "medial-lateral",
|
||||||
label: "Medial / Lateral (no braço)",
|
label: "Medial / Lateral (no braço)",
|
||||||
@@ -134,6 +143,7 @@ export default function PlanesPage() {
|
|||||||
});
|
});
|
||||||
const [visibleAxisGroups, setVisibleAxisGroups] = useState<Record<string, boolean>>({
|
const [visibleAxisGroups, setVisibleAxisGroups] = useState<Record<string, boolean>>({
|
||||||
principal: true,
|
principal: true,
|
||||||
|
"cranial-caudal": false,
|
||||||
"medial-lateral": false,
|
"medial-lateral": false,
|
||||||
"proximal-distal": false,
|
"proximal-distal": false,
|
||||||
"superficial-profundo": false,
|
"superficial-profundo": false,
|
||||||
|
|||||||
Reference in new issue
Block a user