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

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

مخطط المشهد في Aspose.3D FOSS لـ 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)؛ مفيد في خطوط الأنابيب الخالية من الخادم حيث لا يتوفر إدخال/إخراج القرص.


الخطوة 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 هو الطريقة الأكثر أمانًا لتأكيد أن الكيان هو شبكة مضلعات قبل الوصول إلى 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>

نصائح وأفضل الممارسات

  • تحقق دائمًا من عدم وجود قيمة فارغة 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 (جمع) يوفر القائمة الكاملة.

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

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

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

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

انظر أيضًا

 العربية