كيفية استعراض رسم بياني لمشهد ثلاثي الأبعاد في TypeScript

كيفية استعراض رسم بياني لمشهد ثلاثي الأبعاد في TypeScript

مخطط المشهد في Aspose.3D FOSS للـ TypeScript هو شجرة من Node الكائنات الجذرية في scene.rootNode. الاستعراض متكرر: كل عقدة تعرض childNodes قابل للتكرار وخاصية اختيارية entity خاصية. يوضح هذا الدليل كيفية استعراض الشجرة بأكملها، وتحديد أنواع الكيانات، وجمع إحصائيات mesh.

المتطلبات المسبقة

  • 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); مفيد في خطوط الأنابيب بدون خادم حيث لا يتوفر إدخال/إخراج القرص.


الخطوة 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:

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 هي الطريقة الأكثر أمانًا لتأكيد أن الكيان هو polygon mesh قبل الوصول إلى controlPoints, polygonCount, أو عناصر vertex.


الخطوة 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 مصفوفة حتى يتمكن المستدعون من تعبئتها مسبقًا لدمج النتائج عبر عدة subtrees.


الخطوة 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 جذر
Traversal يتخطى الشبكات المتداخلةعدم التكرار في جميع الأطفالتأكد من أن الاستدعاء المتكرر يغطي كل عنصر في node.childNodes
mesh.controlPoints.length هي 0Mesh تم تحميله لكنه لا يحتوي على أي هندسةتحقق من مصدر OBJ للمجموعات الفارغة؛ استخدم mesh.polygonCount كفحص ثانوي
تجاوز سعة المكدس في الهياكل العميقةشجرة مشهد عميقة جدًا (مئات المستويات)استبدل العودية بمكدس صريح باستخدام Array.push / Array.pop

الأسئلة المتكررة

هل scene.rootNode يحمل نفسه كيانًا؟? لا. الـ root node هو container تم إنشاؤه تلقائيًا بواسطة الـ library. لا يحتوي على أي entity. الـ geometry والكائنات الأخرى في الـ scene objects توجد على الـ child nodes مستوى واحد أو أكثر أدناه. rootNode.

ما الفرق بين node.entity و node.entities? node.entity يحتوي على الكيان الأساسي الوحيد (الحالة الشائعة). قد تنتج بعض ملفات FBX و COLLADA القديمة عقدًا تحتوي على كيانات متعددة مرفقة؛ في هذه الحالة node.entities (جمع) يوفر القائمة الكاملة.

هل يمكنني التجول بترتيب العرض أولاً بدلاً من العمق أولاً؟? نعم. استخدم طابورًا بدلاً من استدعاء تكراري: push scene.rootNode إلى مصفوفة، ثم shift وعالج العقد بينما تدفع كل عقدة childNodes إلى ذيل queue.

هل scene.open() متزامن؟? نعم. scene.open() و scene.openFromBuffer() كلاهما يمنعان الخيط المستدعي حتى يتم تحليل الملف بالكامل. غلفهما في خيط عامل إذا كنت بحاجة إلى إبقاء حلقة الأحداث مستجيبة.

كيف أحصل على إحداثيات الفضاء العالمي من عقدة؟? قراءة node.globalTransform;؛ تُعيد قيمة للقراءة فقط GlobalTransform مع مصفوفة الفضاء العالمي، المكوّنة من جميع التحويلات السلفية. للرياضيات الصريحة للمصفوفات، استدعِ node.evaluateGlobalTransform(false).

ما هي أنواع الكيانات الممكنة بخلاف Mesh? Camera, Light,، وكيانات الهيكل العظمي/العظام المخصصة. تحقق من node.entity.constructor.name أو استخدم instanceof مع الفئة المحددة المستوردة من @aspose/3d.

انظر أيضًا

 العربية