رسم بياني للمشهد

رسم بياني للمشهد

أ مخطط المشهد هو نموذج البيانات الأساسي لـ Aspose.3D FOSS لـ Python. كل ملف ثلاثي الأبعاد، سواء تم تحميله من القرص أو تم إنشاؤه في الذاكرة، يُمثَّل كشجرة من Node الكائنات المتجذرة في Scene.root_node. يمكن لكل عقدة أن تحتفظ بعقد فرعية وواحد أو أكثر من Entity الكائنات (meshes, cameras, lights). فهم مخطط المشهد يمنحك وصولًا مباشرًا إلى الهندسة والمواد والتحولات المكانية لكل كائن في المشهد.

التثبيت والإعداد

قم بتثبيت المكتبة من PyPI:

pip install aspose-3d-foss

لا تحتاج إلى امتدادات أصلية أو مترجمات أو حزم نظام إضافية. للحصول على تعليمات التثبيت الكاملة، راجع دليل التثبيت.


نظرة عامة: مفاهيم مخطط المشهد

يتبع مخطط المشهد في Aspose.3D FOSS هيكل احتواء بسيط:

Scene
└── root_node  (Node)
    ├── child_node_A  (Node)
    │   ├── entity: Mesh
    │   └── transform: translation, rotation, scale
    ├── child_node_B  (Node)
    │   └── child_node_C  (Node)
    │       └── entity: Mesh
    └── ...
كائندور
Sceneحاوية المستوى الأعلى. تحتوي على root_node, asset_info, animation_clips, و sub_scenes.
Nodeعقدة شجرة مسماة. لها أب، صفر أو أكثر من الأطفال، صفر أو أكثر من الكيانات، و محلية Transform.
Entityهندسة أو كائن مشهد مرفق بعقدة. أنواع الكيانات الشائعة: Mesh, Camera, Light.
Transformالموضع، الدوران، والقياس في الفضاء المحلي لعقدة. يتم قراءة النتيجة في الفضاء العالمي من global_transform.

خطوة بخطوة: بناء مخطط المشهد برمجيًا

الخطوة 1: إنشاء مشهد

جديد Scene دائمًا يبدأ بـ فارغ root_node:

from aspose.threed import Scene

scene = Scene()
print(scene.root_node.name)   # "" (empty string — root node has no name by default)
print(len(scene.root_node.child_nodes))  # 0

Scene هو نقطة الدخول لكل شيء: تحميل الملفات، حفظ الملفات، إنشاء مقاطع الرسوم المتحركة، والوصول إلى شجرة العقد.


الخطوة 2: إنشاء عقد فرعية

استخدم create_child_node(name) لإضافة عقد مسماة إلى الشجرة:

from aspose.threed import Scene

scene = Scene()

parent = scene.root_node.create_child_node("parent")
child  = parent.create_child_node("child")

print(parent.name)                          # "parent"
print(child.parent_node.name)               # "parent"
print(len(scene.root_node.child_nodes))     # 1

بدلاً من ذلك، أنشئ كائنًا مستقلاً Node وأرفقه صراحةً:

from aspose.threed import Scene, Node

scene = Scene()
node = Node("standalone")
scene.root_node.add_child_node(node)

كلا النهجين ينتجان النتيجة نفسها. create_child_node أكثر اختصارًا لإنشاء مدمج.


الخطوة 3: إنشاء كيان شبكة وإرفاقه

أ Mesh يخزن بيانات الرؤوس (control_points) وتوبولوجيا الوجوه (polygons). أنشئ واحدًا، أضف الهندسة، ثم أرفقه بعقدة:

from aspose.threed import Scene, Node
from aspose.threed.entities import Mesh
from aspose.threed.utilities import Vector3, Vector4

scene = Scene()
parent = scene.root_node.create_child_node("parent")
child  = parent.create_child_node("child")

##Create a quad mesh (four vertices, one polygon)
# Note: control_points returns a copy of the internal list; append to
# _control_points directly to actually add vertices. This is a known
# library limitation — a public add_control_point() API is not yet available.
mesh = Mesh("cube")
mesh._control_points.append(Vector4(0, 0, 0, 1))
mesh._control_points.append(Vector4(1, 0, 0, 1))
mesh._control_points.append(Vector4(1, 1, 0, 1))
mesh._control_points.append(Vector4(0, 1, 0, 1))
mesh.create_polygon(0, 1, 2, 3)

child.add_entity(mesh)

print(f"Mesh name:      {mesh.name}")
print(f"Vertex count:   {len(mesh.control_points)}")
print(f"Polygon count:  {mesh.polygon_count}")

Vector4(x, y, z, w) يمثل إحداثيًا متجانسًا. استخدم w=1 لتمثيل مواضع النقاط العادية.

create_polygon(*indices) يقبل مؤشرات الرؤوس ويسجل وجهًا واحدًا في قائمة المضلعات. مرّر ثلاثة مؤشرات لمثلث، أربعة لمربع.


الخطوة 4: تعيين تحويلات العقدة

كل عقدة لديها Transform التي تتحكم في موقعها واتجاهها وحجمها في الفضاء المحلي:

from aspose.threed.utilities import Vector3, Quaternion

