Ako prechádzať 3D graf scény v TypeScript

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/3d nainštalovaný

Postupný návod

Krok 1: Inštalácia a import

Nainštalujte balík:

npm install @aspose/3d

Importujte 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.entity pred prístupom k špecifickým vlastnostiam entity. Mnoho uzlov sú čisté skupinové uzly, ktoré neobsahujú žiadnu entitu.
  • Použite instanceof namiesto constructor.name pre kontrolu typov v logických vetvách. instanceof je bezpečný pri refaktorovaní; porovnanie reťazcov na constructor.name sa rozbije pri minifikácii.
  • Prechádzať cez for...of nad childNodes: 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ómPríčinaOprava
childNodes má nulovú dĺžku na rootNodeModel nie je načítanýZabezpečte scene.open() dokončené bez chyby pred prechádzaním
node.entity instanceof Mesh nikdy pravdaNesprávne Mesh cesta importuImport Mesh z @aspose/3d/entities, nie z @aspose/3d root
Traversal vynecháva vnorené sieteNerecursing do všetkých detíZabezpečte, aby recursive volanie pokrývalo každý prvok v node.childNodes
mesh.controlPoints.length je 0Mesh načítaná, ale neobsahuje žiadnu geometriuSkontrolujte OBJ zdroj pre prázdne skupiny; použite mesh.polygonCount ako sekundárnu kontrolu
Pretečenie zásobníka pri hlbokých hierarchiáchVeľ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.

Pozri tiež

 Slovenčina