mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 09:05:18 +00:00
Openings now can be explicitly edited and saved and use IOS instead of Blender modifiers
This commit is contained in:
@@ -39,6 +39,7 @@ classes = (
|
|||||||
opening.AddElementOpening,
|
opening.AddElementOpening,
|
||||||
opening.AddPotentialHalfSpaceSolid,
|
opening.AddPotentialHalfSpaceSolid,
|
||||||
opening.AddPotentialOpening,
|
opening.AddPotentialOpening,
|
||||||
|
opening.EditOpenings,
|
||||||
opening.HideBooleans,
|
opening.HideBooleans,
|
||||||
opening.HideOpenings,
|
opening.HideOpenings,
|
||||||
opening.RemoveBooleans,
|
opening.RemoveBooleans,
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ class AuthoringData:
|
|||||||
cls.load_ifc_classes()
|
cls.load_ifc_classes()
|
||||||
cls.load_relating_types()
|
cls.load_relating_types()
|
||||||
cls.load_relating_types_browser()
|
cls.load_relating_types_browser()
|
||||||
|
cls.data["is_voidable_element"] = cls.is_voidable_element()
|
||||||
|
cls.data["has_visible_openings"] = cls.has_visible_openings()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_ifc_classes(cls):
|
def load_ifc_classes(cls):
|
||||||
@@ -53,6 +55,20 @@ class AuthoringData:
|
|||||||
def load_relating_types_browser(cls):
|
def load_relating_types_browser(cls):
|
||||||
cls.data["relating_types_ids_browser"] = cls.relating_types_browser()
|
cls.data["relating_types_ids_browser"] = cls.relating_types_browser()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_voidable_element(cls):
|
||||||
|
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||||
|
return element and element.is_a("IfcElement") and not element.is_a("IfcOpeningElement")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def has_visible_openings(cls):
|
||||||
|
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||||
|
if element and element.is_a("IfcElement") and not element.is_a("IfcOpeningElement"):
|
||||||
|
for opening in [r.RelatedOpeningElement for r in element.HasOpenings]:
|
||||||
|
if tool.Ifc.get_object(opening):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ifc_classes(cls):
|
def ifc_classes(cls):
|
||||||
results = []
|
results = []
|
||||||
|
|||||||
@@ -240,10 +240,9 @@ class AddBoolean(Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
props = context.scene.BIMModelProperties
|
props = context.scene.BIMModelProperties
|
||||||
objs = context.selected_objects
|
if len(context.selected_objects) != 2:
|
||||||
if len(objs) != 2:
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
obj1, obj2 = objs
|
obj1, obj2 = context.selected_objects
|
||||||
element1 = tool.Ifc.get_entity(obj1)
|
element1 = tool.Ifc.get_entity(obj1)
|
||||||
element2 = tool.Ifc.get_entity(obj2)
|
element2 = tool.Ifc.get_entity(obj2)
|
||||||
if element1 and element2:
|
if element1 and element2:
|
||||||
@@ -439,6 +438,38 @@ class HideOpenings(Operator, tool.Ifc.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class EditOpenings(Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.edit_openings"
|
||||||
|
bl_label = "Edit Openings"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
props = bpy.context.scene.BIMModelProperties
|
||||||
|
for obj in context.selected_objects:
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
if not element:
|
||||||
|
continue
|
||||||
|
openings = [r.RelatedOpeningElement for r in element.HasOpenings]
|
||||||
|
for opening in openings:
|
||||||
|
opening_obj = tool.Ifc.get_object(opening)
|
||||||
|
if opening_obj:
|
||||||
|
tool.Geometry.run_geometry_update_representation(obj=opening_obj)
|
||||||
|
tool.Ifc.unlink(element=opening, obj=opening_obj)
|
||||||
|
bpy.data.objects.remove(opening_obj)
|
||||||
|
|
||||||
|
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||||
|
blenderbim.core.geometry.switch_representation(
|
||||||
|
tool.Geometry,
|
||||||
|
obj=obj,
|
||||||
|
representation=body,
|
||||||
|
should_reload=True,
|
||||||
|
enable_dynamic_voids=False,
|
||||||
|
is_global=True,
|
||||||
|
should_sync_changes_first=False,
|
||||||
|
)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class DecorationsHandler:
|
class DecorationsHandler:
|
||||||
installed = None
|
installed = None
|
||||||
|
|
||||||
@@ -509,12 +540,12 @@ class DecorationsHandler:
|
|||||||
else:
|
else:
|
||||||
unselected_edges.append(edge_indices)
|
unselected_edges.append(edge_indices)
|
||||||
|
|
||||||
batch = batch_for_shader(self.shader, "LINES", {"pos": all_vertices}, indices=unselected_edges)
|
batch = batch_for_shader(self.shader, "LINES", {"pos": verts}, indices=unselected_edges)
|
||||||
self.shader.bind()
|
self.shader.bind()
|
||||||
self.shader.uniform_float("color", white)
|
self.shader.uniform_float("color", white)
|
||||||
batch.draw(self.shader)
|
batch.draw(self.shader)
|
||||||
|
|
||||||
batch = batch_for_shader(self.shader, "LINES", {"pos": all_vertices}, indices=selected_edges)
|
batch = batch_for_shader(self.shader, "LINES", {"pos": verts}, indices=selected_edges)
|
||||||
self.shader.uniform_float("color", green)
|
self.shader.uniform_float("color", green)
|
||||||
batch.draw(self.shader)
|
batch.draw(self.shader)
|
||||||
|
|
||||||
|
|||||||
@@ -230,7 +230,17 @@ class BimTool(WorkSpaceTool):
|
|||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.label(text="", icon="EVENT_SHIFT")
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
row.label(text="Opening", icon="EVENT_O")
|
row.label(text="", icon="EVENT_O")
|
||||||
|
if len(context.selected_objects) == 2:
|
||||||
|
row.operator("bim.add_opening", text="Void")
|
||||||
|
else:
|
||||||
|
row.operator("bim.add_potential_opening", text="Void")
|
||||||
|
if AuthoringData.data["is_voidable_element"]:
|
||||||
|
if AuthoringData.data["has_visible_openings"]:
|
||||||
|
row.operator("bim.edit_openings", icon="CHECKMARK", text="")
|
||||||
|
row.operator("bim.hide_openings", icon="CANCEL", text="")
|
||||||
|
else:
|
||||||
|
row.operator("bim.show_openings", icon="HIDE_OFF", text="")
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.label(text="Align")
|
row.label(text="Align")
|
||||||
|
|||||||
@@ -25,72 +25,57 @@ from blenderbim.bim.ifc import IfcStore
|
|||||||
from ifcopenshell.api.void.data import Data
|
from ifcopenshell.api.void.data import Data
|
||||||
|
|
||||||
|
|
||||||
class AddOpening(bpy.types.Operator):
|
class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "bim.add_opening"
|
bl_idname = "bim.add_opening"
|
||||||
bl_label = "Add Opening"
|
bl_label = "Add Opening"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
opening: bpy.props.StringProperty()
|
|
||||||
obj: bpy.props.StringProperty()
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
props = context.scene.BIMModelProperties
|
||||||
obj = context.scene.objects.get(self.obj, context.active_object)
|
if len(context.selected_objects) != 2:
|
||||||
if obj is None:
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
element_id = obj.BIMObjectProperties.ifc_definition_id
|
obj1, obj2 = context.selected_objects
|
||||||
if not element_id:
|
element1 = tool.Ifc.get_entity(obj1)
|
||||||
|
element2 = tool.Ifc.get_entity(obj2)
|
||||||
|
if element1 and element2:
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
element = self.file.by_id(element_id)
|
if not element1 and not element2:
|
||||||
tool.Geometry.clear_cache(element)
|
|
||||||
if element.is_a("IfcOpeningElement"):
|
|
||||||
self.report({"WARNING"}, "An IfcOpeningElement can't be voided")
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
if element2 and not element1:
|
||||||
opening = bpy.data.objects.get(self.opening)
|
obj1, obj2 = obj2, obj1
|
||||||
if opening is None:
|
element1, element2 = element2, element1
|
||||||
|
if element1.is_a("IfcOpeningElement"):
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
opening.display_type = "WIRE"
|
if obj2 not in [o.obj for o in props.openings]:
|
||||||
if not opening.BIMObjectProperties.ifc_definition_id:
|
return {"FINISHED"}
|
||||||
body_context = ifcopenshell.util.representation.get_context(IfcStore.get_file(), "Model", "Body")
|
if not obj1.data or not hasattr(obj1.data, "BIMMeshProperties"):
|
||||||
if not body_context:
|
return {"FINISHED"}
|
||||||
return {"FINISHED"}
|
body_context = ifcopenshell.util.representation.get_context(IfcStore.get_file(), "Model", "Body")
|
||||||
bpy.ops.bim.assign_class(obj=opening.name, ifc_class="IfcOpeningElement", context_id=body_context.id())
|
element2 = blenderbim.core.root.assign_class(
|
||||||
|
tool.Ifc,
|
||||||
# If the IfcOpeningElement already voids another object, remove the boolean modifier
|
tool.Collector,
|
||||||
opening_element = self.file.by_id(opening.BIMObjectProperties.ifc_definition_id)
|
tool.Root,
|
||||||
if opening_element.VoidsElements:
|
obj=obj2,
|
||||||
other_obj = IfcStore.get_element(opening_element.VoidsElements[0].RelatingBuildingElement.id())
|
ifc_class="IfcOpeningElement",
|
||||||
try:
|
should_add_representation=True,
|
||||||
modifier = next(m for m in other_obj.modifiers if m.type == "BOOLEAN" and m.object == opening)
|
context=body_context,
|
||||||
other_obj.modifiers.remove(modifier)
|
|
||||||
except StopIteration:
|
|
||||||
pass
|
|
||||||
|
|
||||||
ifcopenshell.api.run(
|
|
||||||
"void.add_opening",
|
|
||||||
self.file,
|
|
||||||
**{
|
|
||||||
"opening": opening_element,
|
|
||||||
"element": element,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
Data.load(self.file, element_id)
|
ifcopenshell.api.run("void.add_opening", tool.Ifc.get(), opening=element2, element=element1)
|
||||||
|
|
||||||
try:
|
representation = tool.Ifc.get().by_id(obj1.data.BIMMeshProperties.ifc_definition_id)
|
||||||
modifier = next(m for m in obj.modifiers if m.type == "BOOLEAN" and m.object == opening)
|
blenderbim.core.geometry.switch_representation(
|
||||||
except StopIteration:
|
tool.Geometry,
|
||||||
modifier = obj.modifiers.new("IfcOpeningElement", "BOOLEAN")
|
obj=obj1,
|
||||||
modifier.object = opening
|
representation=representation,
|
||||||
finally:
|
should_reload=True,
|
||||||
modifier.operation = "DIFFERENCE"
|
enable_dynamic_voids=False,
|
||||||
modifier.solver = "EXACT"
|
is_global=True,
|
||||||
modifier.use_self = True
|
should_sync_changes_first=False,
|
||||||
modifier.operand_type = "OBJECT"
|
)
|
||||||
|
Data.load(tool.Ifc.get(), element1.id())
|
||||||
context.view_layer.objects.active = obj
|
tool.Ifc.unlink(obj=obj2)
|
||||||
|
bpy.data.objects.remove(obj2)
|
||||||
|
context.view_layer.objects.active = obj1
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user