citologia e histologia
No files matched your search
@@ -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",
|
||||
|
||||
@@ -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).`);
|
||||
@@ -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.`);
|
||||
@@ -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 },
|
||||
];
|
||||
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 544 KiB |
|
After Width: | Height: | Size: 353 KiB |
|
After Width: | Height: | Size: 726 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 357 KiB |
|
After Width: | Height: | Size: 346 KiB |
@@ -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<string, Structure>;
|
||||
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<string, Structure[]>();
|
||||
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<string | null>(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 (
|
||||
<div className="absolute inset-0 z-10 flex items-center justify-center p-4 bg-ink/70">
|
||||
<div className="relative bg-panel border border-white/10 rounded-xl shadow-2xl p-3 sm:p-4 max-w-2xl w-full max-h-full overflow-y-auto">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h2 className="text-sm font-semibold text-white">{title} — clique nos pontos pra explorar</h2>
|
||||
<button
|
||||
onClick={() => toggleSystem(systemSlug)}
|
||||
className="text-slate-400 hover:text-white text-sm px-2 py-1"
|
||||
aria-label="Fechar diagrama"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{images.length > 1 && (
|
||||
<div className="flex flex-wrap gap-1.5 mb-3">
|
||||
{images.map((image) => (
|
||||
<button
|
||||
key={image}
|
||||
onClick={() => setActiveImage(image)}
|
||||
className={`text-xs px-2.5 py-1 rounded-full border transition-colors
|
||||
${
|
||||
image === currentImage
|
||||
? "bg-accent border-accent text-white"
|
||||
: "border-white/15 text-slate-300 hover:border-white/30"
|
||||
}`}
|
||||
>
|
||||
{tabLabel(image)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="relative w-full select-none">
|
||||
<img
|
||||
key={currentImage}
|
||||
src={currentImage}
|
||||
alt={tabLabel(currentImage)}
|
||||
className="w-full h-auto rounded-lg border border-white/10"
|
||||
draggable={false}
|
||||
/>
|
||||
{currentStructures.map((structure) => {
|
||||
const hotspot = MICROSCOPY_HOTSPOTS[structure.slug];
|
||||
if (!hotspot) return null;
|
||||
const isSelected = structure.id === selectedStructureId;
|
||||
return (
|
||||
<button
|
||||
key={structure.id}
|
||||
onClick={() => selectStructure(structure.id)}
|
||||
title={structure.namePt}
|
||||
aria-label={structure.namePt}
|
||||
style={{ left: `${hotspot.x}%`, top: `${hotspot.y}%` }}
|
||||
className={`absolute -translate-x-1/2 -translate-y-1/2 w-4 h-4 rounded-full border-2 transition-all
|
||||
${
|
||||
isSelected
|
||||
? "bg-accent border-white scale-125 shadow-[0_0_0_4px_rgba(79,124,255,0.35)]"
|
||||
: "bg-accent/70 border-white/70 hover:scale-125 hover:bg-accent"
|
||||
}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<p className="text-[11px] text-slate-500 mt-2">
|
||||
Fotomicrografia real (não ilustração). {MICROSCOPY_CREDITS[currentImage] ?? ""}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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<ClipAxis, [number, number, number]> = {
|
||||
@@ -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<string, Structure[]>();
|
||||
for (const s of structuresList) {
|
||||
if (!map.has(s.modelFile)) map.set(s.modelFile, []);
|
||||
|
||||
@@ -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<string, string> = {
|
||||
"/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).",
|
||||
};
|
||||
@@ -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<string, { x: number; y: number }> = {
|
||||
// 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 },
|
||||
};
|
||||
@@ -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() {
|
||||
<Scene bySlug={bySlug} />
|
||||
<QuickRegionNav />
|
||||
<SkinDiagram bySlug={bySlug} />
|
||||
<MicroscopyDiagram bySlug={bySlug} systemSlug="citologia" title="Citologia (A Célula)" />
|
||||
<MicroscopyDiagram bySlug={bySlug} systemSlug="histologia" title="Histologia (Tecidos)" />
|
||||
|
||||
<button
|
||||
onClick={() => setLeftOpen(true)}
|
||||
|
||||