Kako pretraživati 3D graf scene u TypeScript-u

Kako pretraživati 3D graf scene u TypeScript-u

Scene graph u Aspose.3D FOSS za TypeScript je stablo od Node objekata ukorenjenih u scene.rootNode. Traversal je rekurzivan: svaki čvor izlaže childNodes iterable i opcioni entity property. Ovaj vodič pokazuje kako proći kroz celo stablo, identifikovati tipove entiteta i sakupiti mesh statistiku.

Preduslovi

  • Node.js 18 ili noviji
  • TypeScript 5.0 ili noviji
  • @aspose/3d instalirano

Vodič korak po korak

Korak 1: Instalacija i uvoz

Instalirajte paket:

npm install @aspose/3d

Uvezite klase koje se koriste u ovom vodiču:

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

Scene i Mesh su osnovne klase. ObjLoadOptions se koristi u primeru učitavanja; zamenite odgovarajuću klasu opcija za druge formate.


Korak 2: Učitajte scenu iz fajla

Kreirajte Scene i pozovite scene.open() sa putanjom do fajla. Detekcija formata je automatska na osnovu binarnih magičnih brojeva, tako da ne morate da navodite format za GLB, STL ili 3MF fajlove:

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

Takođe možete učitati iz Buffer u memoriji koristeći scene.openFromBuffer(buffer, options); korisno u serverless pipeline-ovima gde disk I/O nije dostupan.


Korak 3: Napišite rekurzivnu funkciju za pretragu

Rekurzija preko childNodes je standardni obrazac. Funkcija posećuje svaki čvor dubinski:

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

Za scenu sa jednim mrežom pod imenom Cube, izlaz će izgledati ovako:

[-] 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 za grupne čvorove, kosti i lokatore. The constructor.name provera radi za bilo koji tip entiteta: Mesh, Camera, Light, itd.


Korak 4: Pristup tipu entiteta na svakom čvoru

Da biste preduzeli akciju na osnovu tipa entiteta, koristite an instanceof proveri nakon null zaštite:

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 najsigurniji način da se potvrdi da je entitet polygon mesh pre pristupa controlPoints, polygonCount, ili elemente vrha.


Korak 5: Filtriraj čvorove po tipu entiteta

Da biste sakupili samo čvorove koji sadrže mrežu, a da ne ispisujete celo stablo, koristite rekurzivni akumulator:

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

Funkcija prihvata opcioni results niz kako bi pozivaoci mogli unapred da ga popune za spajanje rezultata kroz više podstabala.


Korak 6: Prikupi sve mreže i prikaži broj vrhova

Proširite kolektor da ispisuje statistiku po svakoj mreži:

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

Primer izlaza za scenu sa dve mreže:

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>

Saveti i najbolje prakse

  • Uvek proveravaj null node.entity pre pristupa svojstvima specifičnim za entitet. Mnogi čvorovi su čisti grupni čvorovi koji ne nose entitet.
  • Koristi instanceof preko constructor.name za proveru tipova u logičkim granama. instanceof je siguran za refaktorisanje; poređenje stringova na constructor.name se ruši pri minifikaciji.
  • Prođite putem for...of preko childNodes: iterabilni objekat bezbedno obrađuje sve veličine nizova. Izbegavajte numeričko indeksiranje radi buduće kompatibilnosti.
  • Izbegavajte mutiranje stabla tokom prolaza: ne dodajte niti uklanjajte čvorove unutar rekurzivnog poziva. Prvo sakupite rezultate, zatim izmenite.
  • Prosledite niz rezultata kao parametar: ovo sprečava alociranje novog niza pri svakom rekurzivnom pozivu i olakšava spajanje rezultata podstabla.

Uobičajeni problemi

SimptomUzrokIspravka
childNodes ima nultu dužinu na rootNodeModel nije učitanOsiguraj scene.open() završeno bez greške pre prelaženja
node.entity instanceof Mesh nikada tačnoPogrešno Mesh putanja uvozaUvoz Mesh od @aspose/3d/entities, ne od @aspose/3d root
Pretraga propušta ugnježdene meshesNije rekurzija u sve childrenOsigurajte da rekurzivni poziv pokriva svaki element u node.childNodes
mesh.controlPoints.length je 0Mesh učitan, ali ne sadrži geometrijuProverite OBJ izvor za prazne grupe; koristite mesh.polygonCount kao sekundarnu proveru
Stack overflow na dubokim hijerarhijamaVeoma duboko stablo scene (stotine nivoa)Zamenite rekurziju eksplicitnim stekom koristeći Array.push / Array.pop

Često postavljana pitanja

Da li scene.rootNode sam nosi entitet? Ne. Korenski čvor je kontejner koji biblioteka automatski kreira. On nema entitet. Vaša geometrija i ostali objekti scene nalaze se na čvorovima‑deci jedan ili više nivoa ispod rootNode.

Koja je razlika između node.entity i node.entities? node.entity sadrži jedinstvenu primarnu entitet (uobičajeni slučaj). Neki stariji FBX i COLLADA fajlovi mogu proizvesti čvorove sa više prikačenih entiteta; u tom slučaju node.entities (plural) pruža kompletnu listu.

Mogu li da pretražujem u breadth‑first redosledu umesto depth‑first? Da. Koristite red umesto rekurzivnog poziva: push scene.rootNode u niz, zatim shift i procesirajte čvorove dok push‑ujete svaki čvorov childNodes na kraj reda.

Da li je scene.open() sinhronizovan? Da. scene.open() i scene.openFromBuffer() oba blokiraju pozivajući thread dok se fajl potpuno ne parsira. Umotajte ih u radni thread ako treba da održite odzivnost event loop‑a.

Kako da dobijem pozicije u svetskom prostoru iz čvora? Čitaj node.globalTransform; vraća samo za čitanje GlobalTransform sa matricom u svetskom prostoru, sastavljenom od svih transformacija pretka. Za eksplicitnu matricnu matematiku, pozovite node.evaluateGlobalTransform(false).

Koje vrste entiteta su moguće pored Mesh? Camera, Light, i prilagođene entitete skeleta/kosti. Proveri node.entity.constructor.name ili koristite instanceof sa specifičnom klasom uvezenom iz @aspose/3d.

Vidi takođe

 Српски