diff --git a/src/blenderbim/blenderbim/bim/import_ifc.py b/src/blenderbim/blenderbim/bim/import_ifc.py index 9810d39f82..4a31abfe34 100644 --- a/src/blenderbim/blenderbim/bim/import_ifc.py +++ b/src/blenderbim/blenderbim/bim/import_ifc.py @@ -743,18 +743,29 @@ class IfcImporter: def apply_blender_offset_to_matrix_world(self, obj: bpy.types.Object, matrix: np.ndarray) -> mathutils.Matrix: props = bpy.context.scene.BIMGeoreferenceProperties if props.has_blender_offset: - if obj.data and obj.data.get("has_cartesian_point_offset", None): + if not obj.data and tool.Cad.is_x(matrix[0][3], 0) and tool.Cad.is_x(matrix[1][3], 0) and tool.Cad.is_x(matrix[2][3], 0): + # We assume any non-geometric matrix at 0,0,0 is not + # positionally significant and is left alone. This handles + # scenarios where often spatial elements are left at 0,0,0 and + # everything else is at map coordinates. + return mathutils.Matrix(matrix.tolist()) + elif obj.data and obj.data.get("has_cartesian_point_offset", None): obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT" - elif self.is_point_far_away((matrix[:3, 3])): + if cartesian_point_offset := obj.data.get("cartesian_point_offset", None): + offset_x, offset_y, offset_z = map(float, cartesian_point_offset.split(",")) + matrix[0][3] += offset_x + matrix[1][3] += offset_y + matrix[2][3] += offset_z + else: obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT" - matrix = ifcopenshell.util.geolocation.global2local( - matrix, - float(props.blender_eastings) * self.unit_scale, - float(props.blender_northings) * self.unit_scale, - float(props.blender_orthogonal_height) * self.unit_scale, - float(props.blender_x_axis_abscissa), - float(props.blender_x_axis_ordinate), - ) + matrix = ifcopenshell.util.geolocation.global2local( + matrix, + float(props.blender_eastings) * self.unit_scale, + float(props.blender_northings) * self.unit_scale, + float(props.blender_orthogonal_height) * self.unit_scale, + float(props.blender_x_axis_abscissa), + float(props.blender_x_axis_ordinate), + ) return mathutils.Matrix(matrix.tolist()) @@ -1201,7 +1212,6 @@ class IfcImporter: styles.extend(style.Styles) def create_native_faceted_brep(self, element, mesh_name, native_data): - # TODO: georeferencing? # co [x y z x y z x y z ...] # vertex_index [i i i i i ...] # loop_start [0 3 6 9 ...] (for tris) @@ -1226,45 +1236,27 @@ class IfcImporter: mesh = bpy.data.meshes.new("Native") props = bpy.context.scene.BIMGeoreferenceProperties - mat = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement) if props.has_blender_offset and self.is_point_far_away(self.mesh_data["co"][0:3], is_meters=False): - offset_point = np.linalg.inv(mat) @ np.array( - ( - float(props.blender_eastings), - float(props.blender_northings), - float(props.blender_orthogonal_height), - 0.0, - ) - ) - verts = [None] * len(self.mesh_data["co"]) - for i in range(0, len(self.mesh_data["co"]), 3): - verts[i], verts[i + 1], verts[i + 2], _ = native_data["matrix"] @ mathutils.Vector( - ( - *ifcopenshell.util.geolocation.enh2xyz( - self.mesh_data["co"][i] * self.unit_scale, - self.mesh_data["co"][i + 1] * self.unit_scale, - self.mesh_data["co"][i + 2] * self.unit_scale, - offset_point[0] * self.unit_scale, - offset_point[1] * self.unit_scale, - offset_point[2] * self.unit_scale, - float(props.blender_x_axis_abscissa), - float(props.blender_x_axis_ordinate), - ), - 1, - ) - ) + verts_array = np.array(self.mesh_data["co"]) + verts_array *= self.unit_scale + offset_x, offset_y, offset_z = verts_array[0:3] + offset = np.array([-offset_x, -offset_y, -offset_z]) + offset_verts = verts_array + np.tile(offset, len(verts_array) // 3) + + if np.allclose(native_data["matrix"], np.identity(4), atol=1e-8): + verts = offset_verts.tolist() + else: + verts = self.apply_matrix_to_flat_coords(offset_verts, native_data["matrix"]) + mesh["has_cartesian_point_offset"] = True + mesh["cartesian_point_offset"] = f"{offset_x},{offset_y},{offset_z}" else: - verts = [None] * len(self.mesh_data["co"]) - for i in range(0, len(self.mesh_data["co"]), 3): - verts[i], verts[i + 1], verts[i + 2], _ = native_data["matrix"] @ mathutils.Vector( - ( - self.mesh_data["co"][i] * self.unit_scale, - self.mesh_data["co"][i + 1] * self.unit_scale, - self.mesh_data["co"][i + 2] * self.unit_scale, - 1, - ) - ) + verts_array = np.array(self.mesh_data["co"]) + verts_array *= self.unit_scale + if np.allclose(native_data["matrix"], np.identity(4), atol=1e-8): + verts = verts_array.tolist() + else: + verts = self.apply_matrix_to_flat_coords(verts_array, native_data["matrix"]) mesh["has_cartesian_point_offset"] = False mesh.vertices.add(self.mesh_data["total_verts"]) @@ -1281,6 +1273,13 @@ class IfcImporter: mesh["ios_material_ids"] = self.mesh_data["material_ids"] return mesh + def apply_matrix_to_flat_coords(self, coords, matrix): + coords_array = np.array(coords).reshape(-1, 3) + ones = np.ones((coords_array.shape[0], 1)) + homogeneous_coords = np.hstack([coords_array, ones]) + transformed_coords = homogeneous_coords @ matrix.T + return transformed_coords[:, :3].flatten().tolist() + def convert_representation_item_face_based_surface_model(self, item): mesh = item.get_info_2(recursive=True) for face_set in mesh["FbsmFaces"]: @@ -1924,34 +1923,14 @@ class IfcImporter: and geometry.verts and self.is_point_far_away((geometry.verts[0], geometry.verts[1], geometry.verts[2])) ): - offset_point = np.array( - ( - float(props.blender_eastings), - float(props.blender_northings), - float(props.blender_orthogonal_height), - 0.0, - ) - ) - if geometry != shape: - m = shape.transformation.matrix.data - mat = np.array( - ([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1]) - ) - offset_point = np.linalg.inv(mat) @ offset_point - verts = [None] * len(geometry.verts) - for i in range(0, len(geometry.verts), 3): - # Note: this enh2xyz call is crazy slow. - verts[i], verts[i + 1], verts[i + 2] = ifcopenshell.util.geolocation.enh2xyz( - geometry.verts[i], - geometry.verts[i + 1], - geometry.verts[i + 2], - offset_point[0] * self.unit_scale, - offset_point[1] * self.unit_scale, - offset_point[2] * self.unit_scale, - float(props.blender_x_axis_abscissa), - float(props.blender_x_axis_ordinate), - ) + # Shift geometry close to the origin based off that first vert it found + verts_array = np.array(geometry.verts) + offset = np.array([-geometry.verts[0], -geometry.verts[1], -geometry.verts[2]]) + offset_verts = verts_array + np.tile(offset, len(verts_array) // 3) + verts = offset_verts.tolist() + mesh["has_cartesian_point_offset"] = True + mesh["cartesian_point_offset"] = f"{geometry.verts[0]},{geometry.verts[1]},{geometry.verts[2]}" else: verts = geometry.verts mesh["has_cartesian_point_offset"] = False