كيفية التغلب على 3D Scene Graph في Python
الرسم البياني للمشهد في Aspose.3D FOSS هو شجرة من Node الأجسام التي تُدخل في scene.root_node.كل ملف 3D، سواء كان تحميل من OBJ، glTF، STL، COLLADA، أو 3MF, ينتج نفس الهيكل الخشبي.معرفة كيفية عبورها يسمح لك لفحص الهندسة، وتعدد البولينغونات، وفرشاة الكائنات حسب النوع، ومعالجة أجزاء محددة من المشهد المعقد.
خطوة بخطوة دليل
الخطوة الأولى: تثبيت وتصدير
تثبيت Aspose.3D FOSS من PyPI:
asposefoss/3d is not yet published — build from source until it ships. See the project README for build instructions.
استيراد الفصول التي تحتاجها:
from aspose.threed import Scene
from aspose.threed.entities import Meshجميع الفئات العامة تحت aspose.threed أو أجزاء من الحزمة (aspose.threed.entities, aspose.threed.utilities).
الخطوة 2: تحميل المشهد من ملف
استخدم الستاتية Scene.from_file() طريقة فتح أي تنسيق مدعوم يتم الكشف عن النموذج تلقائيًا من ملحق الملف:
scene = Scene.from_file("model.gltf")يمكنك أيضًا فتحها مع خيارات الشحن المباشرة:
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions
options = ObjLoadOptions()
options.enable_materials = True
scene = Scene()
scene.open("model.obj", options)بعد تحميلها،, scene.root_node كل العقد المستوردة هي أطفال أو نسل لهذا العنصر.
الخطوة الثالثة: إعداد وظيفة تجريبية
يزور البروستال الأسهل كل عقدة في الترتيب الأول العميق:
from aspose.threed import Scene
from aspose.threed.entities import Mesh
def traverse(node, depth=0):
prefix = " " * depth
entity_name = type(node.entity).__name__ if node.entity else "-"
print(f"{prefix}[{entity_name}] {node.name}")
for child in node.child_nodes:
traverse(child, depth + 1)
scene = Scene.from_file("model.gltf")
traverse(scene.root_node)نموذج الناتج :
[-]
[-] Armature
[Mesh] Body
[Mesh] Eyes
[-] Ground
[Mesh] Plane<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.child_nodes يعود الأطفال في طلب الإدخال (الأمر الذي أضافه المستورد أو المستخدم).
الخطوة 4: امتلاك الخصائص الواردة في كل عقدة
استخدام node.entity للحصول على الكيان الأول المرفق إلى عقدة، أو node.entities تحقق من النوع قبل الوصول إلى خصائص تنسيقية:
from aspose.threed import Scene
from aspose.threed.entities import Mesh
def print_entity_details(node, depth=0):
indent = " " * depth
for entity in node.entities:
if isinstance(entity, Mesh):
mesh = entity
print(f"{indent}Mesh '{node.name}':")
print(f"{indent} vertices : {len(mesh.control_points)}")
print(f"{indent} polygons : {mesh.polygon_count}")
print(f"{indent} cast_shadows : {mesh.cast_shadows}")
print(f"{indent} receive_shadows: {mesh.receive_shadows}")
for child in node.child_nodes:
print_entity_details(child, depth + 1)
scene = Scene.from_file("model.gltf")
print_entity_details(scene.root_node)node.transform.translation, node.transform.rotation,و ، و node.transform.scaling إعطاء الفضاء المحلي التحول. node.global_transform يقدم النتيجة العالمية المقياس (مع global_transform.scale على نطاق الفضاء العالمي).
الخطوة 5: تصفية النقاط حسب نوع الكيان
لتعمل فقط على أنواع محددة من الكيانات، إضافة إلى isinstance الحرس داخل الممر:
from aspose.threed import Scene
from aspose.threed.entities import Mesh
def find_mesh_nodes(node, results=None):
if results is None:
results = []
for entity in node.entities:
if isinstance(entity, Mesh):
results.append(node)
break # One match per node is enough
for child in node.child_nodes:
find_mesh_nodes(child, results)
return results
scene = Scene.from_file("model.gltf")
mesh_nodes = find_mesh_nodes(scene.root_node)
print(f"Found {len(mesh_nodes)} mesh node(s)")
for n in mesh_nodes:
print(f" {n.name}")هذا النموذج مفيد للعمليات الكبيرة: تطبيق التحويل على كل عقدة، واستبدال المواد، أو تصدير الخشب الفرعي.
الخطوة 6: جمع جميع الأقواس وطباعة أرقام Vertex
إعدادات الماس في مرسوم واحد:
from aspose.threed import Scene
from aspose.threed.entities import Mesh
def collect_meshes(node, results=None):
if results is None:
results = []
if isinstance(node.entity, Mesh):
results.append((node.name, node.entity))
for child in node.child_nodes:
collect_meshes(child, results)
return results
scene = Scene.from_file("model.gltf")
meshes = collect_meshes(scene.root_node)
print(f"Total meshes: {len(meshes)}")
total_verts = 0
total_polys = 0
for name, mesh in meshes:
verts = len(mesh.control_points)
polys = mesh.polygon_count
total_verts += verts
total_polys += polys
print(f" {name}: {verts} vertices, {polys} polygons")
print(f"Scene totals: {total_verts} vertices, {total_polys} polygons")القضايا المشتركة
| مسألة | القرار |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | الحراسة مع if node.entity is not None أو isinstance(node.entity, Mesh) قبل الوصول إلى الممتلكات المزيفة. النقاط دون كيانات العودة None. |
| رحلة توقف مبكراً | تأكد من أن الرد يصل إلى node.child_nodes.إذا كنت تريد أن تتكلم فقط scene.root_node.child_nodes (لا تكرار) ، فمنهم من يفتقر إلى جميع الأبناء. |
| ميتا غائبة من النتائج التي تم جمعها | تأكد من أن تنسيق الملف يحافظ على الهرم. OBJ يلصق كل الهندسة إلى مستوى عقدة واحدة. استخدم glTF أو COLLADA للهيارميات العميقة. |
node.entity لا يعود إلا إلى أول كيان. | استخدام node.entities (القائمة الكاملة) عندما يحمل عقدة عدة كيانات. node.entity و هو قصير اليدين ل node.entities[0] عندما entities غير صامتة . |
collect_meshes إرجاع 0 نتائج لـ STL المعبأة | ملفات STL عادة ما تنتج عقدة مسطحة واحدة مع كيان واحد من الماس مباشرة تحت root_node.- التحقق root_node.child_nodes[0].entity مباشرة . |
| أسماء النود هي سلاسل فارغة | بعض تنسيقات (STL الثنائية، وبعض ملفات OBJ) لا تخزن أسماء الكائنات. name - الشرائح؛ استخدم المؤشر لتحديد الهوية بدلاً من ذلك. |
الأسئلة المتكررة
كم يمكن أن يكون عمق الرسم البياني للمشهد؟?
لا يوجد حد صعب.الحد الأدنى للانتقال الافتراضي لـ Python (1000 إطار) ينطبق على وظائف الانتقال عبرية.للمسارات العميقة جدا، تحويل الانقلاب إلى شريط واضح:
from collections import deque
from aspose.threed import Scene
scene = Scene.from_file("deep.gltf")
queue = deque([(scene.root_node, 0)])
while queue:
node, depth = queue.popleft()
print(" " * depth + node.name)
for child in node.child_nodes:
queue.append((child, depth + 1))هل يمكنني تغيير الشجرة أثناء عبورها؟?
لا تضاف أو إزالة العقد من child_nodes جمع العقد لتعديل في الممر الأول، ثم تطبيق التغييرات في المسار الثاني.
كيف يمكنني العثور على عقد محدد حسب الاسم؟?
استخدام node.get_child(name) للبحث العميق، اجتياز الشجرة مع فلتر الاسم:
def find_by_name(root, name):
if root.name == name:
return root
for child in root.child_nodes:
result = find_by_name(child, name)
if result:
return result
return None
target = find_by_name(scene.root_node, "Wheel_FL")يُفعل node.entity هل يعود دائمًا إلى الماس؟?
لا يمكن أن يحتوي عقدة على أي نوع من الكيانات: Mesh, Camera, Light,- أو منشآت مخصصة.تحقق دائما مع isinstance(node.entity, Mesh) قبل استخدام خصائص مخصصة للطحين.
كيف يمكنني الحصول على موقع الفضاء العالمي في عقدة؟?
قراءة node.global_transform.translation.هذا هو الموقف الذي تم تقييمه في الفضاء العالمي ، ويحسب كل التحولات الأجدية. node.transform.translation إعادة وضع النود.
هل يمكنني حساب الكتل في مشهد دون كتابة كراسل؟?
لا مباشرة من خلال API؛ لا يوجد scene.total_polygon_count الممتلكات - الاستخدام collect_meshes و المبلغ mesh.polygon_count على النتائج، كما هو موضح في الخطوة 6.