3535import bonsai .bim .import_ifc
3636import numpy as np
3737import numpy .typing as npt
38+ from ifcopenshell .util .shape_builder import np_to_4d
3839from mathutils import Vector , Matrix
3940from pathlib import Path
4041from typing import Union , Any , Optional
@@ -634,7 +635,7 @@ def is_point_far_away(
634635 limit = cls .settings .distance_limit
635636 limit = limit if is_meters else (limit / cls .unit_scale )
636637 coords = getattr (point , "Coordinates" , point )
637- return abs (coords [ 0 ] ) > limit or abs ( coords [ 1 ]) > limit or abs ( coords [ 2 ]) > limit
638+ return any ( abs (c ) > limit for c in coords )
638639
639640 @classmethod
640641 def is_element_far_away (cls , element : ifcopenshell .entity_instance ) -> bool :
@@ -879,11 +880,12 @@ def get_offset_point(cls, ifc_file: ifcopenshell.file) -> Union[npt.NDArray[np.f
879880 if not shape :
880881 continue
881882 mat = ifcopenshell .util .shape .get_shape_matrix (shape )
882- point = mat @ np .array ((shape .geometry .verts [0 ], shape .geometry .verts [1 ], shape .geometry .verts [2 ], 1.0 ))
883+ verts = ifcopenshell .util .shape .get_vertices (shape .geometry )
884+ point = (mat @ np_to_4d (verts [0 ]))[:3 ]
883885 if cls .is_point_far_away (point , is_meters = True ):
884886 # Arbitrary origins should be to the nearest millimeter.
885887 # Anything more precise is just ridiculous from a practical surveying perspective.
886- return [round (float (p ), 3 ) / cls .unit_scale for p in point [: 3 ]]
888+ return np . array ( [round (float (p ), 3 ) / cls .unit_scale for p in point ])
887889 break
888890
889891 @classmethod
@@ -960,14 +962,19 @@ def convert_geometry_to_mesh(
960962 cls ,
961963 geometry : ifcopenshell .geom .ShapeType ,
962964 mesh : bpy .types .Mesh ,
963- verts : Optional [list [ float ]] = None ,
965+ verts : Optional [npt . NDArray [ np . float64 ]] = None ,
964966 * ,
965967 load_indexed_maps = True ,
966968 ) -> bpy .types .Mesh :
969+ """
970+ :param verts: Numpy array of shape (n, 3).
971+ """
967972 if verts is None :
968- verts = geometry .verts
969- if geometry .faces :
970- num_vertices = len (verts ) // 3
973+ verts = ifcopenshell .util .shape .get_vertices (geometry )
974+ faces = ifcopenshell .util .shape .get_faces (geometry )
975+ total_faces : int
976+ if total_faces := faces .shape [0 ]:
977+ num_vertices : int = verts .shape [0 ]
971978
972979 # See bug 3546
973980 # ios_edges holds true edges that aren't triangulated.
@@ -978,34 +985,35 @@ def convert_geometry_to_mesh(
978985 mesh ["ios_item_ids" ] = ios_item_ids
979986
980987 mesh .vertices .add (num_vertices )
981- mesh .vertices .foreach_set ("co" , verts )
988+ mesh .vertices .foreach_set ("co" , verts . ravel (). astype ( "f" ) )
982989
983990 is_triangulated = True
991+ num_vertex_indices = faces .size
984992 if is_triangulated :
985- total_faces = len (geometry .faces )
986- num_vertex_indices = len (geometry .faces )
987- loop_start = range (0 , total_faces , 3 )
988- num_loops = total_faces // 3
989- loop_total = [3 ] * num_loops
993+ loop_start = np .arange (0 , num_vertex_indices , 3 , dtype = "I" )
994+ loop_total = np .full (total_faces , 3 , dtype = "I" )
995+ use_smooth = np .zeros (num_vertex_indices , dtype = "?" )
990996
991997 mesh .loops .add (num_vertex_indices )
992- mesh .loops .foreach_set ("vertex_index" , geometry . faces )
993- mesh .polygons .add (num_loops )
998+ mesh .loops .foreach_set ("vertex_index" , faces . ravel (). astype ( "I" ) )
999+ mesh .polygons .add (total_faces )
9941000 mesh .polygons .foreach_set ("loop_start" , loop_start )
9951001 mesh .polygons .foreach_set ("loop_total" , loop_total )
996- mesh .polygons .foreach_set ("use_smooth" , [ 0 ] * total_faces )
1002+ mesh .polygons .foreach_set ("use_smooth" , use_smooth )
9971003 else :
1004+ # TODO: optimize using correct numpy array types.
9981005 faces_array = np .array (geometry .faces , dtype = object )
999- loop_total = tuple (len (face ) for face in faces_array )
1006+ loop_total = np . array ( tuple (len (face ) for face in faces_array ), dtype = "I" )
10001007 loop_start = np .cumsum ((0 ,) + loop_total )[:- 1 ]
10011008 vertex_index = np .concatenate (faces_array )
1009+ use_smooth = np .zeros (num_vertex_indices , dtype = "?" )
10021010
10031011 mesh .loops .add (len (vertex_index ))
10041012 mesh .loops .foreach_set ("vertex_index" , vertex_index )
10051013 mesh .polygons .add (len (loop_start ))
10061014 mesh .polygons .foreach_set ("loop_start" , loop_start )
10071015 mesh .polygons .foreach_set ("loop_total" , loop_total )
1008- mesh .polygons .foreach_set ("use_smooth" , [ 0 ] * len ( geometry . faces ) )
1016+ mesh .polygons .foreach_set ("use_smooth" , use_smooth )
10091017
10101018 mesh .update ()
10111019
@@ -1020,11 +1028,8 @@ def convert_geometry_to_mesh(
10201028 tool .Blender .Attribute .fill_attribute (mesh , "ios_item_ids" , "FACE" , "INT" , ios_item_ids )
10211029 tool .Blender .Attribute .fill_attribute (mesh , "ios_material_ids" , "FACE" , "INT" , geometry .material_ids )
10221030 else :
1023- e = geometry .edges
1024- v = verts
1025- vertices = [[v [i ], v [i + 1 ], v [i + 2 ]] for i in range (0 , len (v ), 3 )]
1026- edges = [[e [i ], e [i + 1 ]] for i in range (0 , len (e ), 2 )]
1027- mesh .from_pydata (vertices , edges , [])
1031+ edges = ifcopenshell .util .shape .get_edges (geometry )
1032+ mesh .from_pydata (verts .tolist (), edges .tolist (), [])
10281033 # TODO: remove error handling after we update build in Bonsai.
10291034 try :
10301035 edges_item_ids = ifcopenshell .util .shape .get_edges_representation_item_ids (geometry ).tolist ()
@@ -1034,8 +1039,8 @@ def convert_geometry_to_mesh(
10341039 tool .Blender .Attribute .fill_attribute (mesh , "ios_edges_item_ids" , "EDGE" , "INT" , edges_item_ids )
10351040 tool .Blender .Attribute .fill_attribute (mesh , "ios_material_ids" , "EDGE" , "INT" , geometry .material_ids )
10361041
1037- mesh ["ios_materials" ] = [m .instance_id () for m in geometry . materials ]
1038- mesh ["ios_material_ids" ] = geometry . material_ids
1042+ mesh ["ios_materials" ] = [m .instance_id () for m in ifcopenshell . util . shape . get_shape_material_styles ( geometry ) ]
1043+ mesh ["ios_material_ids" ] = ifcopenshell . util . shape . get_faces_material_style_ids ( geometry ). tolist ()
10391044 return mesh
10401045
10411046 @classmethod
0 commit comments