Skip to content

Commit 6387c03

Browse files
committed
WIP port reassign and unassign class to partial editing. See IfcOpenShell#1222.
1 parent 0c0ffce commit 6387c03

10 files changed

Lines changed: 184 additions & 50 deletions

File tree

src/ifcblenderexport/blenderbim/bim/module/root/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import bpy
2-
from . import operator
2+
from . import ui, operator
33

44
classes = (
5+
operator.EnableReassignClass,
6+
operator.DisableReassignClass,
57
operator.ReassignClass,
68
operator.AssignClass,
79
operator.UnassignClass,
10+
ui.BIM_PT_class,
811
)
912

1013

src/ifcblenderexport/blenderbim/bim/module/root/assign_class.py renamed to src/ifcblenderexport/blenderbim/bim/module/root/create_product.py

File renamed without changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from blenderbim.bim.ifc import IfcStore
2+
3+
4+
class Data:
5+
products = {}
6+
7+
@classmethod
8+
def load(cls, product_id):
9+
file = IfcStore.get_file()
10+
if not file:
11+
return
12+
product = file.by_id(product_id)
13+
cls.products[product_id] = {
14+
"type": product.is_a(),
15+
"PredefinedType": product.PredefinedType if hasattr(product, "PredefinedType") else None,
16+
"ObjectType": product.ObjectType or None
17+
}

src/ifcblenderexport/blenderbim/bim/module/root/operator.py

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import bpy
22
import numpy as np
33
import ifcopenshell
4-
import blenderbim.bim.module.root.assign_class as assign_class
4+
import blenderbim.bim.module.root.create_product as create_product
5+
import blenderbim.bim.module.root.remove_product as remove_product
6+
import blenderbim.bim.module.root.reassign_class as reassign_class
57
import blenderbim.bim.module.geometry.add_representation as add_representation
68
import blenderbim.bim.module.geometry.assign_styles as assign_styles
79
import blenderbim.bim.module.geometry.assign_representation as assign_representation
@@ -11,9 +13,9 @@
1113
# from blenderbim.bim.module.geometry.data import Data
1214

1315

14-
class ReassignClass(bpy.types.Operator):
15-
bl_idname = "bim.reassign_class"
16-
bl_label = "Reassign IFC Class"
16+
class EnableReassignClass(bpy.types.Operator):
17+
bl_idname = "bim.enable_reassign_class"
18+
bl_label = "Enable Reassign IFC Class"
1719

1820
def execute(self, context):
1921
obj = bpy.context.active_object
@@ -40,6 +42,40 @@ def execute(self, context):
4042
return {"FINISHED"}
4143

4244

