fix6
This commit is contained in:
1 parent
0bc9708829
commit
d62f4a3227
2 files changed
+236
-4
No files matched your search
@@ -14,6 +14,7 @@ import { fileURLToPath } from "node:url";
|
||||
import { SOURCES, SOURCE_TARGETS, RESPIRATORY_KEYWORDS } from "./sources.mjs";
|
||||
import { translateToPt } from "./translate-pt.mjs";
|
||||
import { SYSTEMS } from "../seed/systems.mjs";
|
||||
import { SKELETAL_BONES } from "./skeletal-whitelist.mjs";
|
||||
|
||||
await MeshoptSimplifier.ready;
|
||||
|
||||
@@ -40,6 +41,48 @@ function slugify(s) {
|
||||
.replace(/^-+|-+$/g, "");
|
||||
}
|
||||
|
||||
// O arquivo esquelético real do Z-Anatomy mistura ossos de verdade com
|
||||
// centenas de "acidentes ósseos" (pontos de referência NO osso, ex:
|
||||
// "Deltoid tuberosity" — não são ossos) e marcadores de origem/inserção
|
||||
// muscular (ex: "Pectoralis major muscle.ol" — pertencem ao músculo).
|
||||
// Por isso usamos uma lista de INCLUSÃO com os 206 ossos reais em vez de
|
||||
// aceitar tudo: só o que bate exatamente vira uma estrutura esquelética.
|
||||
const SKELETAL_ALIASES = { atlas: "Atlas (C1)", axis: "Axis (C2)" };
|
||||
const skeletalLookup = new Map(
|
||||
Object.entries(SKELETAL_BONES).map(([key, info]) => [
|
||||
key.toLowerCase(),
|
||||
{ canonicalKey: key, ...info },
|
||||
]),
|
||||
);
|
||||
|
||||
function stripSideSuffix(rawName) {
|
||||
const m = rawName.match(/^(.*)\.([lrij])$/i);
|
||||
if (m) return { base: m[1].trim(), suffix: m[2].toLowerCase() };
|
||||
return { base: rawName.trim(), suffix: null };
|
||||
}
|
||||
|
||||
// Retorna null se o nome bruto não corresponder a nenhum dos 206 ossos —
|
||||
// nesse caso a malha é ignorada (não vira estrutura, mas continua no
|
||||
// arquivo .glb; como não há "structure" apontando pra ela, o frontend já
|
||||
// a mantém oculta automaticamente).
|
||||
function resolveSkeletalBone(rawName) {
|
||||
const { base, suffix } = stripSideSuffix(rawName);
|
||||
const normBase = base.toLowerCase();
|
||||
const aliasTarget = SKELETAL_ALIASES[normBase];
|
||||
const lookupKey = (aliasTarget ?? base).toLowerCase();
|
||||
const entry = skeletalLookup.get(lookupKey);
|
||||
if (!entry) return null;
|
||||
|
||||
if (entry.paired) {
|
||||
// Ignora variantes ".i"/".j"/sem sufixo para ossos pareados — o
|
||||
// Z-Anatomy parece manter cópias duplicadas (visão isolada vs.
|
||||
// esqueleto montado); ".l"/".r" é a marcação inequívoca de lado.
|
||||
if (suffix !== "l" && suffix !== "r") return null;
|
||||
return { canonicalKey: entry.canonicalKey, pt: entry.pt, side: suffix };
|
||||
}
|
||||
return { canonicalKey: entry.canonicalKey, pt: entry.pt, side: "single" };
|
||||
}
|
||||
|
||||
function classifySystem(key, rawName) {
|
||||
const target = SOURCE_TARGETS[key];
|
||||
if (target.system) return target.system;
|
||||
@@ -81,6 +124,7 @@ for (const { fbx, key } of SOURCES) {
|
||||
}
|
||||
|
||||
const nameCounts = new Map(); // dedupe de nomes brutos duplicados no MESMO arquivo
|
||||
const includedBones = new Set(); // dedupe de osso+lado no sistema esquelético
|
||||
let countThisFile = 0;
|
||||
|
||||
function visit(node) {
|
||||
@@ -92,12 +136,32 @@ for (const { fbx, key } of SOURCES) {
|
||||
const uniqueMeshName = n === 1 ? rawName : `${rawName} (${n})`;
|
||||
node.setName(uniqueMeshName);
|
||||
|
||||
const systemSlug = classifySystem(key, rawName);
|
||||
let systemSlug, namePt, nameEn;
|
||||
|
||||
if (key === "skeletal") {
|
||||
const resolved = resolveSkeletalBone(rawName);
|
||||
const dedupeKey = resolved ? `${resolved.canonicalKey}::${resolved.side}` : null;
|
||||
if (!resolved || includedBones.has(dedupeKey)) {
|
||||
for (const child of node.listChildren()) visit(child);
|
||||
return;
|
||||
}
|
||||
includedBones.add(dedupeKey);
|
||||
systemSlug = "esqueletico";
|
||||
const sideLabel = resolved.side === "l" ? " Esquerdo" : resolved.side === "r" ? " Direito" : "";
|
||||
const sideWord = resolved.side === "l" ? "Left " : resolved.side === "r" ? "Right " : "";
|
||||
namePt = `${resolved.pt}${sideLabel}`;
|
||||
nameEn = `${sideWord}${resolved.canonicalKey}`;
|
||||
} else {
|
||||
systemSlug = classifySystem(key, rawName);
|
||||
namePt = translateToPt(rawName) + (n > 1 ? ` (${n})` : "");
|
||||
nameEn = rawName + (n > 1 ? ` (${n})` : "");
|
||||
}
|
||||
|
||||
const material = materialFor(systemSlug);
|
||||
for (const prim of mesh.listPrimitives()) prim.setMaterial(material);
|
||||
|
||||
const sys = systemBySlug.get(systemSlug);
|
||||
const slugPart = slugify(uniqueMeshName) || `sem-nome-${totalStructures}`;
|
||||
const slugPart = slugify(key === "skeletal" ? namePt : uniqueMeshName) || `sem-nome-${totalStructures}`;
|
||||
const slug = `${systemSlug}-${slugPart}`;
|
||||
|
||||
// Gravamos o slug como "extras" do nó (→ vira object.userData no
|
||||
@@ -109,8 +173,8 @@ for (const { fbx, key } of SOURCES) {
|
||||
manifest.push({
|
||||
system: systemSlug,
|
||||
slug,
|
||||
namePt: translateToPt(rawName) + (n > 1 ? ` (${n})` : ""),
|
||||
nameEn: rawName + (n > 1 ? ` (${n})` : ""),
|
||||
namePt,
|
||||
nameEn,
|
||||
synonyms: [],
|
||||
description: "",
|
||||
regionPath: [sys?.namePt ?? systemSlug],
|
||||
@@ -130,6 +194,21 @@ for (const { fbx, key } of SOURCES) {
|
||||
console.warn(` ⚠ nenhuma malha nomeada encontrada em ${fbx} — confira o log do assimp/conversão.`);
|
||||
}
|
||||
|
||||
if (key === "skeletal") {
|
||||
const missing = [];
|
||||
for (const [boneKey, info] of Object.entries(SKELETAL_BONES)) {
|
||||
const sides = info.paired ? ["l", "r"] : ["single"];
|
||||
for (const side of sides) {
|
||||
if (!includedBones.has(`${boneKey}::${side}`)) missing.push(`${boneKey} (${side})`);
|
||||
}
|
||||
}
|
||||
console.log(` → ${includedBones.size}/206 ossos da lista de referência encontrados.`);
|
||||
if (missing.length > 0) {
|
||||
console.warn(` ⚠ ${missing.length} ossos da lista NÃO encontrados no arquivo:`);
|
||||
for (const m of missing) console.warn(` - ${m}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Malhas do Z-Anatomy vêm pensadas para um app desktop (Unity), com
|
||||
// densidade de polígonos bem maior do que um visualizador web aguenta
|
||||
// com dezenas de estruturas na tela ao mesmo tempo. MAS: muitas das
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
// Lista de referência dos 206 ossos do corpo humano adulto, usada como
|
||||
// FILTRO DE INCLUSÃO para o sistema esquelético: o arquivo real do
|
||||
// Z-Anatomy mistura ossos de verdade com centenas de "acidentes ósseos"
|
||||
// (ex: "Deltoid tuberosity", "Greater trochanter" — pontos de referência
|
||||
// NO osso, não ossos em si) e marcadores de origem/inserção muscular
|
||||
// (ex: "Pectoralis major muscle.ol") — só o que bate exatamente com um
|
||||
// nome desta lista vira uma "estrutura" clicável no sistema esquelético;
|
||||
// o resto é ignorado (mas a malha continua no arquivo, só não aparece).
|
||||
//
|
||||
// Chave = nome bruto (em inglês) como aparece no Z-Anatomy, JÁ SEM o
|
||||
// sufixo de lateralidade/variante (.l/.r/.i/.j) — ver stripSuffix() em
|
||||
// classify-skeletal.mjs. `paired: true` = existe versão esquerda/direita.
|
||||
export const SKELETAL_BONES = {
|
||||
// ---- Crânio: neurocrânio (8) ----
|
||||
"Frontal bone": { pt: "Osso Frontal", paired: false },
|
||||
"Parietal bone": { pt: "Osso Parietal", paired: true },
|
||||
"Temporal bone": { pt: "Osso Temporal", paired: true },
|
||||
"Occipital bone": { pt: "Osso Occipital", paired: false },
|
||||
"Sphenoid bone": { pt: "Osso Esfenoide", paired: false },
|
||||
"Ethmoid bone": { pt: "Osso Etmoide", paired: false },
|
||||
|
||||
// ---- Crânio: esqueleto facial (14) ----
|
||||
"Nasal bone": { pt: "Osso Nasal", paired: true },
|
||||
"Maxilla": { pt: "Maxila", paired: true },
|
||||
"Zygomatic bone": { pt: "Osso Zigomático", paired: true },
|
||||
"Lacrimal bone": { pt: "Osso Lacrimal", paired: true },
|
||||
"Palatine bone": { pt: "Osso Palatino", paired: true },
|
||||
"Inferior nasal concha bone": { pt: "Concha Nasal Inferior", paired: true },
|
||||
"Vomer": { pt: "Vômer", paired: false },
|
||||
"Mandible": { pt: "Mandíbula", paired: false },
|
||||
|
||||
// ---- Ossículos da audição (6) ----
|
||||
"Malleus": { pt: "Martelo", paired: true },
|
||||
"Incus": { pt: "Bigorna", paired: true },
|
||||
"Stapes": { pt: "Estribo", paired: true },
|
||||
|
||||
// ---- Osso hioide (1) ----
|
||||
"Hyoid bone": { pt: "Osso Hioide", paired: false },
|
||||
|
||||
// ---- Coluna vertebral (26) ----
|
||||
"Atlas (C1)": { pt: "Atlas (C1)", paired: false },
|
||||
"Axis (C2)": { pt: "Áxis (C2)", paired: false },
|
||||
"Vertebra C3": { pt: "Vértebra C3", paired: false },
|
||||
"Vertebra C4": { pt: "Vértebra C4", paired: false },
|
||||
"Vertebra C5": { pt: "Vértebra C5", paired: false },
|
||||
"Vertebra C6": { pt: "Vértebra C6", paired: false },
|
||||
"Vertebra C7": { pt: "Vértebra C7", paired: false },
|
||||
"Vertebra T1": { pt: "Vértebra T1", paired: false },
|
||||
"Vertebra T2": { pt: "Vértebra T2", paired: false },
|
||||
"Vertebra T3": { pt: "Vértebra T3", paired: false },
|
||||
"Vertebra T4": { pt: "Vértebra T4", paired: false },
|
||||
"Vertebra T5": { pt: "Vértebra T5", paired: false },
|
||||
"Vertebra T6": { pt: "Vértebra T6", paired: false },
|
||||
"Vertebra T7": { pt: "Vértebra T7", paired: false },
|
||||
"Vertebra T8": { pt: "Vértebra T8", paired: false },
|
||||
"Vertebra T9": { pt: "Vértebra T9", paired: false },
|
||||
"Vertebra T10": { pt: "Vértebra T10", paired: false },
|
||||
"Vertebra T11": { pt: "Vértebra T11", paired: false },
|
||||
"Vertebra T12": { pt: "Vértebra T12", paired: false },
|
||||
"Vertebra L1": { pt: "Vértebra L1", paired: false },
|
||||
"Vertebra L2": { pt: "Vértebra L2", paired: false },
|
||||
"Vertebra L3": { pt: "Vértebra L3", paired: false },
|
||||
"Vertebra L4": { pt: "Vértebra L4", paired: false },
|
||||
"Vertebra L5": { pt: "Vértebra L5", paired: false },
|
||||
"Sacrum": { pt: "Sacro", paired: false },
|
||||
"Coccyx": { pt: "Cóccix", paired: false },
|
||||
|
||||
// ---- Caixa torácica (25) ----
|
||||
"Sternum": { pt: "Esterno", paired: false },
|
||||
"First rib": { pt: "1ª Costela", paired: true },
|
||||
"Second rib": { pt: "2ª Costela", paired: true },
|
||||
"Third rib": { pt: "3ª Costela", paired: true },
|
||||
"Fourth rib": { pt: "4ª Costela", paired: true },
|
||||
"Fifth rib": { pt: "5ª Costela", paired: true },
|
||||
"Sixth rib": { pt: "6ª Costela", paired: true },
|
||||
"Seventh rib": { pt: "7ª Costela", paired: true },
|
||||
"Eighth rib": { pt: "8ª Costela", paired: true },
|
||||
"Ninth rib": { pt: "9ª Costela", paired: true },
|
||||
"Tenth rib": { pt: "10ª Costela", paired: true },
|
||||
"Eleventh rib": { pt: "11ª Costela", paired: true },
|
||||
"Twelfth rib": { pt: "12ª Costela", paired: true },
|
||||
|
||||
// ---- Cíngulo do membro superior (4) ----
|
||||
"Clavicle": { pt: "Clavícula", paired: true },
|
||||
"Scapula": { pt: "Escápula", paired: true },
|
||||
|
||||
// ---- Membro superior livre (60) ----
|
||||
"Humerus": { pt: "Úmero", paired: true },
|
||||
"Radius": { pt: "Rádio", paired: true },
|
||||
"Ulna": { pt: "Ulna", paired: true },
|
||||
"Scaphoid bone": { pt: "Osso Escafoide", paired: true },
|
||||
"Lunate bone": { pt: "Osso Semilunar", paired: true },
|
||||
"Triquetrum bone": { pt: "Osso Piramidal", paired: true },
|
||||
"Pisiform bone": { pt: "Osso Pisiforme", paired: true },
|
||||
"Trapezium bone": { pt: "Osso Trapézio", paired: true },
|
||||
"Trapezoid bone": { pt: "Osso Trapezoide", paired: true },
|
||||
"Capitate bone": { pt: "Osso Capitato", paired: true },
|
||||
"Hamate bone": { pt: "Osso Hamato", paired: true },
|
||||
"First metacarpal bone": { pt: "1º Osso Metacarpal", paired: true },
|
||||
"Second metacarpal bone": { pt: "2º Osso Metacarpal", paired: true },
|
||||
"Third metacarpal bone": { pt: "3º Osso Metacarpal", paired: true },
|
||||
"Fourth metacarpal bone": { pt: "4º Osso Metacarpal", paired: true },
|
||||
"Fifth metacarpal bone": { pt: "5º Osso Metacarpal", paired: true },
|
||||
"Proximal phalanx of first finger of hand": { pt: "Falange Proximal do Polegar", paired: true },
|
||||
"Distal phalanx of first finger of hand": { pt: "Falange Distal do Polegar", paired: true },
|
||||
"Proximal phalanx of second finger of hand": { pt: "Falange Proximal do 2º Dedo da Mão", paired: true },
|
||||
"Middle phalanx of second finger of hand": { pt: "Falange Média do 2º Dedo da Mão", paired: true },
|
||||
"Distal phalanx of second finger of hand": { pt: "Falange Distal do 2º Dedo da Mão", paired: true },
|
||||
"Proximal phalanx of third finger of hand": { pt: "Falange Proximal do 3º Dedo da Mão", paired: true },
|
||||
"Middle phalanx of third finger of hand": { pt: "Falange Média do 3º Dedo da Mão", paired: true },
|
||||
"Distal phalanx of third finger of hand": { pt: "Falange Distal do 3º Dedo da Mão", paired: true },
|
||||
"Proximal phalanx of fourth finger of hand": { pt: "Falange Proximal do 4º Dedo da Mão", paired: true },
|
||||
"Middle phalanx of fourth finger of hand": { pt: "Falange Média do 4º Dedo da Mão", paired: true },
|
||||
"Distal phalanx of fourth finger of hand": { pt: "Falange Distal do 4º Dedo da Mão", paired: true },
|
||||
"Proximal phalanx of fifth finger of hand": { pt: "Falange Proximal do 5º Dedo da Mão", paired: true },
|
||||
"Middle phalanx of fifth finger of hand": { pt: "Falange Média do 5º Dedo da Mão", paired: true },
|
||||
"Distal phalanx of fifth finger of hand": { pt: "Falange Distal do 5º Dedo da Mão", paired: true },
|
||||
|
||||
// ---- Cíngulo do membro inferior (2) ----
|
||||
"Hip bone": { pt: "Osso do Quadril", paired: true },
|
||||
|
||||
// ---- Membro inferior livre (60) ----
|
||||
"Femur": { pt: "Fêmur", paired: true },
|
||||
"Patella": { pt: "Patela", paired: true },
|
||||
"Tibia": { pt: "Tíbia", paired: true },
|
||||
"Fibula": { pt: "Fíbula", paired: true },
|
||||
"Talus": { pt: "Tálus", paired: true },
|
||||
"Calcaneus": { pt: "Calcâneo", paired: true },
|
||||
"Navicular bone": { pt: "Osso Navicular", paired: true },
|
||||
"Medial cuneiform bone": { pt: "Cuneiforme Medial", paired: true },
|
||||
"Intermediate cuneiform bone": { pt: "Cuneiforme Intermédio", paired: true },
|
||||
"Lateral cuneiform bone": { pt: "Cuneiforme Lateral", paired: true },
|
||||
"Cuboid bone": { pt: "Osso Cuboide", paired: true },
|
||||
"First metatarsal bone": { pt: "1º Osso Metatarsal", paired: true },
|
||||
"Second metatarsal bone": { pt: "2º Osso Metatarsal", paired: true },
|
||||
"Third metatarsal bone": { pt: "3º Osso Metatarsal", paired: true },
|
||||
"Fourth metatarsal bone": { pt: "4º Osso Metatarsal", paired: true },
|
||||
"Fifth metatarsal bone": { pt: "5º Osso Metatarsal", paired: true },
|
||||
"Proximal phalanx of first finger of foot": { pt: "Falange Proximal do Hálux", paired: true },
|
||||
"Distal phalanx of first finger of foot": { pt: "Falange Distal do Hálux", paired: true },
|
||||
"Proximal phalanx of second finger of foot": { pt: "Falange Proximal do 2º Dedo do Pé", paired: true },
|
||||
"Middle phalanx of second finger of foot": { pt: "Falange Média do 2º Dedo do Pé", paired: true },
|
||||
"Distal phalanx of second finger of foot": { pt: "Falange Distal do 2º Dedo do Pé", paired: true },
|
||||
"Proximal phalanx of third finger of foot": { pt: "Falange Proximal do 3º Dedo do Pé", paired: true },
|
||||
"Middle phalanx of third finger of foot": { pt: "Falange Média do 3º Dedo do Pé", paired: true },
|
||||
"Distal phalanx of third finger of foot": { pt: "Falange Distal do 3º Dedo do Pé", paired: true },
|
||||
"Proximal phalanx of fourth finger of foot": { pt: "Falange Proximal do 4º Dedo do Pé", paired: true },
|
||||
"Middle phalanx of fourth finger of foot": { pt: "Falange Média do 4º Dedo do Pé", paired: true },
|
||||
"Distal phalanx of fourth finger of foot": { pt: "Falange Distal do 4º Dedo do Pé", paired: true },
|
||||
"Proximal phalanx of fifth finger of foot": { pt: "Falange Proximal do 5º Dedo do Pé", paired: true },
|
||||
"Middle phalanx of fifth finger of foot": { pt: "Falange Média do 5º Dedo do Pé", paired: true },
|
||||
"Distal phalanx of fifth finger of foot": { pt: "Falange Distal do 5º Dedo do Pé", paired: true },
|
||||
};
|
||||
Reference in new issue
Block a user