306 lines
13 KiB
JavaScript
306 lines
13 KiB
JavaScript
// Gera manifest-tegumentar.json: o Z-Anatomy-Sample não inclui camadas de
|
|
// pele (epiderme/derme/hipoderme) nem seus anexos (folículo piloso, glândulas
|
|
// etc.) — são estruturas microscópicas, nenhum dataset de corpo inteiro as
|
|
// modela como malha. Por isso o sistema tegumentar (só ele — os demais
|
|
// sistemas continuam vindo de malhas reais via scripts/extract-manifest.mjs,
|
|
// sem nenhuma mudança) usa um "bloco" esquemático construído com geometria
|
|
// paramétrica simples (ver scripts/geometry.mjs e scripts/build-seed-models.mjs,
|
|
// também exclusivos do tegumentar — só o script "build:tegumentar" os chama).
|
|
//
|
|
// MODELO EM DEGRAUS ("staircase cutaway"), como os blocos didáticos de pele
|
|
// usados em aula: cada camada mais profunda tem uma extensão maior no eixo Y
|
|
// do que a camada acima dela, então a camada de cima NÃO cobre a de baixo
|
|
// inteira — sobra um "degrau" onde a superfície da camada mais profunda fica
|
|
// exposta e visível de cima, em vez de tudo empilhado e escondido atrás da
|
|
// camada mais externa (o problema do modelo anterior, que empilhava 3 caixas
|
|
// finas exatamente uma sobre a outra).
|
|
// epiderme: ████ (degrau 1 — só ela)
|
|
// derme: ████ ████ (degraus 1+2 — expõe o degrau 2)
|
|
// hipoderme: ████ ████ ████ (degraus 1+2+3 — expõe o degrau 3)
|
|
// A parede vertical no degrau 1 (voltada para -Y) mostra as 3 camadas
|
|
// empilhadas de uma vez, como um corte transversal — é ali na frente que
|
|
// ficam os componentes (pelo, glândulas, vasos, corpúsculos), flutuando bem
|
|
// próximos da parede (fora do bloco opaco, pra nunca ficarem escondidos
|
|
// dentro dele) e alinhados em X e Z com a camada a que pertencem. Gire o
|
|
// modelo com o mouse pra ver o bloco de todos os ângulos.
|
|
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 = [];
|
|
|
|
// O Z-Anatomy-Sample (verificado na íntegra em Assets/Models/1.0 Models,
|
|
// via API do GitHub) NÃO tem nenhum arquivo de pele/tegumento — só
|
|
// SkeletalSystem, MuscularSystem, NervousSystem, CardioVascular,
|
|
// VisceralSystem, Joints, LymphoidOrgans, Regions e Reference lines —
|
|
// confirma o que já estava documentado: não existe dataset de corpo
|
|
// inteiro com camadas de pele, daqui vem a necessidade do bloco esquemático.
|
|
//
|
|
// Todo o desenho abaixo é escrito em "unidades de projeto" (a escala
|
|
// original, pensada peça por peça) e só na hora de gerar a estrutura final
|
|
// o add() aplica o fator SCALE — assim dá pra aumentar/diminuir o bloco
|
|
// inteiro (pedido do usuário: "aumente o tamanho dele") mudando um único
|
|
// número, sem precisar recalcular à mão a posição de cada uma das ~20 peças.
|
|
const SCALE = 3;
|
|
|
|
function scaleShape(shape) {
|
|
const scaled = { ...shape };
|
|
for (const key of ["w", "h", "d", "r", "rt", "rb"]) {
|
|
if (scaled[key] != null) scaled[key] *= SCALE;
|
|
}
|
|
return scaled;
|
|
}
|
|
|
|
// Escala a posição em torno da âncora do bloco (CX, CY, Z_TOP) — assim o
|
|
// bloco cresce a partir do ponto onde "gruda" no antebraço, em vez de só se
|
|
// afastar dele.
|
|
function scalePosition([x, y, z]) {
|
|
return [CX + (x - CX) * SCALE, CY + (y - CY) * SCALE, Z_TOP - (Z_TOP - z) * SCALE];
|
|
}
|
|
|
|
function add(system, slug, namePt, nameEn, region, shape, position, opts = {}) {
|
|
structures.push({
|
|
system,
|
|
slug,
|
|
namePt,
|
|
nameEn,
|
|
synonyms: opts.synonyms ?? [],
|
|
description: opts.description ?? "",
|
|
regionPath: region,
|
|
modelFile: `${system}.glb`,
|
|
meshName: slug,
|
|
shape: scaleShape(shape),
|
|
position: scalePosition(position),
|
|
rotation: opts.rotation ?? [0, 0, 0],
|
|
color: opts.color,
|
|
});
|
|
}
|
|
|
|
const TX = ["Sistema Tegumentar", "Corte Transversal da Pele (em Degraus)"];
|
|
|
|
// Centro/âncora do bloco (coordenadas do mundo, mesma escala do corpo
|
|
// Z-Anatomy — colado na face do antebraço, como antes).
|
|
const CX = 0.24;
|
|
const CY = 0.85;
|
|
|
|
const BLOCK_W = 0.14; // extensão em X (largura do bloco)
|
|
const STEP = 0.05; // profundidade em Y de cada degrau
|
|
const Y_FRONT = CY - 1.5 * STEP; // borda frontal do bloco (degrau 1), onde fica a parede de corte
|
|
|
|
// Espessura (eixo Z) de cada camada e a profundidade onde cada uma começa/
|
|
// termina — definidas uma vez aqui pra tudo (camadas E componentes) usar os
|
|
// mesmos números e não desalinhar.
|
|
const Z_TOP = 0.038; // superfície da epiderme
|
|
const D_EPI = 0.005;
|
|
const D_DERME = 0.012;
|
|
const D_HIPO = 0.016;
|
|
const Z_EPI_BOTTOM = Z_TOP - D_EPI; // 0.033 = limite epiderme/derme
|
|
const Z_DERME_BOTTOM = Z_EPI_BOTTOM - D_DERME; // 0.021 = limite derme/hipoderme
|
|
const Z_HIPO_BOTTOM = Z_DERME_BOTTOM - D_HIPO; // 0.005 = base do bloco
|
|
|
|
// Os componentes (pelo, glândulas, vasos, corpúsculos...) ficam flutuando
|
|
// bem à frente da parede de corte (fora do volume opaco do bloco, pra nunca
|
|
// serem encobertos por ele) — cada "coluna" X é um componente diferente,
|
|
// como um desenho de corte histológico visto de frente.
|
|
const FRONT_Y = Y_FRONT - 0.01;
|
|
const ROT_Z = [Math.PI / 2, 0, 0]; // deixa cilindros na vertical (eixo nasce em Y, isso gira pra Z)
|
|
|
|
// ---- As 3 camadas da pele, em degraus ----
|
|
add(
|
|
"tegumentar", "epiderme", "Epiderme", "Epidermis", [...TX],
|
|
{ type: "box", w: BLOCK_W, h: STEP, d: D_EPI },
|
|
[CX, Y_FRONT + STEP / 2, Z_TOP - D_EPI / 2],
|
|
{
|
|
color: "#e0a96d",
|
|
description:
|
|
"Camada mais externa e avascular da pele, formada por epitélio pavimentoso estratificado queratinizado; sua função é a principal barreira contra perda de água, atrito, micro-organismos e raios UV (com a ajuda da melanina).",
|
|
},
|
|
);
|
|
add(
|
|
"tegumentar", "derme", "Derme", "Dermis", [...TX],
|
|
{ type: "box", w: BLOCK_W, h: 2 * STEP, d: D_DERME },
|
|
[CX, Y_FRONT + STEP, Z_EPI_BOTTOM - D_DERME / 2],
|
|
{
|
|
color: "#c97b4a",
|
|
description:
|
|
"Camada de tecido conjuntivo rica em colágeno e elastina, vasos sanguíneos, terminações nervosas e os anexos cutâneos (folículos pilosos, glândulas); sustenta a epiderme e dá à pele resistência e elasticidade. Repare no degrau exposto: é a derme aparecendo onde a epiderme já não cobre mais.",
|
|
},
|
|
);
|
|
add(
|
|
"tegumentar", "hipoderme", "Hipoderme (Tecido Subcutâneo)", "Hypodermis (Subcutaneous Tissue)", [...TX],
|
|
{ type: "box", w: BLOCK_W, h: 3 * STEP, d: D_HIPO },
|
|
[CX, CY, Z_DERME_BOTTOM - D_HIPO / 2],
|
|
{
|
|
color: "#f2d4a3",
|
|
description:
|
|
"Camada mais profunda, rica em tecido adiposo; isola termicamente, absorve impactos e serve de reserva de energia — não é tecnicamente parte da pele, mas a conecta aos músculos e ossos subjacentes. É o degrau mais largo, exposto onde nem epiderme nem derme mais chegam.",
|
|
},
|
|
);
|
|
|
|
// ---- 6 "colunas" de componentes, cada uma numa posição X própria, todas
|
|
// flutuando no mesmo plano frontal (FRONT_Y) — a altura (Z) de cada peça
|
|
// já indica em qual camada ela vive. ----
|
|
const COL = (i) => CX - 0.06 + i * 0.024; // 6 colunas igualmente espaçadas
|
|
|
|
// ---- Coluna 1: complexo pilossebáceo (pelo + folículo + músculo eretor + glândula sebácea) ----
|
|
const HX = COL(0);
|
|
const HAIR_TILT = [Math.PI / 2 + 0.3, 0.2, 0];
|
|
|
|
add(
|
|
"tegumentar", "pelo", "Pelo (Haste Capilar)", "Hair Shaft", [...TX, "Complexo Pilossebáceo"],
|
|
{ type: "cylinder", rt: 0.0006, rb: 0.001, h: 0.02 }, [HX, FRONT_Y, Z_TOP + 0.01],
|
|
{
|
|
color: "#4a3527",
|
|
rotation: HAIR_TILT,
|
|
description:
|
|
"Porção do pelo visível acima da superfície da pele, formada por células queratinizadas mortas; sua cor vem da melanina produzida no bulbo piloso.",
|
|
},
|
|
);
|
|
add(
|
|
"tegumentar", "foliculo-piloso", "Folículo Piloso", "Hair Follicle", [...TX, "Complexo Pilossebáceo"],
|
|
{ type: "cylinder", rt: 0.0022, rb: 0.0022, h: 0.03 }, [HX, FRONT_Y, 0.023],
|
|
{
|
|
color: "#8a6a52",
|
|
rotation: HAIR_TILT,
|
|
description:
|
|
"Invaginação da epiderme para dentro da derme/hipoderme que envolve e forma a raiz do pelo; suas células-tronco também participam da regeneração da epiderme após lesões.",
|
|
},
|
|
);
|
|
add(
|
|
"tegumentar", "bulbo-piloso", "Bulbo Piloso", "Hair Bulb", [...TX, "Complexo Pilossebáceo"],
|
|
{ type: "sphere", r: 0.003 }, [HX - 0.004, FRONT_Y, 0.008],
|
|
{
|
|
color: "#6b4c38",
|
|
description:
|
|
"Base dilatada do folículo, na hipoderme; contém a papila dérmica (com vasos que nutrem o pelo) e a matriz germinativa, onde o pelo é produzido por divisão celular.",
|
|
},
|
|
);
|
|
add(
|
|
"tegumentar", "musculo-eretor-do-pelo", "Músculo Eretor do Pelo", "Arrector Pili Muscle", [...TX, "Complexo Pilossebáceo"],
|
|
{ type: "box", w: 0.0012, h: 0.014, d: 0.003 }, [HX + 0.004, FRONT_Y, 0.0285],
|
|
{
|
|
color: "#b5433a",
|
|
rotation: [1.05, 0, 0],
|
|
description:
|
|
"Pequeno músculo liso ligado obliquamente ao folículo; ao se contrair (frio, medo), eleva o pelo e comprime a glândula sebácea — é o responsável pela pele arrepiada (piloereção).",
|
|
},
|
|
);
|
|
add(
|
|
"tegumentar", "glandula-sebacea", "Glândula Sebácea", "Sebaceous Gland", [...TX, "Complexo Pilossebáceo"],
|
|
{ type: "sphere", r: 0.004 }, [HX + 0.007, FRONT_Y, 0.031],
|
|
{
|
|
color: "#f2c14e",
|
|
description:
|
|
"Glândula anexa ao folículo piloso que secreta sebo — mistura oleosa que lubrifica o pelo e a pele e ajuda a impermeabilizá-la; sua obstrução está por trás da acne.",
|
|
},
|
|
);
|
|
|
|
// ---- Coluna 2: glândula sudorípara (écrina) + ducto + poro ----
|
|
const SX = COL(1);
|
|
add(
|
|
"tegumentar", "glandula-sudoripara", "Glândula Sudorípara (Écrina)", "Eccrine Sweat Gland", [...TX, "Glândula Sudorípara"],
|
|
{ type: "sphere", r: 0.005 }, [SX, FRONT_Y, 0.012],
|
|
{
|
|
color: "#4f7cff",
|
|
description:
|
|
"Porção secretora enovelada, na derme profunda/hipoderme; produz o suor (água + eletrólitos), principal mecanismo de termorregulação do corpo por evaporação.",
|
|
},
|
|
);
|
|
add(
|
|
"tegumentar", "ducto-sudoriparo", "Ducto Sudoríparo", "Sweat Duct", [...TX, "Glândula Sudorípara"],
|
|
{ type: "cylinder", rt: 0.0012, rb: 0.0012, h: 0.026 }, [SX, FRONT_Y, 0.025],
|
|
{
|
|
color: "#7ba6ff",
|
|
rotation: ROT_Z,
|
|
description:
|
|
"Canal que conduz o suor da glândula, atravessando a derme e a epiderme, até a superfície da pele.",
|
|
},
|
|
);
|
|
add(
|
|
"tegumentar", "poro-sudoriparo", "Poro Sudoríparo", "Sweat Pore", [...TX, "Glândula Sudorípara"],
|
|
{ type: "sphere", r: 0.0012 }, [SX, FRONT_Y, Z_TOP + 0.0005],
|
|
{
|
|
color: "#a9c6ff",
|
|
description: "Abertura do ducto sudoríparo na superfície da epiderme, por onde o suor é liberado.",
|
|
},
|
|
);
|
|
|
|
// ---- Coluna 3: plexo vascular dérmico ----
|
|
const VX = COL(2);
|
|
add(
|
|
"tegumentar", "arteriola-dermica", "Arteríola Dérmica", "Dermal Arteriole", [...TX, "Plexo Vascular Dérmico"],
|
|
{ type: "cylinder", rt: 0.0012, rb: 0.0012, h: 0.01 }, [VX - 0.004, FRONT_Y, 0.026],
|
|
{
|
|
color: "#c1121f",
|
|
rotation: ROT_Z,
|
|
description: "Ramo arterial que leva sangue oxigenado ao plexo de capilares da derme, nutrindo a pele (a epiderme em si não tem vasos).",
|
|
},
|
|
);
|
|
add(
|
|
"tegumentar", "venula-dermica", "Vênula Dérmica", "Dermal Venule", [...TX, "Plexo Vascular Dérmico"],
|
|
{ type: "cylinder", rt: 0.0012, rb: 0.0012, h: 0.01 }, [VX + 0.004, FRONT_Y, 0.026],
|
|
{
|
|
color: "#3457d5",
|
|
rotation: ROT_Z,
|
|
description: "Ramo venoso que drena o sangue do plexo capilar dérmico de volta à circulação.",
|
|
},
|
|
);
|
|
add(
|
|
"tegumentar", "alca-capilar-dermica", "Alça Capilar Dérmica", "Dermal Capillary Loop", [...TX, "Plexo Vascular Dérmico"],
|
|
{ type: "cylinder", rt: 0.0006, rb: 0.0006, h: 0.006 }, [VX, FRONT_Y, 0.033],
|
|
{
|
|
color: "#e0464f",
|
|
rotation: ROT_Z,
|
|
description: "Alça de capilares que sobe até as papilas dérmicas, logo abaixo da epiderme, levando nutrientes e oxigênio para a base da epiderme por difusão.",
|
|
},
|
|
);
|
|
|
|
// ---- Coluna 4: corpúsculo de Meissner (toque leve) ----
|
|
add(
|
|
"tegumentar", "corpusculo-de-meissner", "Corpúsculo de Meissner", "Meissner's Corpuscle", [...TX, "Receptores Sensoriais"],
|
|
{ type: "sphere", r: 0.0022 }, [COL(3), FRONT_Y, 0.033],
|
|
{
|
|
color: "#34d399",
|
|
description:
|
|
"Receptor sensorial encapsulado logo abaixo da epiderme; detecta toque leve e vibrações de baixa frequência — muito numeroso na ponta dos dedos e nos lábios.",
|
|
},
|
|
);
|
|
|
|
// ---- Coluna 5: corpúsculo de Pacini (pressão profunda) ----
|
|
add(
|
|
"tegumentar", "corpusculo-de-pacini", "Corpúsculo de Pacini", "Pacinian Corpuscle", [...TX, "Receptores Sensoriais"],
|
|
{ type: "sphere", r: 0.006 }, [COL(4), FRONT_Y, 0.012],
|
|
{
|
|
color: "#22d3ee",
|
|
description:
|
|
"Grande receptor sensorial encapsulado em camadas (como uma cebola), na hipoderme; detecta pressão profunda e vibrações de alta frequência.",
|
|
},
|
|
);
|
|
|
|
// ---- Coluna 6: lóbulos de tecido adiposo da hipoderme ----
|
|
const FX = COL(5);
|
|
const FAT_OFFSETS = [
|
|
[0, 0.013], [0.007, 0.017], [-0.006, 0.010], [0.003, 0.019],
|
|
];
|
|
FAT_OFFSETS.forEach(([dx, dz], i) => {
|
|
add(
|
|
"tegumentar", `adipocito-${i + 1}`, `Lóbulo de Tecido Adiposo ${i + 1}`, `Adipose Tissue Lobule ${i + 1}`,
|
|
[...TX, "Tecido Adiposo"],
|
|
{ type: "sphere", r: 0.006 }, [FX + dx, FRONT_Y, dz],
|
|
{
|
|
color: "#f7e08c",
|
|
description:
|
|
"Grupo de adipócitos (células de gordura) da hipoderme; armazena energia, isola termicamente e amortece impactos mecânicos.",
|
|
},
|
|
);
|
|
});
|
|
|
|
writeFileSync(
|
|
path.resolve(__dirname, "manifest-tegumentar.json"),
|
|
JSON.stringify({ structures }, null, 2),
|
|
);
|
|
|
|
console.log(`manifest-tegumentar.json gerado com ${structures.length} estruturas.`);
|