diff --git a/data-pipeline/package.json b/data-pipeline/package.json index 709f0c7..3c61d78 100644 --- a/data-pipeline/package.json +++ b/data-pipeline/package.json @@ -8,9 +8,10 @@ "convert:models": "node scripts/convert-fbx.mjs", "extract:manifest": "node scripts/extract-manifest.mjs", "build:tegumentar": "node seed/build-manifest.mjs", + "build:microscopy": "node seed/build-manifest-microscopy.mjs", "merge:manifest": "node scripts/merge-manifests.mjs", "seed:db": "node scripts/seed-db.mjs", - "run:all": "npm run fetch:models && npm run convert:models && npm run extract:manifest && npm run build:tegumentar && npm run merge:manifest && npm run seed:db" + "run:all": "npm run fetch:models && npm run convert:models && npm run extract:manifest && npm run build:tegumentar && npm run build:microscopy && npm run merge:manifest && npm run seed:db" }, "dependencies": { "@gltf-transform/core": "^4.0.4", diff --git a/data-pipeline/scripts/merge-manifests.mjs b/data-pipeline/scripts/merge-manifests.mjs index 1ecaf17..037c430 100644 --- a/data-pipeline/scripts/merge-manifests.mjs +++ b/data-pipeline/scripts/merge-manifests.mjs @@ -7,12 +7,13 @@ const SEED_DIR = path.resolve(__dirname, "../seed"); const real = JSON.parse(readFileSync(path.join(SEED_DIR, "manifest-real.json"), "utf-8")); const tegumentar = JSON.parse(readFileSync(path.join(SEED_DIR, "manifest-tegumentar.json"), "utf-8")); +const microscopy = JSON.parse(readFileSync(path.join(SEED_DIR, "manifest-microscopy.json"), "utf-8")); -const structures = [...real.structures, ...tegumentar.structures]; +const structures = [...real.structures, ...tegumentar.structures, ...microscopy.structures]; writeFileSync( path.join(SEED_DIR, "manifest.json"), JSON.stringify({ structures }, null, 2), ); -console.log(`manifest.json final: ${structures.length} estruturas (${real.structures.length} reais + ${tegumentar.structures.length} tegumentar).`); +console.log(`manifest.json final: ${structures.length} estruturas (${real.structures.length} reais + ${tegumentar.structures.length} tegumentar + ${microscopy.structures.length} citologia/histologia).`); diff --git a/data-pipeline/seed/build-manifest-microscopy.mjs b/data-pipeline/seed/build-manifest-microscopy.mjs new file mode 100644 index 0000000..b59e140 --- /dev/null +++ b/data-pipeline/seed/build-manifest-microscopy.mjs @@ -0,0 +1,203 @@ +// Gera manifest-microscopy.json (sistemas "citologia" e "histologia"). +// +// MESMA DECISÃO já tomada pro tegumentar (ver build-manifest.mjs): não +// existe malha 3D real e aberta derivada de microscopia (nem de célula, +// nem de tecido histológico — confirmado pesquisando: Allen Institute for +// Cell Science só distribui dado volumétrico bruto (OME-Zarr/TIFF), não +// malha pronta; não existe um "Z-Anatomy da histologia" de código aberto). +// Fabricar formas geométricas (esferas pra núcleo, cilindros pra +// mitocôndria etc.) seria exatamente o "método não realístico" que se +// queria evitar. Em vez disso: FOTOMICROGRAFIAS REAIS (não ilustrações), +// de domínio público ou CC, com hotspots — mesmo padrão do tegumentar. +// +// Curadoria das imagens (baixadas de commons.wikimedia.org, verificadas +// visualmente uma a uma pra confirmar que são fotos reais e não diagramas/ +// ilustrações — vários candidatos populares como "Blausen ..." ou +// "Typical human cell" foram DESCARTADOS por serem ilustrações, não fotos): +// +// citologia/nucleo-celular-met.png +// Micrografia eletrônica de transmissão (MET) real de um núcleo +// celular. Domínio público. https://commons.wikimedia.org/wiki/File:Micrograph_of_a_cell_nucleus.png +// +// histologia/epitelio-simples-colunar.jpg +// Mucosa gástrica normal, H&E. Autor: Nephron. CC BY-SA 3.0. +// https://commons.wikimedia.org/wiki/File:Normal_gastric_mucosa_intermed_mag.jpg +// histologia/epitelio-estratificado-pavimentoso.jpg +// Biópsia normal, H&E. Autor: Samir@enwiki. CC BY-SA 3.0. +// https://commons.wikimedia.org/wiki/File:Tinci%C3%B3n_hematoxilina-eosina.jpg +// histologia/tecido-adiposo.jpg +// Tecido adiposo em corte de parafina (lipídios dissolvidos no +// processamento, por isso os adipócitos aparecem "vazios"). CC BY-SA 3.0. +// https://commons.wikimedia.org/wiki/File:Yellow_adipose_tissue_in_paraffin_section_-_lipids_washed_out.jpg +// histologia/cartilagem-hialina.jpg +// Zonas da cartilagem articular (Bautista et al.). CC0. +// https://commons.wikimedia.org/wiki/File:Histology_of_articular_cartilage_zones.jpg +// histologia/osso-compacto.jpg +// Osso desgastado (ground section), sistemas de Havers. Autor: +// Athikhun.suw. CC BY-SA 4.0. +// https://commons.wikimedia.org/wiki/File:Compact_bone_histology_2014.jpg +// histologia/tecido-muscular.jpg +// Comparação real dos 3 tipos de músculo (esquelético/liso/cardíaco), +// OpenStax College. CC BY 4.0. +// https://commons.wikimedia.org/wiki/File:414_Skeletal_Smooth_Cardiac.jpg +// histologia/tecido-nervoso.jpg +// Medula espinhal de coelho. Autor: John Alan Elson. CC BY-SA 4.0. +// https://commons.wikimedia.org/wiki/File:Rabbitspinalcord100x1.jpg +// +// As posições dos hotspots (x/y em % de cada imagem) ficam em +// frontend/src/data/microscopyHotspots.ts — só apresentação, como no +// tegumentar. Este arquivo só gera os DADOS da estrutura (nome, descrição). +import { writeFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const structures = []; + +function add(system, image, slug, namePt, nameEn, description, region = []) { + structures.push({ + system, + slug, + namePt, + nameEn, + synonyms: [], + description, + regionPath: [system === "citologia" ? "Citologia" : "Histologia", ...region], + // Sem malha 3D — o caminho da fotomicrografia (servida como asset + // estático do frontend, igual à imagem do tegumentar) fica direto no + // campo modelFile; MicroscopyDiagram.tsx usa isso pra saber em qual + // imagem desenhar o hotspot de cada estrutura. + modelFile: image, + meshName: slug, + }); +} + +// --------------------------------------------------------------------- +// CITOLOGIA — núcleo celular (MET real) +// --------------------------------------------------------------------- +const CEL = "/citologia/nucleo-celular-met.png"; +add("citologia", CEL, "nucleo-citologia", "Núcleo Celular", "Cell Nucleus", + "Maior organela da célula eucariótica; guarda o DNA organizado em cromatina e comanda toda a síntese proteica ao transcrever RNA mensageiro."); +add("citologia", CEL, "envoltorio-nuclear", "Envoltório Nuclear", "Nuclear Envelope", + "Dupla membrana que separa o núcleo do citoplasma, perfurada por poros nucleares que controlam a troca de moléculas (RNA sai, proteínas regulatórias entram) entre os dois compartimentos."); +add("citologia", CEL, "cromatina", "Cromatina", "Chromatin", + "DNA associado a proteínas histonas, disperso no núcleo entre divisões celulares; as regiões mais condensadas (heterocromatina) aparecem mais escuras na micrografia."); +add("citologia", CEL, "citoplasma-citologia", "Citoplasma", "Cytoplasm", + "Conteúdo celular entre a membrana plasmática e o envoltório nuclear — citosol mais as organelas (mitocôndrias, retículo, ribossomos) que sustentam o metabolismo da célula."); + +// --------------------------------------------------------------------- +// HISTOLOGIA — Epitélio simples colunar (mucosa gástrica) +// --------------------------------------------------------------------- +const EP1 = "/histologia/epitelio-simples-colunar.jpg"; +add("histologia", EP1, "epitelio-superficie-gastrico", "Epitélio Colunar de Superfície", "Surface Columnar Epithelium", + "Epitélio simples colunar que reveste a superfície do estômago; suas células produzem muco alcalino que protege a mucosa do próprio suco gástrico ácido.", + ["Tecido Epitelial"]); +add("histologia", EP1, "fovéola-gastrica", "Fovéola (Fosseta) Gástrica", "Gastric Pit", + "Invaginação do epitélio de superfície para dentro da mucosa, por onde as glândulas gástricas desembocam seu conteúdo (ácido, enzimas, muco) na luz do estômago.", + ["Tecido Epitelial"]); +add("histologia", EP1, "glandulas-gastricas", "Glândulas Gástricas", "Gastric Glands", + "Glândulas tubulares na lâmina própria, abaixo das fovéolas, formadas por células especializadas (parietais, principais, mucosas) que produzem os componentes do suco gástrico.", + ["Tecido Epitelial"]); +add("histologia", EP1, "lamina-propria-gastrica", "Lâmina Própria", "Lamina Propria", + "Tecido conjuntivo frouxo vascularizado que sustenta o epitélio e suas glândulas, presente sob todo epitélio de revestimento das mucosas do corpo.", + ["Tecido Conjuntivo"]); + +// --------------------------------------------------------------------- +// HISTOLOGIA — Epitélio estratificado pavimentoso +// --------------------------------------------------------------------- +const EP2 = "/histologia/epitelio-estratificado-pavimentoso.jpg"; +add("histologia", EP2, "camada-basal-epitelio", "Camada Basal", "Basal Layer", + "Camada mais profunda do epitélio estratificado, apoiada na membrana basal; suas células cuboides se dividem continuamente para repor as camadas superiores que se descamam.", + ["Tecido Epitelial"]); +add("histologia", EP2, "camadas-intermediarias-epitelio", "Camadas Intermediárias", "Intermediate Layers", + "Células poligonais em maturação, entre a camada basal e a superficial, ficando progressivamente mais achatadas à medida que se aproximam da superfície.", + ["Tecido Epitelial"]); +add("histologia", EP2, "camada-superficial-epitelio", "Camada Superficial", "Superficial Layer", + "Camada mais externa, de células achatadas (pavimentosas); em mucosas úmidas (como esta) as células permanecem vivas e nucleadas, ao contrário da pele, onde se queratinizam e morrem.", + ["Tecido Epitelial"]); +add("histologia", EP2, "membrana-basal-epitelio", "Membrana Basal", "Basement Membrane", + "Lâmina de matriz extracelular que ancora o epitélio ao tecido conjuntivo subjacente, servindo de suporte estrutural e filtro seletivo entre os dois tecidos.", + ["Tecido Epitelial"]); + +// --------------------------------------------------------------------- +// HISTOLOGIA — Tecido adiposo (conjuntivo especializado) +// --------------------------------------------------------------------- +const ADI = "/histologia/tecido-adiposo.jpg"; +add("histologia", ADI, "adipocito", "Adipócito", "Adipocyte", + "Célula de tecido adiposo unilocular, especializada em armazenar gordura numa única grande gotícula lipídica; no preparo histológico de rotina o lipídio se dissolve, deixando a célula com aspecto de \"anel de sinete\" vazio.", + ["Tecido Conjuntivo"]); +add("histologia", ADI, "nucleo-adipocito", "Núcleo do Adipócito", "Adipocyte Nucleus", + "Núcleo achatado, empurrado para a periferia da célula pela enorme gotícula de gordura que ocupa quase todo o citoplasma.", + ["Tecido Conjuntivo"]); +add("histologia", ADI, "septo-conjuntivo-adiposo", "Septo de Tecido Conjuntivo", "Connective Tissue Septum", + "Fina faixa de tecido conjuntivo (com fibras colágenas e vasos sanguíneos) que separa os lóbulos de adipócitos entre si.", + ["Tecido Conjuntivo"]); + +// --------------------------------------------------------------------- +// HISTOLOGIA — Cartilagem hialina (zonas da cartilagem articular) +// --------------------------------------------------------------------- +const CART = "/histologia/cartilagem-hialina.jpg"; +add("histologia", CART, "zona-superficial-cartilagem", "Zona Superficial", "Superficial Zone", + "Camada mais externa da cartilagem articular, com condrócitos achatados e fibras colágenas dispostas paralelamente à superfície — a orientação que melhor resiste às forças de cisalhamento do atrito articular.", + ["Cartilagem"]); +add("histologia", CART, "zona-intermediaria-cartilagem", "Zona Intermediária (Média)", "Middle Zone", + "Zona de transição com condrócitos arredondados dispersos aleatoriamente numa matriz mais espessa, entre a organização paralela da superfície e a colunar da zona profunda.", + ["Cartilagem"]); +add("histologia", CART, "zona-profunda-cartilagem", "Zona Profunda", "Deep Zone", + "Condrócitos organizados em colunas perpendiculares à superfície articular, produzindo a matriz mais rica em proteoglicanos — a arquitetura que melhor resiste às forças de compressão.", + ["Cartilagem"]); +add("histologia", CART, "condrocito", "Condrócito", "Chondrocyte", + "Célula madura da cartilagem, alojada numa lacuna dentro da matriz que ela mesma secretou (colágeno tipo II + proteoglicanos); ao contrário do osso, a cartilagem não tem vasos sanguíneos — o condrócito se nutre só por difusão.", + ["Cartilagem"]); + +// --------------------------------------------------------------------- +// HISTOLOGIA — Osso compacto (sistemas de Havers) +// --------------------------------------------------------------------- +const OSSO = "/histologia/osso-compacto.jpg"; +add("histologia", OSSO, "sistema-de-havers", "Sistema de Havers (Ósteon)", "Haversian System (Osteon)", + "Unidade estrutural cilíndrica do osso compacto — lamelas concêntricas de matriz óssea calcificada organizadas ao redor de um canal central com vasos sanguíneos e nervos.", + ["Tecido Ósseo"]); +add("histologia", OSSO, "lacuna-osteocito", "Lacuna e Osteócito", "Lacuna and Osteocyte", + "Pequena cavidade na matriz óssea calcificada onde vive o osteócito, a célula óssea madura — presa na própria matriz que ajudou a formar quando ainda era um osteoblasto.", + ["Tecido Ósseo"]); +add("histologia", OSSO, "canaliculos-osseos", "Canalículos Ósseos", "Bone Canaliculi", + "Canais microscópicos que conectam lacunas vizinhas entre si e ao canal central, permitindo que os osteócitos troquem nutrientes e sinais através de prolongamentos citoplasmáticos, mesmo enclausurados na matriz dura.", + ["Tecido Ósseo"]); +add("histologia", OSSO, "canal-de-volkmann", "Canal de Volkmann", "Volkmann's Canal", + "Canal transversal/oblíquo que conecta os canais de Havers entre si e à superfície do osso, completando a rede vascular que nutre o tecido ósseo compacto.", + ["Tecido Ósseo"]); + +// --------------------------------------------------------------------- +// HISTOLOGIA — Tecido muscular (esquelético, liso e cardíaco lado a lado) +// --------------------------------------------------------------------- +const MUSC = "/histologia/tecido-muscular.jpg"; +add("histologia", MUSC, "musculo-esqueletico-histologia", "Músculo Esquelético (Estriado)", "Skeletal (Striated) Muscle", + "Fibras longas, cilíndricas e multinucleadas (núcleos na periferia da célula), com estriações transversais bem visíveis; contração voluntária e rápida.", + ["Tecido Muscular"]); +add("histologia", MUSC, "musculo-liso-histologia", "Músculo Liso", "Smooth Muscle", + "Células fusiformes (afiladas nas pontas), mononucleadas, sem estriações visíveis; contração involuntária e lenta, encontrado nas paredes de vísceras e vasos.", + ["Tecido Muscular"]); +add("histologia", MUSC, "musculo-cardiaco-histologia", "Músculo Cardíaco", "Cardiac Muscle", + "Fibras estriadas, geralmente com um único núcleo central, que se ramificam e se conectam a fibras vizinhas por discos intercalares — a base estrutural da contração involuntária e sincronizada do coração.", + ["Tecido Muscular"]); + +// --------------------------------------------------------------------- +// HISTOLOGIA — Tecido nervoso (medula espinhal) +// --------------------------------------------------------------------- +const NERV = "/histologia/tecido-nervoso.jpg"; +add("histologia", NERV, "substancia-branca-histologia", "Substância Branca", "White Matter", + "Região formada principalmente por axônios mielinizados (a bainha de mielina, rica em lipídios, é o que dá a cor clara/pálida); conduz os impulsos entre diferentes níveis do sistema nervoso.", + ["Tecido Nervoso"]); +add("histologia", NERV, "substancia-cinzenta-histologia", "Substância Cinzenta", "Gray Matter", + "Região rica em corpos celulares de neurônios, dendritos e sinapses (sem a mielina que clareia a substância branca); é aqui que ocorre o processamento e a integração dos sinais nervosos.", + ["Tecido Nervoso"]); +add("histologia", NERV, "corpo-neuronal-histologia", "Corpo do Neurônio (Soma)", "Neuron Cell Body", + "Região do neurônio que contém o núcleo e a maquinaria de síntese proteica; a partir dela partem os prolongamentos (dendritos e axônio) que fazem a célula se comunicar com outras.", + ["Tecido Nervoso"]); + +writeFileSync( + path.resolve(__dirname, "manifest-microscopy.json"), + JSON.stringify({ structures }, null, 2), +); + +console.log(`manifest-microscopy.json gerado com ${structures.length} estruturas.`); diff --git a/data-pipeline/seed/systems.mjs b/data-pipeline/seed/systems.mjs index 78cf48e..7e8a685 100644 --- a/data-pipeline/seed/systems.mjs +++ b/data-pipeline/seed/systems.mjs @@ -13,4 +13,10 @@ export const SYSTEMS = [ { slug: "tegumentar", namePt: "Sistema Tegumentar", nameEn: "Integumentary System", colorHex: "#e0a96d", order: 9 }, { slug: "regioes", namePt: "Regiões Anatômicas do Corpo", nameEn: "Body Regions", colorHex: "#f472b6", order: 10 }, { slug: "referencias", namePt: "Planos e Linhas de Referência", nameEn: "Reference Planes and Lines", colorHex: "#a3e635", order: 11 }, + // Citologia/Histologia: sem malha 3D (mesma situação do tegumentar — não + // existe dataset aberto de malha 3D real derivada de microscopia pra + // célula/tecidos), então usam o mesmo padrão de diagrama 2D com hotspots + // sobre fotomicrografias REAIS (não ilustrações) — ver MicroscopyDiagram.tsx. + { slug: "citologia", namePt: "Citologia (A Célula)", nameEn: "Cytology (The Cell)", colorHex: "#22d3ee", order: 12 }, + { slug: "histologia", namePt: "Histologia (Tecidos)", nameEn: "Histology (Tissues)", colorHex: "#fb923c", order: 13 }, ]; diff --git a/frontend/public/citologia/nucleo-celular-met.png b/frontend/public/citologia/nucleo-celular-met.png new file mode 100644 index 0000000..c4c59cb Binary files /dev/null and b/frontend/public/citologia/nucleo-celular-met.png differ diff --git a/frontend/public/histologia/cartilagem-hialina.jpg b/frontend/public/histologia/cartilagem-hialina.jpg new file mode 100644 index 0000000..c498da5 Binary files /dev/null and b/frontend/public/histologia/cartilagem-hialina.jpg differ diff --git a/frontend/public/histologia/epitelio-estratificado-pavimentoso.jpg b/frontend/public/histologia/epitelio-estratificado-pavimentoso.jpg new file mode 100644 index 0000000..aff5a37 Binary files /dev/null and b/frontend/public/histologia/epitelio-estratificado-pavimentoso.jpg differ diff --git a/frontend/public/histologia/epitelio-simples-colunar.jpg b/frontend/public/histologia/epitelio-simples-colunar.jpg new file mode 100644 index 0000000..51353e7 Binary files /dev/null and b/frontend/public/histologia/epitelio-simples-colunar.jpg differ diff --git a/frontend/public/histologia/osso-compacto.jpg b/frontend/public/histologia/osso-compacto.jpg new file mode 100644 index 0000000..807e96d Binary files /dev/null and b/frontend/public/histologia/osso-compacto.jpg differ diff --git a/frontend/public/histologia/tecido-adiposo.jpg b/frontend/public/histologia/tecido-adiposo.jpg new file mode 100644 index 0000000..986710b Binary files /dev/null and b/frontend/public/histologia/tecido-adiposo.jpg differ diff --git a/frontend/public/histologia/tecido-muscular.jpg b/frontend/public/histologia/tecido-muscular.jpg new file mode 100644 index 0000000..a106284 Binary files /dev/null and b/frontend/public/histologia/tecido-muscular.jpg differ diff --git a/frontend/public/histologia/tecido-nervoso.jpg b/frontend/public/histologia/tecido-nervoso.jpg new file mode 100644 index 0000000..0c80435 Binary files /dev/null and b/frontend/public/histologia/tecido-nervoso.jpg differ diff --git a/frontend/src/components/MicroscopyDiagram.tsx b/frontend/src/components/MicroscopyDiagram.tsx new file mode 100644 index 0000000..745a38b --- /dev/null +++ b/frontend/src/components/MicroscopyDiagram.tsx @@ -0,0 +1,126 @@ +import { useMemo, useState } from "react"; +import { useAtlasStore } from "../store/useAtlasStore.js"; +import { MICROSCOPY_HOTSPOTS } from "../data/microscopyHotspots.js"; +import { MICROSCOPY_CREDITS } from "../data/microscopyCredits.js"; +import type { Structure } from "../lib/api.js"; + +// Generalização do SkinDiagram.tsx pra sistemas sem malha 3D que precisam +// de MAIS de uma imagem (citologia/histologia têm uma fotomicrografia real +// por célula/tecido, ao contrário do tegumentar, que é uma imagem só) — ver +// a decisão de usar fotos reais em vez de geometria procedural em +// data-pipeline/seed/build-manifest-microscopy.mjs. +export function MicroscopyDiagram({ + bySlug, + systemSlug, + title, +}: { + bySlug: Map; + systemSlug: string; + title: string; +}) { + const selectedStructureId = useAtlasStore((s) => s.selectedStructureId); + const selectStructure = useAtlasStore((s) => s.selectStructure); + const toggleSystem = useAtlasStore((s) => s.toggleSystem); + + const structures = useMemo( + () => [...bySlug.values()].filter((s) => s.system.slug === systemSlug), + [bySlug, systemSlug], + ); + + // Cada "modelFile" aqui não é uma malha — é o caminho da fotomicrografia + // que aquela estrutura pertence (ver build-manifest-microscopy.mjs). + // Agrupar por ele é o que separa as estruturas em "lâminas" navegáveis. + const byImage = useMemo(() => { + const map = new Map(); + for (const s of structures) { + if (!map.has(s.modelFile)) map.set(s.modelFile, []); + map.get(s.modelFile)!.push(s); + } + return map; + }, [structures]); + + const images = useMemo(() => [...byImage.keys()], [byImage]); + const [activeImage, setActiveImage] = useState(null); + const currentImage = activeImage && byImage.has(activeImage) ? activeImage : images[0]; + + if (structures.length === 0 || !currentImage) return null; + + const currentStructures = byImage.get(currentImage) ?? []; + // O último trecho do regionPath é o "nome da lâmina" (ex: "Tecido Ósseo") + // — mesma lista usada pra dar nome de cada estrutura, reaproveitada aqui + // só como rótulo da aba, pra não precisar de mais um arquivo de dados. + const tabLabel = (image: string) => { + const first = byImage.get(image)?.[0]; + return first?.regionPath[first.regionPath.length - 1] ?? image; + }; + + return ( +
+
+
+

{title} — clique nos pontos pra explorar

+ +
+ + {images.length > 1 && ( +
+ {images.map((image) => ( + + ))} +
+ )} + +
+ {tabLabel(currentImage)} + {currentStructures.map((structure) => { + const hotspot = MICROSCOPY_HOTSPOTS[structure.slug]; + if (!hotspot) return null; + const isSelected = structure.id === selectedStructureId; + return ( +
+ +

+ Fotomicrografia real (não ilustração). {MICROSCOPY_CREDITS[currentImage] ?? ""} +

+
+
+ ); +} diff --git a/frontend/src/components/viewer/Scene.tsx b/frontend/src/components/viewer/Scene.tsx index d184048..3ac011a 100644 --- a/frontend/src/components/viewer/Scene.tsx +++ b/frontend/src/components/viewer/Scene.tsx @@ -6,6 +6,11 @@ import { useAtlasStore, type BodyRegion, type ClipAxis } from "../../store/useAt import { SystemModel } from "./SystemModel.js"; import type { Structure } from "../../lib/api.js"; +// Sistemas apresentados como diagrama 2D (fotomicrografia real com +// hotspots) em vez de malha 3D — ver SkinDiagram.tsx e +// MicroscopyDiagram.tsx. +const NON_3D_SYSTEMS = new Set(["tegumentar", "citologia", "histologia"]); + // Normais de referência em anatomia: sagital separa esq./dir., coronal // (= frontal) separa ant./post., transversal separa sup./inf. const AXIS_NORMAL: Record = { @@ -304,10 +309,14 @@ export function Scene({ // um sistema visível — a visibilidade por malha é resolvida dentro de // cada SystemModel. const structuresByFile = useMemo(() => { - // Tegumentar não tem malha 3D (é um diagrama 2D — ver SkinDiagram.tsx), - // então nunca entra na busca de .glb, senão o GLTFLoader tentaria - // carregar o modelFile placeholder ("n/a") dessas estruturas. - const structuresList = [...bySlug.values()].filter((s) => s.system.slug !== "tegumentar"); + // Tegumentar/Citologia/Histologia não têm malha 3D (são diagramas 2D + // com fotomicrografias reais — ver SkinDiagram.tsx e + // MicroscopyDiagram.tsx), então nunca entram na busca de .glb, senão o + // GLTFLoader tentaria carregar o modelFile dessas estruturas (que é o + // caminho de uma imagem, não um .glb). + const structuresList = [...bySlug.values()].filter( + (s) => !NON_3D_SYSTEMS.has(s.system.slug), + ); const map = new Map(); for (const s of structuresList) { if (!map.has(s.modelFile)) map.set(s.modelFile, []); diff --git a/frontend/src/data/microscopyCredits.ts b/frontend/src/data/microscopyCredits.ts new file mode 100644 index 0000000..6589d8d --- /dev/null +++ b/frontend/src/data/microscopyCredits.ts @@ -0,0 +1,23 @@ +// Crédito/licença de cada fotomicrografia real usada em Citologia/Histologia +// — igual à nota de crédito já usada no diagrama do tegumentar, só que aqui +// tem uma imagem por lâmina em vez de uma imagem única. Ver a curadoria +// completa (com link de origem) em +// data-pipeline/seed/build-manifest-microscopy.mjs. +export const MICROSCOPY_CREDITS: Record = { + "/citologia/nucleo-celular-met.png": + "Micrografia eletrônica de transmissão real. Domínio público (Wikimedia Commons).", + "/histologia/epitelio-simples-colunar.jpg": + "Mucosa gástrica normal, H&E. Nephron, CC BY-SA 3.0 (Wikimedia Commons).", + "/histologia/epitelio-estratificado-pavimentoso.jpg": + "Biópsia normal, H&E. Samir@enwiki, CC BY-SA 3.0 (Wikimedia Commons).", + "/histologia/tecido-adiposo.jpg": + "Corte de parafina (lipídios dissolvidos no processamento). CC BY-SA 3.0 (Wikimedia Commons).", + "/histologia/cartilagem-hialina.jpg": + "Bautista et al., CC0 (Wikimedia Commons).", + "/histologia/osso-compacto.jpg": + "Osso desgastado (ground section). Athikhun.suw, CC BY-SA 4.0 (Wikimedia Commons).", + "/histologia/tecido-muscular.jpg": + "OpenStax College, CC BY 4.0 (Wikimedia Commons).", + "/histologia/tecido-nervoso.jpg": + "Medula espinhal de coelho. John Alan Elson, CC BY-SA 4.0 (Wikimedia Commons).", +}; diff --git a/frontend/src/data/microscopyHotspots.ts b/frontend/src/data/microscopyHotspots.ts new file mode 100644 index 0000000..33a9416 --- /dev/null +++ b/frontend/src/data/microscopyHotspots.ts @@ -0,0 +1,52 @@ +// Posição (em % da largura/altura de CADA imagem) de cada marcador +// clicável dos sistemas "citologia" e "histologia" — mesmo esquema do +// tegumentar (ver skinHotspots.ts), mas aqui um sistema pode ter VÁRIAS +// imagens (uma por lâmina/tecido), então a imagem de cada estrutura é o +// próprio structure.modelFile (ver data-pipeline/seed/build-manifest-microscopy.mjs) +// — este arquivo só guarda x/y dentro daquela imagem. +export const MICROSCOPY_HOTSPOTS: Record = { + // Citologia — /citologia/nucleo-celular-met.png + "nucleo-citologia": { x: 46, y: 52 }, + "envoltorio-nuclear": { x: 70, y: 48 }, + cromatina: { x: 42, y: 60 }, + "citoplasma-citologia": { x: 14, y: 18 }, + + // Histologia — /histologia/epitelio-simples-colunar.jpg + "epitelio-superficie-gastrico": { x: 28, y: 9 }, + "fovéola-gastrica": { x: 47, y: 16 }, + "glandulas-gastricas": { x: 50, y: 68 }, + "lamina-propria-gastrica": { x: 18, y: 46 }, + + // Histologia — /histologia/epitelio-estratificado-pavimentoso.jpg + "camada-basal-epitelio": { x: 50, y: 90 }, + "camadas-intermediarias-epitelio": { x: 50, y: 55 }, + "camada-superficial-epitelio": { x: 50, y: 12 }, + "membrana-basal-epitelio": { x: 22, y: 96 }, + + // Histologia — /histologia/tecido-adiposo.jpg + adipocito: { x: 35, y: 45 }, + "nucleo-adipocito": { x: 39, y: 41 }, + "septo-conjuntivo-adiposo": { x: 30, y: 78 }, + + // Histologia — /histologia/cartilagem-hialina.jpg (3 painéis empilhados) + "zona-superficial-cartilagem": { x: 50, y: 15 }, + "zona-intermediaria-cartilagem": { x: 50, y: 48 }, + "zona-profunda-cartilagem": { x: 50, y: 82 }, + condrocito: { x: 28, y: 86 }, + + // Histologia — /histologia/osso-compacto.jpg + "sistema-de-havers": { x: 48, y: 42 }, + "lacuna-osteocito": { x: 90, y: 12 }, + "canaliculos-osseos": { x: 78, y: 22 }, + "canal-de-volkmann": { x: 62, y: 80 }, + + // Histologia — /histologia/tecido-muscular.jpg (3 painéis: a/b/c) + "musculo-esqueletico-histologia": { x: 50, y: 14 }, + "musculo-liso-histologia": { x: 50, y: 48 }, + "musculo-cardiaco-histologia": { x: 50, y: 83 }, + + // Histologia — /histologia/tecido-nervoso.jpg + "substancia-branca-histologia": { x: 20, y: 25 }, + "substancia-cinzenta-histologia": { x: 65, y: 35 }, + "corpo-neuronal-histologia": { x: 66, y: 30 }, +}; diff --git a/frontend/src/routes/AtlasPage.tsx b/frontend/src/routes/AtlasPage.tsx index 8fea021..af44f47 100644 --- a/frontend/src/routes/AtlasPage.tsx +++ b/frontend/src/routes/AtlasPage.tsx @@ -8,6 +8,7 @@ import { SearchNav } from "../components/SearchNav.js"; import { StructureInfoPanel } from "../components/StructureInfoPanel.js"; import { QuickRegionNav } from "../components/QuickRegionNav.js"; import { SkinDiagram } from "../components/SkinDiagram.js"; +import { MicroscopyDiagram } from "../components/MicroscopyDiagram.js"; import { trackVisit } from "../lib/analytics.js"; // Em telas pequenas não há espaço para os 3 painéis lado a lado — os @@ -72,6 +73,8 @@ export default function AtlasPage() { + +