Πώς να διασχίσετε ένα 3D γράφημα σκηνής σε TypeScript

Πώς να διασχίσετε ένα 3D γράφημα σκηνής σε TypeScript

Το γράφημα σκηνής στο Aspose.3D FOSS for TypeScript είναι ένα δέντρο αντικειμένων Node με ρίζα στο scene.rootNode. Η διάσχιση είναι αναδρομική: κάθε κόμβος εκθέτει έναν επαναλήψιμο childNodes και μια προαιρετική ιδιότητα entity. Αυτός ο οδηγός δείχνει πώς να περιηγηθείτε σε ολόκληρο το δέντρο, να εντοπίσετε τύπους οντοτήτων και να συλλέξετε στατιστικά πλέγματος.

Προαπαιτούμενα

  • Node.js 18 ή νεότερο
  • TypeScript 5.0 ή νεότερο
  • @aspose/3d εγκατεστημένο

Οδηγός βήμα προς βήμα

Βήμα 1: Εγκατάσταση και Εισαγωγή

Εγκαταστήστε το πακέτο:

npm install @aspose/3d

Εισαγάγετε τις κλάσεις που χρησιμοποιούνται σε αυτόν τον οδηγό:

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

Scene και Mesh είναι οι βασικές κλάσεις. ObjLoadOptions χρησιμοποιείται στο παράδειγμα φόρτωσης· αντικαταστήστε την αντίστοιχη κλάση επιλογών για άλλες μορφές.


Βήμα 2: Φόρτωση μιας σκηνής από αρχείο

