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
@@ -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)