TypeScript में 3D सीन ग्राफ़ को कैसे ट्रैवर्स करें
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) का उपयोग करके भी लोड कर सकते हैं; यह सर्वरलेस पाइपलाइन में उपयोगी है जहाँ डिस्क I/O उपलब्ध नहीं है।
चरण 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: प्रत्येक नोड पर एंटिटी टाइप तक पहुँचें
इकाई प्रकार के आधार पर कार्रवाई करने के लिए, null guard के बाद एक instanceof जांच का उपयोग करें:
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>
सलाह और सर्वोत्तम प्रथाएँ
- Always null-check
node.entityentity‑specific properties तक पहुँचने से पहले करें। कई नोड्स शुद्ध समूह नोड्स होते हैं जिनमें कोई entity नहीं होती। - Use
instanceofoverconstructor.namelogic paths में type checks के लिए।instanceofrefactor‑safe है;constructor.nameपर string comparison मिनिफिकेशन से टूट जाता है। - Traverse via
for...ofoverchildNodes: iterable सभी array आकारों को सुरक्षित रूप से संभालता है। आगे की संगतता के लिए numeric indexing से बचें। - Avoid mutating the tree during traversal: recursive call के भीतर नोड्स को जोड़ें या हटाएँ नहीं। पहले परिणाम एकत्र करें, फिर संशोधित करें।
- Pass a results array as a parameter: इससे प्रत्येक recursive call पर नई array आवंटित करने से बचा जा सकता है और subtree परिणामों को मर्ज करना आसान हो जाता है।
सामान्य समस्याएँ
| लक्षण | कारण | समाधान |
|---|---|---|
childNodes का शून्य लंबाई है rootNode पर | मॉडल लोड नहीं हुआ | ट्रैवर्स करने से पहले सुनिश्चित करें कि scene.open() बिना त्रुटि के पूरा हो गया है |
node.entity instanceof Mesh कभी सत्य नहीं होता | गलत Mesh आयात पथ | Mesh को @aspose/3d/entities से आयात करें, @aspose/3d रूट से नहीं |
| ट्रैवर्स ने नेस्टेड मेषेज़ को मिस किया | सभी चाइल्ड्स में पुनरावृति नहीं हो रही | सुनिश्चित करें कि पुनरावर्ती कॉल node.childNodes में हर तत्व को कवर करे |
mesh.controlPoints.length शून्य है | मेष लोड हुआ लेकिन इसमें कोई ज्योमेट्री नहीं है | खाली समूहों के लिए OBJ स्रोत जांचें; द्वितीयक जांच के रूप में mesh.polygonCount का उपयोग करें |
| गहरी पदानुक्रमों पर स्टैक ओवरफ़्लो | बहुत गहरी सीन ट्री (सैकड़ों स्तर) | Array.push / Array.pop का उपयोग करके पुनरावृति को स्पष्ट स्टैक से बदलें |
अक्सर पूछे जाने वाले प्रश्न
क्या scene.rootNode स्वयं में कोई इकाई रखता है?
नहीं। रूट नोड लाइब्रेरी द्वारा स्वचालित रूप से बनाया गया एक कंटेनर है। इसका कोई इकाई नहीं है। आपका ज्यामिति और अन्य सीन ऑब्जेक्ट्स एक या अधिक स्तर नीचे rootNode के चाइल्ड नोड्स पर स्थित होते हैं।
node.entity और node.entities के बीच क्या अंतर है?node.entity एकल प्राथमिक इकाई रखता है (सामान्य मामला)। कुछ पुराने FBX और COLLADA फ़ाइलें कई संलग्न इकाइयों वाले नोड्स उत्पन्न कर सकती हैं; ऐसे में node.entities (बहुवचन) पूरी सूची प्रदान करता है।
क्या मैं गहराई‑प्रथम के बजाय चौड़ाई‑प्रथम क्रम में यात्रा कर सकता हूँ?
हाँ। पुनरावर्ती कॉल के बजाय एक कतार (queue) का उपयोग करें: scene.rootNode को एक एरे में पुश करें, फिर शिफ्ट करें और नोड्स को प्रोसेस करें जबकि प्रत्येक नोड के childNodes को कतार के टेल में पुश किया जाए।
क्या scene.open() सिंक्रोनस है?
हाँ। scene.open() और scene.openFromBuffer() दोनों कॉलिंग थ्रेड को तब तक ब्लॉक कर देते हैं जब तक फ़ाइल पूरी तरह से पार्स नहीं हो जाती। यदि आपको इवेंट लूप को प्रतिक्रियाशील रखना है तो उन्हें एक वर्कर थ्रेड में रैप करें।
मैं नोड से world-space स्थितियां कैसे प्राप्त करूँ?node.globalTransform पढ़ें; यह एक read‑only GlobalTransform लौटाता है जिसमें world-space मैट्रिक्स होता है, जो सभी पूर्वज ट्रांसफ़ॉर्म्स से निर्मित होता है। स्पष्ट मैट्रिक्स गणना के लिए, node.evaluateGlobalTransform(false) को कॉल करें।
Mesh के अलावा कौन से एंटिटी प्रकार संभव हैं?Camera, Light, और कस्टम स्केलेटन/बोन एंटिटीज़। node.entity.constructor.name देखें या instanceof का उपयोग करें, जिसमें @aspose/3d से आयात किया गया विशिष्ट क्लास हो।