Δημιουργήστε ένα Scene και καλέστε το scene.open() με διαδρομή αρχείου. Η ανίχνευση μορφής είναι αυτόματη από τους δυαδικούς αριθμούς μαγικού, έτσι δεν χρειάζεται να καθορίσετε τη μορφή για αρχεία GLB, STL ή 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}`);

Μπορείτε επίσης να φορτώσετε από ένα Buffer στη μνήμη χρησιμοποιώντας scene.openFromBuffer(buffer, options); χρήσιμο σε serverless pipelines όπου δεν είναι διαθέσιμη η πρόσβαση σε δίσκο.


Βήμα 3: Γράψτε μια Αναδρομική Συνάρτηση Διάσχισης

Η επανάληψη πάνω στο childNodes είναι το τυπικό μοτίβο. Η συνάρτηση επισκέπτεται κάθε κόμβο βάθος‑πρώτα:

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

Για μια σκηνή με ένα πλέγμα με όνομα Cube, η έξοδος θα φαίνεται ως εξής:

[-] 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 είναι null για κόμβους ομάδας, οστά και εντοπιστές. Ο έλεγχος constructor.name λειτουργεί για οποιονδήποτε τύπο οντότητας: Mesh, Camera, Light, κ.λπ.


Βήμα 4: Πρόσβαση στον τύπο οντότητας σε κάθε κόμβο

Για να εκτελέσετε ενέργεια βάσει του τύπου οντότητας, χρησιμοποιήστε έναν έλεγχο instanceof μετά το null guard:

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 είναι ο πιο ασφαλής τρόπος να επιβεβαιώσετε ότι η οντότητα είναι ένα πολυγωνικό πλέγμα πριν αποκτήσετε πρόσβαση σε controlPoints, polygonCount ή στοιχεία κορυφής.


Βήμα 5: Φιλτράρισμα Κόμβων ανά Τύπο Οντότητας

Για τη συλλογή μόνο των κόμβων που περιέχουν πλέγμα χωρίς την εκτύπωση του πλήρους δέντρου, χρησιμοποιήστε έναν αναδρομικό συσσωρευτή:

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

Η συνάρτηση δέχεται έναν προαιρετικό results πίνακα ώστε οι καλούντες να μπορούν να τον προσυμπληρώσουν για τη συγχώνευση αποτελεσμάτων σε πολλαπλά υποδέντρα.


Βήμα 6: Συλλογή όλων των πλέγματων και εκτύπωση του αριθμού των κορυφών

Επεκτείνετε τον συλλέκτη ώστε να εκτυπώνει στατιστικά ανά πλέγμα:

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

Παράδειγμα εξόδου για μια σκηνή με δύο πλέγματα:

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>

Συμβουλές και βέλτιστες πρακτικές

  • Πάντα ελέγχετε για null node.entity πριν αποκτήσετε πρόσβαση σε ιδιότητες συγκεκριμένες για οντότητα. Πολλοί κόμβοι είναι καθαροί κόμβοι ομάδας που δεν φέρουν οντότητα.
  • Χρησιμοποιήστε instanceof αντί για constructor.name για ελέγχους τύπου σε λογικές διαδρομές. instanceof είναι ασφαλές για ανασχεδιασμό· η σύγκριση συμβολοσειρών στο constructor.name σπάει με τη μινιφικέση.
  • Διασχίστε μέσω for...of αντί για childNodes: η επαναληπτική δομή διαχειρίζεται όλα τα μεγέθη πίνακα με ασφάλεια. Αποφύγετε την αριθμητική δεικτοδότηση για μελλοντική συμβατότητα.
  • Αποφύγετε την τροποποίηση του δέντρου κατά τη διάρκεια της διαδρομής: μην προσθέτετε ή αφαιρείτε κόμβους μέσα στην αναδρομική κλήση. Συλλέξτε πρώτα τα αποτελέσματα, έπειτα τροποποιήστε.
  • Περάστε έναν πίνακα αποτελεσμάτων ως παράμετρο: αυτό αποτρέπει την εκχώρηση νέου πίνακα σε κάθε αναδρομική κλήση και καθιστά εύκολη τη συγχώνευση των αποτελεσμάτων των υποδέντρων.

Κοινά προβλήματα

ΣύμπτωμαΑιτίαΔιόρθωση
childNodes έχει μηδενικό μήκος στο rootNodeΤο μοντέλο δεν έχει φορτωθείΒεβαιωθείτε ότι το scene.open() ολοκληρώθηκε χωρίς σφάλμα πριν από την διέλευση
node.entity instanceof Mesh ποτέ δεν είναι αληθέςΛάθος διαδρομή εισαγωγής MeshΕισάγετε το Mesh από το @aspose/3d/entities, όχι από τη ρίζα @aspose/3d
Η διέλευση παραλείπει ενσωματωμένα πλέγματαΔεν γίνεται επανάληψη σε όλα τα παιδιάΒεβαιωθείτε ότι η αναδρομική κλήση καλύπτει κάθε στοιχείο στο node.childNodes
mesh.controlPoints.length είναι 0Το πλέγμα φορτώθηκε αλλά δεν περιέχει γεωμετρίαΕλέγξτε την πηγή OBJ για κενές ομάδες· χρησιμοποιήστε το mesh.polygonCount ως δευτερεύουσα επαλήθευση
Υπέρβαση στοίβας σε βαθιές ιεραρχίεςΠολύ βαθύ δέντρο σκηνής (εκατοντάδες επίπεδα)Αντικαταστήστε την επανάληψη με μια ρητή στοίβα χρησιμοποιώντας Array.push / Array.pop

Συχνές Ερωτήσεις

Φέρει το scene.rootNode από μόνο του μια οντότητα;
Όχι. Ο ριζικός κόμβος είναι ένας κοντέινερ που δημιουργείται αυτόματα από τη βιβλιοθήκη. Δεν έχει οντότητα. Η γεωμετρία σας και άλλα αντικείμενα σκηνής ζουν σε θυγατρικούς κόμβους ένα ή περισσότερα επίπεδα κάτω από το rootNode.

Ποια είναι η διαφορά μεταξύ node.entity και node.entities;
node.entity περιέχει τη μοναδική κύρια οντότητα (η κοινή περίπτωση). Ορισμένα παλαιότερα αρχεία FBX και COLLADA μπορεί να δημιουργούν κόμβους με πολλαπλές συνημμένες οντότητες· σε αυτήν την περίπτωση node.entities (πληθυντικός) παρέχει τη πλήρη λίστα.

Μπορώ να διασχίσω με σειρά κατά πλάτος αντί για βάθος;
Ναι. Χρησιμοποιήστε μια queue αντί για μια recursive call: push scene.rootNode σε ένα array, μετά shift και process nodes ενώ push το childNodes του κάθε node στην queue tail.

Είναι το scene.open() συγχρονισμένο; Ναι. Τα scene.open() και scene.openFromBuffer() αποκλείουν το νήμα κλήσης μέχρι να αναλυθεί πλήρως το αρχείο. Τυλίξτε τα σε νήμα εργασίας εάν χρειάζεται να διατηρήσετε το βρόχο γεγονότων ανταποκρινόμενο.

Πώς να λάβω θέσεις σε παγκόσμιο χώρο από έναν κόμβο;
Διαβάστε node.globalTransform; επιστρέφει ένα μόνο για ανάγνωση GlobalTransform με τη μητρώα σε παγκόσμιο χώρο, συντιθέμενη από όλους τους μετασχηματισμούς προγόνων. Για ρητή μαθηματική επεξεργασία μητρώας, καλέστε node.evaluateGlobalTransform(false).

Τι τύποι οντοτήτων είναι δυνατοί εκτός από Mesh;
Camera, Light, και προσαρμοσμένες οντότητες σκελετού/οστών. Ελέγξτε node.entity.constructor.name ή χρησιμοποιήστε instanceof με την συγκεκριμένη κλάση που εισάγεται από @aspose/3d.

Δείτε επίσης

 Ελληνικά