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
|
||||
|
||||
Reference in new issue
Block a user