Cara Menelusuri Graf Adegan 3D dalam TypeScript
Graf adegan dalam Aspose.3D FOSS untuk TypeScript adalah pokok objek Node yang berakar pada scene.rootNode. Penelusuran bersifat rekursif: setiap nod memperlihatkan iterable childNodes dan sifat pilihan entity. Panduan ini menunjukkan cara melintasi seluruh pokok, mengenal pasti jenis entiti, dan mengumpul statistik mesh.
Prasyarat
- Node.js 18 atau versi lebih baru
- TypeScript 5.0 atau versi lebih baru
@aspose/3ddipasang
Panduan Langkah demi Langkah
Langkah 1: Pasang dan Import
Pasang pakej:
npm install @aspose/3dImport kelas yang digunakan dalam panduan ini:
import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
import { Mesh } from '@aspose/3d/entities';Scene dan Mesh adalah kelas teras. ObjLoadOptions digunakan dalam contoh pemuatan; gantikan kelas pilihan yang sepadan untuk format lain.
Langkah 2: Muatkan Adegan dari Fail
Buat Scene dan panggil scene.open() dengan laluan fail. Pengesanan format adalah automatik daripada nombor sihir binari, jadi anda tidak perlu menentukan format untuk fail GLB, STL, atau 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}`);Anda juga boleh memuatkan dari Buffer dalam memori menggunakan scene.openFromBuffer(buffer, options); berguna dalam pipeline tanpa pelayan di mana I/O cakera tidak tersedia.
Langkah 3: Tulis Fungsi Traversal Rekursif
Pengulangan atas childNodes adalah corak standard. Fungsi melawat setiap nod secara kedalaman pertama:
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);Untuk adegan dengan satu mesh bernama Cube, output akan kelihatan seperti:
[-] 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 ialah null untuk nod kumpulan, tulang, dan penentu lokasi. Pemeriksaan constructor.name berfungsi untuk mana-mana jenis entiti: Mesh, Camera, Light, dll.
Langkah 4: Akses Jenis Entiti pada Setiap Nod
Untuk mengambil tindakan berdasarkan jenis entiti, gunakan pemeriksaan instanceof selepas penjaga null:
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 adalah cara yang paling selamat untuk mengesahkan entiti adalah mesh poligon sebelum mengakses controlPoints, polygonCount, atau elemen vertex.
Langkah 5: Tapis Nod mengikut Jenis Entiti
Untuk mengumpulkan hanya nod yang mempunyai mesh tanpa mencetak keseluruhan pokok, gunakan akumulator rekursif:
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)`);Fungsi ini menerima tatasusunan results opsional supaya pemanggil dapat pra‑mengisi ia untuk menggabungkan hasil merentasi pelbagai subpokok.
Langkah 6: Kumpulkan Semua Mesh dan Cetak Bilangan Vertex
Kembangkan pengumpul untuk mencetak statistik per‑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`);
}Contoh output untuk adegan dua-mesh:
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>
Petua dan Amalan Terbaik
- Sentiasa periksa null
node.entitysebelum mengakses sifat khusus entiti. Banyak nod adalah nod kumpulan tulen yang tidak membawa entiti. - Gunakan
instanceofberbandingconstructor.nameuntuk pemeriksaan jenis dalam laluan logik.instanceofselamat untuk refaktor; perbandingan rentetan padaconstructor.nameakan rosak dengan minifikasi. - Lalui melalui
for...ofberbandingchildNodes: iterable mengendalikan semua saiz tatasusunan dengan selamat. Elakkan penomboran indeks secara numerik untuk keserasian ke hadapan. - Elakkan mengubah pokok semasa penelusuran: jangan menambah atau membuang nod dalam panggilan rekursif. Kumpulkan keputusan terlebih dahulu, kemudian ubah.
- Hantar satu tatasusunan keputusan sebagai parameter: ini mengelakkan peruntukan tatasusunan baru pada setiap panggilan rekursif dan memudahkan menggabungkan keputusan subpokok.
Isu Umum
| Gejala | Punca | Penyelesaian |
|---|---|---|
childNodes mempunyai panjang sifar pada rootNode | Model tidak dimuatkan | Pastikan scene.open() selesai tanpa ralat sebelum menelusuri |
node.entity instanceof Mesh tidak pernah benar | Laluan import Mesh yang salah | Import Mesh dari @aspose/3d/entities, bukan dari akar @aspose/3d |
| Penelusuran terlepas mesh bersarang | Tidak melakukan rekursi ke semua anak | Pastikan panggilan rekursif meliputi setiap elemen dalam node.childNodes |
mesh.controlPoints.length ialah 0 | Mesh dimuatkan tetapi tidak mengandungi geometri | Periksa sumber OBJ untuk kumpulan kosong; gunakan mesh.polygonCount sebagai pemeriksaan sekunder |
| Tumpukan melimpah pada hierarki yang dalam | Pokok adegan yang sangat dalam (ratusan peringkat) | Gantikan rekursi dengan tumpukan eksplisit menggunakan Array.push / Array.pop |
Soalan Lazim
Adakah scene.rootNode itu sendiri membawa entiti?
Tidak. Nod akar ialah kontena yang dicipta secara automatik oleh perpustakaan. Ia tidak mempunyai entiti. Geometri anda dan objek‑objek adegan lain berada pada nod anak satu atau lebih tahap di bawah rootNode.
Apakah perbezaan antara node.entity dan node.entities?node.entity menyimpan entiti utama tunggal (kes biasa). Beberapa fail FBX dan COLLADA lama mungkin menghasilkan nod dengan pelbagai entiti yang dilampirkan; dalam kes itu node.entities (jamak) menyediakan senarai penuh.
Bolehkah saya menelusuri dalam susunan lebar-pertama dan bukannya dalam susunan dalam-pertama?
Ya. Gunakan queue dan bukannya panggilan rekursif: push scene.rootNode ke dalam array, kemudian shift dan proses nod sambil push setiap childNodes nod ke ekor queue.
Adakah scene.open() segerak?
Ya. scene.open() dan scene.openFromBuffer() kedua-duanya menyekat thread pemanggil sehingga fail selesai diparse. Balutkan mereka dalam thread pekerja jika anda perlu mengekalkan gelung peristiwa responsif.
Bagaimana saya dapat kedudukan ruang-dunia dari nod?
Baca node.globalTransform; ia mengembalikan GlobalTransform yang hanya‑baca dengan matriks ruang-dunia, yang terdiri daripada semua transformasi nenek moyang. Untuk matematik matriks eksplisit, panggil node.evaluateGlobalTransform(false).
Apakah jenis entiti yang mungkin selain Mesh?Camera, Light, dan entiti rangka/tulang khusus. Semak node.entity.constructor.name atau gunakan instanceof dengan kelas khusus yang diimport dari @aspose/3d.