##Translate the node 2 units along the X axis
child.transform.translation = Vector3(2.0, 0.0, 0.0)

##Scale the node to half its natural size
child.transform.scaling = Vector3(0.5, 0.5, 0.5)

##Rotate 45 degrees around the Y axis using Euler angles
child.transform.euler_angles = Vector3(0.0, 45.0, 0.0)

التحويلات تراكمية: موضع العقدة الفرعية في الفضاء العالمي هو تركيبة تحويلها الخاص مع جميع تحويلات الأسلاف. اقرأ النتيجة المُقيمة في الفضاء العالمي من node.global_transform (غير قابل للتغيير، للقراءة فقط).


الخطوة 5: استعراض رسم المشهد بشكل تكراري

تجول في الشجرة بأكملها عبر الاستدعاء المتكرر عبر node.child_nodes:

def traverse(node, depth=0):
    indent = "  " * depth
    entity_type = type(node.entity).__name__ if node.entity else "None"
    print(f"{indent}{node.name} [{entity_type}]")
    for child in node.child_nodes:
        traverse(child, depth + 1)

traverse(scene.root_node)

مثال على المخرجات للمشهد المبني أعلاه (اسم عقدة الجذر هو سلسلة فارغة):

 [None]
  parent [None]
    child [Mesh]

في المشاهد التي تحتوي على كيانات متعددة لكل عقدة، قم بالتكرار node.entities بدلاً من node.entity:

def traverse_full(node, depth=0):
    indent = "  " * depth
    entity_names = [type(e).__name__ for e in node.entities] or ["None"]
    print(f"{indent}{node.name} [{', '.join(entity_names)}]")
    for child in node.child_nodes:
        traverse_full(child, depth + 1)

traverse_full(scene.root_node)

الخطوة 6: حفظ المشهد

مرّر مسار ملف إلى scene.save(). يتم استنتاج الصيغة من امتداد الملف:

scene.save("scene.gltf")   # JSON glTF 2.0
scene.save("scene.glb")    # Binary GLB container
scene.save("scene.obj")    # Wavefront OBJ
scene.save("scene.stl")    # STL

لخيارات خاصة بالصيغ، مرّر كائن خيارات الحفظ كالمعامل الثاني:

from aspose.threed.formats import GltfSaveOptions

opts = GltfSaveOptions()
scene.save("scene.gltf", opts)

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

  • سمِّ كل عقدة. إعطاء العقد أسماء ذات معنى يجعل تصحيح عمليات التجوال أسهل بكثير ويضمن حفظ الأسماء في الملف المُصدَّر.
  • شبكة واحدة لكل عقدة. الحفاظ على الكيانات بنسبة 1:1 مع العقد يبسط التحويلات واستعلامات التصادم.
  • استخدم create_child_node بدلاً من الإرفاق اليدوي. يقوم بتعيين مرجع الوالد تلقائيًا وهو أقل عرضة للأخطاء.
  • اقرأ global_transform بعد بناء التسلسل الهرمي. تكون النتيجة في الفضاء العالمي مستقرة فقط بمجرد ضبط جميع تحويلات الأسلاف.
  • لا تُغيّر الشجرة أثناء التجوال. إضافة أو إزالة عقد فرعية أثناء التكرار child_nodes سيتسبب في سلوك غير متوقع. اجمع العقد أولاً، ثم عدّلها.
  • نقاط التحكم تستخدم Vector4, ليس Vector3. دائمًا مرّر w=1 لمواضع القمم العادية؛; w=0 يمثل متجه اتجاه (ليس نقطة).
  • mesh.control_points يرجع نسخة. ال control_points الخاصية تُعيد list(self._control_points) — إضافة عناصر إلى القائمة المعادة لا تُغيّر الشبكة. يجب دائمًا الإضافة إلى mesh._control_points مباشرةً عند بناء الهندسة برمجياً. هذه قيود معروفة في المكتبة؛ لا توجد بعد واجهة برمجة تطبيقات عامة للتعديل.

المشكلات الشائعة

المشكلةالحل
AttributeError: 'NoneType' object has no attribute 'polygons'احمِ بـ if node.entity is not None قبل الوصول إلى خصائص الكيان. العقدة التي لا تحتوي على كيانات لديها entity = None.
تظهر الشبكة في الأصل بالرغم من ضبط translationtransform.translation يُطبق إزاحة محلية. إذا كان للعقدة الأصلية نفسها تحويل غير هوية، قد يختلف الموقع العالمي. تحقق global_transform.
العقد الفرعية مفقودة بعد scene.save() / إعادة تحميلبعض الصيغ (OBJ) تُسطّح الهرمية. استخدم glTF أو COLLADA للحفاظ على شجرة العقد الكاملة.
polygon_count هي 0 بعد mesh.create_polygon(...)تحقق من أن مؤشرات الرؤوس الممررة إلى create_polygon ضمن النطاق (0 إلى len(control_points) - 1).
Node.get_child(name) يرجع Noneالاسم حساس لحالة الأحرف. تأكد من سلسلة الاسم الدقيقة المستخدمة عند الإنشاء.
التنقل يزور العقد بترتيب غير متوقعchild_nodes يرجع الأطفال بترتيب الإدراج (الترتيب add_child_node / create_child_node تم استدعاؤه).
 العربية