mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 15:08:51 +00:00
See #1676. You can now add basic space boundaries with the BIM Tool with Shift-B, either by selecting both a space and an element (e.g. wall) or selecting a space and placing the 3D cursor on a face.
This commit is contained in:
@@ -20,6 +20,7 @@ import bpy
|
|||||||
from . import ui, operator, prop
|
from . import ui, operator, prop
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
|
operator.AddBoundary,
|
||||||
operator.ColourByRelatedBuildingElement,
|
operator.ColourByRelatedBuildingElement,
|
||||||
operator.DisableEditingBoundary,
|
operator.DisableEditingBoundary,
|
||||||
operator.DisableEditingBoundaryGeometry,
|
operator.DisableEditingBoundaryGeometry,
|
||||||
|
|||||||
@@ -20,11 +20,14 @@ import bpy
|
|||||||
import bmesh
|
import bmesh
|
||||||
import mathutils
|
import mathutils
|
||||||
import logging
|
import logging
|
||||||
|
import numpy as np
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import ifcopenshell.util.placement
|
import ifcopenshell.util.placement
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import blenderbim.bim.import_ifc as import_ifc
|
import blenderbim.bim.import_ifc as import_ifc
|
||||||
|
from math import pi, inf
|
||||||
|
from mathutils import Vector
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.model.decorator import ProfileDecorator
|
from blenderbim.bim.module.model.decorator import ProfileDecorator
|
||||||
from blenderbim.bim.module.boundary.decorator import BoundaryDecorator
|
from blenderbim.bim.module.boundary.decorator import BoundaryDecorator
|
||||||
@@ -485,9 +488,9 @@ class ShowBoundaries(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
continue
|
continue
|
||||||
if tool.Ifc.is_moved(obj):
|
if tool.Ifc.is_moved(obj):
|
||||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||||
element = tool.Ifc.get_entity(context.active_object)
|
element = tool.Ifc.get_entity(obj)
|
||||||
for rel in element.BoundedBy or []:
|
for rel in element.BoundedBy or []:
|
||||||
boundary_obj = loader.load_boundary(rel, context.active_object)
|
boundary_obj = loader.load_boundary(rel, obj)
|
||||||
new = props.boundaries.add()
|
new = props.boundaries.add()
|
||||||
new.obj = boundary_obj
|
new.obj = boundary_obj
|
||||||
BoundaryDecorator.install(bpy.context)
|
BoundaryDecorator.install(bpy.context)
|
||||||
@@ -521,3 +524,108 @@ class HideBoundaries(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bpy.data.objects.remove(boundary_obj)
|
bpy.data.objects.remove(boundary_obj)
|
||||||
context.scene.BIMBoundaryProperties.boundaries.clear()
|
context.scene.BIMBoundaryProperties.boundaries.clear()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.add_boundary"
|
||||||
|
bl_label = "Add Boundary"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
relating_space = None
|
||||||
|
related_building_element = None
|
||||||
|
relating_space_obj = None
|
||||||
|
related_building_element_obj = None
|
||||||
|
|
||||||
|
objs = context.selected_objects
|
||||||
|
if len(objs) == 2:
|
||||||
|
# The user may select two objects, a space and its related building element
|
||||||
|
for obj in objs:
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
if not element:
|
||||||
|
continue
|
||||||
|
if element.is_a("IfcSpace"):
|
||||||
|
relating_space = element
|
||||||
|
relating_space_obj = obj
|
||||||
|
else:
|
||||||
|
related_building_element = element
|
||||||
|
related_building_element_obj = obj
|
||||||
|
elif len(objs) == 1:
|
||||||
|
# Optionally the user may select just the space, and the building element shall be auto-detected
|
||||||
|
|
||||||
|
def msg(self, context):
|
||||||
|
self.layout.label(text="NO ACTIVE STOREY")
|
||||||
|
|
||||||
|
element = tool.Ifc.get_entity(objs[0])
|
||||||
|
if element.is_a("IfcSpace"):
|
||||||
|
relating_space = element
|
||||||
|
relating_space_obj = objs[0]
|
||||||
|
|
||||||
|
target = bpy.context.scene.cursor.location
|
||||||
|
|
||||||
|
collection = context.view_layer.active_layer_collection.collection
|
||||||
|
collection_obj = bpy.data.objects.get(collection.name)
|
||||||
|
if not collection_obj:
|
||||||
|
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||||
|
return
|
||||||
|
spatial_element = tool.Ifc.get_entity(collection_obj)
|
||||||
|
if not spatial_element:
|
||||||
|
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||||
|
return
|
||||||
|
|
||||||
|
for subelement in ifcopenshell.util.element.get_decomposition(spatial_element):
|
||||||
|
if not (subelement.is_a("IfcWall") or subelement.is_a("IfcSlab")):
|
||||||
|
continue
|
||||||
|
obj = tool.Ifc.get_object(subelement)
|
||||||
|
if obj:
|
||||||
|
raycast = obj.closest_point_on_mesh(obj.matrix_world.inverted() @ target, distance=0.1)
|
||||||
|
if raycast[0]:
|
||||||
|
related_building_element = subelement
|
||||||
|
related_building_element_obj = obj
|
||||||
|
break
|
||||||
|
|
||||||
|
if not relating_space or not related_building_element:
|
||||||
|
return
|
||||||
|
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.from_mesh(relating_space_obj.data)
|
||||||
|
bmesh.ops.dissolve_limit(bm, angle_limit=pi * 2 / 360, verts=bm.verts, edges=bm.edges)
|
||||||
|
|
||||||
|
target_distance = inf
|
||||||
|
target_face = None
|
||||||
|
for face in bm.faces:
|
||||||
|
centroid = relating_space_obj.matrix_world @ face.calc_center_median()
|
||||||
|
raycast = related_building_element_obj.closest_point_on_mesh(related_building_element_obj.matrix_world.inverted() @ centroid, distance=1)
|
||||||
|
if raycast[0]:
|
||||||
|
distance = (related_building_element_obj.matrix_world @ raycast[1] - centroid).length
|
||||||
|
if distance < target_distance:
|
||||||
|
target_face = face
|
||||||
|
target_distance = distance
|
||||||
|
|
||||||
|
if not target_face:
|
||||||
|
return
|
||||||
|
|
||||||
|
bm2 = bmesh.new()
|
||||||
|
bm2.faces.new([bm2.verts.new(v.co.copy()) for v in target_face.verts])
|
||||||
|
mesh = bpy.data.meshes.new("Boundary")
|
||||||
|
bm2.to_mesh(mesh)
|
||||||
|
|
||||||
|
bm2.free()
|
||||||
|
bm.free()
|
||||||
|
|
||||||
|
obj = bpy.data.objects.new("Boundary", mesh)
|
||||||
|
obj.matrix_world = relating_space_obj.matrix_world.copy()
|
||||||
|
bpy.context.scene.collection.objects.link(obj)
|
||||||
|
surface = tool.Model.export_surface(obj)
|
||||||
|
connection_geometry = tool.Ifc.get().createIfcConnectionSurfaceGeometry(surface)
|
||||||
|
|
||||||
|
boundary = tool.Ifc.run("root.create_entity", ifc_class="IfcRelSpaceBoundary")
|
||||||
|
boundary.RelatingSpace = relating_space
|
||||||
|
boundary.RelatedBuildingElement = related_building_element
|
||||||
|
boundary.ConnectionGeometry = connection_geometry
|
||||||
|
|
||||||
|
bpy.data.objects.remove(obj)
|
||||||
|
|
||||||
|
bpy.ops.bim.show_boundaries()
|
||||||
|
obj = tool.Ifc.get_object(boundary)
|
||||||
|
obj.select_set(True)
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class BimTool(WorkSpaceTool):
|
|||||||
# ("mesh.add_wall", {"type": 'LEFTMOUSE', "value": 'PRESS'}, {"properties": []}),
|
# ("mesh.add_wall", {"type": 'LEFTMOUSE', "value": 'PRESS'}, {"properties": []}),
|
||||||
# ("bim.sync_modeling", {"type": 'MOUSEMOVE', "value": 'ANY'}, {"properties": []}),
|
# ("bim.sync_modeling", {"type": 'MOUSEMOVE', "value": 'ANY'}, {"properties": []}),
|
||||||
("bim.hotkey", {"type": "A", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_A")]}),
|
("bim.hotkey", {"type": "A", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_A")]}),
|
||||||
|
("bim.hotkey", {"type": "B", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_B")]}),
|
||||||
("bim.hotkey", {"type": "C", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_C")]}),
|
("bim.hotkey", {"type": "C", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_C")]}),
|
||||||
("bim.hotkey", {"type": "E", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_E")]}),
|
("bim.hotkey", {"type": "E", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_E")]}),
|
||||||
("bim.hotkey", {"type": "F", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_F")]}),
|
("bim.hotkey", {"type": "F", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_F")]}),
|
||||||
@@ -261,6 +262,11 @@ class BimToolUI:
|
|||||||
else:
|
else:
|
||||||
row.operator("bim.add_potential_opening", text="Add Void")
|
row.operator("bim.add_potential_opening", text="Add Void")
|
||||||
|
|
||||||
|
row = cls.layout.row(align=True)
|
||||||
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
|
row.label(text="", icon="EVENT_B")
|
||||||
|
row.operator("bim.add_boundary", text="Add Boundary")
|
||||||
|
|
||||||
if AuthoringData.data["is_voidable_element"]:
|
if AuthoringData.data["is_voidable_element"]:
|
||||||
if AuthoringData.data["has_visible_openings"]:
|
if AuthoringData.data["has_visible_openings"]:
|
||||||
row.operator("bim.edit_openings", icon="CHECKMARK", text="")
|
row.operator("bim.edit_openings", icon="CHECKMARK", text="")
|
||||||
@@ -570,6 +576,9 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
elif self.active_material_usage == "PROFILE":
|
elif self.active_material_usage == "PROFILE":
|
||||||
bpy.ops.bim.extend_profile(join_type="V")
|
bpy.ops.bim.extend_profile(join_type="V")
|
||||||
|
|
||||||
|
def hotkey_S_B(self):
|
||||||
|
bpy.ops.bim.add_boundary()
|
||||||
|
|
||||||
def hotkey_S_O(self):
|
def hotkey_S_O(self):
|
||||||
if len(bpy.context.selected_objects) == 2:
|
if len(bpy.context.selected_objects) == 2:
|
||||||
bpy.ops.bim.add_opening()
|
bpy.ops.bim.add_opening()
|
||||||
|
|||||||
Reference in New Issue
Block a user