45+
class DisableReassignClass(bpy.types.Operator):
46+
bl_idname = "bim.disable_reassign_class"
47+
bl_label = "Disable Reassign IFC Class"
48+
49+
def execute(self, context):
50+
bpy.context.active_object.BIMObjectProperties.is_reassigning_class = False
51+
return {"FINISHED"}
52+
53+
54+
class ReassignClass(bpy.types.Operator):
55+
bl_idname = "bim.reassign_class"
56+
bl_label = "Reassign IFC Class"
57+
58+
def execute(self, context):
59+
obj = bpy.context.active_object
60+
self.file = IfcStore.get_file()
61+
predefined_type = bpy.context.scene.BIMProperties.ifc_predefined_type
62+
if predefined_type == "USERDEFINED":
63+
predefined_type = bpy.context.scene.BIMProperties.ifc_userdefined_type
64+
usecase = reassign_class.Usecase(
65+
self.file,
66+
{
67+
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
68+
"ifc_class": bpy.context.scene.BIMProperties.ifc_class,
69+
"predefined_type": predefined_type,
70+
},
71+
)
72+
product = usecase.execute()
73+
obj.name = "{}/{}".format(product.is_a(), "/".join(obj.name.split("/")[1:]))
74+
obj.BIMObjectProperties.ifc_definition_id = int(product.id())
75+
bpy.context.active_object.BIMObjectProperties.is_reassigning_class = False
76+
return {"FINISHED"}
77+
78+
4379
class AssignClass(bpy.types.Operator):
4480
bl_idname = "bim.assign_class"
4581
bl_label = "Assign IFC Class"
@@ -51,11 +87,14 @@ def execute(self, context):
5187
for obj in objects:
5288
if obj.BIMObjectProperties.ifc_definition_id:
5389
continue
54-
usecase = assign_class.Usecase(
90+
predefined_type = bpy.context.scene.BIMProperties.ifc_predefined_type
91+
if predefined_type == "USERDEFINED":
92+
predefined_type = bpy.context.scene.BIMProperties.ifc_userdefined_type
93+
usecase = create_product.Usecase(
5594
self.file,
5695
{
5796
"ifc_class": bpy.context.scene.BIMProperties.ifc_class,
58-
"predefined_type": bpy.context.scene.BIMProperties.ifc_predefined_type,
97+
"predefined_type": predefined_type,
5998
"name": obj.name,
6099
},
61100
)
@@ -124,12 +163,20 @@ class UnassignClass(bpy.types.Operator):
124163
object_name: bpy.props.StringProperty()
125164

126165
def execute(self, context):
166+
self.file = IfcStore.get_file()
127167
if self.object_name:
128168
objects = [bpy.data.objects.get(self.object_name)]
129169
else:
130170
objects = bpy.context.selected_objects
131171
for obj in objects:
132-
existing_class = None
172+
if not obj.BIMObjectProperties.ifc_definition_id:
173+
continue
174+
usecase = remove_product.Usecase(
175+
self.file,
176+
{"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)},
177+
)
178+
usecase.execute()
179+
obj.BIMObjectProperties.ifc_definition_id = 0
133180
if "/" in obj.name and obj.name[0:3] == "Ifc":
134181
obj.name = "/".join(obj.name.split("/")[1:])
135182
return {"FINISHED"}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import ifcopenshell
2+
import ifcopenshell.util.schema
3+
4+
5+
class Usecase:
6+
def __init__(self, file, settings=None):
7+
self.file = file
8+
self.settings = {
9+
"product": None,
10+
"ifc_class": "IfcBuildingElementProxy",
11+
"predefined_type": None,
12+
}
13+
for key, value in settings.items():
14+
self.settings[key] = value
15+
16+
def execute(self):
17+
element = ifcopenshell.util.schema.reassign_class(
18+
self.file, self.settings["product"], self.settings["ifc_class"]
19+
)
20+
if self.settings["predefined_type"] and hasattr(element, "PredefinedType"):
21+
try:
22+
element.PredefinedType = self.settings["predefined_type"]
23+
except:
24+
element.PredefinedType = "USERDEFINED"
25+
element.ObjectType = self.settings["predefined_type"]
26+
return element
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class Usecase:
2+
def __init__(self, file, settings=None):
3+
self.file = file
4+
self.settings = {
5+
"product": None
6+
}
7+
for key, value in settings.items():
8+
self.settings[key] = value
9+
10+
def execute(self):
11+
self.file.remove(self.settings["product"])
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import bpy
2+
from bpy.types import Panel
3+
from blenderbim.bim.module.root.data import Data
4+
5+
class BIM_PT_class(Panel):
6+
bl_label = "IFC Class"
7+
bl_idname = "BIM_PT_class"
8+
bl_space_type = "PROPERTIES"
9+
bl_region_type = "WINDOW"
10+
bl_context = "object"
11+
12+
def draw(self, context):
13+
props = context.active_object.BIMObjectProperties
14+
if props.ifc_definition_id:
15+
if props.ifc_definition_id not in Data.products:
16+
Data.load(props.ifc_definition_id)
17+
if props.is_reassigning_class:
18+
self.draw_class_dropdowns()
19+
row = self.layout.row(align=True)
20+
row.operator("bim.reassign_class", icon="CHECKMARK")
21+
row.operator("bim.disable_reassign_class", icon="X", text="")
22+
else:
23+
data = Data.products[props.ifc_definition_id]
24+
name = data["type"]
25+
if data["PredefinedType"] and data["PredefinedType"] == "USERDEFINED":
26+
name += "[{}]".format(data["ObjectType"])
27+
elif data["PredefinedType"]:
28+
name += "[{}]".format(data["PredefinedType"])
29+
row = self.layout.row(align=True)
30+
row.label(text=name)
31+
row.operator("bim.enable_reassign_class", icon="GREASEPENCIL", text="")
32+
op = row.operator("bim.unassign_class", icon="X", text="")
33+
op.object_name = context.active_object.name
34+
else:
35+
self.draw_class_dropdowns()
36+
row = self.layout.row(align=True)
37+
op = row.operator("bim.assign_class")
38+
op.object_name = context.active_object.name
39+
40+
def draw_class_dropdowns(self):
41+
props = bpy.context.scene.BIMProperties
42+
row = self.layout.row()
43+
row.prop(props, "ifc_product")
44+
row = self.layout.row()
45+
row.prop(props, "ifc_class")
46+
if props.ifc_predefined_type:
47+
row = self.layout.row()
48+
row.prop(props, "ifc_predefined_type")
49+
if props.ifc_predefined_type == "USERDEFINED":
50+
row = self.layout.row()
51+
row.prop(props, "ifc_userdefined_type")

src/ifcblenderexport/blenderbim/bim/ui.py

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,6 @@ def draw(self, context):
2323
props = context.active_object.BIMObjectProperties
2424
bim_properties = context.scene.BIMProperties
2525

26-
if props.is_reassigning_class or "/" not in context.active_object.name:
27-
row = layout.row()
28-
row.prop(bim_properties, "ifc_product")
29-
row = layout.row()
30-
row.prop(bim_properties, "ifc_class")
31-
if bim_properties.ifc_predefined_type:
32-
row = layout.row()
33-
row.prop(bim_properties, "ifc_predefined_type")
34-
if bim_properties.ifc_predefined_type == "USERDEFINED":
35-
row = layout.row()
36-
row.prop(bim_properties, "ifc_userdefined_type")
37-
row = layout.row(align=True)
38-
if "Ifc" not in context.active_object.name:
39-
op = row.operator("bim.assign_class")
40-
else:
41-
op = row.operator("bim.assign_class")
42-
op.object_name = context.active_object.name
43-
op = row.operator("bim.unassign_class", icon="X", text="")
44-
op.object_name = context.active_object.name
45-
else:
46-
row = layout.row()
47-
row.operator("bim.reassign_class", text="Reassign IFC Class")
48-
4926
if "Ifc" not in context.active_object.name:
5027
return
5128

src/ifccsv/ifccsv.py

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import ifcopenshell
55
import ifcopenshell.util.selector
66
import ifcopenshell.util.element
7+
import ifcopenshell.util.schema
78
import csv
89
import lark
910
import argparse
@@ -13,7 +14,7 @@ class IfcAttributeExtractor:
1314
@staticmethod
1415
def set_element_key(ifc_file, element, key, value):
1516
if key == "type" and element.is_a() != value:
16-
return IfcAttributeExtractor.change_ifc_class(ifc_file, element, value)
17+
return ifcopenshell.util.schema.reassign_class(ifc_file, element, value)
1718
if hasattr(element, key):
1819
setattr(element, key, value)
1920
return element
@@ -32,24 +33,6 @@ def set_element_key(ifc_file, element, key, value):
3233
return element
3334
return element
3435

35-
@staticmethod
36-
def change_ifc_class(ifc_file, element, new_class):
37-
try:
38-
new_element = ifc_file.create_entity(new_class)
39-
except:
40-
print(f"Class of {element} could not be changed to {new_class}")
41-
return element
42-
new_attributes = [new_element.attribute_name(i) for i, attribute in enumerate(new_element)]
43-
for i, attribute in enumerate(element):
44-
try:
45-
new_element[new_attributes.index(element.attribute_name(i))] = attribute
46-
except:
47-
continue
48-
for inverse in ifc_file.get_inverse(element):
49-
ifcopenshell.util.element.replace_attribute(inverse, element, new_element)
50-
ifc_file.remove(element)
51-
return new_element
52-
5336
@staticmethod
5437
def get_element_qto(element, name):
5538
for relationship in element.IsDefinedBy:

src/ifcopenshell-python/ifcopenshell/util/schema.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,25 @@ def is_a(entity, ifc_class):
1818
return False
1919

2020

21+
def reassign_class(ifc_file, element, new_class):
22+
try:
23+
new_element = ifc_file.create_entity(new_class)
24+
except:
25+
print(f"Class of {element} could not be changed to {new_class}")
26+
return element
27+
new_attributes = [new_element.attribute_name(i) for i, attribute in enumerate(new_element)]
28+
for i, attribute in enumerate(element):
29+
try:
30+
new_element[new_attributes.index(element.attribute_name(i))] = attribute
31+
except:
32+
continue
33+
for inverse in ifc_file.get_inverse(element):
34+
ifcopenshell.util.element.replace_attribute(inverse, element, new_element)
35+
ifc_file.remove(element)
36+
return new_element
37+
38+
39+
2140
class Migrator:
2241
def __init__(self):
2342
self.migrated_ids = {}

0 commit comments

Comments
 (0)