diff --git a/frontend/public/tegumentar/skin-cross-section.jpg b/frontend/public/tegumentar/skin-cross-section.jpg index 487c902..cfba9cc 100644 Binary files a/frontend/public/tegumentar/skin-cross-section.jpg and b/frontend/public/tegumentar/skin-cross-section.jpg differ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4f1ad2b..759ed59 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -3,6 +3,7 @@ import { Layout } from "./components/Layout.js"; import AtlasPage from "./routes/AtlasPage.js"; import PlanesPage from "./routes/PlanesPage.js"; import QuizPage from "./routes/QuizPage.js"; +import PhysicalExamPage from "./routes/PhysicalExamPage.js"; export default function App() { return ( @@ -10,6 +11,7 @@ export default function App() { }> } /> } /> + } /> } /> diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 1999a27..92402a9 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -17,6 +17,9 @@ export function Layout() { Planos e Eixos + + Exame Físico + Quiz diff --git a/frontend/src/components/SkinDiagram.tsx b/frontend/src/components/SkinDiagram.tsx index ac9831f..1e24a50 100644 --- a/frontend/src/components/SkinDiagram.tsx +++ b/frontend/src/components/SkinDiagram.tsx @@ -61,7 +61,7 @@ export function SkinDiagram({ bySlug }: { bySlug: Map }) {

- Imagem de domínio público — National Cancer Institute / SEER Training Modules. + Imagem de domínio público (National Cancer Institute / SEER Training Modules), legendas traduzidas para PT-BR.

diff --git a/frontend/src/components/viewer/Mannequin.tsx b/frontend/src/components/viewer/Mannequin.tsx new file mode 100644 index 0000000..482621d --- /dev/null +++ b/frontend/src/components/viewer/Mannequin.tsx @@ -0,0 +1,39 @@ +// Boneco esquemático simples (mesmas proporções/posições usadas em +// PlanesPage) — extraído pra um componente próprio pra ser reaproveitado +// 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. +export function Mannequin() { + const skin = "#8fa3c8"; + return ( + + + + + + + + + + + + + + {[1, -1].map((side) => ( + + + + + + + ))} + {[1, -1].map((side) => ( + + + + + + + ))} + + ); +} diff --git a/frontend/src/data/physicalExam.ts b/frontend/src/data/physicalExam.ts new file mode 100644 index 0000000..0476320 --- /dev/null +++ b/frontend/src/data/physicalExam.ts @@ -0,0 +1,317 @@ +// Pontos do módulo de Exame Físico — coordenadas no MESMO sistema do +// boneco esquemático (ver components/viewer/Mannequin.tsx): tórax é uma +// caixa centrada em (0, 1.2, 0), abdome em (0, 0.75, 0), braços/pernas são +// cilindros em x = ±0.28 / ±0.11. Convenção usada aqui: x negativo = lado +// DIREITO do paciente, x positivo = lado ESQUERDO do paciente (o boneco é +// visto de frente, então a direita do paciente fica à esquerda de quem +// olha — por isso cada descrição já diz o lado por extenso, pra não +// depender de decorar a convenção). +export type ExamTechnique = "inspecao" | "palpacao" | "percussao" | "ausculta"; + +export const TECHNIQUES: { key: ExamTechnique; label: string; color: string }[] = [ + { key: "inspecao", label: "Inspeção", color: "#f2c14e" }, + { key: "palpacao", label: "Palpação", color: "#4f7cff" }, + { key: "percussao", label: "Percussão", color: "#a78bfa" }, + { key: "ausculta", label: "Ausculta", color: "#34d399" }, +]; + +export interface ExamPoint { + id: string; + name: string; + system: string; + technique: ExamTechnique; + position: [number, number, number]; + howTo: string; + whatFor: string; +} + +export const EXAM_POINTS: ExamPoint[] = [ + // ---- Geral ---- + { + id: "ectoscopia", + name: "Ectoscopia Geral", + system: "Geral", + technique: "inspecao", + position: [0, 1.65, 0.1], + howTo: "Observe o paciente por completo antes de tocar: estado geral, nível de consciência, postura, fácies, coloração da pele e mucosas, hidratação.", + whatFor: "Primeira etapa de qualquer exame físico — muitas pistas diagnósticas (icterícia, cianose, palidez, dispneia) aparecem só de olhar, antes de qualquer manobra.", + }, + + // ---- Cardiovascular: ausculta (focos clássicos) ---- + { + id: "foco-aortico", + name: "Foco Aórtico", + system: "Cardiovascular", + technique: "ausculta", + position: [-0.05, 1.4, 0.12], + howTo: "2º espaço intercostal, à direita do esterno. Ausculte com o diafragma do estetoscópio, paciente sentado ou deitado.", + whatFor: "Melhor foco para a valva aórtica — sopros de estenose/insuficiência aórtica costumam ser mais audíveis aqui.", + }, + { + id: "foco-pulmonar", + name: "Foco Pulmonar", + system: "Cardiovascular", + technique: "ausculta", + position: [0.05, 1.4, 0.12], + howTo: "2º espaço intercostal, à esquerda do esterno.", + whatFor: "Melhor foco para a valva pulmonar — sopros de estenose/insuficiência pulmonar e desdobramento de B2.", + }, + { + id: "foco-aortico-acessorio", + name: "Foco Aórtico Acessório (Erb)", + system: "Cardiovascular", + technique: "ausculta", + position: [0.05, 1.32, 0.12], + howTo: "3º espaço intercostal, à esquerda do esterno.", + whatFor: "Bom ponto para captar sopros diastólicos de insuficiência aórtica, que às vezes irradiam melhor pra cá do que pro foco aórtico clássico.", + }, + { + id: "foco-tricuspide", + name: "Foco Tricúspide", + system: "Cardiovascular", + technique: "ausculta", + position: [0.05, 1.22, 0.12], + howTo: "4º-5º espaço intercostal, na borda esternal esquerda baixa.", + whatFor: "Melhor foco para a valva tricúspide — sopros de insuficiência/estenose tricúspide.", + }, + { + id: "foco-mitral", + name: "Foco Mitral (Ápice)", + system: "Cardiovascular", + technique: "ausculta", + position: [0.14, 1.16, 0.13], + howTo: "5º espaço intercostal esquerdo, na linha hemiclavicular (região do ictus cordis).", + whatFor: "Melhor foco para a valva mitral — sopro de estenose mitral (ruflar diastólico) e insuficiência mitral (sopro holossistólico).", + }, + { + id: "ictus-cordis", + name: "Ictus Cordis (Choque de Ponta)", + system: "Cardiovascular", + technique: "palpacao", + position: [0.16, 1.18, 0.135], + howTo: "Palpe com a polpa dos dedos (ou a região tenar) o 5º espaço intercostal esquerdo, linha hemiclavicular.", + whatFor: "Localiza o ápice do coração; desvio lateral ou impulso muito forte sugere cardiomegalia/hipertrofia ventricular.", + }, + { + id: "fremito-precordial", + name: "Frêmito Precordial", + system: "Cardiovascular", + technique: "palpacao", + position: [0.08, 1.28, 0.12], + howTo: "Apoie a palma da mão sobre o precórdio e sinta uma vibração (\"gato ronronando\").", + whatFor: "Frêmito = sopro cardíaco intenso o bastante para ser sentido, geralmente indica lesão valvar significativa.", + }, + + // ---- Respiratório ---- + { + id: "apice-pulmonar-d", + name: "Ápice Pulmonar Direito", + system: "Respiratório", + technique: "ausculta", + position: [-0.12, 1.42, 0.11], + howTo: "Ausculte logo acima da clavícula direita, pedindo ao paciente para respirar fundo pela boca.", + whatFor: "Murmúrio vesicular reduzido/ausente ou ruídos adventícios (estertores, sibilos) localizados aqui sugerem acometimento do ápice pulmonar direito.", + }, + { + id: "apice-pulmonar-e", + name: "Ápice Pulmonar Esquerdo", + system: "Respiratório", + technique: "ausculta", + position: [0.12, 1.42, 0.11], + howTo: "Mesma manobra do lado esquerdo, logo acima da clavícula esquerda.", + whatFor: "Comparação bilateral é essencial — assimetria entre os dois ápices é o achado mais importante.", + }, + { + id: "campo-medio-d", + name: "Campo Médio Direito", + system: "Respiratório", + technique: "ausculta", + position: [-0.15, 1.22, 0.1], + howTo: "Ausculte na linha hemiclavicular/axilar direita, altura do 4º espaço intercostal.", + whatFor: "Zona onde sopros tubários e estertores de pneumonia em lobo médio costumam aparecer primeiro.", + }, + { + id: "campo-medio-e", + name: "Campo Médio Esquerdo", + system: "Respiratório", + technique: "ausculta", + position: [0.15, 1.22, 0.1], + howTo: "Mesma altura, do lado esquerdo.", + whatFor: "Comparação bilateral do murmúrio vesicular e de ruídos adventícios.", + }, + { + id: "base-pulmonar-d", + name: "Base Pulmonar Direita", + system: "Respiratório", + technique: "ausculta", + position: [-0.17, 1.0, 0.08], + howTo: "Ausculte nas bases (região inferior do tórax, de preferência também pelas costas).", + whatFor: "Local clássico de estertores crepitantes (insuficiência cardíaca, pneumonia de base) e de derrame pleural (murmúrio abolido).", + }, + { + id: "base-pulmonar-e", + name: "Base Pulmonar Esquerda", + system: "Respiratório", + technique: "ausculta", + position: [0.17, 1.0, 0.08], + howTo: "Mesma região, do lado esquerdo.", + whatFor: "Comparação bilateral das bases — assimetria é o achado mais valioso.", + }, + { + id: "expansibilidade-toracica", + name: "Expansibilidade Torácica", + system: "Respiratório", + technique: "palpacao", + position: [0, 1.05, -0.12], + howTo: "Coloque as mãos espalmadas nas costas do paciente, polegares próximos à linha média, e peça para respirar fundo — observe se os polegares se afastam simetricamente.", + whatFor: "Redução da expansibilidade de um lado sugere derrame pleural, atelectasia ou pneumotórax naquele hemitórax.", + }, + { + id: "fremito-toracovocal", + name: "Frêmito Toracovocal", + system: "Respiratório", + technique: "palpacao", + position: [0.1, 1.15, 0.11], + howTo: "Apoie a borda ulnar das mãos no tórax e peça ao paciente para dizer \"trinta e três\" repetidamente, comparando os dois lados.", + whatFor: "Aumentado em condensações (pneumonia); diminuído/abolido em derrame pleural, pneumotórax ou DPOC.", + }, + { + id: "percussao-pulmonar", + name: "Percussão dos Campos Pulmonares", + system: "Respiratório", + technique: "percussao", + position: [-0.1, 1.15, 0.11], + howTo: "Percuta comparando os dois hemitóraxes, de cima para baixo, com o dedo plexímetro bem apoiado entre os espaços intercostais.", + whatFor: "Som claro pulmonar normal; hipertimpanismo sugere pneumotórax; macicez/submacicez sugere derrame pleural ou consolidação.", + }, + + // ---- Abdominal ---- + { + id: "inspecao-abdominal", + name: "Inspeção Abdominal", + system: "Abdominal", + technique: "inspecao", + position: [0, 0.82, 0.11], + howTo: "Com o paciente deitado e relaxado, observe forma, simetria, distensão, cicatrizes, circulação colateral e peristaltismo visível.", + whatFor: "Abdome distendido, assimétrico ou com peristaltismo visível pode indicar obstrução, massa ou ascite.", + }, + { + id: "ausculta-abdominal", + name: "Ausculta dos Ruídos Hidroaéreos", + system: "Abdominal", + technique: "ausculta", + position: [0, 0.75, 0.11], + howTo: "Ausculte ANTES de palpar ou percutir (essas manobras alteram o peristaltismo) — um ponto já basta, ouça por 1-2 minutos se estiver avaliando ausência de ruídos.", + whatFor: "Ruídos aumentados sugerem obstrução inicial; ausência de ruídos (\"silêncio abdominal\") é sinal de íleo paralítico/abdome agudo.", + }, + { + id: "percussao-abdominal", + name: "Percussão Abdominal (Timpanismo)", + system: "Abdominal", + technique: "percussao", + position: [-0.08, 0.72, 0.11], + howTo: "Percuta os 4 quadrantes — o timpanismo (som mais alto, de tambor) é normal, pela presença de gás nas alças intestinais.", + whatFor: "Macicez onde deveria haver timpanismo sugere massa, ascite ou visceromegalia; timpanismo excessivo sugere distensão gasosa.", + }, + { + id: "macicez-hepatica", + name: "Delimitação da Loja Hepática", + system: "Abdominal", + technique: "percussao", + position: [-0.12, 0.85, 0.11], + howTo: "Percuta de cima para baixo na linha hemiclavicular direita, do som claro pulmonar até encontrar macicez (borda superior do fígado), depois do timpanismo abdominal até a macicez (borda inferior).", + whatFor: "Estima o tamanho do fígado; um espaço de Traube maciço ou uma loja hepática maior que o esperado sugere hepatomegalia.", + }, + { + id: "palpacao-superficial", + name: "Palpação Superficial", + system: "Abdominal", + technique: "palpacao", + position: [0.08, 0.78, 0.105], + howTo: "Com a mão relaxada, palpe levemente todos os quadrantes, avaliando tensão da parede e sensibilidade — sempre deixando a área dolorosa por último.", + whatFor: "Detecta defesa muscular voluntária/involuntária e localiza dor antes de aprofundar a palpação.", + }, + { + id: "palpacao-profunda", + name: "Palpação Profunda", + system: "Abdominal", + technique: "palpacao", + position: [0.08, 0.7, 0.105], + howTo: "Com as duas mãos sobrepostas, palpe mais profundamente para identificar massas, visceromegalias e pontos dolorosos específicos.", + whatFor: "Identifica massas abdominais, contornos de vísceras e sinais de irritação peritoneal (dor à descompressão brusca).", + }, + { + id: "ponto-mcburney", + name: "Ponto de McBurney", + system: "Abdominal", + technique: "palpacao", + position: [-0.13, 0.62, 0.1], + howTo: "Localize o ponto entre o terço lateral e os dois terços mediais da linha que liga a espinha ilíaca ântero-superior direita ao umbigo; palpe e avalie a descompressão brusca.", + whatFor: "Dor à palpação (e principalmente à descompressão brusca) neste ponto é clássica de apendicite aguda.", + }, + { + id: "sinal-murphy", + name: "Sinal de Murphy", + system: "Abdominal", + technique: "palpacao", + position: [-0.13, 0.85, 0.105], + howTo: "Palpe sob o rebordo costal direito e peça ao paciente para inspirar fundo.", + whatFor: "Sinal positivo = parada súbita da inspiração por dor — sugere colecistite aguda.", + }, + { + id: "palpacao-hepatica", + name: "Palpação Hepática (Borda do Fígado)", + system: "Abdominal", + technique: "palpacao", + position: [-0.15, 0.82, 0.11], + howTo: "Com a mão em garra sob o rebordo costal direito, peça para o paciente inspirar fundo e sinta a borda hepática \"escapar\" sob os dedos.", + whatFor: "Avalia tamanho, consistência e borda do fígado — borda romba/endurecida sugere hepatopatia crônica.", + }, + { + id: "palpacao-baco", + name: "Palpação do Baço", + system: "Abdominal", + technique: "palpacao", + position: [0.15, 0.85, 0.1], + howTo: "Com o paciente em decúbito lateral direito, palpe sob o rebordo costal esquerdo durante a inspiração profunda.", + whatFor: "O baço normal não é palpável — se for palpável, geralmente indica esplenomegalia.", + }, + + // ---- Pulsos periféricos ---- + { + id: "pulso-carotideo", + name: "Pulso Carotídeo", + system: "Vascular", + technique: "palpacao", + position: [-0.06, 1.5, 0.08], + howTo: "Palpe suavemente (nunca os dois lados ao mesmo tempo) na borda anterior do músculo esternocleidomastóideo, no pescoço.", + whatFor: "Avalia amplitude e ritmo do pulso central; útil também pra avaliar sopros carotídeos com a ausculta.", + }, + { + id: "pulso-radial", + name: "Pulso Radial", + system: "Vascular", + technique: "palpacao", + position: [-0.28, 0.83, 0.03], + howTo: "Palpe com 2-3 dedos na face anterior do punho, lateralmente (lado do polegar).", + whatFor: "Pulso periférico mais usado na prática — avalia frequência, ritmo, amplitude e simetria entre os dois lados.", + }, + { + id: "pulso-femoral", + name: "Pulso Femoral", + system: "Vascular", + technique: "palpacao", + position: [-0.1, 0.62, 0.08], + howTo: "Palpe na região inguinal, no ponto médio entre a espinha ilíaca ântero-superior e a sínfise púbica.", + whatFor: "Assimetria ou atraso entre pulso femoral e radial (\"pulso em atraso\") sugere coarctação de aorta.", + }, + { + id: "pulso-pedioso", + name: "Pulso Pedioso", + system: "Vascular", + technique: "palpacao", + position: [-0.11, -0.02, 0.06], + howTo: "Palpe no dorso do pé, lateralmente ao tendão do extensor longo do hálux.", + whatFor: "Ausência sugere doença arterial periférica — compare sempre os dois pés.", + }, +]; diff --git a/frontend/src/routes/PhysicalExamPage.tsx b/frontend/src/routes/PhysicalExamPage.tsx new file mode 100644 index 0000000..455b963 --- /dev/null +++ b/frontend/src/routes/PhysicalExamPage.tsx @@ -0,0 +1,197 @@ +import { useMemo, useState } from "react"; +import { Canvas } from "@react-three/fiber"; +import { CameraControls, Html } from "@react-three/drei"; +import { Mannequin } from "../components/viewer/Mannequin.js"; +import { EXAM_POINTS, TECHNIQUES, type ExamTechnique } from "../data/physicalExam.js"; + +// Módulo de estudo pro exame físico: o mesmo boneco esquemático do módulo +// de Planos e Eixos, agora com pontos clicáveis mostrando ONDE e COMO se +// faz cada manobra (inspeção/palpação/percussão/ausculta) — pensado pra +// quem está estudando propedêutica, não pra troca do atlas 3D real (que +// seria overkill pra um boneco de pontos de exame, que não muda por +// paciente). +export default function PhysicalExamPage() { + const [activeTechniques, setActiveTechniques] = useState>( + () => new Set(TECHNIQUES.map((t) => t.key)), + ); + const [selectedId, setSelectedId] = useState(null); + const [leftOpen, setLeftOpen] = useState(false); + const [rightOpen, setRightOpen] = useState(false); + + function toggleTechnique(key: ExamTechnique) { + setActiveTechniques((prev) => { + const next = new Set(prev); + if (next.has(key)) next.delete(key); + else next.add(key); + return next; + }); + } + + const visiblePoints = useMemo( + () => EXAM_POINTS.filter((p) => activeTechniques.has(p.technique)), + [activeTechniques], + ); + + const selected = EXAM_POINTS.find((p) => p.id === selectedId) ?? null; + const techniqueColor = (t: ExamTechnique) => TECHNIQUES.find((x) => x.key === t)?.color ?? "#fff"; + + return ( +
+ + + {(leftOpen || rightOpen) && ( +
{ + setLeftOpen(false); + setRightOpen(false); + }} + /> + )} + +
+ + + + + + + + + + + {visiblePoints.map((p) => { + const isSelected = p.id === selectedId; + const color = techniqueColor(p.technique); + return ( + +
+ + +
+ ); +} diff --git a/frontend/src/routes/PlanesPage.tsx b/frontend/src/routes/PlanesPage.tsx index 8ccc49b..71090c3 100644 --- a/frontend/src/routes/PlanesPage.tsx +++ b/frontend/src/routes/PlanesPage.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import { Canvas } from "@react-three/fiber"; import { CameraControls, Html, Line } from "@react-three/drei"; import * as THREE from "three"; +import { Mannequin } from "../components/viewer/Mannequin.js"; interface PlaneDef { key: "sagittal" | "coronal" | "transverse"; @@ -38,42 +39,6 @@ const GLOSSARY: { term: string; def: string }[] = [ { term: "Plano Transversal (Axial)", def: "Divide o corpo em partes superior e inferior." }, ]; -function Mannequin() { - const skin = "#8fa3c8"; - return ( - - - - - - - - - - - - - - {[1, -1].map((side) => ( - - - - - - - ))} - {[1, -1].map((side) => ( - - - - - - - ))} - - ); -} - export default function PlanesPage() { const [visible, setVisible] = useState>({ sagittal: true,