mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +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
|
||||
|
||||
classes = (
|
||||
operator.AddBoundary,
|
||||
operator.ColourByRelatedBuildingElement,
|
||||
operator.DisableEditingBoundary,
|
||||
operator.DisableEditingBoundaryGeometry,
|
||||
|
||||
@@ -20,11 +20,14 @@ import bpy
|
||||
import bmesh
|
||||
import mathutils
|
||||
import logging
|
||||
import numpy as np
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim.tool as tool
|
||||
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.module.model.decorator import ProfileDecorator
|
||||
from blenderbim.bim.module.boundary.decorator import BoundaryDecorator
|
||||
@@ -485,9 +488,9 @@ class ShowBoundaries(bpy.types.Operator, tool.Ifc.Operator):
|
||||
continue
|
||||
if tool.Ifc.is_moved(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 []:
|
||||
boundary_obj = loader.load_boundary(rel, context.active_object)
|
||||
boundary_obj = loader.load_boundary(rel, obj)
|
||||
new = props.boundaries.add()
|
||||
new.obj = boundary_obj
|
||||
BoundaryDecorator.install(bpy.context)
|
||||
@@ -521,3 +524,108 @@ class HideBoundaries(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bpy.data.objects.remove(boundary_obj)
|
||||
context.scene.BIMBoundaryProperties.boundaries.clear()
|
||||
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": []}),
|
||||
# ("bim.sync_modeling", {"type": 'MOUSEMOVE', "value": 'ANY'}, {"properties": []}),
|
||||
("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": "E", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_E")]}),
|
||||
("bim.hotkey", {"type": "F", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_F")]}),
|
||||
@@ -261,6 +262,11 @@ class BimToolUI:
|
||||
else:
|
||||
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["has_visible_openings"]:
|
||||
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":
|
||||
bpy.ops.bim.extend_profile(join_type="V")
|
||||
|
||||
def hotkey_S_B(self):
|
||||
bpy.ops.bim.add_boundary()
|
||||
|
||||
def hotkey_S_O(self):
|
||||
if len(bpy.context.selected_objects) == 2:
|
||||
bpy.ops.bim.add_opening()
|
||||
|
||||
Reference in New Issue
Block a user