7.2 KiB
Atlas 3D de Anatomia
Plataforma web dockerizada para estudo de anatomia com um visualizador 3D interativo, camadas por sistema, dissecção virtual (plano de corte), busca/navegação por região e um modo quiz com repetição espaçada (SM-2).
Aviso importante sobre o conteúdo 3D
O dataset aberto BodyParts3D (lifesciencedb.jp/bp3d, CC BY-SA 2.1 Japan) não oferece um endpoint de download programável — os arquivos só são acessíveis por uma aplicação de visualização legada, exigindo download manual estrutura por estrutura. Por isso, o dataset semente incluído aqui (data-pipeline/seed/build-manifest.mjs) usa geometria paramétrica simples (caixas/esferas/cilindros) posicionada nas proporções e localizações anatômicas corretas de um corpo humano padrão — os nomes, sistemas, regiões e relações são anatomia real; só a malha 3D é esquemática, não fotorrealista.
A plataforma (frontend, backend, banco, Docker, todas as interações) está completa e pronta para receber malhas 3D reais. Para evoluir para segmentações reais do BodyParts3D (ou do projeto Z-Anatomy, que reempacota o mesmo dataset em Blender com nomes mais organizados), veja "Expandindo o dataset" abaixo — nenhum código do frontend/backend precisa mudar, só os arquivos .glb e o manifesto.
Arquitetura
Este compose foi adaptado para rodar como um stack separado dentro da infraestrutura existente do host cloud-lua-net, que já tem uma rede Docker externa dockernet e um container nginx central (com HTTP/3) que lê vhosts manuais em docker/nginx/sites/*.conf, mais um par nginx-gen + acme-companion que emite certificados Let's Encrypt via LETSENCRYPT_HOST.
container "nginx" (proxy principal do host, HTTP/3, já existente)
│ vhost manual: docker/nginx/sites/30-atlas.conf
│ proxy_pass → IP fixo do container anatomia-web em dockernet
▼
container "anatomia-web" (nginx interno deste projeto)
├─ serve o build estático do frontend (SPA)
├─ /api/* → proxy → container "anatomia-api"
└─ /models/* → proxy → container "anatomia-api"
container "anatomia-api" (Fastify + Prisma) ──▶ container "anatomia-db" (Postgres)
Não é publicada nenhuma porta no host — anatomia-web só é alcançável dentro da rede dockernet, no mesmo padrão já usado para outros apps desse compose (ex: frontend/backend do ticketz, nextcloud). Isso significa que este projeto roda como um docker compose próprio, independente do compose gigante do host — ele só precisa se conectar à mesma rede externa dockernet, o que já é suportado nativamente pelo Docker (containers de projetos Compose diferentes na mesma rede se enxergam normalmente).
Como rodar
# no host cloud-lua-net, dentro da pasta deste projeto (ex: /home/docker/anatomia)
cp .env.example .env
# Confirme que o IP escolhido está livre antes de subir:
docker network inspect dockernet | grep -A2 IPv4Address
# ajuste ANATOMIA_WEB_IP no .env se 10.15.0.93 já estiver em uso
docker compose up -d --build anatomia-db anatomia-api anatomia-web
docker compose run --rm anatomia-pipeline # gera os modelos GLB semente e popula o banco
Depois, no host, copie deploy/nginx-vhost-atlas.conf para /home/docker/docker/nginx/sites/30-atlas.conf (ajustando o nome do arquivo para não colidir com os já existentes) e garanta que o DNS de atlas.martinsevieira.adv.br aponte para este servidor. O container anatomia-web já sobe com LETSENCRYPT_HOST=atlas.martinsevieira.adv.br, então o acme-companion existente no host emite o certificado automaticamente assim que o vhost estiver ativo e respondendo ao desafio ACME (veja os comentários no próprio arquivo deploy/nginx-vhost-atlas.conf para os detalhes/pré-requisitos).
Para reprocessar o dataset semente depois de editar data-pipeline/seed/build-manifest.mjs, rode docker compose run --rm anatomia-pipeline novamente.
Rodando fora desse host (dev local, sem "dockernet")
O docker-compose.yml deste projeto depende da rede externa dockernet e de um IP fixo, então para testar localmente em outra máquina crie a rede primeiro e ajuste/remova o ipv4_address, ou publique uma porta temporária no serviço anatomia-web, por exemplo:
docker network create dockernet # numa máquina onde ela ainda não existe
E adicione temporariamente ao serviço anatomia-web no docker-compose.yml:
ports:
- "8080:80"
Depois docker compose up -d --build anatomia-db anatomia-api anatomia-web && docker compose run --rm anatomia-pipeline e acesse http://localhost:8080.
Estrutura do projeto
frontend/— React + TypeScript + Vite + react-three-fiber (visualizador 3D), Zustand, Tailwind.backend/— Fastify + TypeScript + Prisma (API REST + arquivos.glbestáticos).data-pipeline/— scripts que geram os modelos 3D semente e populam o banco (roda como serviço Docker one-off, não fica sempre ligado).nginx/nginx.conf— nginx interno que serve o frontend e faz proxy para a API.
Funcionalidades
- Camadas por sistema: esquelético, muscular, nervoso, cardiovascular, respiratório, tegumentar — ligar/desligar independentemente.
- Labels clicáveis: clique em qualquer estrutura no modelo 3D para ver nome, sistema, região e descrição.
- Busca e navegação: busca fuzzy + árvore por sistema/região; selecionar uma estrutura move a câmera até ela.
- Dissecção virtual: plano de corte deslizante nos três planos anatômicos (sagital, coronal, transversal).
- Módulo de planos e eixos anatômicos (
/planos-anatomicos): manequim simples com os três planos de secção, eixos de direção rotulados e glossário de termos (anterior/posterior, medial/lateral, proximal/distal etc.). - Quiz com repetição espaçada (
/quiz): pede para localizar a estrutura no modelo 3D; usa o algoritmo SM-2 para agendar revisões.
Expandindo o dataset
O schema (backend/prisma/schema.prisma) já suporta qualquer sistema anatômico adicional (digestório, urinário, endócrino, reprodutor, linfático, sentidos) — basta adicionar dados, sem mudar código:
- Estruturas novas com geometria esquemática: edite
data-pipeline/seed/build-manifest.mjs, adicione entradas comadd(...)/addBilateral(...)e rode o pipeline novamente. - Malhas 3D reais (BodyParts3D/Z-Anatomy): exporte os
.objdesejados (manualmente pelo visualizador do BodyParts3D, ou do projeto Blender Z-Anatomy), converta para.glb(obj2gltfou exportação direta do Blender), e substitua a geração procedural daquela estrutura por esse arquivo real — nomeie o node/mesh igual aoslugjá usado no manifesto para que clique, quiz e busca continuem funcionando sem alterações de código.
Desenvolvimento local (sem Docker)
# Backend
cd backend && npm install
export DATABASE_URL=postgresql://anatomia:anatomia@localhost:5432/anatomia
npx prisma migrate deploy
npm run dev
# Frontend (em outro terminal)
cd frontend && npm install
npm run dev # http://localhost:5173, faz proxy de /api e /models para :3333
# Dataset semente (em outro terminal, com DATABASE_URL exportada)
cd data-pipeline && npm install
npm run build:seed-models
npm run seed:db