You can now add and edit monetary units, and basic editing of other unit types.

This commit is contained in:
Dion Moult
2021-08-10 17:35:18 +10:00
parent 538029f396
commit ab38e6bf7c
14 changed files with 230 additions and 48 deletions
@@ -236,7 +236,7 @@ class CreateDrawing(bpy.types.Operator):
return svg_path return svg_path
# This is a work in progress. See #1153 and #1564. # This is a work in progress. See #1153 and #1564.
# Switch from old to new if you are testing v0.7.0 # Switch from old to new if you are testing v0.7.0
self.generate_linework_old(svg_path) self.generate_linework_old(context, svg_path)
# self.generate_linework_new(svg_path) # self.generate_linework_new(svg_path)
return svg_path return svg_path
@@ -266,7 +266,7 @@ class CreateDrawing(bpy.types.Operator):
with open(svg_path, "w") as svg: with open(svg_path, "w") as svg:
svg.write(buffer.get_value()) svg.write(buffer.get_value())
def generate_linework_old(self, svg_path): def generate_linework_old(self, context, svg_path):
ifcconvert_path = os.path.join(cwd, "..", "..", "..", "libs", "IfcConvert") ifcconvert_path = os.path.join(cwd, "..", "..", "..", "libs", "IfcConvert")
subprocess.run( subprocess.run(
[ [
@@ -1,4 +1,6 @@
import bpy import bpy
import ifcopenshell
import ifcopenshell.util.schema
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from bpy.props import ( from bpy.props import (
@@ -82,17 +84,8 @@ def getIfcClasses(self, context):
file = IfcStore.get_file() file = IfcStore.get_file()
if len(classes_enum) < 1 and file: if len(classes_enum) < 1 and file:
declaration = IfcStore.get_schema().declaration_by_name(context.scene.BIMRootProperties.ifc_product) declaration = IfcStore.get_schema().declaration_by_name(context.scene.BIMRootProperties.ifc_product)
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
def get_classes(declaration): classes_enum.extend([(c, c, "") for c in sorted([d.name() for d in declarations])])
results = []
if not declaration.is_abstract():
results.append(declaration.name())
for subtype in declaration.subtypes():
results.extend(get_classes(subtype))
return results
classes = get_classes(declaration)
classes_enum.extend([(c, c, "") for c in sorted(classes)])
return classes_enum return classes_enum
@@ -6,6 +6,10 @@ classes = (
operator.LoadUnits, operator.LoadUnits,
operator.DisableUnitEditingUI, operator.DisableUnitEditingUI,
operator.RemoveUnit, operator.RemoveUnit,
operator.AddMonetaryUnit,
operator.EnableEditingUnit,
operator.DisableEditingUnit,
operator.EditUnit,
prop.Unit, prop.Unit,
prop.BIMUnitProperties, prop.BIMUnitProperties,
ui.BIM_PT_units, ui.BIM_PT_units,
@@ -1,5 +1,6 @@
import bpy import bpy
import ifcopenshell.api import ifcopenshell.api
import blenderbim.bim.helper
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.unit.data import Data from ifcopenshell.api.unit.data import Data
@@ -78,6 +79,8 @@ class LoadUnits(bpy.types.Operator):
unit_type = unit.get("UserDefinedType", None) unit_type = unit.get("UserDefinedType", None)
if not unit_type: if not unit_type:
unit_type = unit.get("UnitType", None) unit_type = unit.get("UnitType", None)
if unit["type"] == "IfcMonetaryUnit":
unit_type = "CURRENCY"
new = props.units.add() new = props.units.add()
new.ifc_definition_id = ifc_definition_id new.ifc_definition_id = ifc_definition_id
@@ -86,7 +89,7 @@ class LoadUnits(bpy.types.Operator):
new.icon = icon new.icon = icon
props.is_editing = True props.is_editing = True
# bpy.ops.bim.disable_editing_unit() bpy.ops.bim.disable_editing_unit()
return {"FINISHED"} return {"FINISHED"}
@@ -116,3 +119,73 @@ class RemoveUnit(bpy.types.Operator):
Data.load(self.file) Data.load(self.file)
bpy.ops.bim.load_units() bpy.ops.bim.load_units()
return {"FINISHED"} return {"FINISHED"}
class AddMonetaryUnit(bpy.types.Operator):
bl_idname = "bim.add_monetary_unit"
bl_label = "Add Monetary 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_monetary_unit", self.file)
ifcopenshell.api.run("unit.assign_unit", self.file, units=[unit])
Data.load(self.file)
bpy.ops.bim.load_units()
return {"FINISHED"}
class EnableEditingUnit(bpy.types.Operator):
bl_idname = "bim.enable_editing_unit"
bl_label = "Enable Editing Unit"
bl_options = {"REGISTER", "UNDO"}
unit: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMUnitProperties
while len(props.unit_attributes) > 0:
props.unit_attributes.remove(0)
data = Data.units[self.unit]
blenderbim.bim.helper.import_attributes(data["type"], props.unit_attributes, data)
props.active_unit_id = self.unit
return {"FINISHED"}
class DisableEditingUnit(bpy.types.Operator):
bl_idname = "bim.disable_editing_unit"
bl_label = "Disable Editing Unit"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMUnitProperties.active_unit_id = 0
return {"FINISHED"}
class EditUnit(bpy.types.Operator):
bl_idname = "bim.edit_unit"
bl_label = "Edit Unit"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
props = context.scene.BIMUnitProperties
attributes = blenderbim.bim.helper.export_attributes(props.unit_attributes)
self.file = IfcStore.get_file()
unit = self.file.by_id(props.active_unit_id)
if unit.is_a("IfcMonetaryUnit"):
ifcopenshell.api.run("unit.edit_monetary_unit", self.file, **{"unit": unit, "attributes": attributes})
elif unit.is_a("IfcDerivedUnit"):
ifcopenshell.api.run("unit.edit_derived_unit", self.file, **{"unit": unit, "attributes": attributes})
elif unit.is_a("IfcNamedUnit"):
ifcopenshell.api.run("unit.edit_named_unit", self.file, **{"unit": unit, "attributes": attributes})
Data.load(IfcStore.get_file())
bpy.ops.bim.load_units()
return {"FINISHED"}
@@ -1,4 +1,7 @@
import bpy import bpy
import ifcopenshell
import ifcopenshell.util.schema
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from bpy.props import ( from bpy.props import (
@@ -13,6 +16,23 @@ from bpy.props import (
) )
unitclasses_enum = []
def purge():
global unitclasses_enum
unitclasses_enum = []
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
class Unit(PropertyGroup): class Unit(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
unit_type: StringProperty(name="Unit Type") unit_type: StringProperty(name="Unit Type")
@@ -24,3 +44,6 @@ class BIMUnitProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing") is_editing: BoolProperty(name="Is Editing")
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")
unit_classes: EnumProperty(items=getUnitClasses, name="Unit Classes")
unit_attributes: CollectionProperty(name="Unit Attributes", type=Attribute)
+37 -11
View File
@@ -26,20 +26,37 @@ class BIM_PT_units(Panel):
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text="{} Units Found".format(len(Data.unit_assignment)), icon="SNAP_GRID") row.label(text="{} Units Found".format(len(Data.unit_assignment)), icon="SNAP_GRID")
if self.props.is_editing: if self.props.is_editing:
# row.operator("bim.add_unit", text="", icon="ADD")
row.operator("bim.disable_unit_editing_ui", text="", icon="CANCEL") row.operator("bim.disable_unit_editing_ui", text="", icon="CANCEL")
else: else:
row.operator("bim.load_units", text="", icon="GREASEPENCIL") row.operator("bim.load_units", text="", icon="GREASEPENCIL")
if self.props.is_editing: if not self.props.is_editing:
self.layout.template_list( return
"BIM_UL_units",
"", row = self.layout.row(align=True)
self.props, row.prop(self.props, "unit_classes", text="")
"units",
self.props, if self.props.unit_classes == "IfcMonetaryUnit":
"active_unit_index", row.operator("bim.add_monetary_unit", text="", icon="ADD")
) elif self.props.unit_classes == "IfcDerivedUnit":
pass # TODO
else:
pass # TODO
self.layout.template_list(
"BIM_UL_units",
"",
self.props,
"units",
self.props,
"active_unit_index",
)
if self.props.active_unit_id:
self.draw_editable_ui(context)
def draw_editable_ui(self, context):
blenderbim.bim.helper.draw_attributes(self.props.unit_attributes, self.layout)
class BIM_UL_units(UIList): class BIM_UL_units(UIList):
@@ -49,4 +66,13 @@ class BIM_UL_units(UIList):
row = layout.row(align=True) row = layout.row(align=True)
row.label(text=item.unit_type or "No Type", icon=item.icon) row.label(text=item.unit_type or "No Type", icon=item.icon)
row.label(text=item.name or "Unnamed") row.label(text=item.name or "Unnamed")
row.operator("bim.remove_unit", text="", icon="X").unit = item.ifc_definition_id
if props.active_unit_id == item.ifc_definition_id:
row.operator("bim.edit_unit", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_unit", text="", icon="CANCEL")
elif props.active_unit_id:
row.operator("bim.remove_unit", text="", icon="X").unit = item.ifc_definition_id
else:
op = row.operator("bim.enable_editing_unit", text="", icon="GREASEPENCIL")
op.unit = item.ifc_definition_id
row.operator("bim.remove_unit", text="", icon="X").unit = item.ifc_definition_id
+2 -2
View File
@@ -106,11 +106,11 @@ class Csv2Ifc:
elif self.has_categories: elif self.has_categories:
for category, value in cost_item["CostValues"].items(): for category, value in cost_item["CostValues"].items():
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"]) cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"])
cost_value.AppliedValue = self.file.createIfcReal(value) cost_value.AppliedValue = self.file.createIfcMonetaryMeasure(value)
cost_value.Category = category cost_value.Category = category
else: else:
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"]) cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"])
cost_value.AppliedValue = self.file.createIfcReal(cost_item["CostValues"]) cost_value.AppliedValue = self.file.createIfcMonetaryMeasure(cost_item["CostValues"])
if cost_item["CostQuantities"]: if cost_item["CostQuantities"]:
quantity_class = ifcopenshell.util.unit.get_symbol_quantity_class(cost_item["CostQuantitiesUnit"]) quantity_class = ifcopenshell.util.unit.get_symbol_quantity_class(cost_item["CostQuantitiesUnit"])
@@ -9,5 +9,5 @@ class Usecase:
for name, value in self.settings["attributes"].items(): for name, value in self.settings["attributes"].items():
if name == "AppliedValue" and value is not None: if name == "AppliedValue" and value is not None:
# TODO: support all applied value select types # TODO: support all applied value select types
value = self.file.createIfcReal(value) value = self.file.createIfcMonetaryMeasure(value)
setattr(self.settings["cost_value"], name, value) setattr(self.settings["cost_value"], name, value)
@@ -0,0 +1,9 @@
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"currency": "DOLLARYDOO"}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
return self.file.create_entity("IfcMonetaryUnit", self.settings["currency"])
@@ -1,44 +1,55 @@
import ifcopenshell
import ifcopenshell.util.unit import ifcopenshell.util.unit
class Usecase(): class Usecase:
def __init__(self, file, **settings): def __init__(self, file, **settings):
self.file = file self.file = file
self.settings = { self.settings = {
"length": { "units": None,
"is_metric": True, "length": {"is_metric": True, "raw": "MILLIMETERS"},
"raw": "MILLIMETERS" "area": {"is_metric": True, "raw": "METERS"},
}, "volume": {"is_metric": True, "raw": "METERS"},
"area": {
"is_metric": True,
"raw": "METERS"
},
"volume": {
"is_metric": True,
"raw": "METERS"
},
} }
for key, value in settings.items(): for key, value in settings.items():
self.settings[key] = value self.settings[key] = value
def execute(self): def execute(self):
for unit_type, data in self.settings.items(): # We're going to refactor this to split unit creation and assignment
if data["is_metric"]: if self.settings["units"]:
data["ifc"] = self.create_metric_unit(unit_type, data) units = self.settings["units"]
else: else:
data["ifc"] = self.create_imperial_unit(unit_type, data) del self.settings["units"] # TODO refactor
units = []
for unit_type, data in self.settings.items():
if data["is_metric"]:
units.append(self.create_metric_unit(unit_type, data))
else:
units.append(self.create_imperial_unit(unit_type, data))
unit_assignment = self.get_unit_assignment()
self.assign_units(unit_assignment, units)
return unit_assignment
def get_unit_assignment(self):
unit_assignment = self.file.by_type("IfcUnitAssignment") unit_assignment = self.file.by_type("IfcUnitAssignment")
if unit_assignment: if unit_assignment:
unit_assignment = unit_assignment[0] unit_assignment = unit_assignment[0]
# TODO: handle unit rewriting, which is complicated # TODO: handle unit rewriting, which is complicated
else: else:
unit_assignment = self.file.createIfcUnitAssignment([u["ifc"] for u in self.settings.values()]) unit_assignment = self.file.createIfcUnitAssignment()
if self.file.schema == "IFC2X3": if self.file.schema == "IFC2X3":
self.file.by_type("IfcProject")[0].UnitsInContext = unit_assignment self.file.by_type("IfcProject")[0].UnitsInContext = unit_assignment
else: else:
self.file.by_type("IfcContext")[0].UnitsInContext = unit_assignment self.file.by_type("IfcContext")[0].UnitsInContext = unit_assignment
return unit_assignment return unit_assignment
def assign_units(self, unit_assignment, new_units):
units = set(unit_assignment.Units or [])
for unit in new_units:
units.add(unit)
unit_assignment.Units = list(units)
def create_metric_unit(self, unit_type, data): def create_metric_unit(self, unit_type, data):
type_prefix = "" type_prefix = ""
if unit_type == "area": if unit_type == "area":
@@ -72,7 +83,9 @@ class Usecase():
name = "{}inch".format(name_prefix + " " if name_prefix else "") name = "{}inch".format(name_prefix + " " if name_prefix else "")
elif data["raw"] == "FEET": elif data["raw"] == "FEET":
name = "{}foot".format(name_prefix + " " if name_prefix else "") name = "{}foot".format(name_prefix + " " if name_prefix else "")
value_component = self.file.create_entity("IfcReal", **{"wrappedValue": ifcopenshell.util.unit.si_conversions[name]}) value_component = self.file.create_entity(
"IfcReal", **{"wrappedValue": ifcopenshell.util.unit.si_conversions[name]}
)
conversion_factor = self.file.createIfcMeasureWithUnit(value_component, si_unit) conversion_factor = self.file.createIfcMeasureWithUnit(value_component, si_unit)
return self.file.createIfcConversionBasedUnit( return self.file.createIfcConversionBasedUnit(
dimensional_exponents, "{}UNIT".format(unit_type.upper()), name, conversion_factor dimensional_exponents, "{}UNIT".format(unit_type.upper()), name, conversion_factor
@@ -0,0 +1,10 @@
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"unit": None, "attributes": {}}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["unit"], name, value)
@@ -0,0 +1,10 @@
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"unit": None, "attributes": {}}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["unit"], name, value)
@@ -0,0 +1,10 @@
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"unit": None, "attributes": {}}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["unit"], name, value)
@@ -18,6 +18,17 @@ def is_a(entity, ifc_class):
return False return False
def get_subtypes(entity):
def get_classes(declaration):
results = []
if not declaration.is_abstract():
results.append(declaration)
for subtype in declaration.subtypes():
results.extend(get_classes(subtype))
return results
return get_classes(entity)
def reassign_class(ifc_file, element, new_class): def reassign_class(ifc_file, element, new_class):
try: try:
new_element = ifc_file.create_entity(new_class) new_element = ifc_file.create_entity(new_class)