Kako pretraživati 3D graf scene u Python

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-foss

Uvezite klase koje su vam potrebne:

from aspose.threed import Scene
from aspose.threed.entities import Mesh

Sve 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

ProblemRezolucija
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 ranijeOsigurajte 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 rezultatimaProverite 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 entitetKoristite 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 STLSTL 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 stringoviNeki 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.

 Српски