modelo 3d citologia
This commit is contained in:
1 parent
65a4fdea9e
commit
369638bf98
15 files changed
+308
-56
No files matched your search
@@ -5,4 +5,6 @@ build/
|
|||||||
*.log
|
*.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
data-pipeline/seed/manifest.json
|
data-pipeline/seed/manifest.json
|
||||||
|
data-pipeline/seed/manifest-*.json
|
||||||
data-pipeline/seed/models/
|
data-pipeline/seed/models/
|
||||||
|
data-pipeline/converted/
|
||||||
@@ -3,8 +3,17 @@
|
|||||||
# Alpine essa dependência é bem mais dolorosa de resolver.
|
# Alpine essa dependência é bem mais dolorosa de resolver.
|
||||||
FROM node:20-bookworm
|
FROM node:20-bookworm
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends assimp-utils ca-certificates \
|
&& apt-get install -y --no-install-recommends assimp-utils ca-certificates python3 python3-venv \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Ambiente Python isolado só pra buscar as organelas reais da célula
|
||||||
|
# (fetch-cell-organelles.py) — cloud-volume/trimesh não têm nada a ver com
|
||||||
|
# o resto do pipeline (100% Node/glTF-transform), então ficam num venv
|
||||||
|
# próprio em vez de poluir o Python do sistema.
|
||||||
|
RUN python3 -m venv /opt/venv-cell \
|
||||||
|
&& /opt/venv-cell/bin/pip install --no-cache-dir --upgrade pip \
|
||||||
|
&& /opt/venv-cell/bin/pip install --no-cache-dir cloud-volume trimesh
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package.json ./
|
COPY package.json ./
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|||||||
@@ -9,9 +9,12 @@
|
|||||||
"extract:manifest": "node scripts/extract-manifest.mjs",
|
"extract:manifest": "node scripts/extract-manifest.mjs",
|
||||||
"build:tegumentar": "node seed/build-manifest.mjs",
|
"build:tegumentar": "node seed/build-manifest.mjs",
|
||||||
"build:microscopy": "node seed/build-manifest-microscopy.mjs",
|
"build:microscopy": "node seed/build-manifest-microscopy.mjs",
|
||||||
|
"cell:fetch": "/opt/venv-cell/bin/python scripts/fetch-cell-organelles.py",
|
||||||
|
"cell:model": "node scripts/build-cell-model.mjs",
|
||||||
|
"cell:manifest": "node seed/build-manifest-cell.mjs",
|
||||||
"merge:manifest": "node scripts/merge-manifests.mjs",
|
"merge:manifest": "node scripts/merge-manifests.mjs",
|
||||||
"seed:db": "node scripts/seed-db.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 build:microscopy && 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 cell:fetch && npm run cell:model && npm run cell:manifest && npm run merge:manifest && npm run seed:db"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gltf-transform/core": "^4.0.4",
|
"@gltf-transform/core": "^4.0.4",
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// Lê citologia-raw.glb (gerado por fetch-cell-organelles.py a partir de
|
||||||
|
// dados REAIS de FIB-SEM do Janelia OpenOrganelle/CellMap — ver aquele
|
||||||
|
// script para a curadoria completa e a licença CC BY 4.0), simplifica e
|
||||||
|
// comprime com o MESMO recipe já usado pras malhas do Z-Anatomy em
|
||||||
|
// extract-manifest.mjs, e salva o resultado final em seed/models/citologia.glb.
|
||||||
|
//
|
||||||
|
// Diferente do Z-Anatomy, aqui não precisamos marcar nós excluídos nem
|
||||||
|
// resolver nomes duplicados — o .glb de entrada já tem exatamente 5 nós,
|
||||||
|
// um por organela, com o nome final (ex: "celula-nucleo") definido no
|
||||||
|
// próprio script Python. weld/simplify/dedup/meshopt só afetam a
|
||||||
|
// geometria, não o nome dos nós, então o "meshName" no manifesto
|
||||||
|
// (build-manifest-cell.mjs) pode confiar que bate exatamente com isso.
|
||||||
|
import { NodeIO } from "@gltf-transform/core";
|
||||||
|
import { ALL_EXTENSIONS } from "@gltf-transform/extensions";
|
||||||
|
import { prune, dedup, weld, simplify, meshopt } from "@gltf-transform/functions";
|
||||||
|
import { MeshoptSimplifier, MeshoptEncoder } from "meshoptimizer";
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { mkdirSync } from "node:fs";
|
||||||
|
|
||||||
|
await MeshoptSimplifier.ready;
|
||||||
|
await MeshoptEncoder.ready;
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const IN_PATH = path.resolve(__dirname, "../converted/citologia/citologia-raw.glb");
|
||||||
|
const OUT_DIR = path.resolve(__dirname, "../seed/models");
|
||||||
|
mkdirSync(OUT_DIR, { recursive: true });
|
||||||
|
|
||||||
|
const io = new NodeIO()
|
||||||
|
.registerExtensions(ALL_EXTENSIONS)
|
||||||
|
.registerDependencies({ "meshopt.encoder": MeshoptEncoder });
|
||||||
|
|
||||||
|
console.log(`Lendo ${IN_PATH}...`);
|
||||||
|
const doc = await io.read(IN_PATH);
|
||||||
|
|
||||||
|
const meshNames = doc
|
||||||
|
.getRoot()
|
||||||
|
.listNodes()
|
||||||
|
.map((n) => n.getName())
|
||||||
|
.filter(Boolean);
|
||||||
|
console.log(`Organelas encontradas: ${meshNames.join(", ")}`);
|
||||||
|
|
||||||
|
await doc.transform(
|
||||||
|
weld(),
|
||||||
|
// As organelas maiores (membrana, RE) chegam com bem mais geometria do
|
||||||
|
// que o necessário pra estudo interativo — mesmo ratio conservador já
|
||||||
|
// usado no resto do atlas.
|
||||||
|
simplify({ simplifier: MeshoptSimplifier, ratio: 0.5, error: 0.01 }),
|
||||||
|
prune(),
|
||||||
|
dedup(),
|
||||||
|
meshopt({ encoder: MeshoptEncoder, level: "medium" }),
|
||||||
|
);
|
||||||
|
|
||||||
|
const outPath = path.join(OUT_DIR, "citologia.glb");
|
||||||
|
await io.write(outPath, doc);
|
||||||
|
console.log(`✓ ${outPath} salvo.`);
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Busca organelas REAIS de uma célula HeLa inteira, reconstruída em 3D a
|
||||||
|
# partir de FIB-SEM (microscopia eletrônica volumétrica) e segmentada por
|
||||||
|
# machine learning pelo projeto CellMap/COSEM do Janelia Research Campus
|
||||||
|
# (HHMI) — publicado como OpenOrganelle, CC BY 4.0.
|
||||||
|
# https://openorganelle.janelia.org/ | dataset "jrc_hela-2"
|
||||||
|
# DOI da coleção: https://doi.org/10.25378/janelia.c.7456966
|
||||||
|
#
|
||||||
|
# POR QUE ISSO E NÃO GEOMETRIA PROCEDURAL: é o mesmo princípio já usado pro
|
||||||
|
# resto do atlas (malhas reais do Z-Anatomy) — aqui não existe um "Z-Anatomy
|
||||||
|
# da célula" pronto pra converter de FBX, mas o Janelia publica a segmentação
|
||||||
|
# 3D real como malhas no formato Neuroglancer "precomputed" (multi-resolução,
|
||||||
|
# comprimido com Draco). Este script usa a biblioteca `cloud-volume` (Seung
|
||||||
|
# Lab) pra baixar e decodificar essas malhas direto do bucket público, sem
|
||||||
|
# fabricar nenhuma forma.
|
||||||
|
#
|
||||||
|
# Escolhas de curadoria (documentadas pra poder ajustar depois):
|
||||||
|
# - Cada organela é uma "camada" de segmentação própria no dataset
|
||||||
|
# (nucleus_seg, mito_seg, er_seg, golgi_seg, pm_seg), com um ID por
|
||||||
|
# instância. Pegamos a maior instância de núcleo/membrana (a célula
|
||||||
|
# mais completa no campo de visão) e TODAS as instâncias de
|
||||||
|
# mitocôndria/RE/Golgi (são realmente múltiplas organelas de verdade,
|
||||||
|
# não fragmentos de ruído — juntar todas reproduz a distribuição real).
|
||||||
|
# - LOD (nível de detalhe) escolhido por organela: mais fino pra
|
||||||
|
# estruturas pequenas/poucas (núcleo, Golgi), mais grosso pras que têm
|
||||||
|
# MUITA geometria agregada (mitocôndrias, RE) — o pipeline Node.js
|
||||||
|
# (build-cell-model.mjs) simplifica/comprime tudo de qualquer forma.
|
||||||
|
# - Todas as peças são recentralizadas usando o MESMO deslocamento (o
|
||||||
|
# centro da membrana plasmática) — cada organela NÃO pode ser
|
||||||
|
# recentralizada individualmente, isso destruiria a posição relativa
|
||||||
|
# real dentro da célula.
|
||||||
|
# - Escala: nanômetros (unidade nativa do dataset) → micrômetros (÷1000),
|
||||||
|
# só por conveniência numérica — a câmera do app se ajusta ao tamanho
|
||||||
|
# real do conteúdo de qualquer forma (ver Scene.tsx/CameraController).
|
||||||
|
#
|
||||||
|
# Vesículas (~40 mil instâncias) e endossomos (~5.600) ficaram de fora
|
||||||
|
# desta primeira versão por volume de dados — dá pra adicionar depois como
|
||||||
|
# mais uma organela seguindo exatamente o mesmo padrão abaixo.
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import cloudvolume as cv
|
||||||
|
import trimesh
|
||||||
|
|
||||||
|
DATASET = "jrc_hela-2"
|
||||||
|
BASE = f"precomputed://https://janelia-cosem-datasets.s3.amazonaws.com/{DATASET}/neuroglancer/mesh"
|
||||||
|
OUT_DIR = os.path.join(os.path.dirname(__file__), "..", "converted", "citologia")
|
||||||
|
|
||||||
|
# (nome do nó no glb, camada de segmentação, IDs de instância, LOD)
|
||||||
|
GROUPS = [
|
||||||
|
("celula-membrana-plasmatica", "pm_seg", [2], 4),
|
||||||
|
("celula-nucleo", "nucleus_seg", [1], 3),
|
||||||
|
("celula-reticulo-endoplasmatico", "er_seg", list(range(1, 25)), 4),
|
||||||
|
("celula-complexo-de-golgi", "golgi_seg", list(range(1, 9)), 3),
|
||||||
|
("celula-mitocondrias", "mito_seg", list(range(1, 422)), 4),
|
||||||
|
]
|
||||||
|
|
||||||
|
SCALE_NM_TO_UM = 1.0 / 1000.0
|
||||||
|
|
||||||
|
|
||||||
|
def make_volume(seg_layer):
|
||||||
|
# CloudVolume espera o "info" completo de uma camada de imagem/
|
||||||
|
# segmentação (com "scales"), mas só precisamos da parte de malha —
|
||||||
|
# os valores de "scales" abaixo são um placeholder mínimo válido só
|
||||||
|
# pra passar da checagem interna da biblioteca; a resolução REAL usada
|
||||||
|
# pra posicionar os vértices vem do "transform" dentro do info.json de
|
||||||
|
# cada camada de malha (lido automaticamente pelo cloud-volume).
|
||||||
|
info = {
|
||||||
|
"type": "segmentation",
|
||||||
|
"data_type": "uint64",
|
||||||
|
"num_channels": 1,
|
||||||
|
"scales": [{
|
||||||
|
"chunk_sizes": [[64, 64, 64]],
|
||||||
|
"encoding": "raw",
|
||||||
|
"key": "1_1_1",
|
||||||
|
"resolution": [4, 4, 5.24],
|
||||||
|
"size": [1, 1, 1],
|
||||||
|
"voxel_offset": [0, 0, 0],
|
||||||
|
}],
|
||||||
|
"mesh": seg_layer,
|
||||||
|
}
|
||||||
|
return cv.CloudVolume(BASE, info=info, use_https=True)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_group(name, seg_layer, ids, lod):
|
||||||
|
t0 = time.time()
|
||||||
|
vol = make_volume(seg_layer)
|
||||||
|
meshes = vol.mesh.get(ids, lod=lod)
|
||||||
|
parts = []
|
||||||
|
for m in meshes.values():
|
||||||
|
if m is None or len(m.vertices) == 0 or len(m.faces) == 0:
|
||||||
|
continue
|
||||||
|
# A decodificação Draco em algumas LODs mais grosseiras às vezes
|
||||||
|
# produz vértices NaN/Inf pra fragmentos degenerados — zera-los
|
||||||
|
# em vez de excluir a organela inteira mantém a peça utilizável.
|
||||||
|
vertices = np.nan_to_num(m.vertices, nan=0.0, posinf=0.0, neginf=0.0)
|
||||||
|
parts.append(trimesh.Trimesh(vertices=vertices, faces=m.faces, process=False))
|
||||||
|
if not parts:
|
||||||
|
raise RuntimeError(f"{name}: nenhuma geometria válida encontrada (verifique os IDs/LOD)")
|
||||||
|
combined = trimesh.util.concatenate(parts)
|
||||||
|
print(f" {name}: {len(combined.vertices)} vértices, {len(combined.faces)} faces ({time.time() - t0:.1f}s)", flush=True)
|
||||||
|
return combined
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print(f"Baixando organelas reais de {DATASET} (Janelia OpenOrganelle/CellMap, CC BY 4.0)...")
|
||||||
|
results = {}
|
||||||
|
for name, seg_layer, ids, lod in GROUPS:
|
||||||
|
results[name] = fetch_group(name, seg_layer, ids, lod)
|
||||||
|
|
||||||
|
center = results["celula-membrana-plasmatica"].bounds.mean(axis=0)
|
||||||
|
|
||||||
|
scene = trimesh.Scene()
|
||||||
|
for name, mesh in results.items():
|
||||||
|
mesh.vertices = (mesh.vertices - center) * SCALE_NM_TO_UM
|
||||||
|
scene.add_geometry(mesh, node_name=name, geom_name=name)
|
||||||
|
|
||||||
|
os.makedirs(OUT_DIR, exist_ok=True)
|
||||||
|
out_path = os.path.join(OUT_DIR, "citologia-raw.glb")
|
||||||
|
scene.export(out_path)
|
||||||
|
print(f"✓ {out_path} salvo — pronto para o build-cell-model.mjs simplificar/comprimir.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -8,12 +8,13 @@ const SEED_DIR = path.resolve(__dirname, "../seed");
|
|||||||
const real = JSON.parse(readFileSync(path.join(SEED_DIR, "manifest-real.json"), "utf-8"));
|
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 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 microscopy = JSON.parse(readFileSync(path.join(SEED_DIR, "manifest-microscopy.json"), "utf-8"));
|
||||||
|
const cell = JSON.parse(readFileSync(path.join(SEED_DIR, "manifest-cell.json"), "utf-8"));
|
||||||
|
|
||||||
const structures = [...real.structures, ...tegumentar.structures, ...microscopy.structures];
|
const structures = [...real.structures, ...tegumentar.structures, ...microscopy.structures, ...cell.structures];
|
||||||
|
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
path.join(SEED_DIR, "manifest.json"),
|
path.join(SEED_DIR, "manifest.json"),
|
||||||
JSON.stringify({ structures }, null, 2),
|
JSON.stringify({ structures }, null, 2),
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(`manifest.json final: ${structures.length} estruturas (${real.structures.length} reais + ${tegumentar.structures.length} tegumentar + ${microscopy.structures.length} citologia/histologia).`);
|
console.log(`manifest.json final: ${structures.length} estruturas (${real.structures.length} reais + ${tegumentar.structures.length} tegumentar + ${microscopy.structures.length} histologia + ${cell.structures.length} citologia 3D).`);
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
// Gera manifest-cell.json — as 5 organelas do sistema "citologia", com
|
||||||
|
// malha 3D REAL (ao contrário de "histologia", que continua sendo
|
||||||
|
// fotomicrografias 2D — ver build-manifest-microscopy.mjs).
|
||||||
|
//
|
||||||
|
// Fonte: célula HeLa (jrc_hela-2) reconstruída em 3D a partir de FIB-SEM
|
||||||
|
// real e segmentada por machine learning pelo projeto CellMap/COSEM do
|
||||||
|
// Janelia Research Campus (HHMI) — OpenOrganelle, CC BY 4.0.
|
||||||
|
// https://openorganelle.janelia.org/ — DOI da coleção:
|
||||||
|
// https://doi.org/10.25378/janelia.c.7456966
|
||||||
|
// Extração das malhas: data-pipeline/scripts/fetch-cell-organelles.py
|
||||||
|
// Simplificação/compressão final: data-pipeline/scripts/build-cell-model.mjs
|
||||||
|
//
|
||||||
|
// O nome de cada nó do citologia.glb (definido no script Python) é usado
|
||||||
|
// aqui como "meshName" — sem simplificação de nome/exclusão como no
|
||||||
|
// esquelético, já que são só 5 peças, cada uma com nome final único.
|
||||||
|
import { writeFileSync } from "node:fs";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const MODEL_FILE = "citologia.glb";
|
||||||
|
|
||||||
|
const CREDIT =
|
||||||
|
"Malha 3D real, segmentada por FIB-SEM (célula HeLa, jrc_hela-2) pelo projeto CellMap/Janelia Research Campus — OpenOrganelle, CC BY 4.0.";
|
||||||
|
|
||||||
|
function structure(slug, namePt, nameEn, description) {
|
||||||
|
return {
|
||||||
|
system: "citologia",
|
||||||
|
slug,
|
||||||
|
namePt,
|
||||||
|
nameEn,
|
||||||
|
synonyms: [],
|
||||||
|
description: `${description} ${CREDIT}`,
|
||||||
|
regionPath: ["Citologia", "Célula (HeLa) — FIB-SEM real"],
|
||||||
|
modelFile: MODEL_FILE,
|
||||||
|
meshName: slug,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const structures = [
|
||||||
|
structure(
|
||||||
|
"celula-membrana-plasmatica",
|
||||||
|
"Membrana Plasmática",
|
||||||
|
"Plasma Membrane",
|
||||||
|
"Envoltório que delimita a célula, controlando a entrada e saída de substâncias; formada por uma bicamada fosfolipídica com proteínas de membrana.",
|
||||||
|
),
|
||||||
|
structure(
|
||||||
|
"celula-nucleo",
|
||||||
|
"Núcleo",
|
||||||
|
"Nucleus",
|
||||||
|
"Organela que guarda o DNA da célula e comanda a síntese proteica; a forma irregular e volumosa vista aqui é real e típica de células HeLa (uma linhagem tumoral, com núcleo frequentemente atípico).",
|
||||||
|
),
|
||||||
|
structure(
|
||||||
|
"celula-reticulo-endoplasmatico",
|
||||||
|
"Retículo Endoplasmático",
|
||||||
|
"Endoplasmic Reticulum",
|
||||||
|
"Rede de membranas contínua com o envoltório nuclear, espalhada por todo o citoplasma; participa da síntese de proteínas (porção rugosa) e lipídios, além do armazenamento de cálcio.",
|
||||||
|
),
|
||||||
|
structure(
|
||||||
|
"celula-complexo-de-golgi",
|
||||||
|
"Complexo de Golgi",
|
||||||
|
"Golgi Apparatus",
|
||||||
|
"Conjunto de sáculos membranosos empilhados, tipicamente posicionado perto do núcleo (posição confirmada nesta reconstrução real); processa, modifica e empacota proteínas vindas do retículo endoplasmático.",
|
||||||
|
),
|
||||||
|
structure(
|
||||||
|
"celula-mitocondrias",
|
||||||
|
"Mitocôndrias",
|
||||||
|
"Mitochondria",
|
||||||
|
"População completa de mitocôndrias segmentadas nesta célula — organelas responsáveis pela produção de ATP por respiração celular; note a variação real de tamanho e forma entre elas, incluindo fragmentação, comum em linhagens tumorais.",
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
writeFileSync(
|
||||||
|
path.resolve(__dirname, "manifest-cell.json"),
|
||||||
|
JSON.stringify({ structures }, null, 2),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`manifest-cell.json gerado com ${structures.length} organelas.`);
|
||||||
@@ -1,24 +1,20 @@
|
|||||||
// Gera manifest-microscopy.json (sistemas "citologia" e "histologia").
|
// Gera manifest-microscopy.json (sistema "histologia" — tecidos).
|
||||||
//
|
//
|
||||||
// MESMA DECISÃO já tomada pro tegumentar (ver build-manifest.mjs): não
|
// Citologia (a célula) NÃO usa mais este padrão: em vez de fotomicrografia
|
||||||
// existe malha 3D real e aberta derivada de microscopia (nem de célula,
|
// 2D, virou um modelo 3D real de verdade (segmentação FIB-SEM de uma
|
||||||
// nem de tecido histológico — confirmado pesquisando: Allen Institute for
|
// célula HeLa completa) — ver build-manifest-cell.mjs. Pra tecidos
|
||||||
// Cell Science só distribui dado volumétrico bruto (OME-Zarr/TIFF), não
|
// histológicos propriamente ditos, porém, a mesma limitação do tegumentar
|
||||||
// malha pronta; não existe um "Z-Anatomy da histologia" de código aberto).
|
// (ver build-manifest.mjs) continua valendo: não existe malha 3D real e
|
||||||
// Fabricar formas geométricas (esferas pra núcleo, cilindros pra
|
// aberta de tecido histológico. Fabricar formas geométricas seria
|
||||||
// mitocôndria etc.) seria exatamente o "método não realístico" que se
|
// exatamente o "método não realístico" a evitar — por isso aqui é
|
||||||
// queria evitar. Em vez disso: FOTOMICROGRAFIAS REAIS (não ilustrações),
|
// FOTOMICROGRAFIA REAL (não ilustração), de domínio público ou CC, com
|
||||||
// de domínio público ou CC, com hotspots — mesmo padrão do tegumentar.
|
// hotspots, mesmo padrão do tegumentar.
|
||||||
//
|
//
|
||||||
// Curadoria das imagens (baixadas de commons.wikimedia.org, verificadas
|
// Curadoria das imagens (baixadas de commons.wikimedia.org, verificadas
|
||||||
// visualmente uma a uma pra confirmar que são fotos reais e não diagramas/
|
// visualmente uma a uma pra confirmar que são fotos reais e não diagramas/
|
||||||
// ilustrações — vários candidatos populares como "Blausen ..." ou
|
// ilustrações — vários candidatos populares como "Blausen ..." ou
|
||||||
// "Typical human cell" foram DESCARTADOS por serem ilustrações, não fotos):
|
// "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
|
// histologia/epitelio-simples-colunar.jpg
|
||||||
// Mucosa gástrica normal, H&E. Autor: Nephron. CC BY-SA 3.0.
|
// 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
|
// https://commons.wikimedia.org/wiki/File:Normal_gastric_mucosa_intermed_mag.jpg
|
||||||
@@ -62,29 +58,16 @@ function add(system, image, slug, namePt, nameEn, description, region = []) {
|
|||||||
nameEn,
|
nameEn,
|
||||||
synonyms: [],
|
synonyms: [],
|
||||||
description,
|
description,
|
||||||
regionPath: [system === "citologia" ? "Citologia" : "Histologia", ...region],
|
regionPath: ["Histologia", ...region],
|
||||||
// Sem malha 3D — o caminho da fotomicrografia (servida como asset
|
// Sem malha 3D — o caminho da fotomicrografia (servida como asset
|
||||||
// estático do frontend, igual à imagem do tegumentar) fica direto no
|
// estático do frontend, igual à imagem do tegumentar) fica direto no
|
||||||
// campo modelFile; MicroscopyDiagram.tsx usa isso pra saber em qual
|
// campo modelFile; MicroscopyDiagram.tsx usa isso pra saber em qual
|
||||||
// imagem desenhar o hotspot de cada estrutura.
|
// imagem desenhar o hotspot de cada estrutura.
|
||||||
modelFile: image,
|
modelFile: image,
|
||||||
meshName: slug,
|
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)
|
// HISTOLOGIA — Epitélio simples colunar (mucosa gástrica)
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
|||||||
@@ -13,10 +13,11 @@ export const SYSTEMS = [
|
|||||||
{ slug: "tegumentar", namePt: "Sistema Tegumentar", nameEn: "Integumentary System", colorHex: "#e0a96d", order: 9 },
|
{ 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: "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 },
|
{ 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
|
// Citologia: malha 3D REAL (segmentação FIB-SEM de uma célula HeLa
|
||||||
// existe dataset aberto de malha 3D real derivada de microscopia pra
|
// inteira, Janelia OpenOrganelle/CellMap) — ver build-manifest-cell.mjs.
|
||||||
// célula/tecidos), então usam o mesmo padrão de diagrama 2D com hotspots
|
// Histologia: sem malha 3D (não existe dataset aberto de malha 3D real
|
||||||
// sobre fotomicrografias REAIS (não ilustrações) — ver MicroscopyDiagram.tsx.
|
// de tecido histológico), usa fotomicrografias REAIS 2D com hotspots
|
||||||
|
// — ver build-manifest-microscopy.mjs e MicroscopyDiagram.tsx.
|
||||||
{ slug: "citologia", namePt: "Citologia (A Célula)", nameEn: "Cytology (The Cell)", colorHex: "#22d3ee", order: 12 },
|
{ 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 },
|
{ slug: "histologia", namePt: "Histologia (Tecidos)", nameEn: "Histology (Tissues)", colorHex: "#fb923c", order: 13 },
|
||||||
];
|
];
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 33 KiB |
@@ -5,9 +5,9 @@ import { MICROSCOPY_CREDITS } from "../data/microscopyCredits.js";
|
|||||||
import type { Structure } from "../lib/api.js";
|
import type { Structure } from "../lib/api.js";
|
||||||
|
|
||||||
// Generalização do SkinDiagram.tsx pra sistemas sem malha 3D que precisam
|
// Generalização do SkinDiagram.tsx pra sistemas sem malha 3D que precisam
|
||||||
// de MAIS de uma imagem (citologia/histologia têm uma fotomicrografia real
|
// de MAIS de uma imagem (histologia tem uma fotomicrografia real por
|
||||||
// por célula/tecido, ao contrário do tegumentar, que é uma imagem só) — ver
|
// tecido, ao contrário do tegumentar, que é uma imagem só) — ver a decisão
|
||||||
// a decisão de usar fotos reais em vez de geometria procedural em
|
// de usar fotos reais em vez de geometria procedural em
|
||||||
// data-pipeline/seed/build-manifest-microscopy.mjs.
|
// data-pipeline/seed/build-manifest-microscopy.mjs.
|
||||||
export function MicroscopyDiagram({
|
export function MicroscopyDiagram({
|
||||||
bySlug,
|
bySlug,
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import type { Structure } from "../../lib/api.js";
|
|||||||
|
|
||||||
// Sistemas apresentados como diagrama 2D (fotomicrografia real com
|
// Sistemas apresentados como diagrama 2D (fotomicrografia real com
|
||||||
// hotspots) em vez de malha 3D — ver SkinDiagram.tsx e
|
// hotspots) em vez de malha 3D — ver SkinDiagram.tsx e
|
||||||
// MicroscopyDiagram.tsx.
|
// MicroscopyDiagram.tsx. Citologia usa malha 3D real (ver
|
||||||
const NON_3D_SYSTEMS = new Set(["tegumentar", "citologia", "histologia"]);
|
// build-manifest-cell.mjs), então não entra aqui.
|
||||||
|
const NON_3D_SYSTEMS = new Set(["tegumentar", "histologia"]);
|
||||||
|
|
||||||
// Normais de referência em anatomia: sagital separa esq./dir., coronal
|
// Normais de referência em anatomia: sagital separa esq./dir., coronal
|
||||||
// (= frontal) separa ant./post., transversal separa sup./inf.
|
// (= frontal) separa ant./post., transversal separa sup./inf.
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
// completa (com link de origem) em
|
// completa (com link de origem) em
|
||||||
// data-pipeline/seed/build-manifest-microscopy.mjs.
|
// data-pipeline/seed/build-manifest-microscopy.mjs.
|
||||||
export const MICROSCOPY_CREDITS: Record<string, string> = {
|
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":
|
"/histologia/epitelio-simples-colunar.jpg":
|
||||||
"Mucosa gástrica normal, H&E. Nephron, CC BY-SA 3.0 (Wikimedia Commons).",
|
"Mucosa gástrica normal, H&E. Nephron, CC BY-SA 3.0 (Wikimedia Commons).",
|
||||||
"/histologia/epitelio-estratificado-pavimentoso.jpg":
|
"/histologia/epitelio-estratificado-pavimentoso.jpg":
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
// Posição (em % da largura/altura de CADA imagem) de cada marcador
|
// Posição (em % da largura/altura de CADA imagem) de cada marcador
|
||||||
// clicável dos sistemas "citologia" e "histologia" — mesmo esquema do
|
// clicável do sistema "histologia" — mesmo esquema do tegumentar (ver
|
||||||
// tegumentar (ver skinHotspots.ts), mas aqui um sistema pode ter VÁRIAS
|
// skinHotspots.ts), mas aqui o sistema tem VÁRIAS imagens (uma por lâmina/
|
||||||
// imagens (uma por lâmina/tecido), então a imagem de cada estrutura é o
|
// tecido), então a imagem de cada estrutura é o próprio structure.modelFile
|
||||||
// próprio structure.modelFile (ver data-pipeline/seed/build-manifest-microscopy.mjs)
|
// (ver data-pipeline/seed/build-manifest-microscopy.mjs) — este arquivo só
|
||||||
// — este arquivo só guarda x/y dentro daquela imagem.
|
// guarda x/y dentro daquela imagem. Citologia (a célula) não usa mais isso
|
||||||
|
// — virou um modelo 3D real, ver build-manifest-cell.mjs.
|
||||||
export const MICROSCOPY_HOTSPOTS: Record<string, { x: number; y: number }> = {
|
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
|
// Histologia — /histologia/epitelio-simples-colunar.jpg
|
||||||
"epitelio-superficie-gastrico": { x: 28, y: 9 },
|
"epitelio-superficie-gastrico": { x: 28, y: 9 },
|
||||||
"fovéola-gastrica": { x: 47, y: 16 },
|
"fovéola-gastrica": { x: 47, y: 16 },
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ export default function AtlasPage() {
|
|||||||
<Scene bySlug={bySlug} />
|
<Scene bySlug={bySlug} />
|
||||||
<QuickRegionNav />
|
<QuickRegionNav />
|
||||||
<SkinDiagram bySlug={bySlug} />
|
<SkinDiagram bySlug={bySlug} />
|
||||||
<MicroscopyDiagram bySlug={bySlug} systemSlug="citologia" title="Citologia (A Célula)" />
|
|
||||||
<MicroscopyDiagram bySlug={bySlug} systemSlug="histologia" title="Histologia (Tecidos)" />
|
<MicroscopyDiagram bySlug={bySlug} systemSlug="histologia" title="Histologia (Tecidos)" />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in new issue
Block a user