diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 71dcc08bb9..8f8c301913 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -156,7 +156,9 @@ class UpdateRepresentation(bpy.types.Operator): def update_obj_mesh_representation(self, context, obj): product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id) - tool.Geometry.clear_cache(product) + + if not product.is_a("IfcGridAxis"): + tool.Geometry.clear_cache(product) if product.is_a("IfcGridAxis"): ifcopenshell.api.run("grid.create_axis_curve", self.file, **{"axis_curve": obj, "grid_axis": product}) diff --git a/src/blenderbim/blenderbim/bim/module/unit/__init__.py b/src/blenderbim/blenderbim/bim/module/unit/__init__.py index 11f5ab1395..3eab1de36b 100644 --- a/src/blenderbim/blenderbim/bim/module/unit/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/unit/__init__.py @@ -20,18 +20,19 @@ import bpy from . import ui, prop, operator classes = ( - operator.AssignSceneUnits, - operator.AssignUnit, - operator.UnassignUnit, - operator.LoadUnits, - operator.DisableUnitEditingUI, - operator.RemoveUnit, + operator.AddContextDependentUnit, + operator.AddConversionBasedUnit, operator.AddMonetaryUnit, operator.AddSIUnit, - operator.AddContextDependentUnit, - operator.EnableEditingUnit, + operator.AssignSceneUnits, + operator.AssignUnit, operator.DisableEditingUnit, + operator.DisableUnitEditingUI, operator.EditUnit, + operator.EnableEditingUnit, + operator.LoadUnits, + operator.RemoveUnit, + operator.UnassignUnit, prop.Unit, prop.BIMUnitProperties, ui.BIM_PT_units, diff --git a/src/blenderbim/blenderbim/bim/module/unit/data.py b/src/blenderbim/blenderbim/bim/module/unit/data.py index 1111ed0dfa..971e57b03a 100644 --- a/src/blenderbim/blenderbim/bim/module/unit/data.py +++ b/src/blenderbim/blenderbim/bim/module/unit/data.py @@ -16,6 +16,10 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +import ifcopenshell +import ifcopenshell.util.unit +import ifcopenshell.util.schema +import ifcopenshell.util.attribute import blenderbim.tool as tool @@ -29,9 +33,32 @@ class UnitsData: @classmethod def load(cls): - cls.data = {"total_units": cls.get_total_units()} + cls.data = { + "unit_classes": cls.unit_classes(), + "named_unit_types": cls.named_unit_types(), + "conversion_unit_types": cls.conversion_unit_types(), + "total_units": cls.get_total_units() + } cls.is_loaded = True + @classmethod + def unit_classes(cls): + declarations = ifcopenshell.util.schema.get_subtypes(tool.Ifc.schema().declaration_by_name("IfcNamedUnit")) + results = [(c, c, "") for c in sorted([d.name() for d in declarations])] + results.extend([("IfcDerivedUnit", "IfcDerivedUnit", ""), ("IfcMonetaryUnit", "IfcMonetaryUnit", "")]) + return results + + @classmethod + def named_unit_types(cls): + values = ifcopenshell.util.attribute.get_enum_items( + tool.Ifc.schema().declaration_by_name("IfcNamedUnit").all_attributes()[1] + ) + return [(c, c, "") for c in sorted(values)] + + @classmethod + def conversion_unit_types(cls): + return [(u, u, "") for u in ifcopenshell.util.unit.si_conversions.keys()] + @classmethod def get_total_units(cls): ifc = tool.Ifc.get() diff --git a/src/blenderbim/blenderbim/bim/module/unit/operator.py b/src/blenderbim/blenderbim/bim/module/unit/operator.py index 580a3d06f9..9eb56a31fc 100644 --- a/src/blenderbim/blenderbim/bim/module/unit/operator.py +++ b/src/blenderbim/blenderbim/bim/module/unit/operator.py @@ -94,6 +94,16 @@ class RemoveUnit(bpy.types.Operator, Operator): core.remove_unit(tool.Ifc, tool.Unit, unit=tool.Ifc.get().by_id(self.unit)) +class AddConversionBasedUnit(bpy.types.Operator, Operator): + bl_idname = "bim.add_conversion_based_unit" + bl_label = "Add Conversion Based Unit" + bl_options = {"REGISTER", "UNDO"} + name: bpy.props.StringProperty() + + def _execute(self, context): + core.add_conversion_based_unit(tool.Ifc, tool.Unit, name=self.name) + + class AddMonetaryUnit(bpy.types.Operator, Operator): bl_idname = "bim.add_monetary_unit" bl_label = "Add Monetary Unit" diff --git a/src/blenderbim/blenderbim/bim/module/unit/prop.py b/src/blenderbim/blenderbim/bim/module/unit/prop.py index 46fa36c9ca..22ef9c7a0e 100644 --- a/src/blenderbim/blenderbim/bim/module/unit/prop.py +++ b/src/blenderbim/blenderbim/bim/module/unit/prop.py @@ -17,11 +17,9 @@ # along with BlenderBIM Add-on. If not, see . import bpy -import ifcopenshell -import ifcopenshell.util.schema -import ifcopenshell.util.attribute from blenderbim.bim.ifc import IfcStore from blenderbim.bim.prop import StrProperty, Attribute +from blenderbim.bim.module.unit.data import UnitsData from bpy.types import PropertyGroup from bpy.props import ( PointerProperty, @@ -35,34 +33,22 @@ from bpy.props import ( ) -unitclasses_enum = [] -namedunittypes_enum = [] +def get_unit_classes(self, context): + if not UnitsData.is_loaded: + UnitsData.load() + return UnitsData.data["unit_classes"] -def purge(): - global unitclasses_enum - global namedunittypes_enum - unitclasses_enum = [] - namedunittypes_enum = [] +def get_conversion_unit_types(self, context): + if not UnitsData.is_loaded: + UnitsData.load() + return UnitsData.data["conversion_unit_types"] -def getUnitClasses(self, context): - global unitclasses_enum - if not len(unitclasses_enum) and IfcStore.get_file(): - declarations = ifcopenshell.util.schema.get_subtypes(IfcStore.get_schema().declaration_by_name("IfcNamedUnit")) - unitclasses_enum.extend([(c, c, "") for c in sorted([d.name() for d in declarations])]) - unitclasses_enum.extend([("IfcDerivedUnit", "IfcDerivedUnit", ""), ("IfcMonetaryUnit", "IfcMonetaryUnit", "")]) - return unitclasses_enum - - -def getNamedUnitTypes(self, context): - global namedunittypes_enum - if not len(namedunittypes_enum) and IfcStore.get_file(): - values = ifcopenshell.util.attribute.get_enum_items( - IfcStore.get_schema().declaration_by_name("IfcNamedUnit").all_attributes()[1] - ) - namedunittypes_enum.extend([(c, c, "") for c in sorted(values)]) - return namedunittypes_enum +def get_named_unit_types(self, context): + if not UnitsData.is_loaded: + UnitsData.load() + return UnitsData.data["named_unit_types"] class Unit(PropertyGroup): @@ -78,6 +64,7 @@ class BIMUnitProperties(PropertyGroup): units: CollectionProperty(name="Units", type=Unit) active_unit_index: IntProperty(name="Active Unit Index") active_unit_id: IntProperty(name="Active Unit Id") - unit_classes: EnumProperty(items=getUnitClasses, name="Unit Classes") - named_unit_types: EnumProperty(items=getNamedUnitTypes, name="Named Unit Types") + unit_classes: EnumProperty(items=get_unit_classes, name="Unit Classes") + conversion_unit_types: EnumProperty(items=get_conversion_unit_types, name="Conversion Unit Types") + named_unit_types: EnumProperty(items=get_named_unit_types, name="Named Unit Types") unit_attributes: CollectionProperty(name="Unit Attributes", type=Attribute) diff --git a/src/blenderbim/blenderbim/bim/module/unit/ui.py b/src/blenderbim/blenderbim/bim/module/unit/ui.py index 75a4b5175d..8f053d108b 100644 --- a/src/blenderbim/blenderbim/bim/module/unit/ui.py +++ b/src/blenderbim/blenderbim/bim/module/unit/ui.py @@ -57,6 +57,10 @@ class BIM_PT_units(Panel): if self.props.unit_classes == "IfcMonetaryUnit": row.operator("bim.add_monetary_unit", text="", icon="ADD") + elif self.props.unit_classes in ("IfcConversionBasedUnit", "IfcConversionBasedUnitWithOffset"): + row.prop(self.props, "conversion_unit_types", text="") + op = row.operator("bim.add_conversion_based_unit", text="", icon="ADD") + op.name = self.props.conversion_unit_types elif self.props.unit_classes == "IfcDerivedUnit": pass # TODO elif self.props.unit_classes == "IfcSIUnit": diff --git a/src/blenderbim/blenderbim/core/unit.py b/src/blenderbim/blenderbim/core/unit.py index 4c38e370ca..f5fd274a66 100644 --- a/src/blenderbim/blenderbim/core/unit.py +++ b/src/blenderbim/blenderbim/core/unit.py @@ -32,7 +32,9 @@ def assign_scene_units(ifc, unit): areaunit = ifc.run("unit.add_conversion_based_unit", name=area_name) volumeunit = ifc.run("unit.add_conversion_based_unit", name=volume_name) - ifc.run("unit.assign_unit", units=[lengthunit, areaunit, volumeunit]) + planeangleunit = ifc.run("unit.add_conversion_based_unit", name="degree") + + ifc.run("unit.assign_unit", units=[lengthunit, areaunit, volumeunit, planeangleunit]) def assign_unit(ifc, unit_tool, unit=None): @@ -77,6 +79,12 @@ def add_context_dependent_unit(ifc, unit, unit_type=None, name=None): return result +def add_conversion_based_unit(ifc, unit, name=None): + result = ifc.run("unit.add_conversion_based_unit", name=name) + unit.import_units() + return result + + def enable_editing_unit(unit_tool, unit=None): unit_tool.set_active_unit(unit) unit_tool.import_unit_attributes(unit) diff --git a/src/blenderbim/test/bim/feature/unit.feature b/src/blenderbim/test/bim/feature/unit.feature index ec6cd50019..e8d686b50a 100644 --- a/src/blenderbim/test/bim/feature/unit.feature +++ b/src/blenderbim/test/bim/feature/unit.feature @@ -50,6 +50,16 @@ Scenario: Add context dependent unit When I press "bim.add_context_dependent_unit(name='THINGAMAJIGS', unit_type='LENGTHUNIT')" Then nothing happens +Scenario: Add conversion based unit + Given an empty IFC project + When I press "bim.add_conversion_based_unit(name='inch')" + Then nothing happens + +Scenario: Add conversion based unit with offset + Given an empty IFC project + When I press "bim.add_conversion_based_unit(name='fahrenheit')" + Then nothing happens + Scenario: Enable editing unit Given an empty IFC project And the variable "unit" is "{ifc}.by_type('IfcSIUnit')[0].id()" diff --git a/src/blenderbim/test/core/test_unit.py b/src/blenderbim/test/core/test_unit.py index 9cc837b7e8..5c7143ceaa 100644 --- a/src/blenderbim/test/core/test_unit.py +++ b/src/blenderbim/test/core/test_unit.py @@ -39,7 +39,9 @@ class TestAssignSceneUnits: "unit.add_si_unit", unit_type="VOLUMEUNIT", name="name", prefix="prefix" ).should_be_called().will_return("volumeunit") - ifc.run("unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit"]).should_be_called() + ifc.run("unit.add_conversion_based_unit", name="degree").should_be_called().will_return("planeangleunit") + + ifc.run("unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit", "planeangleunit"]).should_be_called() subject.assign_scene_units(ifc, unit) def test_creating_and_assigning_imperial_units(self, ifc, unit): @@ -53,7 +55,9 @@ class TestAssignSceneUnits: unit.get_scene_unit_name("volume").should_be_called().will_return("volumename") ifc.run("unit.add_conversion_based_unit", name="volumename").should_be_called().will_return("volumeunit") - ifc.run("unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit"]).should_be_called() + ifc.run("unit.add_conversion_based_unit", name="degree").should_be_called().will_return("planeangleunit") + + ifc.run("unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit", "planeangleunit"]).should_be_called() subject.assign_scene_units(ifc, unit) @@ -115,6 +119,13 @@ class TestAddContextDependentUnit: assert subject.add_context_dependent_unit(ifc, unit, unit_type="unit_type", name="name") == "unit" +class TestAddConversionBasedUnit: + def test_run(self, ifc, unit): + ifc.run("unit.add_conversion_based_unit", name="name").should_be_called().will_return("unit") + unit.import_units().should_be_called() + assert subject.add_conversion_based_unit(ifc, unit, name="name") == "unit" + + class TestEnableEditingUnit: def test_run(self, unit): unit.set_active_unit("unit").should_be_called()