Fix #2264 refactor property data structures to be separate from attributes

This commit is contained in:
Dion Moult
2022-07-06 18:25:29 +10:00
parent 87f110bae5
commit 084823164e
5 changed files with 158 additions and 122 deletions
@@ -122,85 +122,91 @@ class EnablePsetEditing(bpy.types.Operator):
for prop_template in pset_template.HasPropertyTemplates:
if not prop_template.is_a("IfcSimplePropertyTemplate"):
continue # Other types not yet supported
if prop_template.TemplateType == "P_SINGLEVALUE":
try:
data_type = ifcopenshell.util.attribute.get_primitive_type(
IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType or "IfcLabel")
)
except:
# TODO: Occurs if the data type is something that exists in
# IFC4 and not in IFC2X3. To fully fix this we need to
# generate the IFC2X3 pset template definitions.
continue
elif prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
data_type = "float"
elif prop_template.TemplateType == "Q_COUNT":
data_type = "integer"
self.load_single_value(prop_template, data)
elif prop_template.TemplateType == "P_ENUMERATEDVALUE":
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
#selected_enum_items = [e.EnumerationValues.wrappedValue for e in Data.properties[e]]
data_type = ""
else:
continue # Other types not yet supported
self.load_enumerated_value(prop_template, data)
new = self.props.properties.add()
new.name = prop_template.Name
new.is_null = data.get(prop_template.Name, None) is None
new.is_optional = True
new.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
new.data_type = data_type
if data_type == "string":
new.string_value = "" if new.is_null else data[prop_template.Name]
elif data_type == "integer":
new.int_value = 0 if new.is_null else data[prop_template.Name]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else data[prop_template.Name]
elif data_type == "boolean":
new.bool_value = False if new.is_null else data[prop_template.Name]
if prop_template.TemplateType == "P_ENUMERATEDVALUE":
def load_single_value(self, prop_template, data):
try:
data_type = ifcopenshell.util.attribute.get_primitive_type(
IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType or "IfcLabel")
)
except:
# TODO: Occurs if the data type is something that exists in
# IFC4 and not in IFC2X3. To fully fix this we need to
# generate the IFC2X3 pset template definitions.
return
new.set_value(prop_template.Enumerators.EnumerationValues[0].wrappedValue)
for enum in enum_items:
new_enum = new.enumerated_values.add()
data_type = new.get_value_name()
setattr(new_enum, data_type, enum)
if data.get(prop_template.Name):
new_enum.is_selected = enum in data[prop_template.Name]
prop = self.props.properties.add()
prop.value_type = "IfcPropertySingleValue"
metadata = prop.metadata
metadata.name = prop_template.Name
metadata.is_null = data.get(prop_template.Name, None) is None
metadata.is_optional = True
metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
metadata.data_type = data_type
if data_type == "string":
metadata.string_value = "" if metadata.is_null else data[prop_template.Name]
elif data_type == "integer":
metadata.int_value = 0 if metadata.is_null else data[prop_template.Name]
elif data_type == "float":
metadata.float_value = 0.0 if metadata.is_null else data[prop_template.Name]
elif data_type == "boolean":
metadata.bool_value = False if metadata.is_null else data[prop_template.Name]
def load_enumerated_value(self, prop_template, data):
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
selected_enum_items = data.get(prop_template.Name, [])
prop = self.props.properties.add()
prop.value_type = "IfcPropertyEnumeratedValue"
metadata = prop.metadata
metadata.name = prop_template.Name
metadata.is_null = data.get(prop_template.Name, None) is None
metadata.is_optional = True
metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
# Cute hack to abuse the metadata to find the Blender data_type
metadata.set_value(enum_items[0])
data_type = metadata.get_value_name()
for enum in enum_items:
new = prop.enumerated_value.enumerated_values.add()
setattr(new, data_type, enum)
new.is_selected = enum in selected_enum_items
def load_from_pset_data(self, pset_data):
for prop_id in pset_data["Properties"]:
prop = Data.properties[prop_id]
if prop["type"] == "IfcPropertyEnumeratedValue":
new = self.props.properties.add()
new.name = prop["Name"]
new.is_null = new.enumerated_values is None
new.is_optional = True
new.set_value(prop["EnumerationReference"].EnumerationValues[0].wrappedValue)
enum_ref = [v.wrappedValue for v in prop["EnumerationReference"].EnumerationValues]
enum_vals = [v.wrappedValue for v in prop["EnumerationValues"]]
for enum in enum_ref:
new_enum = new.enumerated_values.add()
data_type = new.get_value_name()
setattr(new_enum, data_type, enum)
if enum in enum_vals:
new_enum.is_selected = True
else:
new_enum.is_selected = False
simple_prop = self.props.properties.add()
simple_prop.value_type = "IfcPropertyEnumeratedValue"
metadata = simple_prop.metadata
metadata.name = prop["Name"]
metadata.is_null = len(simple_prop.enumerated_value.enumerated_values) == 0
metadata.is_optional = True
metadata.set_value(prop["EnumerationReference"].EnumerationValues[0].wrappedValue)
enum_items = [v.wrappedValue for v in prop["EnumerationReference"].EnumerationValues]
selected_enum_items = [v.wrappedValue for v in prop["EnumerationValues"]]
data_type = metadata.get_value_name()
for enum in enum_items:
new = simple_prop.enumerated_value.enumerated_values.add()
setattr(new, data_type, enum)
new.is_selected = enum in selected_enum_items
else:
value = prop["NominalValue"]
new = self.props.properties.add()
new.set_value(value)
new.name = prop["Name"]
new.is_null = value is None
new.is_optional = True
new.set_value(new.get_value_default() if new.is_null else value)
prop = self.props.properties.add()
metadata = prop.metadata
metadata.set_value(value)
metadata.name = prop["Name"]
metadata.is_null = value is None
metadata.is_optional = True
metadata.set_value(metadata.get_value_default() if metadata.is_null else value)
class DisablePsetEditing(bpy.types.Operator, Operator):
@@ -236,11 +242,13 @@ class EditPset(bpy.types.Operator, Operator):
else:
data = Data.psets if pset_id in Data.psets else Data.qtos
for prop in props.properties:
if len(prop.enumerated_values) != 0:
value_name = prop.get_value_name()
properties[prop.name] = [e[value_name] for e in prop.enumerated_values if e.is_selected]
else:
properties[prop.name] = prop.get_value()
if prop.value_type == "IfcPropertySingleValue":
properties[prop.metadata.name] = prop.metadata.get_value()
elif prop.value_type == "IfcPropertyEnumeratedValue":
value_name = prop.metadata.get_value_name()
properties[prop.metadata.name] = [
e[value_name] for e in prop.enumerated_value.enumerated_values if e.is_selected
]
if pset_id in Data.psets:
ifcopenshell.api.run(
@@ -507,15 +515,15 @@ class BIM_OT_add_edit_custom_property(bpy.types.Operator):
continue
ifc_element = tool.Ifc.get().by_id(ifc_definition_id)
for prop in props:
for prop in props:
value = getattr(prop, prop.get_value_name())
primary_measure_type = prop.primary_measure_type
primary_measure_type = prop.primary_measure_type
if prop.template_type == "IfcPropertyEnumeratedValue":
value_ifc_entity = self.generate_enum_entity(prop)
elif prop.template_type == "IfcPropertySingleValue":
value_ifc_entity = getattr(self.file, f"create{primary_measure_type}")(value)
new_pset = ifcopenshell.api.run("pset.add_pset", self.file, product=ifc_element, name=prop.pset_name)
ifcopenshell.api.run(
"pset.edit_pset", self.file, pset=new_pset, properties={prop.property_name: value_ifc_entity}
@@ -523,22 +531,26 @@ class BIM_OT_add_edit_custom_property(bpy.types.Operator):
Data.load(IfcStore.get_file(), ifc_definition_id)
self.report({"INFO"}, "Finished applying changes")
return {"FINISHED"}
def generate_enum_entity(self, prop):
prop_type = prop.get_value_name()
prop_enum = self.file.create_entity(
"IFCPROPERTYENUMERATION",
Name=prop.property_name,
EnumerationValues=tuple(self.file.create_entity(
prop.primary_measure_type, ev[prop_type]) for ev in prop.enum_values)
)
"IFCPROPERTYENUMERATION",
Name=prop.property_name,
EnumerationValues=tuple(
self.file.create_entity(prop.primary_measure_type, ev[prop_type]) for ev in prop.enum_values
),
)
prop_enum_value = self.file.create_entity(
"IFCPROPERTYENUMERATEDVALUE",
Name=prop.property_name,
EnumerationValues=tuple(self.file.create_entity(
prop.primary_measure_type, ev[prop_type]) for ev in prop.enum_values if ev.is_selected == True),
EnumerationReference=prop_enum
)
"IFCPROPERTYENUMERATEDVALUE",
Name=prop.property_name,
EnumerationValues=tuple(
self.file.create_entity(prop.primary_measure_type, ev[prop_type])
for ev in prop.enum_values
if ev.is_selected == True
),
EnumerationReference=prop_enum,
)
return prop_enum_value