WIP implement void module with add / remove openings. See #1222.

This commit is contained in:
Dion Moult
2021-01-12 15:12:53 +11:00
parent 1836136049
commit 9400eab0ff
10 changed files with 282 additions and 78 deletions
@@ -34,11 +34,12 @@ class AddRepresentation(bpy.types.Operator):
bl_idname = "bim.add_representation"
bl_label = "Add Representation"
obj: bpy.props.StringProperty()
context_id: bpy.props.IntProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
self.context_id = bpy.context.scene.BIMProperties.contexts
context_id = self.context_id or int(bpy.context.scene.BIMProperties.contexts)
bpy.ops.bim.edit_object_placement(obj=obj.name)
@@ -46,7 +47,7 @@ class AddRepresentation(bpy.types.Operator):
result = add_representation.Usecase(
self.file,
{
"context": self.file.by_id(int(self.context_id)),
"context": self.file.by_id(context_id),
"geometry": obj.data,
"total_items": max(1, len(obj.material_slots)),
},
@@ -71,7 +72,7 @@ class AddRepresentation(bpy.types.Operator):
existing_mesh = obj.data
mesh = obj.data.copy()
mesh.name = "{}/{}".format(self.context_id, result.id())
mesh.name = "{}/{}".format(context_id, result.id())
mesh.BIMMeshProperties.ifc_definition_id = int(result.id())
obj.data = mesh
Data.load(obj.BIMObjectProperties.ifc_definition_id)
@@ -76,6 +76,7 @@ class AssignClass(bpy.types.Operator):
ifc_class: bpy.props.StringProperty()
predefined_type: bpy.props.StringProperty()
userdefined_type: bpy.props.StringProperty()
context_id: bpy.props.IntProperty()
def execute(self, context):
objects = [bpy.data.objects.get(self.obj)] if self.obj else bpy.context.selected_objects
@@ -107,7 +108,7 @@ class AssignClass(bpy.types.Operator):
obj.name = "{}/{}".format(product.is_a(), obj.name)
obj.BIMObjectProperties.ifc_definition_id = int(product.id())
bpy.ops.bim.add_representation(obj=obj.name)
bpy.ops.bim.add_representation(obj=obj.name, context_id=self.context_id)
if product.is_a("IfcElementType"):
self.place_in_types_collection(obj)
@@ -160,12 +161,12 @@ class AssignClass(bpy.types.Operator):
class UnassignClass(bpy.types.Operator):
bl_idname = "bim.unassign_class"
bl_label = "Unassign IFC Class"
object_name: bpy.props.StringProperty()
obj: bpy.props.StringProperty()
def execute(self, context):
self.file = IfcStore.get_file()
if self.object_name:
objects = [bpy.data.objects.get(self.object_name)]
if self.obj:
objects = [bpy.data.objects.get(self.obj)]
else:
objects = bpy.context.selected_objects
for obj in objects:
@@ -3,6 +3,7 @@ from bpy.types import Panel
from blenderbim.bim.module.root.data import Data
from blenderbim.bim.ifc import IfcStore
class BIM_PT_class(Panel):
bl_label = "IFC Class"
bl_idname = "BIM_PT_class"
@@ -34,8 +35,7 @@ class BIM_PT_class(Panel):
row = self.layout.row(align=True)
row.label(text=name)
row.operator("bim.enable_reassign_class", icon="GREASEPENCIL", text="")
op = row.operator("bim.unassign_class", icon="X", text="")
op.object_name = context.active_object.name
row.operator("bim.unassign_class", icon="X", text="").obj = context.active_object.name
else:
self.draw_class_dropdowns()
row = self.layout.row(align=True)
@@ -0,0 +1,16 @@
import bpy
from . import ui, operator
classes = (
operator.AddOpening,
operator.RemoveOpening,
ui.BIM_PT_voids,
)
def register():
pass
def unregister():
pass
@@ -0,0 +1,16 @@
import ifcopenshell
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {"opening": None, "element": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
self.file.create_entity("IfcRelVoidsElement", **{
"GlobalId": ifcopenshell.guid.new(),
"RelatingBuildingElement": self.settings["element"],
"RelatedOpeningElement": self.settings["opening"]
})
@@ -0,0 +1,53 @@
from blenderbim.bim.ifc import IfcStore
class Data:
products = {}
openings = {}
fillings = {}
@classmethod
def load(cls, product_id):
print('loading', product_id)
file = IfcStore.get_file()
if not file:
return
cls.products[product_id] = set()
product = file.by_id(product_id)
for rel in file.by_type("IfcRelVoidsElement"):
building_element = rel.RelatingBuildingElement
if building_element != product:
continue
opening = rel.RelatedOpeningElement
opening_id = int(opening.id())
cls.products[product_id].add(opening_id)
cls.openings[opening_id] = {"Name": opening.Name, "HasFillings": set()}
for rel in file.by_type("IfcRelFillsElement"):
opening = rel.RelatingOpeningElement
opening_id = int(opening.id())
if opening_id not in cls.openings:
continue
filling = rel.RelatedBuildingElement
filling_id = int(filling.id())
cls.fillings[filling_id] = {"Name": filling.Name, "FillsVoid": opening_id}
cls.openings[opening_id]["HasFillings"].add(filling_id)
return # See bug #1224
#if not hasattr(product, "HasOpenings") or not product.HasOpenings:
# return
#for rel_voids_element in product.HasOpenings:
# opening = rel_voids_element.RelatedOpeningElement
# opening_id = int(opening.id())
# fillings = []
# if opening.HasFillings:
# for rel_fills_element in opening.HasFillings:
# filling = rel_fills_element.RelatedBuildingElement
# filling_id = int(filling.id())
# fillings.append(filling_id)
# cls.fillings[filling_id] = {
# "Name": filling.Name,
# }
# cls.openings[opening_id] = {
# "Name": opening.Name,
# "HasFillings": fillings,
# }
# cls.products[product_id].append(opening_id)
@@ -0,0 +1,74 @@
import bpy
import blenderbim.bim.module.void.add_opening as add_opening
import blenderbim.bim.module.void.remove_opening as remove_opening
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.void.data import Data
from blenderbim.bim.module.context.data import Data as ContextData
class AddOpening(bpy.types.Operator):
bl_idname = "bim.add_opening"
bl_label = "Add Opening"
opening: bpy.props.StringProperty()
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
opening = bpy.data.objects.get(self.opening)
if not opening.BIMObjectProperties.ifc_definition_id:
body_context_id = None
if not ContextData.is_loaded:
ContextData.load()
for context in ContextData.contexts.values():
for subcontext_id, subcontext in context["HasSubContexts"].items():
if subcontext["ContextType"] == "Model" and subcontext["ContextIdentifier"] == "Body":
body_context_id = subcontext_id
break
if not body_context_id:
return {"FINISHED"}
bpy.ops.bim.assign_class(obj=opening.name, ifc_class="IfcOpeningElement", context_id=body_context_id)
self.file = IfcStore.get_file()
element_id = obj.BIMObjectProperties.ifc_definition_id
add_opening.Usecase(self.file, {
"opening": self.file.by_id(opening.BIMObjectProperties.ifc_definition_id),
"element": self.file.by_id(element_id)
}).execute()
Data.load(element_id)
has_modifier = False
for modifier in obj.modifiers:
if modifier.type == "BOOLEAN" and modifier.object and modifier.object == opening:
has_modifier = True
break
if not has_modifier:
modifier = obj.modifiers.new("IfcOpeningElement", "BOOLEAN")
modifier.operation = "DIFFERENCE"
modifier.object = opening
return {"FINISHED"}
class RemoveOpening(bpy.types.Operator):
bl_idname = "bim.remove_opening"
bl_label = "Remove Opening"
opening_id: bpy.props.IntProperty()
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
for modifier in obj.modifiers:
if modifier.type != "BOOLEAN":
continue
if modifier.object and modifier.object.BIMObjectProperties.ifc_definition_id == self.opening_id:
modifier.object.BIMObjectProperties.ifc_definition_id = 0
if "/" in modifier.object.name and modifier.object.name[0:3] == "Ifc":
modifier.object.name = "/".join(modifier.object.name.split("/")[1:])
obj.modifiers.remove(modifier)
break
remove_opening.Usecase(self.file, {"opening": self.file.by_id(self.opening_id)}).execute()
Data.load(obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
@@ -0,0 +1,20 @@
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {"opening": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
to_remove = [] # See bug #1224
for rel in self.file.by_type("IfcRelVoidsElement"):
if rel.RelatedOpeningElement == self.settings["opening"]:
to_remove.append(rel)
break
for rel in self.file.by_type("IfcRelFillsElement"):
if rel.RelatingOpeningElement == self.settings["opening"]:
to_remove.append(rel)
break
self.file.remove(self.settings["opening"])
for element in to_remove:
self.file.remove(element)
@@ -0,0 +1,56 @@
import bpy
from bpy.types import Panel
from blenderbim.bim.module.void.data import Data
class BIM_PT_voids(Panel):
bl_label = "IFC Voids"
bl_idname = "BIM_PT_voids"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
def draw(self, context):
props = context.active_object.BIMObjectProperties
if props.ifc_definition_id not in Data.products:
Data.load(props.ifc_definition_id)
row = self.layout.row(align=True)
if len(context.selected_objects) == 2:
op = row.operator("bim.add_opening", icon="ADD", text="Add Selected")
for obj in context.selected_objects:
if "IfcOpeningElement" in obj.name or not obj.BIMObjectProperties.ifc_definition_id:
op.opening = obj.name
else:
op.obj = obj.name
opening_id = None
obj_name = None
for obj in context.selected_objects:
if obj.BIMObjectProperties.ifc_definition_id in Data.openings:
opening_id = obj.BIMObjectProperties.ifc_definition_id
elif obj.BIMObjectProperties.ifc_definition_id in Data.fillings:
opening_id = Data.fillings[obj.BIMObjectProperties.ifc_definition_id]["FillsVoid"]
else:
obj_name = obj.name
if opening_id and obj_name:
op = row.operator("bim.remove_opening", icon="X", text="Remove Selected")
op.opening_id = opening_id
op.obj = obj_name
else:
row.label(text="Select an opening and an element to modify", icon="HELP")
for opening_id in Data.products[props.ifc_definition_id]:
opening = Data.openings[opening_id]
if opening["HasFillings"]:
for filling_id in opening["HasFillings"]:
filling = Data.fillings[filling_id]
row = self.layout.row(align=True)
row.label(text=opening["Name"])
row.label(text=filling["Name"])
else:
row = self.layout.row(align=True)
row.label(text=opening["Name"])
op = row.operator("bim.remove_opening", icon="X", text="")
op.opening_id = opening_id