Kako pretraživati 3D graf scene u Python
Graf scene u Aspose.3D FOSS je stablo od Node objekata čiji koren je scene.root_node. Svaki 3D fajl, bilo da je učitan iz OBJ, glTF, STL, COLLADA ili 3MF, proizvodi istu strukturu stabla. Poznavanje načina njegovog prelaženja omogućava vam da pregledate geometriju, prebrojite poligone, filtrirate objekte po tipu i obradite određene delove složene scene.
Vodič korak po korak
Korak 1: Instalacija i uvoz
Instalirajte Aspose.3D FOSS sa PyPI:
pip install aspose-3d-fossUvezite klase koje su vam potrebne:
from aspose.threed import Scene
from aspose.threed.entities import MeshSve javne klase se nalaze pod aspose.threed ili njegovim podpaketima (aspose.threed.entities, aspose.threed.utilities).
Korak 2: Učitajte scenu iz fajla
Koristite statički Scene.from_file() metod za otvaranje bilo kog podržanog formata. Format se automatski otkriva na osnovu ekstenzije fajla:
scene = Scene.from_file("model.gltf")Takođe možete otvoriti sa eksplicitnim opcijama učitavanja:
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions
options = ObjLoadOptions()
options.enable_materials = True
scene = Scene()
scene.open("model.obj", options)Nakon učitavanja, scene.root_node je koren stabla. Svi uvezeni čvorovi su deca ili potomci ovog čvora.
Korak 3: Napišite rekurzivnu funkciju za pretragu
Najjednostavnija pretraga posećuje svaki čvor u dubinskom pretraživanju (depth‑first) redosledu:
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)Primer izlaza:
[-]
[-] 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>
Napomena: Koren čvor ima prazno ime (""), pa prva linija prikazuje [-] bez imena koje sledi.
node.child_nodes vraća decu u redosledu umetanja (redosled u kojem ih je uvoznik ili korisnik dodao).
Korak 4: Pristupite svojstvima entiteta na svakom čvoru
Koristite node.entity da biste dobili prvi entitet prikačen čvoru, ili node.entities da iterirate sve njih. Proverite tip pre pristupa svojstvima specifičnim za format:
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, i node.transform.scaling daju transformaciju u lokalnom prostoru. node.global_transform daje izračunati rezultat u svetskom prostoru (sa global_transform.scale za skaliranje u svetskom prostoru).
Korak 5: Filtriraj čvorove po tipu entiteta
Da biste radili samo na određenim tipovima entiteta, dodajte isinstance zaštitu unutar prolaza:
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}")Ovaj obrazac je koristan za grupne operacije: primenu transformacije na svaki čvor mreže, zamenu materijala ili izvoz podstabala.
Korak 6: Prikupi sve mreže i prikaži broj vrhova
Aggregirajte statistiku mreža u jednom prolazu:
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")Uobičajeni problemi
| Problem | Rezolucija |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | Zaštiti sa if node.entity is not None ili isinstance(node.entity, Mesh) pre pristupa svojstvima mreže. Čvorovi bez entiteta vraćaju None. |
| Pretraga se zaustavlja ranije | Osigurajte da rekurzija dopre do node.child_nodes. Ako iterirate samo scene.root_node.child_nodes (ne rekurzivno), propuštate sve potomke. |
| Mesh nedostaje u prikupljenim rezultatima | Proverite da li format fajla čuva hijerarhiju. OBJ izravnava svu geometriju u jedan nivo čvora. Koristite glTF ili COLLADA za duboke hijerarhije. |
node.entity vraća samo prvi entitet | Koristite node.entities (celu listu) kada čvor nosi više entiteta. node.entity je skraćenica za node.entities[0] kada entities nije prazan. |
collect_meshes vraća 0 rezultata za učitani STL | STL fajlovi obično generišu jedan ravni čvor sa jednom mesh entitetom direktno ispod root_node. Proveri root_node.child_nodes[0].entity direktno. |
| Imena čvorova su prazni stringovi | Neki formati (binary STL, neki OBJ fajlovi) ne čuvaju imena objekata. Čvorovi će imati prazne name stringove; koristite indeks za identifikaciju umesto toga. |
Često postavljana pitanja
Koliko dubok može biti graf scene?
Ne postoji čvrsto ograničenje. Python‑ovo podrazumevano ograničenje rekurzije (1000 frejmova) se primenjuje na rekurzivne funkcije prolaza. Za veoma duboke hijerarhije, pretvorite rekurziju u eksplicitni stek:
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))Mogu li da modifikujem stablo dok ga prolazim?
Nemojte dodavati ili uklanjati čvorove iz child_nodes dok ga iterirate. Prikupite čvorove za izmenu u prvom prolazu, a zatim primenite promene u drugom prolazu.
Kako da pronađem određeni čvor po imenu?
Koristite node.get_child(name) da biste pronašli direktno dete po imenu. Za duboku pretragu, pređite kroz stablo sa filterom po imenu:
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")Da li node.entity uvek vraća Mesh?
Ne. Čvor može da sadrži bilo koji tip entiteta: Mesh, Camera, Light, ili prilagođene entitete. Uvek proverite sa isinstance(node.entity, Mesh) pre nego što koristite svojstva specifična za mesh.
Kako da dobijem poziciju čvora u svetskom prostoru?
Čitanje node.global_transform.translation. Ovo je izračunata pozicija u svetskom prostoru, uzimajući u obzir sve transformacije pretka. To je samo za čitanje; izmenite node.transform.translation da biste pomerili čvor.
Mogu li da izbrojim ukupan broj poligona u sceni bez pisanja traversala?
Ne direktno kroz API; ne postoji scene.total_polygon_count svojstvo. Koristite collect_meshes i sabirajte mesh.polygon_count preko rezultata, kao što je prikazano u koraku 6.