mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
WIP implement void module with add / remove openings. See #1222.
This commit is contained in:
@@ -5,25 +5,33 @@ bpy = sys.modules.get("bpy")
|
||||
|
||||
if bpy is not None:
|
||||
import bpy
|
||||
import blenderbim.bim.module.root as module_root
|
||||
import blenderbim.bim.module.aggregate as module_aggregate
|
||||
import blenderbim.bim.module.attribute as module_attribute
|
||||
import blenderbim.bim.module.bcf as module_bcf
|
||||
import blenderbim.bim.module.context as module_context
|
||||
import blenderbim.bim.module.covetool as module_covetool
|
||||
import blenderbim.bim.module.debug as module_debug
|
||||
import blenderbim.bim.module.geometry as module_geometry
|
||||
import blenderbim.bim.module.model as module_model
|
||||
import blenderbim.bim.module.owner as module_owner
|
||||
import blenderbim.bim.module.project as module_project
|
||||
import blenderbim.bim.module.pset as module_pset
|
||||
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
|
||||
import blenderbim.bim.module.cobie as module_cobie
|
||||
import importlib
|
||||
from . import ui, prop, operator
|
||||
|
||||
modules = {
|
||||
"root": None,
|
||||
"aggregate": None,
|
||||
"attribute": None,
|
||||
"bcf": None,
|
||||
"cobie": None,
|
||||
"context": None,
|
||||
"covetool": None,
|
||||
"debug": None,
|
||||
"geometry": None,
|
||||
"model": None,
|
||||
"owner": None,
|
||||
"project": None,
|
||||
"pset": None,
|
||||
"spatial": None,
|
||||
"style": None,
|
||||
"type": None,
|
||||
"unit": None,
|
||||
"void": None,
|
||||
}
|
||||
|
||||
for name in modules.keys():
|
||||
modules[name] = importlib.import_module(f"blenderbim.bim.module.{name}")
|
||||
|
||||
classes = [
|
||||
operator.SelectClass,
|
||||
operator.SelectType,
|
||||
@@ -286,24 +294,9 @@ if bpy is not None:
|
||||
ui.BIM_ADDON_preferences,
|
||||
]
|
||||
|
||||
classes.extend(module_root.classes)
|
||||
classes.extend(module_aggregate.classes)
|
||||
classes.extend(module_attribute.classes)
|
||||
classes.extend(module_bcf.classes)
|
||||
classes.extend(module_context.classes)
|
||||
classes.extend(module_covetool.classes)
|
||||
classes.extend(module_debug.classes)
|
||||
classes.extend(module_geometry.classes)
|
||||
classes.extend(module_model.classes)
|
||||
classes.extend(module_owner.classes)
|
||||
classes.extend(module_project.classes)
|
||||
classes.extend(module_pset.classes)
|
||||
classes.extend(module_spatial.classes)
|
||||
classes.extend(module_style.classes)
|
||||
classes.extend(module_type.classes)
|
||||
classes.extend(module_unit.classes)
|
||||
classes.extend(module_cobie.classes)
|
||||
|
||||
for module in modules.values():
|
||||
classes.extend(module.classes)
|
||||
|
||||
def menu_func_export(self, context):
|
||||
self.layout.operator(operator.ExportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcjson)")
|
||||
|
||||
@@ -333,23 +326,10 @@ if bpy is not None:
|
||||
bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties)
|
||||
bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
|
||||
bpy.types.SCENE_PT_unit.append(ui.ifc_units)
|
||||
module_root.register()
|
||||
module_aggregate.register()
|
||||
module_attribute.register()
|
||||
module_bcf.register()
|
||||
module_context.register()
|
||||
module_covetool.register()
|
||||
module_debug.register()
|
||||
module_geometry.register()
|
||||
module_model.register()
|
||||
module_owner.register()
|
||||
module_project.register()
|
||||
module_pset.register()
|
||||
module_spatial.register()
|
||||
module_style.register()
|
||||
module_type.register()
|
||||
module_unit.register()
|
||||
module_cobie.register()
|
||||
|
||||
for module in modules.values():
|
||||
module.register()
|
||||
|
||||
bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
|
||||
bpy.app.handlers.load_post.append(prop.toggleDecorationsOnLoad)
|
||||
|
||||
@@ -370,21 +350,8 @@ if bpy is not None:
|
||||
del bpy.types.Camera.BIMCameraProperties
|
||||
del bpy.types.TextCurve.BIMTextProperties
|
||||
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
|
||||
module_cobie.unregister()
|
||||
module_unit.unregister()
|
||||
module_type.unregister()
|
||||
module_style.unregister()
|
||||
module_spatial.unregister()
|
||||
module_pset.unregister()
|
||||
module_project.unregister()
|
||||
module_owner.unregister()
|
||||
module_model.unregister()
|
||||
module_geometry.unregister()
|
||||
module_debug.unregister()
|
||||
module_covetool.unregister()
|
||||
module_context.unregister()
|
||||
module_bcf.unregister()
|
||||
module_attribute.register()
|
||||
module_aggregate.register()
|
||||
module_root.unregister()
|
||||
|
||||
for module in reversed(list(modules.values())):
|
||||
module.unregister()
|
||||
|
||||
bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler)
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user