mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-10-02 04:59:52 +00:00
Fix #1648. Adding openings now intelligently detect the shape of the element you are voiding.
This commit is contained in:
@@ -11,8 +11,7 @@ classes = (
|
|||||||
wall.AlignWall,
|
wall.AlignWall,
|
||||||
wall.FlipWall,
|
wall.FlipWall,
|
||||||
wall.SplitWall,
|
wall.SplitWall,
|
||||||
wall.AddWallOpening,
|
opening.AddElementOpening,
|
||||||
slab.AddSlabOpening,
|
|
||||||
profile.ExtendProfile,
|
profile.ExtendProfile,
|
||||||
prop.BIMModelProperties,
|
prop.BIMModelProperties,
|
||||||
ui.BIM_PT_authoring,
|
ui.BIM_PT_authoring,
|
||||||
|
|||||||
@@ -52,6 +52,46 @@ def mode_callback(obj, data):
|
|||||||
bm.free()
|
bm.free()
|
||||||
|
|
||||||
|
|
||||||
|
class AddElementOpening(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.add_element_opening"
|
||||||
|
bl_label = "Add Element Opening"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
selected_objs = context.selected_objects
|
||||||
|
if len(selected_objs) == 0 or not context.active_object:
|
||||||
|
return {"FINISHED"}
|
||||||
|
obj = context.active_object
|
||||||
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||||
|
return {"FINISHED"}
|
||||||
|
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
local_location = obj.matrix_world.inverted() @ context.scene.cursor.location
|
||||||
|
raycast = obj.closest_point_on_mesh(local_location, distance=0.01)
|
||||||
|
if not raycast[0]:
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
# The opening shall be based on the smallest bounding dimension of the element
|
||||||
|
dimension = min(obj.dimensions)
|
||||||
|
bpy.ops.mesh.primitive_cube_add(size=dimension * 2)
|
||||||
|
opening = context.selected_objects[0]
|
||||||
|
|
||||||
|
# Place the opening in the middle of the element
|
||||||
|
global_location = obj.matrix_world @ raycast[1]
|
||||||
|
normal = raycast[2]
|
||||||
|
normal.negate()
|
||||||
|
global_normal = obj.matrix_world.to_quaternion() @ normal
|
||||||
|
opening.location = global_location + (global_normal * (dimension / 2))
|
||||||
|
|
||||||
|
opening.rotation_euler = obj.rotation_euler
|
||||||
|
opening.name = "Opening"
|
||||||
|
bpy.ops.bim.add_opening(opening=opening.name, obj=obj.name)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def add_object(self, context):
|
def add_object(self, context):
|
||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
bmesh.ops.create_cube(bm, size=self.size)
|
bmesh.ops.create_cube(bm, size=self.size)
|
||||||
|
|||||||
@@ -244,12 +244,17 @@ def regenerate_profile_usage(usecase_path, ifc_file, settings):
|
|||||||
|
|
||||||
|
|
||||||
def ensure_material_assigned(usecase_path, ifc_file, settings):
|
def ensure_material_assigned(usecase_path, ifc_file, settings):
|
||||||
elements = []
|
if usecase_path == "material.assign_material":
|
||||||
for rel in ifc_file.by_type("IfcRelAssociatesMaterial"):
|
if not settings.get("Material", None):
|
||||||
if rel.RelatingMaterial == settings["material"] or [
|
return
|
||||||
e for e in ifc_file.traverse(rel.RelatingMaterial) if e == settings["material"]
|
elements = [self.settings["product"]]
|
||||||
]:
|
else:
|
||||||
elements.extend(rel.RelatedObjects)
|
elements = []
|
||||||
|
for rel in ifc_file.by_type("IfcRelAssociatesMaterial"):
|
||||||
|
if rel.RelatingMaterial == settings["material"] or [
|
||||||
|
e for e in ifc_file.traverse(rel.RelatingMaterial) if e == settings["material"]
|
||||||
|
]:
|
||||||
|
elements.extend(rel.RelatedObjects)
|
||||||
|
|
||||||
for element in elements:
|
for element in elements:
|
||||||
obj = IfcStore.get_element(element.GlobalId)
|
obj = IfcStore.get_element(element.GlobalId)
|
||||||
|
|||||||
@@ -199,42 +199,6 @@ def calculate_quantities(usecase_path, ifc_file, settings):
|
|||||||
PsetData.load(ifc_file, obj.BIMObjectProperties.ifc_definition_id)
|
PsetData.load(ifc_file, obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
|
||||||
|
|
||||||
class AddSlabOpening(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.add_slab_opening"
|
|
||||||
bl_label = "Add Slab Opening"
|
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
|
||||||
|
|
||||||
def _execute(self, context):
|
|
||||||
selected_objs = context.selected_objects
|
|
||||||
if len(selected_objs) == 0 or not context.active_object:
|
|
||||||
return {"FINISHED"}
|
|
||||||
slab_obj = context.active_object
|
|
||||||
if not slab_obj.BIMObjectProperties.ifc_definition_id:
|
|
||||||
return {"FINISHED"}
|
|
||||||
slab = IfcStore.get_file().by_id(slab_obj.BIMObjectProperties.ifc_definition_id)
|
|
||||||
local_location = slab_obj.matrix_world.inverted() @ context.scene.cursor.location
|
|
||||||
raycast = slab_obj.closest_point_on_mesh(local_location, distance=0.01)
|
|
||||||
if not raycast[0]:
|
|
||||||
return {"FINISHED"}
|
|
||||||
bpy.ops.mesh.primitive_cube_add(size=slab_obj.dimensions[2] * 2)
|
|
||||||
opening = context.selected_objects[0]
|
|
||||||
|
|
||||||
# Place the opening in the middle of the slab
|
|
||||||
global_location = slab_obj.matrix_world @ raycast[1]
|
|
||||||
normal = raycast[2]
|
|
||||||
normal.negate()
|
|
||||||
global_normal = slab_obj.matrix_world.to_quaternion() @ normal
|
|
||||||
opening.location = global_location + (global_normal * (slab_obj.dimensions[2] / 2))
|
|
||||||
|
|
||||||
opening.rotation_euler = slab_obj.rotation_euler
|
|
||||||
opening.name = "Opening"
|
|
||||||
bpy.ops.bim.add_opening(opening=opening.name, obj=slab_obj.name)
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class DumbSlabGenerator:
|
class DumbSlabGenerator:
|
||||||
def __init__(self, relating_type):
|
def __init__(self, relating_type):
|
||||||
self.relating_type = relating_type
|
self.relating_type = relating_type
|
||||||
|
|||||||
@@ -41,42 +41,6 @@ def mode_callback(obj, data):
|
|||||||
bm.free()
|
bm.free()
|
||||||
|
|
||||||
|
|
||||||
class AddWallOpening(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.add_wall_opening"
|
|
||||||
bl_label = "Add Wall Opening"
|
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
|
||||||
|
|
||||||
def _execute(self, context):
|
|
||||||
selected_objs = context.selected_objects
|
|
||||||
if len(selected_objs) == 0 or not context.active_object:
|
|
||||||
return {"FINISHED"}
|
|
||||||
wall_obj = context.active_object
|
|
||||||
if not wall_obj.BIMObjectProperties.ifc_definition_id:
|
|
||||||
return {"FINISHED"}
|
|
||||||
wall = IfcStore.get_file().by_id(wall_obj.BIMObjectProperties.ifc_definition_id)
|
|
||||||
local_location = wall_obj.matrix_world.inverted() @ context.scene.cursor.location
|
|
||||||
raycast = wall_obj.closest_point_on_mesh(local_location, distance=0.01)
|
|
||||||
if not raycast[0]:
|
|
||||||
return {"FINISHED"}
|
|
||||||
bpy.ops.mesh.primitive_cube_add(size=wall_obj.dimensions[1] * 2)
|
|
||||||
opening = bpy.context.selected_objects[0]
|
|
||||||
|
|
||||||
# Place the opening in the middle of the wall
|
|
||||||
global_location = wall_obj.matrix_world @ raycast[1]
|
|
||||||
normal = raycast[2]
|
|
||||||
normal.negate()
|
|
||||||
global_normal = wall_obj.matrix_world.to_quaternion() @ normal
|
|
||||||
opening.location = global_location + (global_normal * (wall_obj.dimensions[1] / 2))
|
|
||||||
|
|
||||||
opening.rotation_euler = wall_obj.rotation_euler
|
|
||||||
opening.name = "Opening"
|
|
||||||
bpy.ops.bim.add_opening(opening=opening.name, obj=wall_obj.name)
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class JoinWall(bpy.types.Operator):
|
class JoinWall(bpy.types.Operator):
|
||||||
bl_idname = "bim.join_wall"
|
bl_idname = "bim.join_wall"
|
||||||
bl_label = "Join Wall"
|
bl_label = "Join Wall"
|
||||||
|
|||||||
@@ -64,14 +64,6 @@ 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="Split", icon="EVENT_S")
|
row.label(text="Split", icon="EVENT_S")
|
||||||
row = layout.row(align=True)
|
|
||||||
row.label(text="", icon="EVENT_SHIFT")
|
|
||||||
row.label(text="Opening", icon="EVENT_O")
|
|
||||||
|
|
||||||
if props.ifc_class == "IfcSlabType":
|
|
||||||
row = layout.row(align=True)
|
|
||||||
row.label(text="", icon="EVENT_SHIFT")
|
|
||||||
row.label(text="Opening", icon="EVENT_O")
|
|
||||||
|
|
||||||
if props.ifc_class in ["IfcColumnType", "IfcBeamType", "IfcMemberType"]:
|
if props.ifc_class in ["IfcColumnType", "IfcBeamType", "IfcMemberType"]:
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
@@ -80,6 +72,10 @@ class BimTool(WorkSpaceTool):
|
|||||||
row.label(text="", icon="EVENT_SHIFT")
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
row.label(text="Extend", icon="EVENT_E")
|
row.label(text="Extend", icon="EVENT_E")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
|
row.label(text="Opening", icon="EVENT_O")
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.label(text="Align")
|
row.label(text="Align")
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
@@ -142,10 +138,7 @@ class Hotkey(bpy.types.Operator):
|
|||||||
bpy.ops.bim.align_product(align_type="NEGATIVE")
|
bpy.ops.bim.align_product(align_type="NEGATIVE")
|
||||||
|
|
||||||
def hotkey_S_O(self):
|
def hotkey_S_O(self):
|
||||||
if self.props.ifc_class == "IfcWallType":
|
bpy.ops.bim.add_element_opening()
|
||||||
bpy.ops.bim.add_wall_opening()
|
|
||||||
elif self.props.ifc_class == "IfcSlabType":
|
|
||||||
bpy.ops.bim.add_slab_opening()
|
|
||||||
|
|
||||||
def hotkey_A_D(self):
|
def hotkey_A_D(self):
|
||||||
bpy.ops.bim.toggle_decomposition_parenting()
|
bpy.ops.bim.toggle_decomposition_parenting()
|
||||||
|
|||||||
Reference in New Issue
Block a user