Com recórrer un gràfic de escena 3D en TypeScript

Com recórrer un gràfic de escena 3D en TypeScript

El gràfic de escena a Aspose.3D FOSS per a TypeScript és un arbre de Node objectes arrelats a scene.rootNode. El recorregut és recursiu: cada node exposa un childNodes iterable i una opcional entity propietat. Aquesta guia mostra com recórrer tot l’arbre, identificar els tipus d’entitat i recopilar estadístiques de malles.

Requisits

  • Node.js 18 o posterior
  • TypeScript 5.0 o posterior
  • @aspose/3d instal·lat

Guia pas a pas

Pas 1: Instal·lar i importar

Instal·la el paquet:

npm install @aspose/3d

Importa les classes utilitzades en aquesta guia:

import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
import { Mesh } from '@aspose/3d/entities';

Scene i Mesh són les classes principals. ObjLoadOptions s’utilitza a l’exemple de càrrega; substituïu la classe d’opcions corresponent per a altres formats.


Pas 2: Carregar una escena des d’un fitxer

Creeu un Scene i crideu scene.open() amb una ruta de fitxer. La detecció del format és automàtica a partir dels números màgics binaris, de manera que no cal especificar el format per a fitxers GLB, STL o 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}`);

També podeu carregar des d’un Buffer en memòria utilitzant scene.openFromBuffer(buffer, options); útil en pipelines sense servidor on l’E/S de disc no està disponible.


Pas 3: Escriure una funció de recorregut recursiu

Recursió sobre childNodes és el patró estàndard. La funció visita cada node en profunditat:

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);

Per a una escena amb una malla anomenada Cube, l’output serà així:

[-] 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 és null per a nodes de grup, ossos i localitzadors. El constructor.name comprovació funciona per a qualsevol tipus d’entitat: Mesh, Camera, Light, etc.


Pas 4: Accedir al tipus d’entitat a cada node

Per prendre acció segons el tipus d’entitat, utilitzeu un instanceof comproveu després de la protecció contra nul·litat:

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 és la manera més segura de confirmar que l’entitat és una malla de polígons abans d’accedir a controlPoints, polygonCount, o elements de vèrtex.


Pas 5: Filtrar nodes per tipus d’entitat

Per a recollir només els nodes que continguin malles sense imprimir tot l’arbre, utilitzeu un acumulador recursiu:

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)`);

La funció accepta un paràmetre opcional results matriu perquè els cridadors la puguin pre-poblar per combinar resultats a través de múltiples subarbres.


Pas 6: Recollir totes les malles i imprimir el recompte de vèrtexs

Amplieu el recullidor per imprimir estadístiques per malla:

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`);
}

Exemple de sortida per a una escena amb dues malles:

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>

Consells i bones pràctiques

  • Comproveu sempre la nul·litat node.entity abans d’accedir a propietats específiques de l’entitat. Molts nodes són nodes de grup purs que no contenen cap entitat.
  • Utilitzeu instanceof sobre constructor.name per a comprovacions de tipus en rutes lògiques. instanceof és segur per a refactoritzacions; comparació de cadenes a constructor.name es trenca amb la minificació.
  • Recorre via for...of sobre childNodes: l’iterable gestiona totes les mides d’array de manera segura. Evita l’indexació numèrica per a una compatibilitat futura.
  • Evita mutar l’arbre durant el recorregut: no afegeixis ni eliminis nodes dins la crida recursiva. Recull els resultats primer, després modifica.
  • Passa un array de resultats com a paràmetre: això evita assignar un array nou en cada crida recursiva i facilita la fusió dels resultats del subarbre.

Problemes comuns

SímptomaCausaCorrecció
childNodes té una longitud zero en rootNodeModel no carregatAssegura’t scene.open() s’ha completat sense errors abans de recórrer
node.entity instanceof Mesh mai certIncorrecte Mesh ruta d’importacióImporta Mesh des de @aspose/3d/entities, no prové de @aspose/3d arrel
El recorregut no detecta malles imbricadesNo s’està recorrent tots els fillsAssegureu-vos que la crida recursiva cobreixi cada element a node.childNodes
mesh.controlPoints.length és 0Malla carregada però no conté cap geometriaComproveu la font OBJ per a grups buits; utilitzeu mesh.polygonCount com a comprovació secundària
Desbordament de pila en jerarquies profundesArbre d’escena molt profund (centenars de nivells)Substituïu la recursió per una pila explícita utilitzant Array.push / Array.pop

Preguntes freqüents

Fa scene.rootNode porta ell mateix una entitat? No. El node arrel és un contenidor creat automàticament per la biblioteca. No té cap entitat. La vostra geometria i altres objectes de l’escena viuen en nodes fills un o més nivells més avall rootNode.

Quina és la diferència entre node.entity i node.entities? node.entity conté l’entitat primària única (el cas comú). Alguns fitxers FBX i COLLADA més antics poden generar nodes amb diverses entitats adjuntes; en aquest cas node.entities (plural) proporciona la llista completa.

Puc recórrer en ordre d’amplada en lloc d’ordre de profunditat? Sí. Utilitzeu una cua en lloc d’una crida recursiva: empenyeu scene.rootNode a una matriu, després desplaceu i processeu els nodes mentre empenyeu cada node’s childNodes al final de la cua.

És scene.open() sincrònic? Sí. scene.open() i scene.openFromBuffer() ambdós bloquegen el fil que crida fins que el fitxer s’ha analitzat completament. Envolta’ls en un fil de treball si necessites mantenir el bucle d’esdeveniments responsiu.

Com puc obtenir les posicions en l’espai mundial d’un node? Llegeix node.globalTransform; retorna una només de lectura GlobalTransform amb la matriu d’espai mundial, composta a partir de totes les transformacions dels avantpassats. Per a càlculs explícits de matrius, crida node.evaluateGlobalTransform(false).

Quins tipus d’entitat són possibles a més de Mesh? Camera, Light, i entitats d’esquelet/os personalitzades. Comprova node.entity.constructor.name o utilitza instanceof amb la classe específica importada des de @aspose/3d.

Vegeu també

 Català