Jak procházet 3D scénový graf v TypeScriptu
Graf scény v Aspose.3D FOSS pro TypeScript je strom Node objektů s kořenem v scene.rootNode. Procházení je rekurzivní: každý uzel poskytuje childNodes iterable a volitelnou entity vlastnost. Tento průvodce ukazuje, jak projít celý strom, identifikovat typy entit a shromáždit statistiky mesh.
Požadavky
- Node.js 18 nebo novější
- TypeScript 5.0 nebo novější
@aspose/3dnainstalováno
Průvodce krok za krokem
Krok 1: Instalace a import
Nainstalujte balíček:
npm install @aspose/3dImportujte třídy použité v tomto průvodci:
import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
import { Mesh } from '@aspose/3d/entities';Scene a Mesh jsou základní třídy. ObjLoadOptions se používá v příkladu načítání; nahraďte odpovídající třídu možností pro jiné formáty.
Krok 2: Načíst scénu ze souboru
Vytvořte Scene a zavolejte scene.open() s cestou k souboru. Detekce formátu je automatická na základě binárních magických čísel, takže není nutné zadávat formát pro soubory GLB, STL nebo 3MF:
import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
const scene = new Scene();
scene.open('model.obj', new ObjLoadOptions());
console.log(`Root node: "${scene.rootNode.name}"`);
console.log(`Top-level children: ${scene.rootNode.childNodes.length}`);Můžete také načíst z Buffer v paměti pomocí scene.openFromBuffer(buffer, options); užitečné v serverless pipelinech, kde není k dispozici diskové I/O.
Krok 3: Napište rekurzivní funkci pro průchod
Rekurze nad childNodes je standardní vzor. Funkce prochází každý uzel hloubkově:
function traverse(node: any, depth = 0): void {
const indent = ' '.repeat(depth);
const entityType = node.entity ? node.entity.constructor.name : '-';
console.log(`${indent}[${entityType}] ${node.name}`);
for (const child of node.childNodes) {
traverse(child, depth + 1);
}
}
traverse(scene.rootNode);Pro scénu s jedním meshem pojmenovaným Cube bude výstup vypadat takto:
[-] RootNode
[Mesh] Cube<button class=“hextra-code-copy-btn hx-group/copybtn hx-transition-all active:hx-opacity-50 hx-bg-primary-700/5 hx-border hx-border-black/5 hx-text-gray-600 hover:hx-text-gray-900 hx-rounded-md hx-p-1.5 dark:hx-bg-primary-300/10 dark:hx-border-white/10 dark:hx-text-gray-400 dark:hover:hx-text-gray-50” title=“Zkopírovat kód”
<div class="copy-icon group-[.copied]/copybtn:hx-hidden hx-pointer-events-none hx-h-4 hx-w-4"></div>
<div class="success-icon hx-hidden group-[.copied]/copybtn:hx-block hx-pointer-events-none hx-h-4 hx-w-4"></div>
node.entity je null pro skupinové uzly, kosti a lokátory. Kontrola constructor.name funguje pro jakýkoli typ entity: Mesh, Camera, Light, atd.
Krok 4: Přístup k typu entity na každém uzlu
Pro provedení akce na základě typu entity použijte kontrolu instanceof po null guardu:
import { Mesh } from '@aspose/3d/entities';
function visitWithTypeCheck(node: any, depth = 0): void {
const indent = ' '.repeat(depth);
if (node.entity instanceof Mesh) {
const mesh = node.entity as Mesh;
console.log(`${indent}MESH "${node.name}": ${mesh.controlPoints.length} vertices`);
} else if (node.entity) {
console.log(`${indent}${node.entity.constructor.name} "${node.name}"`);
} else {
console.log(`${indent}GROUP "${node.name}"`);
}
for (const child of node.childNodes) {
visitWithTypeCheck(child, depth + 1);
}
}
visitWithTypeCheck(scene.rootNode);instanceof Mesh je nejbezpečnější způsob, jak potvrdit, že entita je polygonová síť, před přístupem k controlPoints, polygonCount nebo vrcholovým prvkům.
Krok 5: Filtrovat uzly podle typu entity
Chcete-li shromáždit pouze uzly nesoucí síť bez výpisu celého stromu, použijte rekurzivní akumulátor:
import { Mesh } from '@aspose/3d/entities';
function collectMeshes(
node: any,
results: Array<{ name: string; mesh: Mesh }> = []
): Array<{ name: string; mesh: Mesh }> {
if (node.entity instanceof Mesh) {
results.push({ name: node.name, mesh: node.entity as Mesh });
}
for (const child of node.childNodes) {
collectMeshes(child, results);
}
return results;
}
const meshNodes = collectMeshes(scene.rootNode);
console.log(`Found ${meshNodes.length} mesh node(s)`);Funkce přijímá volitelné pole results, aby volající mohli předem naplnit jej pro sloučení výsledků napříč více podstromy.
Krok 6: Shromáždit všechny sítě a vypsat počty vrcholů
Rozšiřte sběrač tak, aby vypisoval statistiky pro každý mesh:
import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
import { Mesh } from '@aspose/3d/entities';
function collectMeshes(node: any, results: Array<{name: string, mesh: Mesh}> = []) {
if (node.entity instanceof Mesh) {
results.push({ name: node.name, mesh: node.entity as Mesh });
}
for (const child of node.childNodes) {
collectMeshes(child, results);
}
return results;
}
const scene = new Scene();
scene.open('model.obj', new ObjLoadOptions());
const meshes = collectMeshes(scene.rootNode);
for (const { name, mesh } of meshes) {
console.log(`${name}: ${mesh.controlPoints.length} vertices, ${mesh.polygonCount} polygons`);
}Příklad výstupu pro scénu se dvěma sítěmi:
Cube: 8 vertices, 6 polygons
Sphere: 482 vertices, 480 polygons<button class=“hextra-code-copy-btn hx-group/copybtn hx-transition-all active:hx-opacity-50 hx-bg-primary-700/5 hx-border hx-border-black/5 hx-text-gray-600 hover:hx-text-gray-900 hx-rounded-md hx-p-1.5 dark:hx-bg-primary-300/10 dark:hx-border-white/10 dark:hx-text-gray-400 dark:hover:hx-text-gray-50” title=“Zkopírovat kód”
<div class="copy-icon group-[.copied]/copybtn:hx-hidden hx-pointer-events-none hx-h-4 hx-w-4"></div>
<div class="success-icon hx-hidden group-[.copied]/copybtn:hx-block hx-pointer-events-none hx-h-4 hx-w-4"></div>
Tipy a osvědčené postupy
- Vždy provádějte kontrolu null
node.entitypřed přístupem k vlastnostem specifickým pro entitu. Mnoho uzlů jsou čisté skupinové uzly, které neobsahují žádnou entitu. - Používejte
instanceofmístoconstructor.namepro kontrolu typů v logických cestách.instanceofje bezpečný při refaktoringu; porovnání řetězců naconstructor.nameselže při minifikaci. - Procházejte pomocí
for...ofmístochildNodes: iterovatelný objekt bezpečně zvládá všechny velikosti polí. Vyhněte se číselnému indexování pro budoucí kompatibilitu. - Vyhněte se mutaci stromu během procházení: nepřidávejte ani neodstraňujte uzly uvnitř rekurzivního volání. Nejprve shromážděte výsledky, pak upravte.
- Předávejte pole výsledků jako parametr: tím se zabrání alokaci nového pole při každém rekurzivním volání a usnadní se sloučení výsledků podstromů.
Časté problémy
| Příznak | Příčina | Oprava |
|---|---|---|
childNodes má nulovou délku na rootNode | Model není načten | Zajistěte, aby scene.open() byl dokončen bez chyby před procházením |
node.entity instanceof Mesh nikdy není pravda | Špatná cesta importu Mesh | Importujte Mesh z @aspose/3d/entities, ne z kořene @aspose/3d |
| Procházení opomíjí vnořené sítě | Nerekurzuje do všech potomků | Zajistěte, aby rekurzivní volání pokrývalo každý prvek v node.childNodes |
mesh.controlPoints.length je 0 | Síť načtena, ale neobsahuje žádnou geometrii | Zkontrolujte zdroj OBJ pro prázdné skupiny; použijte mesh.polygonCount jako sekundární kontrolu |
| Přetečení zásobníku při hlubokých hierarchiích | Velmi hluboký strom scény (stovky úrovní) | Nahraďte rekurzi explicitním zásobníkem pomocí Array.push / Array.pop |
Často kladené otázky
Nese scene.rootNode samotný entitu?
Ne. Kořenový uzel je kontejner vytvořený automaticky knihovnou. Nemá žádnou entitu. Vaše geometrie a další objekty scény jsou umístěny na podřízených uzlech o jednu nebo více úrovní níže než rootNode.
Jaký je rozdíl mezi node.entity a node.entities?
node.entity obsahuje jedinou primární entitu (běžný případ). Některé starší soubory FBX a COLLADA mohou vytvářet uzly s více připojenými entitami; v takovém případě node.entities (množné číslo) poskytuje úplný seznam.
Mohu procházet v šířkovém pořadí místo hloubkového?
Ano. Použijte frontu místo rekurzivního volání: vložte scene.rootNode do pole, poté posuňte a zpracovávejte uzly při vkládání childNodes každého uzlu do konce fronty.
Je scene.open() synchronní?
Ano. scene.open() a scene.openFromBuffer() blokují volající vlákno, dokud není soubor plně zpracován. Zabalte je do pracovního vlákna, pokud potřebujete udržet smyčku událostí responzivní.
Jak získám pozice ve světovém prostoru z uzlu?
Přečtěte node.globalTransform; vrací jen pro čtení GlobalTransform s maticí ve světovém prostoru, složenou ze všech transformací předků. Pro explicitní maticové výpočty zavolejte node.evaluateGlobalTransform(false).
Jaké typy entit jsou možné kromě Mesh?Camera, Light a vlastní entity kostry/kosti. Zkontrolujte node.entity.constructor.name nebo použijte instanceof se specifickou třídou importovanou z @aspose/3d.