1+ """
2+ PyHelios External Geometry Import Example
3+
4+ This example demonstrates the four main ways to import external geometry into PyHelios:
5+ 1. Loading PLY files
6+ 2. Loading OBJ files
7+ 3. Loading Helios XML files
8+ 4. Importing from NumPy arrays (compatible with trimesh, Open3D, etc.)
9+
10+ It also shows how to retrieve primitive information and work with the data.
11+ """
12+
13+ import numpy as np
14+ from pyhelios import Context , DataTypes
15+
16+ def main ():
17+ # Create a PyHelios context
18+ context = Context ()
19+ print ("PyHelios External Geometry Import Demo" )
20+ print ("=" * 50 )
21+
22+ # ==================== METHOD 1: PLY FILE LOADING ====================
23+ print ("\n 1. PLY File Loading" )
24+ try :
25+ # Simple PLY loading
26+ ply_uuids = context .loadPLY ("models/example.ply" , silent = True )
27+ print (f" Simple PLY load: { len (ply_uuids )} primitives loaded" )
28+
29+ # PLY loading with transformations
30+ origin = DataTypes .vec3 (2.0 , 0.0 , 1.0 )
31+ color = DataTypes .RGBcolor (0.8 , 0.2 , 0.2 ) # Red tint
32+ rotation = DataTypes .SphericalCoord (1.0 , 0.0 , 0.0 , np .pi / 4 ) # 45 degree rotation
33+
34+ transformed_uuids = context .loadPLY (
35+ filename = "models/example.ply" ,
36+ origin = origin ,
37+ height = 2.0 ,
38+ rotation = rotation ,
39+ color = color ,
40+ upaxis = "YUP" ,
41+ silent = True
42+ )
43+ print (f" Transformed PLY load: { len (transformed_uuids )} primitives loaded" )
44+
45+ except Exception as e :
46+ print (f" PLY loading failed (file may not exist): { e } " )
47+
48+ # ==================== METHOD 2: OBJ FILE LOADING ====================
49+ print ("\n 2. OBJ File Loading" )
50+ try :
51+ # Simple OBJ loading
52+ obj_uuids = context .loadOBJ ("models/example.obj" , silent = True )
53+ print (f" Simple OBJ load: { len (obj_uuids )} primitives loaded" )
54+
55+ # OBJ loading with full transformations
56+ origin = DataTypes .vec3 (- 2.0 , 0.0 , 1.0 )
57+ scale = DataTypes .vec3 (0.5 , 0.5 , 0.5 ) # Half scale
58+ rotation = DataTypes .SphericalCoord (1.0 , 0.0 , 0.0 , - np .pi / 6 ) # -30 degree rotation
59+ color = DataTypes .RGBcolor (0.2 , 0.8 , 0.2 ) # Green tint
60+
61+ transformed_obj_uuids = context .loadOBJ (
62+ filename = "models/example.obj" ,
63+ origin = origin ,
64+ scale = scale ,
65+ rotation = rotation ,
66+ color = color ,
67+ upaxis = "ZUP" ,
68+ silent = True
69+ )
70+ print (f" Transformed OBJ load: { len (transformed_obj_uuids )} primitives loaded" )
71+
72+ except Exception as e :
73+ print (f" OBJ loading failed (file may not exist): { e } " )
74+
75+ # ==================== METHOD 3: XML FILE LOADING ====================
76+ print ("\n 3. Helios XML File Loading" )
77+ try :
78+ xml_uuids = context .loadXML ("geometry/scene.xml" , quiet = True )
79+ print (f" XML load: { len (xml_uuids )} primitives loaded" )
80+
81+ except Exception as e :
82+ print (f" XML loading failed (file may not exist): { e } " )
83+
84+ # ==================== METHOD 4: NUMPY ARRAY IMPORT ====================
85+ print ("\n 4. NumPy Array Import (trimesh/Open3D compatible)" )
86+
87+ # Create a simple triangular mesh (tetrahedron)
88+ vertices = np .array ([
89+ [0.0 , 0.0 , 0.0 ], # vertex 0
90+ [1.0 , 0.0 , 0.0 ], # vertex 1
91+ [0.5 , 1.0 , 0.0 ], # vertex 2
92+ [0.5 , 0.5 , 1.0 ] # vertex 3
93+ ], dtype = np .float32 )
94+
95+ faces = np .array ([
96+ [0 , 1 , 2 ], # bottom face
97+ [0 , 1 , 3 ], # front face
98+ [1 , 2 , 3 ], # right face
99+ [0 , 2 , 3 ] # left face
100+ ], dtype = np .int32 )
101+
102+ # Per-vertex colors (RGBA)
103+ colors = np .array ([
104+ [1.0 , 0.0 , 0.0 ], # red
105+ [0.0 , 1.0 , 0.0 ], # green
106+ [0.0 , 0.0 , 1.0 ], # blue
107+ [1.0 , 1.0 , 0.0 ] # yellow
108+ ], dtype = np .float32 )
109+
110+ # Import triangles with per-vertex colors
111+ array_uuids = context .addTrianglesFromArrays (vertices , faces , colors )
112+ print (f" NumPy array import: { len (array_uuids )} triangles added" )
113+ print (f" Triangle UUIDs: { array_uuids } " )
114+
115+ # ==================== TEXTURED TRIANGLE IMPORT ====================
116+ print ("\n 5. Textured Triangle Import" )
117+
118+ # Create a simple quad (2 triangles) with UV coordinates
119+ quad_vertices = np .array ([
120+ [3.0 , 0.0 , 0.0 ], # bottom-left
121+ [4.0 , 0.0 , 0.0 ], # bottom-right
122+ [4.0 , 1.0 , 0.0 ], # top-right
123+ [3.0 , 1.0 , 0.0 ] # top-left
124+ ], dtype = np .float32 )
125+
126+ quad_faces = np .array ([
127+ [0 , 1 , 2 ], # first triangle
128+ [0 , 2 , 3 ] # second triangle
129+ ], dtype = np .int32 )
130+
131+ # UV coordinates for texture mapping
132+ uv_coords = np .array ([
133+ [0.0 , 0.0 ], # bottom-left UV
134+ [1.0 , 0.0 ], # bottom-right UV
135+ [1.0 , 1.0 ], # top-right UV
136+ [0.0 , 1.0 ] # top-left UV
137+ ], dtype = np .float32 )
138+
139+ try :
140+ textured_uuids = context .addTrianglesFromArraysTextured (
141+ vertices = quad_vertices ,
142+ faces = quad_faces ,
143+ uv_coords = uv_coords ,
144+ texture_file = "textures/test_texture.png"
145+ )
146+ print (f" Textured triangles: { len (textured_uuids )} triangles added" )
147+ print (f" Textured UUIDs: { textured_uuids } " )
148+
149+ except Exception as e :
150+ print (f" Textured triangle import failed: { e } " )
151+
152+ # ==================== PRIMITIVE INFO RETRIEVAL ====================
153+ print ("\n 6. Primitive Information Retrieval" )
154+
155+ # Get information about the triangles we just created
156+ if array_uuids :
157+ first_triangle_uuid = array_uuids [0 ]
158+
159+ # Get detailed primitive information
160+ prim_info = context .getPrimitiveInfo (first_triangle_uuid )
161+ print (f" Triangle { first_triangle_uuid } info:" )
162+ print (f" Type: { prim_info .primitive_type } " )
163+ print (f" Area: { prim_info .area :.4f} " )
164+ print (f" Normal: ({ prim_info .normal .x :.3f} , { prim_info .normal .y :.3f} , { prim_info .normal .z :.3f} )" )
165+ print (f" Centroid: ({ prim_info .centroid .x :.3f} , { prim_info .centroid .y :.3f} , { prim_info .centroid .z :.3f} )" )
166+ print (f" Color: RGB({ prim_info .color .r :.3f} , { prim_info .color .g :.3f} , { prim_info .color .b :.3f} )" )
167+ print (f" Vertices:" )
168+ for i , vertex in enumerate (prim_info .vertices ):
169+ print (f" v{ i } : ({ vertex .x :.3f} , { vertex .y :.3f} , { vertex .z :.3f} )" )
170+
171+ # Get info for all primitives
172+ all_primitive_info = context .getAllPrimitiveInfo ()
173+ print (f"\n Total primitives in context: { len (all_primitive_info )} " )
174+
175+ # Summary by type
176+ type_counts = {}
177+ for info in all_primitive_info :
178+ ptype = info .primitive_type .name
179+ type_counts [ptype ] = type_counts .get (ptype , 0 ) + 1
180+
181+ print (" Primitive counts by type:" )
182+ for ptype , count in type_counts .items ():
183+ print (f" { ptype } : { count } " )
184+
185+ # ==================== PRIMITIVE DATA EXAMPLE ====================
186+ print ("\n 7. Primitive Data (User-defined key-value pairs)" )
187+ print (" Note: Primitive data methods require additional C++ wrapper implementation" )
188+ print (" This demonstrates the intended API for user-defined metadata:" )
189+ print (" - context.setPrimitiveData(uuid, 'temperature', 25.5)" )
190+ print (" - context.setPrimitiveData(uuid, 'material_id', 42)" )
191+ print (" - context.setPrimitiveData(uuid, 'source_file', 'imported_model.obj')" )
192+ print (" - temp = context.getPrimitiveData(uuid, 'temperature', float)" )
193+
194+ # ==================== INTEGRATION WITH POPULAR LIBRARIES ====================
195+ print ("\n 8. Integration with Popular Python 3D Libraries" )
196+ print (" The array format used by PyHelios is compatible with:" )
197+ print (" - trimesh: mesh = trimesh.Trimesh(vertices=vertices, faces=faces)" )
198+ print (" - Open3D: mesh = o3d.geometry.TriangleMesh()" )
199+ print (" mesh.vertices = o3d.utility.Vector3dVector(vertices)" )
200+ print (" mesh.triangles = o3d.utility.Vector3iVector(faces)" )
201+ print (" - numpy-stl: mesh = numpy_stl.mesh.Mesh(vertices.reshape(-1, 9))" )
202+
203+ print ("\n " + "=" * 50 )
204+ print ("External geometry import demo completed!" )
205+ print (f"Context contains { context .getPrimitiveCount ()} total primitives" )
206+
207+
208+ if __name__ == "__main__" :
209+ main ()
0 commit comments