Apply object operators to all selected objects (#1696)

* Remove container executes on all selected objects

* Pass silently if assigning or removing container on non-bim object

* Assign / Unassign Material executes on all selected objects

* AddRepresentation executes on all selected objects

* Assign / Unassign Constraint executes on all selected objects

* Fix error when trying to assign representation to non-mesh object

* Assign / Unassign Document executes on all selected objects

* Code Cleanup & Consistency

* Fix Attribute data type not being assigned when it makes sense

* Revert changes to geometry operator

* Revert "Fix Attribute data type not being assigned when it makes sense"

This reverts commit 1117c8670c.

* Revert changes to material operator

* Use attribute helper functions

* Filter select entities in import_attributes

* revert changes to attribute fetching

* Revert "Filter select entities in import_attributes"

This reverts commit 7f84c2ea63.

* Enforce new attribute data type to string

* Restrict import_attributes usecase

* Remove trailing spaces

* Revert changes to IfcProjectedCRS attributes
This is done prior to the planned unit refactor
This commit is contained in:
Gorgious56
2021-08-26 13:44:33 +02:00
committed by GitHub
parent 5e1fbacef7
commit 104e9c9ca7
9 changed files with 106 additions and 114 deletions
@@ -76,6 +76,7 @@ class EnableEditingClassification(bpy.types.Operator):
new.name = attribute.name() new.name = attribute.name()
new.is_null = classification_data[attribute.name()] is None new.is_null = classification_data[attribute.name()] is None
new.is_optional = attribute.optional() new.is_optional = attribute.optional()
new.data_type = "string"
if attribute.name() == "ReferenceTokens": if attribute.name() == "ReferenceTokens":
new.string_value = "" if new.is_null else json.dumps(classification_data[attribute.name()]) new.string_value = "" if new.is_null else json.dumps(classification_data[attribute.name()])
else: else:
@@ -162,6 +163,7 @@ class EnableEditingClassificationReference(bpy.types.Operator):
new.name = attribute.name() new.name = attribute.name()
new.is_null = reference_data[attribute.name()] is None new.is_null = reference_data[attribute.name()] is None
new.is_optional = attribute.optional() new.is_optional = attribute.optional()
new.data_type = "string"
new.string_value = "" if new.is_null else reference_data[attribute.name()] new.string_value = "" if new.is_null else reference_data[attribute.name()]
props.active_reference_id = self.reference props.active_reference_id = self.reference
return {"FINISHED"} return {"FINISHED"}
@@ -187,17 +187,21 @@ class AssignConstraint(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context) return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
"constraint.assign_constraint", for obj in objs:
self.file, obj_id = obj.BIMObjectProperties.ifc_definition_id
**{ if not obj_id:
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), continue
"constraint": self.file.by_id(self.constraint), ifcopenshell.api.run(
} "constraint.assign_constraint",
) self.file,
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id) **{
"product": self.file.by_id(obj_id),
"constraint": self.file.by_id(self.constraint),
}
)
Data.load(self.file, obj_id)
return {"FINISHED"} return {"FINISHED"}
@@ -212,15 +216,19 @@ class UnassignConstraint(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context) return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
"constraint.unassign_constraint", for obj in objs:
self.file, obj_id = obj.BIMObjectProperties.ifc_definition_id
**{ if not obj_id:
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), continue
"constraint": self.file.by_id(self.constraint), ifcopenshell.api.run(
} "constraint.unassign_constraint",
) self.file,
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id) **{
"product": self.file.by_id(obj_id),
"constraint": self.file.by_id(self.constraint),
}
)
Data.load(self.file, obj_id)
return {"FINISHED"} return {"FINISHED"}
@@ -276,17 +276,21 @@ class AssignDocument(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context) return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
"document.assign_document", for obj in objs:
self.file, obj_id = obj.BIMObjectProperties.ifc_definition_id
**{ if not obj_id:
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), continue
"document": self.file.by_id(self.document), ifcopenshell.api.run(
} "document.assign_document",
) self.file,
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id) **{
"product": self.file.by_id(obj_id),
"document": self.file.by_id(self.document),
}
)
Data.load(self.file, obj_id)
return {"FINISHED"} return {"FINISHED"}
@@ -301,15 +305,19 @@ class UnassignDocument(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context) return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
"document.unassign_document", for obj in objs:
self.file, obj_id = obj.BIMObjectProperties.ifc_definition_id
**{ if not obj_id:
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), continue
"document": self.file.by_id(self.document), ifcopenshell.api.run(
} "document.unassign_document",
) self.file,
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id) **{
"product": self.file.by_id(obj_id),
"document": self.file.by_id(self.document),
}
)
Data.load(self.file, obj_id)
return {"FINISHED"} return {"FINISHED"}
@@ -39,7 +39,7 @@ class EnableEditingGeoreferencing(bpy.types.Operator):
props = context.scene.BIMGeoreferenceProperties props = context.scene.BIMGeoreferenceProperties
props.projected_crs.clear() props.projected_crs.clear()
for attribute in IfcStore.get_schema().declaration_by_name("IfcProjectedCRS").all_attributes(): for attribute in IfcStore.get_schema().declaration_by_name("IfcProjectedCRS").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity": if data_type == "entity":
@@ -69,18 +69,9 @@ class EnableEditingGeoreferencing(bpy.types.Operator):
props.map_unit_imperial = Data.projected_crs["MapUnit"]["Name"] props.map_unit_imperial = Data.projected_crs["MapUnit"]["Name"]
props.map_conversion.clear() props.map_conversion.clear()
blenderbim.bim.helper.import_attributes(
for attribute in IfcStore.get_schema().declaration_by_name("IfcMapConversion").all_attributes(): "IfcMapConversion", props.map_conversion, Data.map_conversion, self.import_map_conversion_attributes
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) )
if data_type == "entity" or data_type == "select":
continue
new = props.map_conversion.add()
new.name = attribute.name()
new.is_null = Data.map_conversion[attribute.name()] is None
new.is_optional = attribute.optional()
# Enforce a string data type to prevent data loss in single-precision Blender props
new.data_type = "string"
new.string_value = "" if new.is_null else str(Data.map_conversion[attribute.name()])
props.has_true_north = bool(Data.true_north) props.has_true_north = bool(Data.true_north)
if Data.true_north: if Data.true_north:
@@ -90,6 +81,12 @@ class EnableEditingGeoreferencing(bpy.types.Operator):
props.is_editing = True props.is_editing = True
return {"FINISHED"} return {"FINISHED"}
def import_map_conversion_attributes(self, name, prop, data):
if name not in ["SourceCRS", "TargetCRS"]:
# Enforce a string data type to prevent data loss in single-precision Blender props
prop.data_type = "string"
prop.string_value = "" if prop.is_null else str(data[name])
return True
class DisableEditingGeoreferencing(bpy.types.Operator): class DisableEditingGeoreferencing(bpy.types.Operator):
bl_idname = "bim.disable_editing_georeferencing" bl_idname = "bim.disable_editing_georeferencing"
@@ -20,6 +20,7 @@
import bpy import bpy
import ifcopenshell.util.attribute import ifcopenshell.util.attribute
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.group.data import Data from ifcopenshell.api.group.data import Data
@@ -121,17 +122,8 @@ class EnableEditingGroup(bpy.types.Operator):
props = context.scene.BIMGroupProperties props = context.scene.BIMGroupProperties
props.group_attributes.clear() props.group_attributes.clear()
data = Data.groups[self.group] blenderbim.bim.helper.import_attributes("IfcGroup", props.group_attributes, Data.groups[self.group])
for attribute in IfcStore.get_schema().declaration_by_name("IfcGroup").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = props.group_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.string_value = "" if new.is_null else data[attribute.name()]
props.active_group_id = self.group props.active_group_id = self.group
return {"FINISHED"} return {"FINISHED"}
@@ -21,6 +21,7 @@ import bpy
import json import json
import ifcopenshell.util.attribute import ifcopenshell.util.attribute
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.layer.data import Data from ifcopenshell.api.layer.data import Data
@@ -63,17 +64,10 @@ class EnableEditingLayer(bpy.types.Operator):
props = context.scene.BIMLayerProperties props = context.scene.BIMLayerProperties
props.layer_attributes.clear() props.layer_attributes.clear()
data = Data.layers[self.layer] blenderbim.bim.helper.import_attributes(
"IfcPresentationLayerAssignment", props.layer_attributes, Data.layers[self.layer]
)
for attribute in IfcStore.get_schema().declaration_by_name("IfcPresentationLayerAssignment").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity" or data_type == "select":
continue
new = props.layer_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.string_value = "" if new.is_null else data[attribute.name()]
props.active_layer_id = self.layer props.active_layer_id = self.layer
return {"FINISHED"} return {"FINISHED"}
@@ -430,14 +430,7 @@ class EnableEditingAssignedMaterial(bpy.types.Operator):
props.material_set_attributes.clear() props.material_set_attributes.clear()
for attribute in IfcStore.get_schema().declaration_by_name(material_set_class).all_attributes(): blenderbim.bim.helper.import_attributes(material_set_class, props.material_set_attributes, material_set_data)
if "<string>" not in str(attribute.type_of_attribute):
continue
if attribute.name() in material_set_data:
new = props.material_set_attributes.add()
new.name = attribute.name()
new.is_null = material_set_data[attribute.name()] is None
new.string_value = "" if new.is_null else material_set_data[attribute.name()]
return {"FINISHED"} return {"FINISHED"}
def import_attributes(self, name, prop, data): def import_attributes(self, name, prop, data):
@@ -509,10 +502,7 @@ class EditAssignedMaterial(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
material_set = self.file.by_id(self.material_set) material_set = self.file.by_id(self.material_set)
attributes = blenderbim.bim.helper.export_attributes(props.material_set_attributes)
attributes = {}
for attribute in props.material_set_attributes:
attributes[attribute.name] = None if attribute.is_null else attribute.string_value
ifcopenshell.api.run( ifcopenshell.api.run(
"material.edit_assigned_material", "material.edit_assigned_material",
self.file, self.file,
@@ -48,7 +48,9 @@ class AssignContainer(bpy.types.Operator):
self.relating_structure or sprops.spatial_elements[sprops.active_spatial_element_index].ifc_definition_id self.relating_structure or sprops.spatial_elements[sprops.active_spatial_element_index].ifc_definition_id
) )
for related_element in related_elements: for related_element in related_elements:
oprops = related_element.BIMObjectProperties oprops = related_element.BIMObjectProperties
if not oprops.ifc_definition_id:
continue
props = related_element.BIMObjectSpatialProperties props = related_element.BIMObjectSpatialProperties
ifcopenshell.api.run( ifcopenshell.api.run(
@@ -134,25 +136,33 @@ class RemoveContainer(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context) return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
obj = bpy.data.objects.get(self.obj, context.active_object) active_object = context.active_object
oprops = obj.BIMObjectProperties
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
"spatial.remove_container", self.file, **{"product": self.file.by_id(oprops.ifc_definition_id)} for obj in objs:
) obj_id = obj.BIMObjectProperties.ifc_definition_id
Data.load(IfcStore.get_file(), oprops.ifc_definition_id) if not obj_id:
continue
ifcopenshell.api.run(
"spatial.remove_container",
self.file,
**{
"product": self.file.by_id(obj_id)
}
)
Data.load(self.file, obj_id)
aggregate_collection = bpy.data.collections.get(obj.name) aggregate_collection = bpy.data.collections.get(obj.name)
if aggregate_collection: if aggregate_collection:
self.remove_collection(context.scene.collection, aggregate_collection) self.remove_collection(context.scene.collection, aggregate_collection)
for collection in bpy.data.collections: for collection in bpy.data.collections:
self.remove_collection(collection, spatial_collection) self.remove_collection(collection, spatial_collection)
context.scene.collection.children.link(aggregate_collection) context.scene.collection.children.link(aggregate_collection)
else: else:
for collection in obj.users_collection: for collection in obj.users_collection:
collection.objects.unlink(obj) collection.objects.unlink(obj)
context.scene.collection.objects.link(obj) context.scene.collection.objects.link(obj)
context.view_layer.objects.active = obj context.view_layer.objects.active = active_object
return {"FINISHED"} return {"FINISHED"}
def remove_collection(self, parent, child): def remove_collection(self, parent, child):
@@ -20,6 +20,7 @@
import bpy import bpy
import ifcopenshell.util.attribute import ifcopenshell.util.attribute
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.system.data import Data from ifcopenshell.api.system.data import Data
@@ -121,17 +122,7 @@ class EnableEditingSystem(bpy.types.Operator):
props = context.scene.BIMSystemProperties props = context.scene.BIMSystemProperties
props.system_attributes.clear() props.system_attributes.clear()
data = Data.systems[self.system] blenderbim.bim.helper.import_attributes("IfcSystem", props.system_attributes, Data.systems[self.system])
for attribute in IfcStore.get_schema().declaration_by_name("IfcSystem").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = props.system_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.string_value = "" if new.is_null else data[attribute.name()]
props.active_system_id = self.system props.active_system_id = self.system
return {"FINISHED"} return {"FINISHED"}