Ako prechádzať 3D graf scény v TypeScript
Scénový graf v Aspose.3D FOSS pre TypeScript je stromom Node objektov zakorenených v scene.rootNode. Prechádzanie je rekurzívne: každý uzol poskytuje childNodes iterovateľný a voliteľný entity vlastnosť. Tento sprievodca ukazuje, ako prejsť celý strom, identifikovať typy entít a zhromaždiť štatistiky mesh.
Požiadavky
- Node.js 18 alebo novší
- TypeScript 5.0 alebo novší
@aspose/3dnainštalovaný
Postupný návod
Krok 1: Inštalácia a import
Nainštalujte balík:
npm install @aspose/3dImportujte triedy použité v tomto návode:
import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
import { Mesh } from '@aspose/3d/entities';Scene a Mesh sú základné triedy. ObjLoadOptions sa používa v príklade načítania; nahraďte zodpovedajúcou triedou možností pre iné formáty.
Krok 2: Načítanie scény zo súboru
Vytvorte Scene a zavolajte scene.open() s cestou k súboru. Detekcia formátu je automatická na základe binárnych magických čísel, takže nie je potrebné špecifikovať formát pre súbory GLB, STL alebo 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 tiež načítať z Buffer v pamäti pomocou scene.openFromBuffer(buffer, options); užitočné v serverless pipeline, kde nie je k dispozícii diskové I/O.
Krok 3: Napíšte rekurzívnu funkciu prechodu
Rekurzia nad childNodes je štandardný vzor. Funkcia prechádza každý uzol do hĺbky:
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);Pre scénu s jedným meshom s názvom Cube, výstup bude vyzerať 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=“Copy code”
<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 pre skupinové uzly, kosti a lokátory. The constructor.name kontrola funguje pre akýkoľvek typ entity: Mesh, Camera, Light, atď.
Krok 4: Prístup k typu entity na každom uzle
Ak chcete konať na základe typu entity, použite instanceof kontrolu po null guarde:
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 najbezpečnejší spôsob, ako potvrdiť, že entita je polygonová sieť pred prístupom k controlPoints, polygonCount, alebo k vrcholovým prvkom.
Krok 5: Filtrovať uzly podľa typu entity
Ak chcete zhromaždiť iba uzly obsahujúce sieť bez vypisovania celého stromu, použite rekurzívny 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)`);Funkcia prijíma voliteľný results pole, aby volajúci mohli predvyplniť pre zlúčenie výsledkov naprieč viacerými podstromami.
Krok 6: Zozbierať všetky mesh a vypísať počty vrcholov
Rozšírte zberač tak, aby vypisoval štatistiky pre každú sieť:
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`);
}Ukážkový výstup pre scénu s dvoma sieťami:
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=“Copy code”
<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 osvedčené postupy
- Vždy kontrolujte null
node.entitypred prístupom k špecifickým vlastnostiam entity. Mnoho uzlov sú čisté skupinové uzly, ktoré neobsahujú žiadnu entitu. - Použite
instanceofnamiestoconstructor.namepre kontrolu typov v logických vetvách.instanceofje bezpečný pri refaktorovaní; porovnanie reťazcov naconstructor.namesa rozbije pri minifikácii. - Prechádzať cez
for...ofnadchildNodes: iterovateľný objekt bezpečne spracováva všetky veľkosti poľa. Vyhnite sa číselnému indexovaniu pre budúcu kompatibilitu. - Vyhnite sa modifikácii stromu počas prechádzania: nepridávajte ani neodstraňujte uzly v rámci rekurzívneho volania. Najprv zozbierajte výsledky, potom ich upravte.
- Predajte pole výsledkov ako parameter: tým sa zabráni alokácii nového poľa pri každom rekurzívnom volaní a uľahčuje zlučovanie výsledkov podstromov.
Bežné problémy
| Symptóm | Príčina | Oprava |
|---|---|---|
childNodes má nulovú dĺžku na rootNode | Model nie je načítaný | Zabezpečte scene.open() dokončené bez chyby pred prechádzaním |
node.entity instanceof Mesh nikdy pravda | Nesprávne Mesh cesta importu | Import Mesh z @aspose/3d/entities, nie z @aspose/3d root |
| Traversal vynecháva vnorené siete | Nerecursing do všetkých detí | Zabezpečte, aby recursive volanie pokrývalo každý prvok v node.childNodes |
mesh.controlPoints.length je 0 | Mesh načítaná, ale neobsahuje žiadnu geometriu | Skontrolujte OBJ zdroj pre prázdne skupiny; použite mesh.polygonCount ako sekundárnu kontrolu |
| Pretečenie zásobníka pri hlbokých hierarchiách | Veľmi hlboký strom scény (stovky úrovní) | Nahraďte rekurziu explicitným zásobníkom pomocou Array.push / Array.pop |
Často kladené otázky
Robí scene.rootNode sám nesie entitu? Nie. Korenový uzol je kontajner vytvorený automaticky knižnicou. Nemá žiadnu entitu. Vaša geometria a ďalšie objekty scény sa nachádzajú na podriadených uzloch jeden alebo viac úrovní nižšie. rootNode.
Aký je rozdiel medzi node.entity a node.entities? node.entity obsahuje jedinú primárnu entitu (bežný prípad). Niektoré staršie súbory FBX a COLLADA môžu vytvárať uzly s viacerými pripojenými entitami; v takom prípade node.entities (množné číslo) poskytuje úplný zoznam.
Môžem prechádzať v poradí šírkového prehľadávania namiesto hĺbkového? Áno. Použite frontu namiesto rekurzívneho volania: push scene.rootNode do poľa, potom posunúť a spracovať uzly pri vkladaní každého uzla childNodes do konca fronty.
Je scene.open() synchrónny? Áno. scene.open() a scene.openFromBuffer() oba blokujú volajúce vlákno, kým nie je súbor úplne spracovaný. Zabaľte ich do pracovného vlákna, ak potrebujete, aby slučka udalostí zostala responzívna.
Ako získať pozície vo svetovom priestore z uzla? Čítať node.globalTransform; vráti len na čítanie GlobalTransform s maticou vo svetovom priestore, zostavenou zo všetkých transformácií predkov. Pre explicitné maticové výpočty zavolajte node.evaluateGlobalTransform(false).
Aké typy entít sú možné okrem Mesh? Camera, Light, a vlastných entít kostry/kosti. Skontrolujte node.entity.constructor.name alebo použite instanceof s konkrétnou triedou importovanou z @aspose/3d.