Rewrite coordinate offset to use np.array not Vector() for precision

This commit is contained in:
Dion Moult
2024-09-27 23:08:54 +10:00
parent 9f02737a2e
commit 4261fdbed6
5 changed files with 41 additions and 32 deletions
+4 -4
View File
@@ -1368,7 +1368,10 @@ class IfcImporter:
mesh = bpy.data.meshes.new(tool.Loader.get_mesh_name_from_shape(geometry)) mesh = bpy.data.meshes.new(tool.Loader.get_mesh_name_from_shape(geometry))
if cartesian_point_offset: if cartesian_point_offset is False:
verts = geometry.verts
mesh["has_cartesian_point_offset"] = False
elif cartesian_point_offset is not None:
verts_array = np.array(geometry.verts) verts_array = np.array(geometry.verts)
offset = np.array([-cartesian_point_offset[0], -cartesian_point_offset[1], -cartesian_point_offset[2]]) offset = np.array([-cartesian_point_offset[0], -cartesian_point_offset[1], -cartesian_point_offset[2]])
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3) offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
@@ -1378,9 +1381,6 @@ class IfcImporter:
mesh["cartesian_point_offset"] = ( mesh["cartesian_point_offset"] = (
f"{cartesian_point_offset[0]},{cartesian_point_offset[1]},{cartesian_point_offset[2]}" f"{cartesian_point_offset[0]},{cartesian_point_offset[1]},{cartesian_point_offset[2]}"
) )
elif cartesian_point_offset is False:
verts = geometry.verts
mesh["has_cartesian_point_offset"] = False
elif geometry.verts and tool.Loader.is_point_far_away( elif geometry.verts and tool.Loader.is_point_far_away(
(geometry.verts[0], geometry.verts[1], geometry.verts[2]), is_meters=True (geometry.verts[0], geometry.verts[1], geometry.verts[2]), is_meters=True
): ):
@@ -1831,8 +1831,8 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
rep_obj = bpy.context.scene.BIMGeometryProperties.representation_obj rep_obj = bpy.context.scene.BIMGeometryProperties.representation_obj
if coordinate_offset := tool.Geometry.get_cartesian_point_offset(rep_obj): if (coordinate_offset := tool.Geometry.get_cartesian_point_offset(rep_obj)) is not None:
verts = [(v.co + coordinate_offset) / unit_scale for v in obj.data.vertices] verts = [((np.array(v.co) + coordinate_offset) / unit_scale).tolist() for v in obj.data.vertices]
else: else:
verts = [v.co / unit_scale for v in obj.data.vertices] verts = [v.co / unit_scale for v in obj.data.vertices]
+31 -22
View File
@@ -22,6 +22,7 @@ import struct
import hashlib import hashlib
import logging import logging
import numpy as np import numpy as np
import numpy.typing as npt
import multiprocessing import multiprocessing
import ifcopenshell import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
@@ -460,12 +461,12 @@ class Geometry(bonsai.core.tool.Geometry):
return ifcopenshell.util.representation.get_representation(element, context) return ifcopenshell.util.representation.get_representation(element, context)
@classmethod @classmethod
def get_cartesian_point_offset(cls, obj: bpy.types.Object) -> Vector | None: def get_cartesian_point_offset(cls, obj: bpy.types.Object) -> npt.NDArray[np.float64] | None:
if ( if (
obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT" obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT"
and obj.BIMObjectProperties.cartesian_point_offset and obj.BIMObjectProperties.cartesian_point_offset
): ):
return Vector(tuple(map(float, obj.BIMObjectProperties.cartesian_point_offset.split(",")))) return np.array(tuple(map(float, obj.BIMObjectProperties.cartesian_point_offset.split(","))))
@classmethod @classmethod
def get_element_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: def get_element_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
@@ -693,8 +694,11 @@ class Geometry(bonsai.core.tool.Geometry):
if element.is_a("IfcAnnotation") and ifc_importer.is_curve_annotation(element): if element.is_a("IfcAnnotation") and ifc_importer.is_curve_annotation(element):
mesh = ifc_importer.create_curve(element, shape) mesh = ifc_importer.create_curve(element, shape)
elif shape: elif shape:
cartesian_point_offset = cls.get_cartesian_point_offset(obj)
if cartesian_point_offset is None:
cartesian_point_offset = False
mesh = ifc_importer.create_mesh( mesh = ifc_importer.create_mesh(
element, shape, cartesian_point_offset=cls.get_cartesian_point_offset(obj) or False element, shape, cartesian_point_offset=cartesian_point_offset
) )
ifc_importer.material_creator.load_existing_materials() ifc_importer.material_creator.load_existing_materials()
shape_has_openings = cls.does_shape_has_openings(shape) shape_has_openings = cls.does_shape_has_openings(shape)
@@ -1435,10 +1439,10 @@ class Geometry(bonsai.core.tool.Geometry):
rep_obj = props.representation_obj rep_obj = props.representation_obj
coordinate_offset = cls.get_cartesian_point_offset(rep_obj) coordinate_offset = cls.get_cartesian_point_offset(rep_obj)
rep_matrix = rep_obj.matrix_world.copy() rep_matrix = np.array(rep_obj.matrix_world.copy())
if coordinate_offset: if coordinate_offset is not None:
rep_matrix.translation -= coordinate_offset rep_matrix[:, 3][0:3] -= coordinate_offset
rep_matrix_i = rep_matrix.inverted() rep_matrix_i = np.linalg.inv(rep_matrix)
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get()) builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
has_changed = False has_changed = False
@@ -1459,9 +1463,9 @@ class Geometry(bonsai.core.tool.Geometry):
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position) ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position)
continue continue
position = rep_matrix_i @ obj.matrix_world position = rep_matrix_i @ np.array(obj.matrix_world)
position.translation /= unit_scale position[:, 3][0:3] /= unit_scale
item.Position = builder.create_axis2_placement_3d_from_matrix(np.array(position)) item.Position = builder.create_axis2_placement_3d_from_matrix(position)
if old_position: if old_position:
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position) ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position)
@@ -1482,31 +1486,36 @@ class Geometry(bonsai.core.tool.Geometry):
@classmethod @classmethod
def import_item(cls, obj: bpy.types.Object) -> None: def import_item(cls, obj: bpy.types.Object) -> None:
props = bpy.context.scene.BIMGeometryProperties props = bpy.context.scene.BIMGeometryProperties
rep_obj = props.representation_obj
tool.Loader.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get()) tool.Loader.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get())
tool.Loader.settings.context_settings = tool.Loader.create_settings() tool.Loader.settings.context_settings = tool.Loader.create_settings()
tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True) tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True)
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
geometry = tool.Loader.create_generic_shape(item)
obj.data.clear_geometry() obj.data.clear_geometry()
tool.Loader.convert_geometry_to_mesh(geometry, obj.data)
rep_obj = props.representation_obj geometry = tool.Loader.create_generic_shape(item)
if (cartesian_point_offset := cls.get_cartesian_point_offset(rep_obj)) is not None:
verts_array = np.array(geometry.verts)
offset = np.array([-cartesian_point_offset[0], -cartesian_point_offset[1], -cartesian_point_offset[2]])
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
verts = offset_verts.tolist()
else:
verts = geometry.verts
tool.Loader.convert_geometry_to_mesh(geometry, obj.data, verts=verts)
obj.matrix_world = rep_obj.matrix_world.copy() obj.matrix_world = rep_obj.matrix_world.copy()
if coordinate_offset := cls.get_cartesian_point_offset(rep_obj):
for vert in obj.data.vertices:
vert.co -= coordinate_offset
if (is_swept_area := item.is_a("IfcSweptAreaSolid")) or item.is_a("IfcConic"): if (is_swept_area := item.is_a("IfcSweptAreaSolid")) or item.is_a("IfcConic"):
position = item.Position position = item.Position
# Positional is optionaly only for SweptAreaSolid. # Positional is optionaly only for SweptAreaSolid.
if position or not is_swept_area: if position or not is_swept_area:
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
position = Matrix(ifcopenshell.util.placement.get_axis2placement(position).tolist()) position = ifcopenshell.util.placement.get_axis2placement(position)
position.translation *= unit_scale position[:, 3][0:3] *= unit_scale
item_matrix = rep_obj.matrix_world.copy() item_matrix = np.array(rep_obj.matrix_world.copy())
if coordinate_offset: if cartesian_point_offset is not None:
item_matrix.translation -= coordinate_offset item_matrix[:, 3][0:3] -= cartesian_point_offset
item_matrix = item_matrix @ position item_matrix = Matrix(item_matrix @ position)
transformation = obj.matrix_world.inverted() @ item_matrix transformation = obj.matrix_world.inverted() @ item_matrix
transformation_i = transformation.inverted() transformation_i = transformation.inverted()
@@ -83,7 +83,7 @@ def add_representation(
"context": context, "context": context,
"blender_object": blender_object, "blender_object": blender_object,
"geometry": geometry, "geometry": geometry,
"coordinate_offset": coordinate_offset, "coordinate_offset": Vector(coordinate_offset) if coordinate_offset is not None else None,
"total_items": total_items, "total_items": total_items,
"unit_scale": unit_scale, "unit_scale": unit_scale,
"should_force_faceted_brep": should_force_faceted_brep, "should_force_faceted_brep": should_force_faceted_brep,
@@ -1086,12 +1086,12 @@ class ShapeBuilder:
) )
return points, segments, transition_arc return points, segments, transition_arc
def mesh(self, points: list[Vector], faces: list[list[int]]) -> ifcopenshell.entity_instance: def mesh(self, points: list[list[float]], faces: list[list[int]]) -> ifcopenshell.entity_instance:
if self.file.schema == "IFC2X3": if self.file.schema == "IFC2X3":
return self.faceted_brep(points, faces) return self.faceted_brep(points, faces)
return self.polygonal_face_set(points, faces) return self.polygonal_face_set(points, faces)
def faceted_brep(self, points: list[Vector], faces: list[list[int]]) -> ifcopenshell.entity_instance: def faceted_brep(self, points: list[list[float]], faces: list[list[int]]) -> ifcopenshell.entity_instance:
"""Generate an IfcFacetedBrep with a closed shell """Generate an IfcFacetedBrep with a closed shell
Note that :func:`polygonal_face_set` is recommended in IFC4. Note that :func:`polygonal_face_set` is recommended in IFC4.
@@ -1109,7 +1109,7 @@ class ShapeBuilder:
] ]
return self.file.createIfcFacetedBrep(self.file.createIfcClosedShell(faces)) return self.file.createIfcFacetedBrep(self.file.createIfcClosedShell(faces))
def polygonal_face_set(self, points: list[Vector], faces: list[list[int]]) -> ifcopenshell.entity_instance: def polygonal_face_set(self, points: list[list[float]], faces: list[list[int]]) -> ifcopenshell.entity_instance:
""" """
Generate an IfcPolygonalFaceSet Generate an IfcPolygonalFaceSet