WIP migrate types to partial editing. See #1222.

This commit is contained in:
Dion Moult
2021-01-07 10:47:50 +11:00
parent 02ac150a51
commit fcc2b9ee13
18 changed files with 330 additions and 87 deletions
@@ -16,6 +16,7 @@ if bpy is not None:
import blenderbim.bim.module.project as module_project
import blenderbim.bim.module.spatial as module_spatial
import blenderbim.bim.module.style as module_style
import blenderbim.bim.module.type as module_type
import blenderbim.bim.module.unit as module_unit
from . import ui, prop, operator
@@ -150,7 +151,6 @@ if bpy is not None:
operator.ImportIfcCsv,
operator.EyedropIfcCsv,
operator.ReloadIfcFile,
operator.SelectSimilarType,
operator.AddIfcFile,
operator.RemoveIfcFile,
operator.SelectDocIfcFile,
@@ -285,7 +285,6 @@ if bpy is not None:
ui.BIM_PT_debug,
ui.BIM_PT_material,
ui.BIM_PT_presentation_layer_data,
ui.BIM_PT_object,
ui.BIM_PT_object_material,
ui.BIM_PT_object_psets,
ui.BIM_PT_object_qto,
@@ -323,6 +322,7 @@ if bpy is not None:
classes.extend(module_project.classes)
classes.extend(module_spatial.classes)
classes.extend(module_style.classes)
classes.extend(module_type.classes)
classes.extend(module_unit.classes)
def menu_func_export(self, context):
@@ -366,6 +366,7 @@ if bpy is not None:
module_project.register()
module_spatial.register()
module_style.register()
module_type.register()
module_unit.register()
bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
bpy.app.handlers.load_post.append(prop.toggleDecorationsOnLoad)
@@ -389,6 +390,7 @@ if bpy is not None:
del bpy.types.TextCurve.BIMTextProperties
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
module_unit.unregister()
module_type.unregister()
module_style.unregister()
module_spatial.unregister()
module_project.unregister()
@@ -59,7 +59,6 @@ class IfcParser:
self.rel_contained_in_spatial_structure = {}
self.rel_nests = {}
self.rel_space_boundaries = {}
self.rel_defines_by_type = {}
self.rel_defines_by_qto = {}
self.rel_defines_by_pset = {}
self.rel_associates_document_object = {}
@@ -371,9 +370,6 @@ class IfcParser:
product.update(metadata_override)
type_product = obj.BIMObjectProperties.relating_type
if type_product and self.is_a_type(self.get_ifc_class(type_product.name)):
reference = self.get_type_product_reference(type_product.name)
self.rel_defines_by_type.setdefault(reference, []).append(self.product_index)
if product["has_boundary_condition"]:
product["boundary_condition_class"] = obj.BIMObjectProperties.boundary_condition.name
@@ -1374,7 +1370,6 @@ class IfcExporter:
self.relate_objects_to_objects()
self.relate_elements_to_spatial_structures()
self.relate_nested_elements_to_hosted_elements()
self.relate_objects_to_types()
self.relate_objects_to_qtos()
self.relate_objects_to_psets()
self.relate_objects_to_opening_elements()
@@ -3177,17 +3172,6 @@ class IfcExporter:
[o["ifc"] for o in related_objects],
)
def relate_objects_to_types(self):
for relating_type, related_objects in self.ifc_parser.rel_defines_by_type.items():
self.file.createIfcRelDefinesByType(
ifcopenshell.guid.new(),
self.owner_history,
None,
None,
[self.ifc_parser.products[o]["ifc"] for o in related_objects],
self.ifc_parser.type_products[relating_type]["ifc"],
)
def relate_objects_to_qtos(self):
for relating_property_key, related_objects in self.ifc_parser.rel_defines_by_qto.items():
self.file.createIfcRelDefinesByProperties(
@@ -5,6 +5,7 @@ import ifcopenshell
class IfcStore:
path = ""
file = None
schema = None
pset_template_path = ""
pset_template_file = None
@@ -15,3 +16,11 @@ class IfcStore:
if IfcStore.path:
IfcStore.file = ifcopenshell.open(IfcStore.path)
return IfcStore.file
@staticmethod
def get_schema():
if IfcStore.file is None:
return
elif IfcStore.schema is None:
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema)
return IfcStore.schema
@@ -976,7 +976,6 @@ class IfcImporter:
self.add_element_representation_items(element, obj)
self.add_element_classifications(element, obj)
self.add_element_document_relations(element, obj)
self.add_defines_by_type_relation(element, obj)
self.add_opening_relation(element, obj)
self.add_product_definitions(element, obj)
self.added_data[element.GlobalId] = obj
@@ -1426,11 +1425,6 @@ class IfcImporter:
new_prop.name = prop.Name
new_prop.string_value = str(value)
def add_defines_by_type_relation(self, element, obj):
related_type = ifcopenshell.util.element.get_type(element)
if related_type:
obj.BIMObjectProperties.relating_type = self.type_products[related_type.GlobalId]
def add_opening_relation(self, element, obj):
if not element.is_a("IfcOpeningElement"):
return
@@ -1732,7 +1726,6 @@ class IfcImporter:
self.place_object_in_spatial_tree(element, obj)
self.add_element_classifications(element, obj)
self.add_element_document_relations(element, obj)
self.add_defines_by_type_relation(element, obj)
self.add_product_definitions(element, obj)
self.aggregates[element.GlobalId] = obj
self.aggregate_collections[rel_aggregate.id()] = collection
@@ -1794,7 +1787,6 @@ class IfcImporter:
obj.matrix_world = self.get_element_matrix(element, mesh_name)
self.add_element_classifications(element, obj)
self.add_element_document_relations(element, obj)
self.add_defines_by_type_relation(element, obj)
self.add_product_definitions(element, obj)
self.added_data[element.GlobalId] = obj
@@ -29,10 +29,7 @@ class AssignObject(bpy.types.Operator):
Data.load(props.ifc_definition_id)
bpy.ops.bim.disable_editing_aggregate(obj=related_object.name)
file = IfcStore.get_file()
ifc_schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(file.schema)
declaration = ifc_schema.declaration_by_name(product.is_a())
declaration = IfcStore.get_schema().declaration_by_name(product.is_a())
# TODO: we may not need this conditional if aggregates stop using collection instances
if ifcopenshell.util.schema.is_a(declaration, "IfcSpatialElement"):
related_collection = related_object.users_collection[0]
@@ -13,8 +13,7 @@ class Data:
product = file.by_id(product_id)
cls.products[product_id] = []
attributes = product.get_info()
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(file.schema)
declaration = schema.declaration_by_name(product.is_a())
declaration = IfcStore.get_schema().declaration_by_name(product.is_a())
for attribute in declaration.all_attributes():
data_type = str(attribute.type_of_attribute())
value = getattr(product, attribute.name())
@@ -20,8 +20,6 @@ class EnableReassignClass(bpy.types.Operator):
def execute(self, context):
obj = bpy.context.active_object
ifc_class = obj.name.split("/")[0]
file = IfcStore.get_file()
ifc_schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(file.schema)
bpy.context.active_object.BIMObjectProperties.is_reassigning_class = True
ifc_products = [
"IfcElement",
@@ -34,7 +32,7 @@ class EnableReassignClass(bpy.types.Operator):
"IfcAnnotation",
]
for ifc_product in ifc_products:
if ifcopenshell.util.schema.is_a(ifc_schema.declaration_by_name(ifc_class), ifc_product):
if ifcopenshell.util.schema.is_a(IfcStore.get_schema().declaration_by_name(ifc_class), ifc_product):
bpy.context.scene.BIMProperties.ifc_product = ifc_product
bpy.context.scene.BIMProperties.ifc_class = obj.name.split("/")[0]
predefined_type = obj.BIMObjectProperties.attributes.get("PredefinedType")
@@ -87,8 +85,7 @@ class AssignClass(bpy.types.Operator):
def execute(self, context):
objects = [bpy.data.objects.get(self.obj)] if self.obj else bpy.context.selected_objects
self.file = IfcStore.get_file()
ifc_schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self.file.schema)
self.declaration = ifc_schema.declaration_by_name(self.ifc_class)
self.declaration = IfcStore.get_schema().declaration_by_name(self.ifc_class)
if self.predefined_type == "USERDEFINED":
self.predefined_type = self.ifc_userdefined_type
elif self.predefined_type == "":
@@ -15,7 +15,7 @@ class Usecase:
contained_in_structure = self.settings["product"].ContainedInStructure
contains_elements = self.settings["relating_structure"].ContainsElements
if contains_elements and contained_in_structure == contains_elements:
if contains_elements and contained_in_structure and contained_in_structure[0] == contains_elements[0]:
return
if contained_in_structure:
@@ -0,0 +1,19 @@
import bpy
from . import ui, operator
classes = (
operator.AssignType,
operator.UnassignType,
operator.EnableEditingType,
operator.DisableEditingType,
operator.SelectSimilarType,
ui.BIM_PT_type,
)
def register():
pass
def unregister():
pass
@@ -0,0 +1,51 @@
import ifcopenshell
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {
"related_object": None,
"relating_type": None,
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
if self.file.schema == "IFC2X3":
is_typed_by = None
is_defined_by = self.settings["related_object"].IsDefinedBy
for rel in is_defined_by:
if rel.is_a("IfcRelDefinesByType"):
is_typed_by = [rel]
break
types = self.settings["relating_type"].ObjectTypeOf
else:
is_typed_by = self.settings["related_object"].IsTypedBy
types = self.settings["relating_type"].Types
if types and is_typed_by == types:
return
if is_typed_by:
related_objects = list(is_typed_by[0].RelatedObjects)
related_objects.remove(self.settings["related_object"])
if related_objects:
is_typed_by[0].RelatedObjects = related_objects
else:
self.file.remove(is_typed_by)
if types:
related_objects = list(types[0].RelatedObjects)
related_objects.append(self.settings["related_object"])
types[0].RelatedObjects = related_objects
else:
types = self.file.create_entity(
"IfcRelDefinesByType",
**{
"GlobalId": ifcopenshell.guid.new(),
# TODO "OwnerHistory": None
"RelatedObjects": [self.settings["related_object"]],
"RelatingType": self.settings["relating_type"],
}
)
@@ -0,0 +1,27 @@
from blenderbim.bim.ifc import IfcStore
class Data:
products = {}
@classmethod
def load(cls, product_id):
file = IfcStore.get_file()
if not file:
return
product = file.by_id(product_id)
if file.schema == "IFC2X3" and not hasattr(product, "IsDefinedBy"):
cls.products[product_id] = None
elif file.schema != "IFC2X3" and not hasattr(product, "IsTypedBy"):
cls.products[product_id] = None
elif hasattr(product, "IsTypedBy") and product.IsTypedBy:
type = product.IsTypedBy[0].RelatingType
cls.products[product_id] = {"type": type.is_a(), "Name": type.Name, "id": int(type.id())}
elif hasattr(product, "IsDefinedBy") and product.IsDefinedBy:
cls.products[product_id] = {"type": None, "Name": None}
for rel in product.IsDefinedBy:
if rel.is_a("IfcRelDefinesByType"):
type = rel.RelatingType
cls.products[product_id] = {"type": type.is_a(), "Name": type.Name, "id": int(type.id())}
else:
cls.products[product_id] = {"type": None, "Name": None, "id": None}
@@ -0,0 +1,32 @@
import ifcopenshell
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {
"related_object": None,
"relating_type": None,
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
if self.settings["related_object"]:
if self.file.schema == "IFC2X3":
is_defined_by = self.settings["related_object"].IsDefinedBy
for rel in is_defined_by:
if rel.is_a("IfcRelDefinesByType"):
return set([int(o.id()) for o in rel.RelatedObjects])
else:
is_typed_by = self.settings["related_object"].IsTypedBy
if is_typed_by:
return set([int(o.id()) for o in is_typed_by[0].RelatedObjects])
elif self.settings["relating_type"]:
if self.file.schema == "IFC2X3":
types = self.settings["relating_type"].ObjectTypeOf
else:
types = self.settings["relating_type"].Types
if types:
return set([int(o.id()) for o in types[0].RelatedObjects])
return set()
@@ -0,0 +1,97 @@
import bpy
import ifcopenshell.util.schema
import blenderbim.bim.module.type.assign_type as assign_type
import blenderbim.bim.module.type.unassign_type as unassign_type
import blenderbim.bim.module.type.get_related_objects as get_related_objects
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.type.data import Data
class AssignType(bpy.types.Operator):
bl_idname = "bim.assign_type"
bl_label = "Assign Type"
relating_type: bpy.props.StringProperty()
related_object: bpy.props.StringProperty()
def execute(self, context):
self.file = IfcStore.get_file()
related_object = bpy.data.objects.get(self.related_object) if self.related_object else bpy.context.active_object
props = related_object.BIMObjectProperties
relating_type = bpy.data.objects.get(self.relating_type) if self.relating_type else props.relating_type
if not relating_type or not relating_type.BIMObjectProperties.ifc_definition_id:
return {"FINISHED"}
assign_type.Usecase(
self.file,
{
"related_object": self.file.by_id(props.ifc_definition_id),
"relating_type": self.file.by_id(relating_type.BIMObjectProperties.ifc_definition_id),
},
).execute()
Data.load(props.ifc_definition_id)
bpy.ops.bim.disable_editing_type(obj=related_object.name)
return {"FINISHED"}
class UnassignType(bpy.types.Operator):
bl_idname = "bim.unassign_type"
bl_label = "Unassign Type"
related_object: bpy.props.StringProperty()
def execute(self, context):
self.file = IfcStore.get_file()
related_object = bpy.data.objects.get(self.related_object) if self.related_object else bpy.context.active_object
props = related_object.BIMObjectProperties
unassign_type.Usecase(
self.file,
{
"related_object": self.file.by_id(props.ifc_definition_id),
},
).execute()
Data.load(props.ifc_definition_id)
return {"FINISHED"}
class EnableEditingType(bpy.types.Operator):
bl_idname = "bim.enable_editing_type"
bl_label = "Enable Editing Type"
def execute(self, context):
bpy.context.active_object.BIMObjectProperties.relating_type = None
bpy.context.active_object.BIMObjectProperties.is_editing_type = True
return {"FINISHED"}
class DisableEditingType(bpy.types.Operator):
bl_idname = "bim.disable_editing_type"
bl_label = "Disable Editing Type"
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
obj.BIMObjectProperties.is_editing_type = False
return {"FINISHED"}
class SelectSimilarType(bpy.types.Operator):
bl_idname = "bim.select_similar_type"
bl_label = "Select Similar Type"
related_object: bpy.props.StringProperty()
def execute(self, context):
self.file = IfcStore.get_file()
related_object = bpy.data.objects.get(self.related_object) if self.related_object else bpy.context.active_object
props = related_object.BIMObjectProperties
product = self.file.by_id(props.ifc_definition_id)
declaration = IfcStore.get_schema().declaration_by_name(product.is_a())
if ifcopenshell.util.schema.is_a(declaration, "IfcElementType"):
related_objects = get_related_objects.Usecase(self.file, {
"relating_type": self.file.by_id(props.ifc_definition_id)
}).execute()
else:
related_objects = get_related_objects.Usecase(self.file, {
"related_object": self.file.by_id(props.ifc_definition_id)
}).execute()
for obj in bpy.context.visible_objects:
if obj.BIMObjectProperties.ifc_definition_id in related_objects:
obj.select_set(True)
return {"FINISHED"}
@@ -0,0 +1,49 @@
from bpy.types import Panel
from blenderbim.bim.module.type.data import Data
class BIM_PT_type(Panel):
bl_label = "IFC Construction Type"
bl_idname = "BIM_PT_type"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@classmethod
def poll(cls, context):
props = context.active_object.BIMObjectProperties
if not props.ifc_definition_id:
return False
if props.ifc_definition_id not in Data.products:
Data.load(props.ifc_definition_id)
if not Data.products[props.ifc_definition_id]:
return False
return True
def draw(self, context):
props = context.active_object.BIMObjectProperties
if not props.ifc_definition_id:
return
if props.ifc_definition_id not in Data.products:
Data.load(props.ifc_definition_id)
if props.is_editing_type:
row = self.layout.row(align=True)
row.prop(props, "relating_type", text="")
row.operator("bim.assign_type", icon="CHECKMARK", text="")
row.operator("bim.disable_editing_type", icon="X", text="")
else:
row = self.layout.row(align=True)
name = "{}/{}".format(
Data.products[props.ifc_definition_id]["type"], Data.products[props.ifc_definition_id]["Name"]
)
if name == "None/None":
label = "This object has no type"
else:
label = name
row.label(text=label)
row.operator("bim.select_similar_type", icon="RESTRICT_SELECT_OFF", text="")
row.operator("bim.enable_editing_type", icon="GREASEPENCIL", text="")
if name != "None/None":
row.operator("bim.unassign_type", icon="X", text="")
@@ -0,0 +1,31 @@
import ifcopenshell
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {
"related_object": None
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
if self.file.schema == "IFC2X3":
is_typed_by = None
is_defined_by = self.settings["related_object"].IsDefinedBy
for rel in is_defined_by:
if rel.is_a("IfcRelDefinesByType"):
is_typed_by = rel
else:
is_typed_by = self.settings["related_object"].IsTypedBy
if is_typed_by:
is_typed_by = is_typed_by[0]
if is_typed_by:
related_objects = list(is_typed_by.RelatedObjects)
related_objects.remove(self.settings["related_object"])
if related_objects:
is_typed_by.RelatedObjects = related_objects
else:
self.file.remove(is_typed_by)
@@ -2832,23 +2832,6 @@ class ReloadIfcFile(bpy.types.Operator):
ifc_importer.execute()
class SelectSimilarType(bpy.types.Operator):
bl_idname = "bim.select_similar_type"
bl_label = "Select Similar Type"
def execute(self, context):
if context.active_object.BIMObjectProperties.relating_type:
relating_type = context.active_object.BIMObjectProperties.relating_type
elif "Type/" in context.active_object.name:
relating_type = context.active_object
else:
return {"FINISHED"}
for obj in bpy.context.visible_objects:
if obj.BIMObjectProperties.relating_type == relating_type:
obj.select_set(True)
return {"FINISHED"}
class AddIfcFile(bpy.types.Operator):
bl_idname = "bim.add_ifc_file"
bl_label = "Add IFC File"
+6 -6
View File
@@ -128,8 +128,7 @@ def getIfcPredefinedTypes(self, context):
global types_enum
file = IfcStore.get_file()
if len(types_enum) < 1 and file:
ifc_schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(file.schema)
declaration = ifc_schema.declaration_by_name(self.ifc_class)
declaration = IfcStore.get_schema().declaration_by_name(self.ifc_class)
for attribute in declaration.attributes():
if attribute.name() == "PredefinedType":
types_enum.extend([(e, e, "") for e in attribute.type_of_attribute().declared_type().enumeration_items()])
@@ -271,8 +270,7 @@ def getIfcClasses(self, context):
global classes_enum
file = IfcStore.get_file()
if len(classes_enum) < 1 and file:
ifc_schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(file.schema)
declaration = ifc_schema.declaration_by_name(self.ifc_product)
declaration = IfcStore.get_schema().declaration_by_name(self.ifc_product)
def get_classes(declaration):
results = []
if not declaration.is_abstract():
@@ -1258,7 +1256,7 @@ class BIMProperties(PropertyGroup):
)
export_should_force_faceted_brep: BoolProperty(name="Export with Faceted Breps", default=False)
export_should_force_triangulation: BoolProperty(name="Export with Triangulation", default=False)
export_should_export_from_memory: BoolProperty(name="Export from Memory", default=False)
export_should_export_from_memory: BoolProperty(name="Export from Memory", default=True)
import_should_ignore_site_coordinates: BoolProperty(name="Import Ignoring Site Coordinates", default=False)
import_should_ignore_building_coordinates: BoolProperty(name="Import Ignoring Building Coordinates", default=False)
import_should_reset_absolute_coordinates: BoolProperty(name="Import Resetting Absolute Coordinates", default=False)
@@ -1270,7 +1268,7 @@ class BIMProperties(PropertyGroup):
import_should_auto_set_workarounds: BoolProperty(name="Automatically Set Vendor Workarounds", default=True)
import_should_use_legacy: BoolProperty(name="Import with Legacy Importer", default=False)
import_should_import_native: BoolProperty(name="Import Native Representations", default=False)
import_export_should_roundtrip_native: BoolProperty(name="Roundtrip Native Representations", default=False)
import_export_should_roundtrip_native: BoolProperty(name="Roundtrip Native Representations", default=True)
import_should_use_cpu_multiprocessing: BoolProperty(name="Import with CPU Multiprocessing", default=True)
import_should_import_with_profiling: BoolProperty(name="Import with Profiling", default=True)
import_should_import_aggregates: BoolProperty(name="Import Aggregates", default=True)
@@ -1481,6 +1479,8 @@ class BIMObjectProperties(PropertyGroup):
relating_object: PointerProperty(name="Aggregate", type=bpy.types.Object)
is_editing_aggregate: BoolProperty(name="Is Editing Aggregate")
is_editing_container: BoolProperty(name="Is Editing Container")
relating_type: PointerProperty(name="Type", type=bpy.types.Object)
is_editing_type: BoolProperty(name="Is Editing Type")
relating_type: PointerProperty(name="Type Product", type=bpy.types.Object)
relating_structure: PointerProperty(name="Spatial Container", type=bpy.types.Object)
psets: CollectionProperty(name="Psets", type=PsetQto)
-26
View File
@@ -5,32 +5,6 @@ from bpy.types import Panel
from bpy.props import StringProperty
class BIM_PT_object(Panel):
bl_label = "IFC Object"
bl_idname = "BIM_PT_object"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@classmethod
def poll(cls, context):
return context.active_object is not None and hasattr(context.active_object, "BIMObjectProperties")
def draw(self, context):
if context.active_object is None:
return
layout = self.layout
props = context.active_object.BIMObjectProperties
bim_properties = context.scene.BIMProperties
if "Ifc" not in context.active_object.name:
return
row = layout.row(align=True)
row.prop(props, "relating_type")
row.operator("bim.select_similar_type", icon="RESTRICT_SELECT_OFF", text="")
class BIM_PT_object_material(Panel):
bl_label = "IFC Object Material"
bl_idname = "BIM_PT_object_material"