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()