From a997b2f999adb998f649ec965c71807946d55832 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 2 Jan 2021 19:33:32 +1100 Subject: [PATCH] WIP you can now partially edit the object placement of an object. See #1222. --- .../module/geometry/add_object_placement.py | 63 +++++++++++++++++++ .../geometry/add_shape_representation.py | 15 +---- .../blenderbim/bim/operator.py | 8 +++ .../ifcopenshell/util/unit.py | 14 +++++ 4 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 src/ifcblenderexport/blenderbim/bim/module/geometry/add_object_placement.py diff --git a/src/ifcblenderexport/blenderbim/bim/module/geometry/add_object_placement.py b/src/ifcblenderexport/blenderbim/bim/module/geometry/add_object_placement.py new file mode 100644 index 0000000000..6d7fc56eaf --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/geometry/add_object_placement.py @@ -0,0 +1,63 @@ +import numpy as np +import ifcopenshell.util.placement + + +class Usecase: + def __init__(self, file, settings=None): + self.file = file + self.settings = { + "product": None, + "matrix": np.eye(4) + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) + if not self.settings["product"].ObjectPlacement: + placement_rel_to = None + else: + placement_rel_to = self.settings["product"].ObjectPlacement.PlacementRelTo + placement = self.file.createIfcLocalPlacement( + placement_rel_to, self.get_relative_placement(placement_rel_to) + ) + self.settings["product"].ObjectPlacement = placement + return placement + + def get_relative_placement(self, placement_rel_to): + if placement_rel_to: + relating_object_matrix = ifcopenshell.util.placement.get_local_placement(placement_rel_to) + relating_object_matrix[0][3] = self.convert_unit_to_si(relating_object_matrix[0][3]) + relating_object_matrix[1][3] = self.convert_unit_to_si(relating_object_matrix[1][3]) + relating_object_matrix[2][3] = self.convert_unit_to_si(relating_object_matrix[2][3]) + else: + relating_object_matrix = np.eye(4) + m = self.settings["matrix"] + x = np.array((m[0][0], m[1][0], m[2][0])) + z = np.array((m[0][2], m[1][2], m[2][2])) + o = np.array((m[0][3], m[1][3], m[2][3])) + object_matrix = ifcopenshell.util.placement.a2p(o, z, x) + #relative_placement_matrix = relating_object_matrix.inverted() @ object_matrix + relative_placement_matrix = relating_object_matrix @ object_matrix + return self.create_ifc_axis_2_placement_3d( + relative_placement_matrix[:,3][0:3], + relative_placement_matrix[:,2][0:3], + relative_placement_matrix[:,0][0:3], + ) + + def create_ifc_axis_2_placement_3d(self, point, up, forward): + return self.file.createIfcAxis2Placement3D( + self.create_cartesian_point(point), + self.file.createIfcDirection(up.tolist()), + self.file.createIfcDirection(forward.tolist()), + ) + + def create_cartesian_point(self, co): + co = self.convert_si_to_unit(co) + return self.file.createIfcCartesianPoint(co.tolist()) + + def convert_si_to_unit(self, co): + return co / self.unit_scale + + def convert_unit_to_si(self, co): + return co * self.unit_scale diff --git a/src/ifcblenderexport/blenderbim/bim/module/geometry/add_shape_representation.py b/src/ifcblenderexport/blenderbim/bim/module/geometry/add_shape_representation.py index f0446c2a48..eb39fc524d 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/geometry/add_shape_representation.py +++ b/src/ifcblenderexport/blenderbim/bim/module/geometry/add_shape_representation.py @@ -21,26 +21,13 @@ class Usecase: def execute(self): if self.settings["unit_scale"] is None: - self.settings["unit_scale"] = self.calculate_unit_scale() + self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) if self.settings["context"].ContextType == "Model": return self.create_model_representation() elif self.settings["context"].ContextType == "Plan": return self.create_plan_representation() return self.create_variable_representation() - def calculate_unit_scale(self): - units = self.file.by_type("IfcUnitAssignment")[0] - unit_scale = 1 - for unit in units.Units: - if not hasattr(unit, "UnitType") or unit.UnitType != "LENGTHUNIT": - continue - while unit.is_a("IfcConversionBasedUnit"): - unit_scale *= unit.ConversionFactor.ValueComponent.wrappedValue - unit = unit.ConversionFactor.UnitComponent - if unit.is_a("IfcSIUnit"): - unit_scale *= ifcopenshell.util.unit.get_prefix_multiplier(unit.Prefix) - return unit_scale - def create_model_representation(self): if self.settings["context"].is_a() == "IfcGeometricRepresentationContext": return self.create_variable_representation() diff --git a/src/ifcblenderexport/blenderbim/bim/operator.py b/src/ifcblenderexport/blenderbim/bim/operator.py index b2f6ec136c..66480ba6ab 100644 --- a/src/ifcblenderexport/blenderbim/bim/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/operator.py @@ -13,6 +13,7 @@ import ifcopenshell.util.geolocation import ifcopenshell.util.element import ifcopenshell.util.schema import tempfile +import numpy as np from . import export_ifc from . import import_ifc from . import qto @@ -4351,6 +4352,13 @@ class BakeParametricGeometry(bpy.types.Operator): self.file = ifc.IfcStore.get_file() element = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id) + import blenderbim.bim.module.geometry.add_object_placement as add_object_placement + usecase = add_object_placement.Usecase(self.file, { + "product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), + "matrix": np.array(obj.matrix_world) + }) + result = usecase.execute() + import blenderbim.bim.module.geometry.add_shape_representation as add_shape_representation usecase = add_shape_representation.Usecase(self.file, { "context": element.ContextOfItems, diff --git a/src/ifcopenshell-python/ifcopenshell/util/unit.py b/src/ifcopenshell-python/ifcopenshell/util/unit.py index 2937f8213c..dde1b29c51 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/unit.py +++ b/src/ifcopenshell-python/ifcopenshell/util/unit.py @@ -142,3 +142,17 @@ def convert(value, from_prefix, from_unit, to_prefix, to_unit): value *= 1 / get_prefix_multiplier(to_prefix) value *= 1 / get_prefix_multiplier(to_prefix) return value + + +def calculate_unit_scale(file): + units = file.by_type("IfcUnitAssignment")[0] + unit_scale = 1 + for unit in units.Units: + if not hasattr(unit, "UnitType") or unit.UnitType != "LENGTHUNIT": + continue + while unit.is_a("IfcConversionBasedUnit"): + unit_scale *= unit.ConversionFactor.ValueComponent.wrappedValue + unit = unit.ConversionFactor.UnitComponent + if unit.is_a("IfcSIUnit"): + unit_scale *= get_prefix_multiplier(unit.Prefix) + return unit_scale