From fd163a3d1e98d0aec07fb3cbda34eaa39cbb5e2c Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 13 Aug 2021 16:18:05 +1000 Subject: [PATCH] Implement ability to add SI units --- .../blenderbim/bim/module/unit/__init__.py | 1 + .../blenderbim/bim/module/unit/operator.py | 26 +++++++- .../blenderbim/bim/module/unit/prop.py | 15 +++++ .../blenderbim/bim/module/unit/ui.py | 5 +- .../ifcopenshell/api/unit/add_si_unit.py | 9 +++ .../ifcopenshell/util/unit.py | 62 ++++++++++++++----- 6 files changed, 99 insertions(+), 19 deletions(-) create mode 100644 src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py diff --git a/src/blenderbim/blenderbim/bim/module/unit/__init__.py b/src/blenderbim/blenderbim/bim/module/unit/__init__.py index c687cffdef..a74eb70da1 100644 --- a/src/blenderbim/blenderbim/bim/module/unit/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/unit/__init__.py @@ -8,6 +8,7 @@ classes = ( operator.DisableUnitEditingUI, operator.RemoveUnit, operator.AddMonetaryUnit, + operator.AddSIUnit, operator.EnableEditingUnit, operator.DisableEditingUnit, operator.EditUnit, diff --git a/src/blenderbim/blenderbim/bim/module/unit/operator.py b/src/blenderbim/blenderbim/bim/module/unit/operator.py index f8bd288822..9a4f3bfc00 100644 --- a/src/blenderbim/blenderbim/bim/module/unit/operator.py +++ b/src/blenderbim/blenderbim/bim/module/unit/operator.py @@ -1,5 +1,6 @@ import bpy import ifcopenshell.api +import ifcopenshell.util.unit import blenderbim.bim.helper from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.unit.data import Data @@ -156,8 +157,29 @@ class AddMonetaryUnit(bpy.types.Operator): def _execute(self, context): props = context.scene.BIMUnitProperties self.file = IfcStore.get_file() - unit = ifcopenshell.api.run("unit.add_monetary_unit", self.file) - ifcopenshell.api.run("unit.assign_unit", self.file, units=[unit]) + ifcopenshell.api.run("unit.add_monetary_unit", self.file) + Data.load(self.file) + bpy.ops.bim.load_units() + return {"FINISHED"} + + +class AddSIUnit(bpy.types.Operator): + bl_idname = "bim.add_si_unit" + bl_label = "Add SI Unit" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + props = context.scene.BIMUnitProperties + self.file = IfcStore.get_file() + unit = ifcopenshell.api.run( + "unit.add_si_unit", + self.file, + unit_type=props.named_unit_types, + name=ifcopenshell.util.unit.si_type_names[props.named_unit_types], + ) Data.load(self.file) bpy.ops.bim.load_units() return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/unit/prop.py b/src/blenderbim/blenderbim/bim/module/unit/prop.py index 20056e3f84..1d863fdb8d 100644 --- a/src/blenderbim/blenderbim/bim/module/unit/prop.py +++ b/src/blenderbim/blenderbim/bim/module/unit/prop.py @@ -1,6 +1,7 @@ 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 bpy.types import PropertyGroup @@ -17,11 +18,14 @@ from bpy.props import ( unitclasses_enum = [] +namedunittypes_enum = [] def purge(): global unitclasses_enum + global namedunittypes_enum unitclasses_enum = [] + namedunittypes_enum = [] def getUnitClasses(self, context): @@ -33,6 +37,16 @@ def getUnitClasses(self, context): 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 + + class Unit(PropertyGroup): name: StringProperty(name="Name") unit_type: StringProperty(name="Unit Type") @@ -47,4 +61,5 @@ class BIMUnitProperties(PropertyGroup): 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_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 5235e5b868..7b904a4b2d 100644 --- a/src/blenderbim/blenderbim/bim/module/unit/ui.py +++ b/src/blenderbim/blenderbim/bim/module/unit/ui.py @@ -40,8 +40,11 @@ class BIM_PT_units(Panel): row.operator("bim.add_monetary_unit", text="", icon="ADD") elif self.props.unit_classes == "IfcDerivedUnit": pass # TODO + elif self.props.unit_classes == "IfcSIUnit": + row.prop(self.props, "named_unit_types", text="") + row.operator("bim.add_si_unit", text="", icon="ADD") else: - pass # TODO + row.prop(self.props, "named_unit_types", text="") self.layout.template_list( "BIM_UL_units", diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py new file mode 100644 index 0000000000..3a2154bf14 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py @@ -0,0 +1,9 @@ +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"unit_type": "LENGTHUNIT", "name": "METRE"} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + return self.file.create_entity("IfcSIUnit", UnitType=self.settings["unit_type"], Name=self.settings["name"]) diff --git a/src/ifcopenshell-python/ifcopenshell/util/unit.py b/src/ifcopenshell-python/ifcopenshell/util/unit.py index 6ccc01705e..c8fa26037f 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/unit.py +++ b/src/ifcopenshell-python/ifcopenshell/util/unit.py @@ -25,7 +25,7 @@ unit_names = [ "CANDELA", "COULOMB", "CUBIC_METRE", - "DEGREE CELSIUS", + "DEGREE_CELSIUS", "FARAD", "GRAM", "GRAY", @@ -43,7 +43,7 @@ unit_names = [ "SECOND", "SIEMENS", "SIEVERT", - "SQUARE METRE", + "SQUARE_METRE", "METRE", "STERADIAN", "TESLA", @@ -52,7 +52,6 @@ unit_names = [ "WEBER", ] - si_dimensions = { "METRE": (1, 0, 0, 0, 0, 0, 0), "SQUARE_METRE": (2, 0, 0, 0, 0, 0, 0), @@ -87,6 +86,39 @@ si_dimensions = { "OTHERWISE": (0, 0, 0, 0, 0, 0, 0), } +# See https://github.com/buildingSMART/IFC4.3.x-development/issues/72 +si_type_names = { + "ABSORBEDDOSEUNIT": "GRAY", + "AMOUNTOFSUBSTANCEUNIT": "MOLE", + "AREAUNIT": "SQUARE_METRE", + "DOSEEQUIVALENTUNIT": "SIEVERT", + "ELECTRICCAPACITANCEUNIT": "FARAD", + "ELECTRICCHARGEUNIT": "COULOMB", + "ELECTRICCONDUCTANCEUNIT": "SIEMENS", + "ELECTRICCURRENTUNIT": "AMPERE", + "ELECTRICRESISTANCEUNIT": "OHM", + "ELECTRICVOLTAGEUNIT": "VOLT", + "ENERGYUNIT": "JOULE", + "FORCEUNIT": "NEWTON", + "FREQUENCYUNIT": "HERTZ", + "ILLUMINANCEUNIT": "LUX", + "INDUCTANCEUNIT": "HENRY", + "LENGTHUNIT": "METRE", + "LUMINOUSFLUXUNIT": "LUMEN", + "LUMINOUSINTENSITYUNIT": "CANDELA", + "MAGNETICFLUXDENSITYUNIT": "TESLA", + "MAGNETICFLUXUNIT": "WEBER", + "MASSUNIT": "GRAM", + "PLANEANGLEUNIT": "RADIAN", + "POWERUNIT": "WATT", + "PRESSUREUNIT": "PASCAL", + "RADIOACTIVITYUNIT": "BECQUEREL", + "SOLIDANGLEUNIT": "STERADIAN", + "THERMODYNAMICTEMPERATUREUNIT": "KELVIN", # Or, DEGREE_CELSIUS, but this is a quirk of IFC + "TIMEUNIT": "SECOND", + "VOLUMEUNIT": "CUBIC_METRE", +} + si_conversions = { "inch": 0.0254, "foot": 0.3048, @@ -169,7 +201,7 @@ def get_prefix_multiplier(text): def get_unit_name(text): text = text.upper().replace("METER", "METRE") for name in unit_names: - if name in text: + if name.replace("_", " ") in text: return name @@ -263,19 +295,17 @@ def convert(value, from_prefix, from_unit, to_prefix, to_unit): return value -"""Returns a unit scale factor to convert to and from IFC project length units and SI meters - -Example:: - - ifc_project_length * unit_scale = si_meters - si_meters / unit_scale = ifc_project_length - -:returns: The scale factor -:rtype: float -""" - - def calculate_unit_scale(file): + """Returns a unit scale factor to convert to and from IFC project length units and SI meters + + Example:: + + ifc_project_length * unit_scale = si_meters + si_meters / unit_scale = ifc_project_length + + :returns: The scale factor + :rtype: float + """ units = file.by_type("IfcUnitAssignment")[0] unit_scale = 1 for unit in units.Units: