mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 10:06:47 +00:00
Add new opening functions to BIM tool workspace UI
This commit is contained in:
@@ -162,41 +162,66 @@ class AddElementOpening(bpy.types.Operator):
|
||||
return obj
|
||||
|
||||
|
||||
def add_object(self, context):
|
||||
props = context.scene.BIMModelProperties
|
||||
bm = bmesh.new()
|
||||
bmesh.ops.create_cube(bm, size=self.size)
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.edges.ensure_lookup_table()
|
||||
bm.faces.ensure_lookup_table()
|
||||
mesh = bpy.data.meshes.new(name="Dumb Opening")
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
obj = object_data_add(context, mesh, operator=self)
|
||||
obj.name = "Opening"
|
||||
|
||||
has_deleted_opening = True
|
||||
while has_deleted_opening:
|
||||
has_deleted_opening = False
|
||||
for i, opening in enumerate(props.openings):
|
||||
if not opening.obj:
|
||||
props.openings.remove(i)
|
||||
has_deleted_opening = True
|
||||
|
||||
new = props.openings.add()
|
||||
new.obj = obj
|
||||
|
||||
DecorationsHandler.install(context)
|
||||
|
||||
|
||||
class AddPotentialOpening(Operator, AddObjectHelper):
|
||||
bl_idname = "bim.add_potential_opening"
|
||||
bl_label = "Add Potential Opening"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
size: FloatProperty(name="Size", default=0.5)
|
||||
x: FloatProperty(name="X", default=0.5)
|
||||
y: FloatProperty(name="Y", default=0.5)
|
||||
z: FloatProperty(name="Z", default=0.5)
|
||||
|
||||
def draw_settings(context, layout, tool):
|
||||
row = self.layout.row()
|
||||
row.prop(data=self, property="x", label="Size X")
|
||||
row = self.layout.row()
|
||||
row.prop(data=self, property="y")
|
||||
row = self.layout.row()
|
||||
row.prop(data=self, property="z")
|
||||
|
||||
def execute(self, context):
|
||||
add_object(self, context)
|
||||
props = context.scene.BIMModelProperties
|
||||
|
||||
new_matrix = None
|
||||
if context.selected_objects and context.active_object:
|
||||
new_matrix = context.active_object.matrix_world.copy()
|
||||
new_matrix.col[3] = context.scene.cursor.location.to_4d().to_4d()
|
||||
|
||||
x = self.x / 2
|
||||
y = self.y / 2
|
||||
z = self.z / 2
|
||||
verts = [
|
||||
Vector((-x, -y, -z)),
|
||||
Vector((-x, -y, z)),
|
||||
Vector((-x, y, -z)),
|
||||
Vector((-x, y, z)),
|
||||
Vector((x, -y, -z)),
|
||||
Vector((x, -y, z)),
|
||||
Vector((x, y, -z)),
|
||||
Vector((x, y, z)),
|
||||
]
|
||||
edges = []
|
||||
faces = [
|
||||
[0, 1, 3, 2],
|
||||
[2, 3, 7, 6],
|
||||
[6, 7, 5, 4],
|
||||
[4, 5, 1, 0],
|
||||
[2, 6, 4, 0],
|
||||
[7, 3, 1, 5],
|
||||
]
|
||||
mesh = bpy.data.meshes.new(name="Opening")
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
obj = object_data_add(context, mesh, operator=self)
|
||||
obj.name = "Opening"
|
||||
|
||||
if new_matrix:
|
||||
obj.matrix_world = new_matrix
|
||||
|
||||
tool.Model.clear_scene_openings()
|
||||
|
||||
new = props.openings.add()
|
||||
new.obj = obj
|
||||
|
||||
DecorationsHandler.install(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -218,13 +243,7 @@ class AddPotentialHalfSpaceSolid(Operator, AddObjectHelper):
|
||||
obj = object_data_add(context, mesh, operator=self)
|
||||
obj.name = "HalfSpaceSolid"
|
||||
|
||||
has_deleted_opening = True
|
||||
while has_deleted_opening:
|
||||
has_deleted_opening = False
|
||||
for i, opening in enumerate(props.openings):
|
||||
if not opening.obj:
|
||||
props.openings.remove(i)
|
||||
has_deleted_opening = True
|
||||
tool.Model.clear_scene_openings()
|
||||
|
||||
new = props.openings.add()
|
||||
new.obj = obj
|
||||
@@ -263,13 +282,7 @@ class AddBoolean(Operator, tool.Ifc.Operator):
|
||||
matrix=obj1.matrix_world.inverted() @ obj2.matrix_world,
|
||||
)
|
||||
|
||||
has_deleted_opening = True
|
||||
while has_deleted_opening:
|
||||
has_deleted_opening = False
|
||||
for i, opening in enumerate(props.openings):
|
||||
if not opening.obj:
|
||||
props.openings.remove(i)
|
||||
has_deleted_opening = True
|
||||
tool.Model.clear_scene_openings()
|
||||
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Geometry,
|
||||
@@ -334,13 +347,7 @@ class ShowBooleans(Operator, tool.Ifc.Operator, AddObjectHelper):
|
||||
obj = object_data_add(bpy.context, mesh, operator=self)
|
||||
obj.name = "HalfSpaceSolid"
|
||||
|
||||
has_deleted_opening = True
|
||||
while has_deleted_opening:
|
||||
has_deleted_opening = False
|
||||
for i, opening in enumerate(props.openings):
|
||||
if not opening.obj:
|
||||
props.openings.remove(i)
|
||||
has_deleted_opening = True
|
||||
tool.Model.clear_scene_openings()
|
||||
|
||||
new = props.openings.add()
|
||||
new.obj = obj
|
||||
@@ -425,6 +432,7 @@ class HideOpenings(Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
to_delete = set()
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
@@ -433,8 +441,11 @@ class HideOpenings(Operator, tool.Ifc.Operator):
|
||||
for opening in openings:
|
||||
opening_obj = tool.Ifc.get_object(opening)
|
||||
if opening_obj:
|
||||
tool.Ifc.unlink(element=opening, obj=opening_obj)
|
||||
bpy.data.objects.remove(opening_obj)
|
||||
to_delete.add(opening_obj)
|
||||
for opening_obj in to_delete:
|
||||
tool.Ifc.unlink(element=opening, obj=opening_obj)
|
||||
bpy.data.objects.remove(opening_obj)
|
||||
tool.Model.clear_scene_openings()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -200,3 +200,6 @@ class BIMModelProperties(PropertyGroup):
|
||||
)
|
||||
length: bpy.props.FloatProperty(default=42.0)
|
||||
openings: bpy.props.CollectionProperty(type=ObjProperty)
|
||||
x: bpy.props.FloatProperty(name="X", default=0.5)
|
||||
y: bpy.props.FloatProperty(name="Y", default=0.5)
|
||||
z: bpy.props.FloatProperty(name="Z", default=0.5)
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
import os
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.type.prop as type_prop
|
||||
from blenderbim.bim.helper import prop_with_search, close_operator_panel
|
||||
from bpy.types import WorkSpaceTool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.model.data import AuthoringData
|
||||
from blenderbim.bim.module.model import prop
|
||||
|
||||
@@ -56,13 +56,13 @@ class BimTool(WorkSpaceTool):
|
||||
("bim.hotkey", {"type": "O", "value": "PRESS", "alt": True}, {"properties": [("hotkey", "A_O")]}),
|
||||
)
|
||||
|
||||
def draw_settings(context, layout, tool):
|
||||
def draw_settings(context, layout, something):
|
||||
props = context.scene.BIMModelProperties
|
||||
is_tool_header = context.region.type == "TOOL_HEADER"
|
||||
is_sidebar = context.region.type == "UI"
|
||||
|
||||
row = layout.row(align=True)
|
||||
if not IfcStore.get_file():
|
||||
if not tool.Ifc.get():
|
||||
row.label(text="No IFC Project", icon="ERROR")
|
||||
return
|
||||
|
||||
@@ -232,9 +232,9 @@ class BimTool(WorkSpaceTool):
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_O")
|
||||
if len(context.selected_objects) == 2:
|
||||
row.operator("bim.add_opening", text="Void")
|
||||
row.operator("bim.add_opening", text="Apply Void")
|
||||
else:
|
||||
row.operator("bim.add_potential_opening", text="Void")
|
||||
row.operator("bim.add_potential_opening", text="Add Void")
|
||||
if AuthoringData.data["is_voidable_element"]:
|
||||
if AuthoringData.data["has_visible_openings"]:
|
||||
row.operator("bim.edit_openings", icon="CHECKMARK", text="")
|
||||
@@ -265,7 +265,7 @@ class BimTool(WorkSpaceTool):
|
||||
row.label(text="Decomposition", icon="EVENT_D")
|
||||
|
||||
|
||||
class Hotkey(bpy.types.Operator):
|
||||
class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.hotkey"
|
||||
bl_label = "Hotkey"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -273,10 +273,7 @@ class Hotkey(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
return tool.Ifc.get()
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.BIMModelProperties
|
||||
@@ -288,6 +285,16 @@ class Hotkey(bpy.types.Operator):
|
||||
getattr(self, f"hotkey_{self.hotkey}")()
|
||||
return {"FINISHED"}
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMModelProperties
|
||||
if self.hotkey == "S_O":
|
||||
row = self.layout.row()
|
||||
row.prop(props, "x")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "y")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "z")
|
||||
|
||||
def hotkey_S_A(self):
|
||||
bpy.ops.bim.add_constr_type_instance()
|
||||
|
||||
@@ -366,10 +373,16 @@ class Hotkey(bpy.types.Operator):
|
||||
bpy.ops.bim.extend_profile(join_type="V")
|
||||
|
||||
def hotkey_S_O(self):
|
||||
bpy.ops.bim.add_element_opening(voided_building_element="", filling_building_element="")
|
||||
if len(bpy.context.selected_objects) == 2:
|
||||
bpy.ops.bim.add_opening()
|
||||
else:
|
||||
bpy.ops.bim.add_potential_opening(x=self.props.x, y=self.props.y, z=self.props.z)
|
||||
|
||||
def hotkey_A_D(self):
|
||||
bpy.ops.bim.toggle_decomposition_parenting()
|
||||
|
||||
def hotkey_A_O(self):
|
||||
bpy.ops.bim.toggle_opening_visibility()
|
||||
if AuthoringData.data["has_visible_openings"]:
|
||||
bpy.ops.bim.hide_openings()
|
||||
else:
|
||||
bpy.ops.bim.show_openings()
|
||||
|
||||
@@ -24,7 +24,6 @@ classes = (
|
||||
operator.RemoveOpening,
|
||||
operator.AddFilling,
|
||||
operator.RemoveFilling,
|
||||
operator.ToggleOpeningVisibility,
|
||||
operator.ToggleDecompositionParenting,
|
||||
prop.VoidProperties,
|
||||
ui.BIM_PT_voids,
|
||||
|
||||
@@ -37,9 +37,7 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj1, obj2 = context.selected_objects
|
||||
element1 = tool.Ifc.get_entity(obj1)
|
||||
element2 = tool.Ifc.get_entity(obj2)
|
||||
if element1 and element2:
|
||||
return {"FINISHED"}
|
||||
if not element1 and not element2:
|
||||
if type(element1) == type(element2):
|
||||
return {"FINISHED"}
|
||||
if element2 and not element1:
|
||||
obj1, obj2 = obj2, obj1
|
||||
@@ -50,6 +48,13 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
if not obj1.data or not hasattr(obj1.data, "BIMMeshProperties"):
|
||||
return {"FINISHED"}
|
||||
|
||||
has_visible_openings = False
|
||||
for opening in [r.RelatedOpeningElement for r in element1.HasOpenings]:
|
||||
if tool.Ifc.get_object(opening):
|
||||
has_visible_openings = True
|
||||
break
|
||||
|
||||
body_context = ifcopenshell.util.representation.get_context(IfcStore.get_file(), "Model", "Body")
|
||||
element2 = blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
@@ -73,8 +78,11 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
Data.load(tool.Ifc.get(), element1.id())
|
||||
tool.Ifc.unlink(obj=obj2)
|
||||
bpy.data.objects.remove(obj2)
|
||||
|
||||
if not has_visible_openings:
|
||||
tool.Ifc.unlink(obj=obj2)
|
||||
bpy.data.objects.remove(obj2)
|
||||
|
||||
context.view_layer.objects.active = obj1
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -177,18 +185,6 @@ class RemoveFilling(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ToggleOpeningVisibility(bpy.types.Operator):
|
||||
bl_idname = "bim.toggle_opening_visibility"
|
||||
bl_label = "Toggle Opening Visibility"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
for project in [c for c in context.view_layer.layer_collection.children if "IfcProject" in c.name]:
|
||||
for collection in [c for c in project.children if "IfcOpeningElements" in c.name]:
|
||||
collection.hide_viewport = not collection.hide_viewport
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ToggleDecompositionParenting(bpy.types.Operator):
|
||||
bl_idname = "bim.toggle_decomposition_parenting"
|
||||
bl_label = "Toggle Decomposition Parenting"
|
||||
|
||||
@@ -51,7 +51,7 @@ class Model(blenderbim.core.tool.Model):
|
||||
@classmethod
|
||||
def load_openings(cls, element, openings):
|
||||
if not openings:
|
||||
return
|
||||
return []
|
||||
obj = tool.Ifc.get_object(element)
|
||||
ifc_import_settings = import_ifc.IfcImportSettings.factory()
|
||||
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
|
||||
@@ -69,3 +69,14 @@ class Model(blenderbim.core.tool.Model):
|
||||
opening_obj.matrix_parent_inverse = obj.matrix_world.inverted()
|
||||
ifc_importer.place_objects_in_collections()
|
||||
return ifc_importer.added_data.values()
|
||||
|
||||
@classmethod
|
||||
def clear_scene_openings(cls):
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
has_deleted_opening = True
|
||||
while has_deleted_opening:
|
||||
has_deleted_opening = False
|
||||
for i, opening in enumerate(props.openings):
|
||||
if not opening.obj:
|
||||
props.openings.remove(i)
|
||||
has_deleted_opening = True
|
||||
|
||||
Reference in New Issue
Block a user