#2104. Support for adding conversion based units to the project.

This commit is contained in:
Dion Moult
2022-03-27 10:38:53 +11:00
parent 0303d217bf
commit ca03a88601
9 changed files with 102 additions and 42 deletions
@@ -156,7 +156,9 @@ class UpdateRepresentation(bpy.types.Operator):
def update_obj_mesh_representation(self, context, obj): def update_obj_mesh_representation(self, context, obj):
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id) 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"): if product.is_a("IfcGridAxis"):
ifcopenshell.api.run("grid.create_axis_curve", self.file, **{"axis_curve": obj, "grid_axis": product}) ifcopenshell.api.run("grid.create_axis_curve", self.file, **{"axis_curve": obj, "grid_axis": product})
@@ -20,18 +20,19 @@ import bpy
from . import ui, prop, operator from . import ui, prop, operator
classes = ( classes = (
operator.AssignSceneUnits, operator.AddContextDependentUnit,
operator.AssignUnit, operator.AddConversionBasedUnit,
operator.UnassignUnit,
operator.LoadUnits,
operator.DisableUnitEditingUI,
operator.RemoveUnit,
operator.AddMonetaryUnit, operator.AddMonetaryUnit,
operator.AddSIUnit, operator.AddSIUnit,
operator.AddContextDependentUnit, operator.AssignSceneUnits,
operator.EnableEditingUnit, operator.AssignUnit,
operator.DisableEditingUnit, operator.DisableEditingUnit,
operator.DisableUnitEditingUI,
operator.EditUnit, operator.EditUnit,
operator.EnableEditingUnit,
operator.LoadUnits,
operator.RemoveUnit,
operator.UnassignUnit,
prop.Unit, prop.Unit,
prop.BIMUnitProperties, prop.BIMUnitProperties,
ui.BIM_PT_units, ui.BIM_PT_units,
@@ -16,6 +16,10 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.util.unit
import ifcopenshell.util.schema
import ifcopenshell.util.attribute
import blenderbim.tool as tool import blenderbim.tool as tool
@@ -29,9 +33,32 @@ class UnitsData:
@classmethod @classmethod
def load(cls): 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 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 @classmethod
def get_total_units(cls): def get_total_units(cls):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
@@ -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)) 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): class AddMonetaryUnit(bpy.types.Operator, Operator):
bl_idname = "bim.add_monetary_unit" bl_idname = "bim.add_monetary_unit"
bl_label = "Add Monetary Unit" bl_label = "Add Monetary Unit"
@@ -17,11 +17,9 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
import ifcopenshell
import ifcopenshell.util.schema
import ifcopenshell.util.attribute
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.unit.data import UnitsData
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from bpy.props import ( from bpy.props import (
PointerProperty, PointerProperty,
@@ -35,34 +33,22 @@ from bpy.props import (
) )
unitclasses_enum = [] def get_unit_classes(self, context):
namedunittypes_enum = [] if not UnitsData.is_loaded:
UnitsData.load()
return UnitsData.data["unit_classes"]
def purge(): def get_conversion_unit_types(self, context):
global unitclasses_enum if not UnitsData.is_loaded:
global namedunittypes_enum UnitsData.load()
unitclasses_enum = [] return UnitsData.data["conversion_unit_types"]
namedunittypes_enum = []
def getUnitClasses(self, context): def get_named_unit_types(self, context):
global unitclasses_enum if not UnitsData.is_loaded:
if not len(unitclasses_enum) and IfcStore.get_file(): UnitsData.load()
declarations = ifcopenshell.util.schema.get_subtypes(IfcStore.get_schema().declaration_by_name("IfcNamedUnit")) return UnitsData.data["named_unit_types"]
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
class Unit(PropertyGroup): class Unit(PropertyGroup):
@@ -78,6 +64,7 @@ class BIMUnitProperties(PropertyGroup):
units: CollectionProperty(name="Units", type=Unit) units: CollectionProperty(name="Units", type=Unit)
active_unit_index: IntProperty(name="Active Unit Index") active_unit_index: IntProperty(name="Active Unit Index")
active_unit_id: IntProperty(name="Active Unit Id") active_unit_id: IntProperty(name="Active Unit Id")
unit_classes: EnumProperty(items=getUnitClasses, name="Unit Classes") unit_classes: EnumProperty(items=get_unit_classes, name="Unit Classes")
named_unit_types: EnumProperty(items=getNamedUnitTypes, name="Named Unit Types") 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) unit_attributes: CollectionProperty(name="Unit Attributes", type=Attribute)
@@ -57,6 +57,10 @@ class BIM_PT_units(Panel):
if self.props.unit_classes == "IfcMonetaryUnit": if self.props.unit_classes == "IfcMonetaryUnit":
row.operator("bim.add_monetary_unit", text="", icon="ADD") 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": elif self.props.unit_classes == "IfcDerivedUnit":
pass # TODO pass # TODO
elif self.props.unit_classes == "IfcSIUnit": elif self.props.unit_classes == "IfcSIUnit":
+9 -1
View File
@@ -32,7 +32,9 @@ def assign_scene_units(ifc, unit):
areaunit = ifc.run("unit.add_conversion_based_unit", name=area_name) areaunit = ifc.run("unit.add_conversion_based_unit", name=area_name)
volumeunit = ifc.run("unit.add_conversion_based_unit", name=volume_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): 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 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): def enable_editing_unit(unit_tool, unit=None):
unit_tool.set_active_unit(unit) unit_tool.set_active_unit(unit)
unit_tool.import_unit_attributes(unit) unit_tool.import_unit_attributes(unit)
@@ -50,6 +50,16 @@ Scenario: Add context dependent unit
When I press "bim.add_context_dependent_unit(name='THINGAMAJIGS', unit_type='LENGTHUNIT')" When I press "bim.add_context_dependent_unit(name='THINGAMAJIGS', unit_type='LENGTHUNIT')"
Then nothing happens 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 Scenario: Enable editing unit
Given an empty IFC project Given an empty IFC project
And the variable "unit" is "{ifc}.by_type('IfcSIUnit')[0].id()" And the variable "unit" is "{ifc}.by_type('IfcSIUnit')[0].id()"
+13 -2
View File
@@ -39,7 +39,9 @@ class TestAssignSceneUnits:
"unit.add_si_unit", unit_type="VOLUMEUNIT", name="name", prefix="prefix" "unit.add_si_unit", unit_type="VOLUMEUNIT", name="name", prefix="prefix"
).should_be_called().will_return("volumeunit") ).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) subject.assign_scene_units(ifc, unit)
def test_creating_and_assigning_imperial_units(self, 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") 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.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) 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" 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: class TestEnableEditingUnit:
def test_run(self, unit): def test_run(self, unit):
unit.set_active_unit("unit").should_be_called() unit.set_active_unit("unit").should_be_called()