TypeScript'te 3B Sahne Grafiğini Nasıl Gezinilir
TypeScript için Aspose.3D FOSS’taki sahne grafiği bir ağaçtır Node kökü scene.rootNode. Traversal özyinelemelidir: her düğüm bir childNodes iterable ve isteğe bağlı bir entity özellik. Bu kılavuz, tüm ağacı nasıl dolaşacağınızı, varlık türlerini tanımlamayı ve mesh istatistiklerini toplamayı gösterir.
Önkoşullar
- Node.js 18 veya daha yeni bir sürüm
- TypeScript 5.0 veya daha yeni bir sürüm
@aspose/3dkuruldu
Adım Adım Kılavuz
Adım 1: Kurulum ve İçe Aktarma
Paketi kurun:
npm install @aspose/3dBu kılavuzda kullanılan sınıfları içe aktarın:
import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
import { Mesh } from '@aspose/3d/entities';Scene ve Mesh temel sınıflardır. ObjLoadOptions yükleme örneğinde kullanılır; diğer formatlar için eşleşen seçenek sınıfını değiştirin.
Adım 2: Bir Dosyadan Sahne Yükleyin
Bir oluşturun Scene ve çağırın scene.open() bir dosya yolu ile. Biçim algılaması ikili sihirli sayılardan otomatik olarak yapılır, bu yüzden GLB, STL veya 3MF dosyaları için biçimi belirtmeniz gerekmez:
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}`);Ayrıca birinden yükleyebilirsiniz Buffer bellekte kullanarak scene.openFromBuffer(buffer, options); disk I/O’nun mevcut olmadığı sunucusuz boru hatlarında faydalıdır.
Adım 3: Rekürsif Bir Gezinme Fonksiyonu Yazın
Özyineleme tamamlandı childNodes standart bir desendir. Fonksiyon her düğümü derinlik öncelikli olarak ziyaret eder:
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);Bir mesh adıyla bir sahne için Cube, çıktı şu şekilde görünecek:
[-] 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 dır null grup düğümleri, kemikler ve konumlandırıcılar için. Bu constructor.name kontrol, herhangi bir varlık türü için çalışır: Mesh, Camera, Light, vb.
Adım 4: Her Düğümdeki Varlık Türüne Erişme
Varlık türüne göre işlem yapmak için, bir instanceof null korumasından sonra kontrol et:
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 erişmeden önce entity’nin bir polygon mesh olduğunu doğrulamanın en güvenli yoludur controlPoints, polygonCount, ya da vertex elemanları.
Adım 5: Düğümleri Varlık Türüne Göre Filtrele
Tam ağacı yazdırmadan yalnızca ağ (mesh) içeren düğümleri toplamak için, özyinelemeli bir biriktirici kullanın:
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)`);Fonksiyon isteğe bağlı bir results array, çağırıcıların birden fazla alt ağaçta sonuçları birleştirmek amacıyla önceden doldurabilmesi için.
Adım 6: Tüm Mesh’leri Topla ve Vertex Sayılarını Yazdır
Toplayıcıyı, her ağ (mesh) için istatistikleri yazdıracak şekilde genişletin:
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`);
}İki ağ (mesh) içeren bir sahne için örnek çıktı:
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>
İpuçları ve En İyi Uygulamalar
- Her zaman null kontrolü yap
node.entityentity-özgü özelliklere erişmeden önce. Birçok düğüm, hiçbir entity taşımayan saf grup düğümüdür. - Kullan
instanceofyerineconstructor.namemantıksal yollar içinde type kontrolleri için.instanceofrefactor-safe’dir; string karşılaştırmasıconstructor.nameminification ile bozulur. - Şu yöntemle dolaş
for...ofüzerindechildNodes: iterable tüm dizi boyutlarını güvenli bir şekilde işler. İleri uyumluluk için sayısal indekslemeyi önleyin. - Geçiş sırasında ağacı değiştirmekten kaçının: özyinelemeli çağrı içinde düğüm eklemeyin veya kaldırmayın. Önce sonuçları toplayın, ardından değiştirin.
- Sonuçlar dizisini bir parametre olarak geçirin: bu, her özyinelemeli çağrıda yeni bir dizi tahsis edilmesini önler ve alt ağaç sonuçlarını birleştirmeyi kolaylaştırır.
Yaygın Sorunlar
| Semptom | Neden | Düzeltme |
|---|---|---|
childNodes şu üzerinde uzunluğu sıfır rootNode | Model yüklenmedi | Garantile scene.open() gezinmeden önce hatasız tamamlanmış |
node.entity instanceof Mesh asla doğru değil | Yanlış Mesh import yolu | İçe Aktar Mesh den @aspose/3d/entities, den değil @aspose/3d kök |
| Gezi, iç içe ağları kaçırıyor | Tüm alt öğelere yineleme yapılmıyor | Yinelemeli çağrının her öğeyi kapsadığından emin olun node.childNodes |
mesh.controlPoints.length 0’dır | Mesh yüklendi ancak geometri içermiyor | Boş gruplar için OBJ kaynağını kontrol edin; kullanın mesh.polygonCount ikincil bir kontrol olarak |
| Derin hiyerarşilerde yığın taşması | Çok derin sahne ağacı (yüzlerce seviye) | Özyinelemeyi, kullanarak açık bir yığınla değiştirin Array.push / Array.pop |
Sık Sorulan Sorular
Yapıyor mu scene.rootNode kendi içinde bir varlık taşıyor mu? Hayır. Kök düğüm, kütüphane tarafından otomatik olarak oluşturulan bir kapsayıcıdır. Bir varlığı yoktur. Geometriniz ve diğer sahne nesneleriniz, bir veya daha fazla seviye aşağıdaki alt düğümlerde bulunur. rootNode.
Arasındaki fark nedir node.entity ve node.entities? node.entity tek bir birincil varlığı tutar (yaygın durum). Bazı eski FBX ve COLLADA dosyaları, birden fazla ekli varlık içeren düğümler üretebilir; bu durumda node.entities (çoğul) tam listeyi sağlar.
Derinlik öncelikli yerine genişlik öncelikli olarak dolaşabilir miyim? Evet. Özyinelemeli çağrı yerine bir kuyruk kullanın: push scene.rootNode bir diziye, ardından shift yaparak ve düğümleri işlerken her düğümün childNodes kuyruk kuyruğunun sonuna ekleyin.
Bu scene.open() senkron mu? Evet. scene.open() ve scene.openFromBuffer() her ikisi de dosya tamamen ayrıştırılana kadar çağıran iş parçacığını engeller. Olay döngüsünün yanıt vermesini sürdürmeniz gerekiyorsa, bunları bir işçi iş parçacığında sarın.
Bir düğümden dünya uzayı konumlarını nasıl alabilirim? Oku node.globalTransform; yalnızca okuma izni döndürür GlobalTransform tüm ata dönüşümlerinden oluşan dünya-uzayı matrisiniyle. Açık matris hesaplamaları için, şu fonksiyonu çağırın node.evaluateGlobalTransform(false).
Hangi varlık türleri mümkündür, şunların dışında Mesh? Camera, Light, ve özel iskelet/kemik varlıkları. Kontrol edin node.entity.constructor.name ya da kullan instanceof belirli sınıfı şu kaynaktan içe aktararak @aspose/3d.