mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 14:41:25 +00:00
Merge pull request #3838 from maxfb87/covering_tool
Covering tool (see #3754)
This commit is contained in:
@@ -38,3 +38,4 @@ import ifcopenshell
|
|||||||
class BIMCoveringProperties(PropertyGroup):
|
class BIMCoveringProperties(PropertyGroup):
|
||||||
pass
|
pass
|
||||||
# depth: bpy.props.FloatProperty(name="Depth", default=0.1, subtype="DISTANCE", description="Flooring depth")
|
# depth: bpy.props.FloatProperty(name="Depth", default=0.1, subtype="DISTANCE", description="Flooring depth")
|
||||||
|
depth: bpy.props.FloatProperty(name="ceiling_height", default=2.7, subtype="DISTANCE", description="Ceiling height")
|
||||||
|
|||||||
@@ -42,13 +42,14 @@ class CoveringTool(WorkSpaceTool):
|
|||||||
bl_description = "Create and edit coverings"
|
bl_description = "Create and edit coverings"
|
||||||
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.covering")
|
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.covering")
|
||||||
bl_widget = None
|
bl_widget = None
|
||||||
|
ifc_element_type = "IfcCoveringType"
|
||||||
bl_keymap = tool.Blender.get_default_selection_keypmap() + (
|
bl_keymap = tool.Blender.get_default_selection_keypmap() + (
|
||||||
("bim.covering_hotkey", {"type": "A", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_A")]}),
|
("bim.covering_hotkey", {"type": "A", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_A")]}),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def draw_settings(cls, context, layout, ws_tool):
|
def draw_settings(cls, context, layout, ws_tool):
|
||||||
CoveringToolUI.draw(context, layout, ifc_element_type="IfcCoveringType")
|
CoveringToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||||
|
|
||||||
def add_layout_hotkey(layout, text, hotkey, description):
|
def add_layout_hotkey(layout, text, hotkey, description):
|
||||||
args = ["covering", layout, text, hotkey, description]
|
args = ["covering", layout, text, hotkey, description]
|
||||||
@@ -71,7 +72,6 @@ class CoveringToolUI:
|
|||||||
elif AuthoringData.data["ifc_element_type"] != ifc_element_type:
|
elif AuthoringData.data["ifc_element_type"] != ifc_element_type:
|
||||||
AuthoringData.load(ifc_element_type)
|
AuthoringData.load(ifc_element_type)
|
||||||
|
|
||||||
|
|
||||||
if context.region.type == "TOOL_HEADER":
|
if context.region.type == "TOOL_HEADER":
|
||||||
cls.draw_header_interface()
|
cls.draw_header_interface()
|
||||||
elif context.region.type in ("UI", "WINDOW"):
|
elif context.region.type in ("UI", "WINDOW"):
|
||||||
@@ -93,8 +93,33 @@ class CoveringToolUI:
|
|||||||
row.label(text="", icon="EVENT_A")
|
row.label(text="", icon="EVENT_A")
|
||||||
active_obj = bpy.context.active_object
|
active_obj = bpy.context.active_object
|
||||||
element = tool.Ifc.get_entity(active_obj)
|
element = tool.Ifc.get_entity(active_obj)
|
||||||
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
|
||||||
op = row.operator("bim.add_instance_flooring_coverings_from_walls")
|
if AuthoringData.data["predefined_type"] == "FLOORING":
|
||||||
|
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||||
|
op = row.operator("bim.add_instance_flooring_coverings_from_walls")
|
||||||
|
# PLEASE KEEP COMMENTS AS A REMINDER
|
||||||
|
# elif element and bpy.context.selected_objects and element.is_a("IfcSpace"):
|
||||||
|
# op. = row.operator("bim.add_istance_flooring_from_spaces"):
|
||||||
|
else:
|
||||||
|
# op = row.operator("bim.add_instance_flooring_from_cursor")
|
||||||
|
op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||||
|
op.from_invoke = True
|
||||||
|
if cls.props.relating_type_id.isnumeric():
|
||||||
|
op.relating_type_id = int(cls.props.relating_type_id)
|
||||||
|
|
||||||
|
# elif AuthoringData.data["predefined_type"] == "CEILING":
|
||||||
|
# row = cls.layout.row(align=True)
|
||||||
|
# row.prop(data=cls.props, property="ceiling_height", text="ceiling height")
|
||||||
|
# if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||||
|
# op = row.operator("bim.add_instance_ceiling_coverings_from_walls")
|
||||||
|
# elif element and bpy.context.selected_objects and element.is_a("IfcSpace"):
|
||||||
|
# op. = row.operator("bim.add_istance_flooring_from_spaces"):
|
||||||
|
# else:
|
||||||
|
# op = row.operator("bim.add_instance_ceiling_from_cursor")
|
||||||
|
# op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||||
|
# op.from_invoke = True
|
||||||
|
# if cls.props.relating_type_id.isnumeric():
|
||||||
|
# op.relating_type_id = int(cls.props.relating_type_id)
|
||||||
else:
|
else:
|
||||||
op = row.operator("bim.add_constr_type_instance", text="Add")
|
op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||||
op.from_invoke = True
|
op.from_invoke = True
|
||||||
@@ -159,8 +184,11 @@ class Hotkey(bpy.types.Operator, Operator):
|
|||||||
def hotkey_S_A(self):
|
def hotkey_S_A(self):
|
||||||
active_obj = bpy.context.active_object
|
active_obj = bpy.context.active_object
|
||||||
element = tool.Ifc.get_entity(active_obj)
|
element = tool.Ifc.get_entity(active_obj)
|
||||||
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
if AuthoringData.data["predefined_type"] == "FLOORING":
|
||||||
bpy.ops.bim.add_instance_flooring_coverings_from_walls()
|
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||||
|
bpy.ops.bim.add_instance_flooring_coverings_from_walls()
|
||||||
|
else:
|
||||||
|
bpy.ops.bim.add_constr_type_instance()
|
||||||
else:
|
else:
|
||||||
bpy.ops.bim.add_constr_type_instance()
|
bpy.ops.bim.add_constr_type_instance()
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import blenderbim.core.covering as core
|
|||||||
|
|
||||||
class AddInstanceFlooringCoveringsFromWalls(bpy.types.Operator, tool.Ifc.Operator):
|
class AddInstanceFlooringCoveringsFromWalls(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "bim.add_instance_flooring_coverings_from_walls"
|
bl_idname = "bim.add_instance_flooring_coverings_from_walls"
|
||||||
bl_label = "Add Typed Covering From Walls"
|
bl_label = "Add Flooring From Walls"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Add instance flooring coverings from selected walls. The active object must be a wall and layered vertically"
|
bl_description = "Add instance flooring coverings from selected walls. The active object must be a wall and layered vertically"
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class AuthoringData:
|
|||||||
cls.data["ifc_element_type"] = cls.ifc_element_type
|
cls.data["ifc_element_type"] = cls.ifc_element_type
|
||||||
cls.data["ifc_classes"] = cls.ifc_classes()
|
cls.data["ifc_classes"] = cls.ifc_classes()
|
||||||
cls.data["relating_type_id"] = cls.relating_type_id() # only after .ifc_classes()
|
cls.data["relating_type_id"] = cls.relating_type_id() # only after .ifc_classes()
|
||||||
|
cls.data["predefined_type"] = cls.predefined_type()
|
||||||
cls.data["type_class"] = cls.type_class()
|
cls.data["type_class"] = cls.type_class()
|
||||||
|
|
||||||
# only after .type_class()
|
# only after .type_class()
|
||||||
@@ -247,6 +248,13 @@ class AuthoringData:
|
|||||||
return [(str(e.id()), e.Name or "Unnamed", e.Description or "") for e in results]
|
return [(str(e.id()), e.Name or "Unnamed", e.Description or "") for e in results]
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def predefined_type(cls):
|
||||||
|
relating_type_id = cls.props.relating_type_id
|
||||||
|
relating_type = tool.Ifc.get().by_id(int(relating_type_id))
|
||||||
|
predefined_type = relating_type.PredefinedType
|
||||||
|
return predefined_type
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def selected_material_usages(cls):
|
def selected_material_usages(cls):
|
||||||
selected_usages = {}
|
selected_usages = {}
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ def update_type_class(self, context):
|
|||||||
def update_relating_type_id(self, context):
|
def update_relating_type_id(self, context):
|
||||||
AuthoringData.data["relating_type_id"] = AuthoringData.relating_type_id()
|
AuthoringData.data["relating_type_id"] = AuthoringData.relating_type_id()
|
||||||
AuthoringData.data["type_thumbnail"] = AuthoringData.type_thumbnail()
|
AuthoringData.data["type_thumbnail"] = AuthoringData.type_thumbnail()
|
||||||
|
AuthoringData.data["predefined_type"] = AuthoringData.predefined_type()
|
||||||
|
|
||||||
|
|
||||||
def update_type_page(self, context):
|
def update_type_page(self, context):
|
||||||
|
|||||||
@@ -50,13 +50,7 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
def msg(self, context):
|
def msg(self, context):
|
||||||
self.layout.label(text="NO ACTIVE STOREY")
|
self.layout.label(text="NO ACTIVE STOREY")
|
||||||
|
|
||||||
props = context.scene.BIMModelProperties
|
# props = context.scene.BIMModelProperties
|
||||||
relating_type_id = props.relating_type_id
|
|
||||||
relating_type = None
|
|
||||||
if relating_type_id:
|
|
||||||
relating_type = tool.Ifc.get().by_id(int(relating_type_id))
|
|
||||||
if not relating_type.is_a("IfcSpaceType"):
|
|
||||||
relating_type = None
|
|
||||||
|
|
||||||
collection = context.view_layer.active_layer_collection.collection
|
collection = context.view_layer.active_layer_collection.collection
|
||||||
collection_obj = collection.BIMCollectionProperties.obj
|
collection_obj = collection.BIMCollectionProperties.obj
|
||||||
@@ -68,151 +62,7 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||||
return
|
return
|
||||||
|
|
||||||
active_obj = bpy.context.active_object
|
core.generate_space(tool.Ifc, tool.Spatial, tool.Model, tool.Type)
|
||||||
element = None
|
|
||||||
if bpy.context.selected_objects and active_obj:
|
|
||||||
element = tool.Ifc.get_entity(active_obj)
|
|
||||||
mat = active_obj.matrix_world
|
|
||||||
local_bbox_center = 0.125 * sum((Vector(b) for b in active_obj.bound_box), Vector())
|
|
||||||
global_bbox_center = mat @ local_bbox_center
|
|
||||||
x = global_bbox_center.x
|
|
||||||
y = global_bbox_center.y
|
|
||||||
z = (mat @ Vector(active_obj.bound_box[0])).z
|
|
||||||
|
|
||||||
h = active_obj.dimensions.z
|
|
||||||
else:
|
|
||||||
x, y = context.scene.cursor.location.xy
|
|
||||||
z = collection_obj.matrix_world.translation.z
|
|
||||||
mat = Matrix()
|
|
||||||
mat.translation = (x, y, z)
|
|
||||||
h = 3
|
|
||||||
|
|
||||||
calculation_rl = context.scene.BIMModelProperties.rl3
|
|
||||||
self.cut_point = collection_obj.matrix_world.translation.copy() + Vector((0, 0, calculation_rl))
|
|
||||||
self.cut_normal = Vector((0, 0, 1))
|
|
||||||
boundary_lines = []
|
|
||||||
|
|
||||||
gross_settings = ifcopenshell.geom.settings()
|
|
||||||
gross_settings.set(gross_settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
|
||||||
|
|
||||||
for obj in bpy.context.visible_objects:
|
|
||||||
visible_element = tool.Ifc.get_entity(obj)
|
|
||||||
|
|
||||||
if (
|
|
||||||
not visible_element
|
|
||||||
or obj.type != "MESH"
|
|
||||||
or not self.is_bounding_class(visible_element)
|
|
||||||
or not tool.Drawing.is_intersecting_plane(obj, self.cut_point, self.cut_normal)
|
|
||||||
):
|
|
||||||
continue
|
|
||||||
|
|
||||||
old_mesh = None
|
|
||||||
if visible_element.HasOpenings:
|
|
||||||
new_mesh = self.create_mesh(ifcopenshell.geom.create_shape(gross_settings, visible_element))
|
|
||||||
old_mesh = obj.data
|
|
||||||
obj.data = new_mesh
|
|
||||||
|
|
||||||
local_cut_point = obj.matrix_world.inverted() @ self.cut_point
|
|
||||||
local_cut_normal = obj.matrix_world.inverted().to_quaternion() @ self.cut_normal
|
|
||||||
verts, edges = tool.Drawing.bisect_mesh_with_plane(obj, local_cut_point, local_cut_normal)
|
|
||||||
|
|
||||||
if old_mesh:
|
|
||||||
obj.data = old_mesh
|
|
||||||
bpy.data.meshes.remove(new_mesh)
|
|
||||||
|
|
||||||
for edge in edges or []:
|
|
||||||
boundary_lines.append(
|
|
||||||
shapely.LineString(
|
|
||||||
[Vector((round(x, 3) for x in verts[edge[0]])), Vector((round(x, 3) for x in verts[edge[1]]))]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
|
|
||||||
closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
|
|
||||||
|
|
||||||
space_polygon = None
|
|
||||||
for polygon in closed_polygons.geoms:
|
|
||||||
if shapely.contains_xy(polygon, x, y):
|
|
||||||
space_polygon = shapely.force_3d(polygon)
|
|
||||||
|
|
||||||
if not space_polygon:
|
|
||||||
return
|
|
||||||
|
|
||||||
bm = bmesh.new()
|
|
||||||
bm.verts.index_update()
|
|
||||||
bm.edges.index_update()
|
|
||||||
|
|
||||||
mat_invert = mat.inverted()
|
|
||||||
new_verts = [bm.verts.new(mat_invert @ Vector([v[0], v[1], z])) for v in space_polygon.exterior.coords[0:-1]]
|
|
||||||
[bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
|
|
||||||
bm.edges.new((new_verts[len(new_verts) - 1], new_verts[0]))
|
|
||||||
|
|
||||||
for interior in space_polygon.interiors:
|
|
||||||
new_verts = [bm.verts.new(mat_invert @ Vector([v[0], v[1], z])) for v in interior.coords[0:-1]]
|
|
||||||
[bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
|
|
||||||
bm.edges.new((new_verts[len(new_verts) - 1], new_verts[0]))
|
|
||||||
|
|
||||||
bm.verts.index_update()
|
|
||||||
bm.edges.index_update()
|
|
||||||
|
|
||||||
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=1e-5)
|
|
||||||
bmesh.ops.triangle_fill(bm, edges=bm.edges)
|
|
||||||
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 5, verts=bm.verts, edges=bm.edges)
|
|
||||||
|
|
||||||
extrusion = bmesh.ops.extrude_face_region(bm, geom=bm.faces)
|
|
||||||
extruded_verts = [g for g in extrusion["geom"] if isinstance(g, bmesh.types.BMVert)]
|
|
||||||
bmesh.ops.translate(bm, vec=[0.0, 0.0, h], verts=extruded_verts)
|
|
||||||
|
|
||||||
mesh = bpy.data.meshes.new(name="Space")
|
|
||||||
bm.to_mesh(mesh)
|
|
||||||
bm.free()
|
|
||||||
|
|
||||||
if element and element.is_a("IfcSpace"):
|
|
||||||
mesh.name = active_obj.data.name
|
|
||||||
mesh.BIMMeshProperties.ifc_definition_id = active_obj.data.BIMMeshProperties.ifc_definition_id
|
|
||||||
tool.Geometry.change_object_data(active_obj, mesh, is_global=True)
|
|
||||||
tool.Ifc.edit(active_obj)
|
|
||||||
else:
|
|
||||||
if relating_type:
|
|
||||||
name = tool.Model.generate_occurrence_name(relating_type, "IfcSpace")
|
|
||||||
else:
|
|
||||||
name = "Space"
|
|
||||||
obj = bpy.data.objects.new("Space", mesh)
|
|
||||||
obj.matrix_world = mat
|
|
||||||
collection.objects.link(obj)
|
|
||||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcSpace")
|
|
||||||
element = tool.Ifc.get_entity(obj)
|
|
||||||
if relating_type:
|
|
||||||
blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type)
|
|
||||||
|
|
||||||
def is_bounding_class(self, element):
|
|
||||||
for ifc_class in ["IfcWall", "IfcColumn", "IfcMember", "IfcVirtualElement", "IfcPlate"]:
|
|
||||||
if element.is_a(ifc_class):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def create_mesh(self, shape):
|
|
||||||
geometry = shape.geometry
|
|
||||||
mesh = bpy.data.meshes.new("tmp")
|
|
||||||
verts = geometry.verts
|
|
||||||
if geometry.faces:
|
|
||||||
num_vertices = len(verts) // 3
|
|
||||||
total_faces = len(geometry.faces)
|
|
||||||
loop_start = range(0, total_faces, 3)
|
|
||||||
num_loops = total_faces // 3
|
|
||||||
loop_total = [3] * num_loops
|
|
||||||
num_vertex_indices = len(geometry.faces)
|
|
||||||
|
|
||||||
mesh.vertices.add(num_vertices)
|
|
||||||
mesh.vertices.foreach_set("co", verts)
|
|
||||||
mesh.loops.add(num_vertex_indices)
|
|
||||||
mesh.loops.foreach_set("vertex_index", geometry.faces)
|
|
||||||
mesh.polygons.add(num_loops)
|
|
||||||
mesh.polygons.foreach_set("loop_start", loop_start)
|
|
||||||
mesh.polygons.foreach_set("loop_total", loop_total)
|
|
||||||
mesh.polygons.foreach_set("use_smooth", [0] * total_faces)
|
|
||||||
mesh.update()
|
|
||||||
return mesh
|
|
||||||
|
|
||||||
|
|
||||||
class GenerateSpacesFromWalls(bpy.types.Operator, tool.Ifc.Operator):
|
class GenerateSpacesFromWalls(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
@@ -233,6 +83,26 @@ class GenerateSpacesFromWalls(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
# walls (i.e. prismatic) in the active object storey.
|
# walls (i.e. prismatic) in the active object storey.
|
||||||
# In order to run, the active object must be a wall and
|
# In order to run, the active object must be a wall and
|
||||||
# there must be selected walls
|
# there must be selected walls
|
||||||
|
|
||||||
|
active_obj = bpy.context.active_object
|
||||||
|
element = tool.Ifc.get_entity(active_obj)
|
||||||
|
container = tool.Spatial.get_container(element)
|
||||||
|
|
||||||
|
if not active_obj:
|
||||||
|
self.report({"ERROR"}, "No active object. Please select a wall")
|
||||||
|
return
|
||||||
|
|
||||||
|
element = tool.Ifc.get_entity(active_obj)
|
||||||
|
if element and not element.is_a("IfcWall"):
|
||||||
|
return self.report({"ERROR"}, "The active object is not a wall. Please select a wall.")
|
||||||
|
|
||||||
|
if not container:
|
||||||
|
self.report({"ERROR"}, "The wall is not contained.")
|
||||||
|
|
||||||
|
if not bpy.context.selected_objects:
|
||||||
|
self.report({"ERROR"}, "No selected objects found. Please select walls.")
|
||||||
|
return
|
||||||
|
|
||||||
core.generate_spaces_from_walls(tool.Ifc, tool.Spatial, tool.Collector)
|
core.generate_spaces_from_walls(tool.Ifc, tool.Spatial, tool.Collector)
|
||||||
|
|
||||||
class ToggleSpaceVisibility(bpy.types.Operator, tool.Ifc.Operator):
|
class ToggleSpaceVisibility(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import blenderbim.core
|
|
||||||
|
|
||||||
|
|
||||||
def reference_structure(ifc, spatial, structure=None, element=None):
|
def reference_structure(ifc, spatial, structure=None, element=None):
|
||||||
if spatial.can_reference(structure, element):
|
if spatial.can_reference(structure, element):
|
||||||
@@ -128,35 +126,57 @@ def select_decomposed_elements(spatial):
|
|||||||
spatial.select_products(spatial.get_decomposed_elements(container))
|
spatial.select_products(spatial.get_decomposed_elements(container))
|
||||||
|
|
||||||
|
|
||||||
# HERE STARTS SPATIAL TOOL
|
#HERE STARTS SPATIAL TOOL
|
||||||
|
def generate_space(ifc, spatial, model, Type):
|
||||||
|
active_obj = spatial.get_active_obj()
|
||||||
|
selected_objects = spatial.get_selected_objects()
|
||||||
|
element = None
|
||||||
|
relating_type_id = spatial.get_relating_type_id()
|
||||||
|
|
||||||
|
relating_type = None
|
||||||
|
if relating_type_id:
|
||||||
|
relating_type = ifc.get().by_id(int(relating_type_id))
|
||||||
|
if not relating_type.is_a("IfcSpaceType"):
|
||||||
|
relating_type = None
|
||||||
|
|
||||||
|
if selected_objects and active_obj:
|
||||||
|
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_active_obj(active_obj) ##mat
|
||||||
|
element = ifc.get_entity(active_obj)
|
||||||
|
|
||||||
|
else:
|
||||||
|
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_cursor() ##mat
|
||||||
|
|
||||||
|
|
||||||
|
space_polygon = spatial.get_space_polygon_from_context_visible_objects(x, y)
|
||||||
|
|
||||||
|
if not space_polygon:
|
||||||
|
return
|
||||||
|
|
||||||
|
bm = spatial.get_bmesh_from_polygon(space_polygon, h=h) ##mat
|
||||||
|
|
||||||
|
mesh = spatial.get_named_mesh_from_bmesh(name = "Space", bmesh = bm)
|
||||||
|
|
||||||
|
if element and element.is_a("IfcSpace"):
|
||||||
|
spatial.edit_active_space_obj_from_mesh(mesh)
|
||||||
|
else:
|
||||||
|
if relating_type:
|
||||||
|
name = model.generate_occurrence_name(relating_type, "IfcSpace")
|
||||||
|
else:
|
||||||
|
name = "Space"
|
||||||
|
|
||||||
|
obj = spatial.get_named_obj_from_mesh(name, mesh) ##mat
|
||||||
|
spatial.set_obj_origin_to_cursor_position(obj)
|
||||||
|
spatial.link_obj_to_active_collection(obj)
|
||||||
|
spatial.assign_ifcspace_class_to_obj(obj)
|
||||||
|
|
||||||
|
element = ifc.get_entity(obj)
|
||||||
|
|
||||||
|
if relating_type:
|
||||||
|
spatial.assign_relating_type_to_element(ifc, Type, element, relating_type)
|
||||||
|
|
||||||
def generate_spaces_from_walls(ifc, spatial, collector):
|
def generate_spaces_from_walls(ifc, spatial, collector):
|
||||||
import bpy
|
z = spatial.get_active_obj_z()
|
||||||
|
h = spatial.get_active_obj_height()
|
||||||
active_obj = bpy.context.active_object
|
|
||||||
element = ifc.get_entity(active_obj)
|
|
||||||
container = spatial.get_container(element)
|
|
||||||
|
|
||||||
if not active_obj:
|
|
||||||
self.report({"ERROR"}, "No active object. Please select a wall")
|
|
||||||
return
|
|
||||||
|
|
||||||
element = ifc.get_entity(active_obj)
|
|
||||||
if element and not element.is_a("IfcWall"):
|
|
||||||
return self.report({"ERROR"}, "The active object is not a wall. Please select a wall.")
|
|
||||||
|
|
||||||
if not container:
|
|
||||||
self.report({"ERROR"}, "The wall is not contained.")
|
|
||||||
|
|
||||||
if not bpy.context.selected_objects:
|
|
||||||
self.report({"ERROR"}, "No selected objects found. Please select walls.")
|
|
||||||
return
|
|
||||||
|
|
||||||
x, y, z = active_obj.matrix_world.translation.xyz
|
|
||||||
mat = active_obj.matrix_world
|
|
||||||
h = active_obj.dimensions.z
|
|
||||||
selected_objects = bpy.context.selected_objects
|
|
||||||
|
|
||||||
union = spatial.get_union_shape_from_selected_objects()
|
union = spatial.get_union_shape_from_selected_objects()
|
||||||
|
|
||||||
@@ -166,23 +186,17 @@ def generate_spaces_from_walls(ifc, spatial, collector):
|
|||||||
bm = spatial.get_bmesh_from_polygon(poly, h)
|
bm = spatial.get_bmesh_from_polygon(poly, h)
|
||||||
|
|
||||||
name = "Space" + str(i)
|
name = "Space" + str(i)
|
||||||
mesh = bpy.data.meshes.new(name=name)
|
|
||||||
bm.to_mesh(mesh)
|
|
||||||
bm.free()
|
|
||||||
|
|
||||||
obj = bpy.data.objects.new(name, mesh)
|
obj = spatial.get_named_obj_from_bmesh(name, bmesh = bm)
|
||||||
obj.matrix_world = mat
|
|
||||||
|
|
||||||
spatial.set_obj_origin_to_bboxcenter(obj)
|
spatial.set_obj_origin_to_bboxcenter(obj)
|
||||||
|
spatial.traslate_obj_to_z_location(obj, z)
|
||||||
|
|
||||||
if z != 0:
|
spatial.link_obj_to_active_collection(obj)
|
||||||
obj.location = obj.location + Vector((0, 0, z))
|
spatial.assign_ifcspace_class_to_obj(obj)
|
||||||
|
|
||||||
bpy.context.view_layer.active_layer_collection.collection.objects.link(obj)
|
|
||||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcSpace")
|
|
||||||
container_obj = ifc.get_object(container)
|
|
||||||
blenderbim.core.spatial.assign_container(ifc, collector, spatial, structure_obj=container_obj, element_obj=obj)
|
|
||||||
|
|
||||||
|
spatial.assign_container_to_obj(obj)
|
||||||
|
|
||||||
|
|
||||||
def toggle_space_visibility(ifc, spatial):
|
def toggle_space_visibility(ifc, spatial):
|
||||||
model = ifc.get()
|
model = ifc.get()
|
||||||
|
|||||||
@@ -809,6 +809,13 @@ class Spatial:
|
|||||||
def set_relative_object_matrix(cls, target_obj, relative_to_obj, matrix): pass
|
def set_relative_object_matrix(cls, target_obj, relative_to_obj, matrix): pass
|
||||||
def show_scene_objects(cls): pass
|
def show_scene_objects(cls): pass
|
||||||
#HERE STARTS SPATIAL TOOL
|
#HERE STARTS SPATIAL TOOL
|
||||||
|
def is_bounding_class(cls, visible_element): pass
|
||||||
|
def get_space_polygon_from_context_visible_objects(cls, x, y): pass
|
||||||
|
def get_boundary_lines_from_context_visible_objects(cls, visible_objects): pass
|
||||||
|
def get_gross_mesh_from_element(cls, visible_element): pass
|
||||||
|
def create_mesh_from_shape(cls, shape): pass
|
||||||
|
def get_x_y_z_h_mat_from_active_obj(cls, active_obj): pass
|
||||||
|
def get_x_y_z_h_mat_from_cursor(cls): pass
|
||||||
def get_union_shape_from_selected_objects(cls): pass
|
def get_union_shape_from_selected_objects(cls): pass
|
||||||
def get_boundary_elements(cls, selected_objects): pass
|
def get_boundary_elements(cls, selected_objects): pass
|
||||||
def get_polygons(cls, boundary_elements): pass
|
def get_polygons(cls, boundary_elements): pass
|
||||||
@@ -819,14 +826,24 @@ class Spatial:
|
|||||||
def get_buffered_poly_from_linear_ring(cls, linear_ring): pass
|
def get_buffered_poly_from_linear_ring(cls, linear_ring): pass
|
||||||
def get_bmesh_from_polygon(cls, poly, h): pass
|
def get_bmesh_from_polygon(cls, poly, h): pass
|
||||||
def get_named_obj_from_bmesh(cls, name, bmesh): pass
|
def get_named_obj_from_bmesh(cls, name, bmesh): pass
|
||||||
|
def get_named_obj_from_mesh(cls, name, mesh): pass
|
||||||
|
def get_named_mesh_from_bmesh(cls, name, bmesh): pass
|
||||||
|
def edit_active_space_obj_from_mesh(cls, mesh): pass
|
||||||
def set_obj_origin_to_bboxcenter(cls, obj): pass
|
def set_obj_origin_to_bboxcenter(cls, obj): pass
|
||||||
def get_active_obj_z(cls, obj): pass
|
def set_obj_origin_to_cursor_position(cls, obj): pass
|
||||||
|
def get_selected_objects(cls): pass
|
||||||
|
def get_active_obj(cls): pass
|
||||||
|
def get_active_obj_z(cls): pass
|
||||||
|
def get_active_obj_height(cls): pass
|
||||||
|
def get_relating_type_id(cls): pass
|
||||||
def traslate_obj_to_z_location(cls, obj): pass
|
def traslate_obj_to_z_location(cls, obj): pass
|
||||||
def link_obj_to_active_collection(cls, obj): pass
|
def link_obj_to_active_collection(cls, obj): pass
|
||||||
def get_2d_vertices_from_obj(cls, obj): pass
|
def get_2d_vertices_from_obj(cls, obj): pass
|
||||||
def assign_swept_area_outer_curve_from_2d_vertices(cls, obj, vertices): pass
|
def assign_swept_area_outer_curve_from_2d_vertices(cls, obj, vertices): pass
|
||||||
def get_body_representation(cls, obj): pass
|
def get_body_representation(cls, obj): pass
|
||||||
|
def assign_ifcspace_class_to_obj(cls, obj): pass
|
||||||
def assign_type_to_obj(cls, obj): pass
|
def assign_type_to_obj(cls, obj): pass
|
||||||
|
def assign_relating_type_to_element(cls, ifc, Type, element, relating_type): pass
|
||||||
def regen_obj_representation(cls, ifc, geometry, obj, body): pass
|
def regen_obj_representation(cls, ifc, geometry, obj, body): pass
|
||||||
def toggle_spaces_visibility_wired_and_textured(cls, spaces): pass
|
def toggle_spaces_visibility_wired_and_textured(cls, spaces): pass
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import bpy
|
|||||||
import bmesh
|
import bmesh
|
||||||
import shapely
|
import shapely
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import blenderbim.core.type
|
||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
import blenderbim.core.root
|
import blenderbim.core.root
|
||||||
import blenderbim.core.spatial
|
import blenderbim.core.spatial
|
||||||
@@ -270,6 +271,123 @@ class Spatial(blenderbim.core.tool.Spatial):
|
|||||||
|
|
||||||
#HERE STARTS SPATIAL TOOL
|
#HERE STARTS SPATIAL TOOL
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_bounding_class(cls, visible_element):
|
||||||
|
for ifc_class in ["IfcWall", "IfcColumn", "IfcMember", "IfcVirtualElement", "IfcPlate"]:
|
||||||
|
if visible_element.is_a(ifc_class):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_space_polygon_from_context_visible_objects(cls, x, y):
|
||||||
|
boundary_lines = cls.get_boundary_lines_from_context_visible_objects()
|
||||||
|
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
|
||||||
|
closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
|
||||||
|
space_polygon = None
|
||||||
|
for polygon in closed_polygons.geoms:
|
||||||
|
if shapely.contains_xy(polygon, x, y):
|
||||||
|
space_polygon = shapely.force_3d(polygon)
|
||||||
|
return space_polygon
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_boundary_lines_from_context_visible_objects(cls):
|
||||||
|
calculation_rl = bpy.context.scene.BIMModelProperties.rl3
|
||||||
|
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||||
|
collection_obj = collection.BIMCollectionProperties.obj
|
||||||
|
cut_point = collection_obj.matrix_world.translation.copy() + Vector((0, 0, calculation_rl))
|
||||||
|
cut_normal = Vector((0, 0, 1))
|
||||||
|
boundary_lines = []
|
||||||
|
|
||||||
|
for obj in bpy.context.visible_objects:
|
||||||
|
|
||||||
|
|
||||||
|
visible_element = tool.Ifc.get_entity(obj)
|
||||||
|
|
||||||
|
if (
|
||||||
|
not visible_element
|
||||||
|
or obj.type != "MESH"
|
||||||
|
or not cls.is_bounding_class(visible_element)
|
||||||
|
or not tool.Drawing.is_intersecting_plane(obj, cut_point, cut_normal)
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
old_mesh = None
|
||||||
|
if visible_element.HasOpenings:
|
||||||
|
new_mesh = cls.get_gross_mesh_from_element(visible_element)
|
||||||
|
old_mesh = obj.data
|
||||||
|
obj.data = new_mesh
|
||||||
|
|
||||||
|
local_cut_point = obj.matrix_world.inverted() @ cut_point
|
||||||
|
local_cut_normal = obj.matrix_world.inverted().to_quaternion() @ cut_normal
|
||||||
|
verts, edges = tool.Drawing.bisect_mesh_with_plane(obj, local_cut_point, local_cut_normal)
|
||||||
|
|
||||||
|
if old_mesh:
|
||||||
|
obj.data = old_mesh
|
||||||
|
bpy.data.meshes.remove(new_mesh)
|
||||||
|
|
||||||
|
for edge in edges or []:
|
||||||
|
boundary_lines.append(
|
||||||
|
shapely.LineString(
|
||||||
|
[Vector((round(x, 3) for x in verts[edge[0]])), Vector((round(x, 3) for x in verts[edge[1]]))]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return boundary_lines
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_gross_mesh_from_element(cls, visible_element):
|
||||||
|
gross_settings = ifcopenshell.geom.settings()
|
||||||
|
gross_settings.set(gross_settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||||
|
new_mesh = cls.create_mesh_from_shape(ifcopenshell.geom.create_shape(gross_settings, visible_element))
|
||||||
|
return new_mesh
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_mesh_from_shape(cls, shape):
|
||||||
|
geometry = shape.geometry
|
||||||
|
mesh = bpy.data.meshes.new("tmp")
|
||||||
|
verts = geometry.verts
|
||||||
|
if geometry.faces:
|
||||||
|
num_vertices = len(verts) // 3
|
||||||
|
total_faces = len(geometry.faces)
|
||||||
|
loop_start = range(0, total_faces, 3)
|
||||||
|
num_loops = total_faces // 3
|
||||||
|
loop_total = [3] * num_loops
|
||||||
|
num_vertex_indices = len(geometry.faces)
|
||||||
|
|
||||||
|
mesh.vertices.add(num_vertices)
|
||||||
|
mesh.vertices.foreach_set("co", verts)
|
||||||
|
mesh.loops.add(num_vertex_indices)
|
||||||
|
mesh.loops.foreach_set("vertex_index", geometry.faces)
|
||||||
|
mesh.polygons.add(num_loops)
|
||||||
|
mesh.polygons.foreach_set("loop_start", loop_start)
|
||||||
|
mesh.polygons.foreach_set("loop_total", loop_total)
|
||||||
|
mesh.polygons.foreach_set("use_smooth", [0] * total_faces)
|
||||||
|
mesh.update()
|
||||||
|
return mesh
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_x_y_z_h_mat_from_active_obj(cls, active_obj):
|
||||||
|
element = tool.Ifc.get_entity(active_obj)
|
||||||
|
mat = active_obj.matrix_world
|
||||||
|
local_bbox_center = 0.125 * sum((Vector(b) for b in active_obj.bound_box), Vector())
|
||||||
|
global_bbox_center = mat @ local_bbox_center
|
||||||
|
x = global_bbox_center.x
|
||||||
|
y = global_bbox_center.y
|
||||||
|
z = (mat @ Vector(active_obj.bound_box[0])).z
|
||||||
|
|
||||||
|
h = active_obj.dimensions.z
|
||||||
|
return x, y, z, h, mat
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_x_y_z_h_mat_from_cursor(cls):
|
||||||
|
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||||
|
collection_obj = collection.BIMCollectionProperties.obj
|
||||||
|
x, y = bpy.context.scene.cursor.location.xy
|
||||||
|
z = collection_obj.matrix_world.translation.z
|
||||||
|
mat = Matrix()
|
||||||
|
h = 3
|
||||||
|
return x, y, z, h, mat
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_union_shape_from_selected_objects(cls):
|
def get_union_shape_from_selected_objects(cls):
|
||||||
selected_objects = bpy.context.selected_objects
|
selected_objects = bpy.context.selected_objects
|
||||||
@@ -369,7 +487,11 @@ class Spatial(blenderbim.core.tool.Spatial):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_bmesh_from_polygon(cls, poly, h):
|
def get_bmesh_from_polygon(cls, poly, h):
|
||||||
mat = bpy.context.active_object.matrix_world
|
if bpy.context.selected_objects and bpy.context.active_object:
|
||||||
|
mat = bpy.context.active_object.matrix_world
|
||||||
|
else:
|
||||||
|
mat = Matrix()
|
||||||
|
|
||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
bm.verts.index_update()
|
bm.verts.index_update()
|
||||||
bm.edges.index_update()
|
bm.edges.index_update()
|
||||||
@@ -398,13 +520,32 @@ class Spatial(blenderbim.core.tool.Spatial):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_named_obj_from_bmesh(cls, name, bmesh):
|
def get_named_obj_from_bmesh(cls, name, bmesh):
|
||||||
|
mesh = cls.get_named_mesh_from_bmesh(name, bmesh)
|
||||||
|
obj = cls.get_named_obj_from_mesh(name, mesh)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_named_obj_from_mesh(cls, name, mesh):
|
||||||
|
obj = bpy.data.objects.new(name, mesh)
|
||||||
|
if bpy.context.active_object:
|
||||||
|
mat = bpy.context.active_object.matrix_world
|
||||||
|
obj.matrix_world = mat
|
||||||
|
return obj
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_named_mesh_from_bmesh(cls, name, bmesh):
|
||||||
mesh = bpy.data.meshes.new(name=name)
|
mesh = bpy.data.meshes.new(name=name)
|
||||||
bmesh.to_mesh(mesh)
|
bmesh.to_mesh(mesh)
|
||||||
bmesh.free()
|
bmesh.free()
|
||||||
obj = bpy.data.objects.new(name, mesh)
|
return mesh
|
||||||
mat = bpy.context.active_object.matrix_world
|
|
||||||
obj.matrix_world = mat
|
@classmethod
|
||||||
return obj
|
def edit_active_space_obj_from_mesh(cls, mesh):
|
||||||
|
active_obj = bpy.context.active_object
|
||||||
|
mesh.name = active_obj.data.name
|
||||||
|
mesh.BIMMeshProperties.ifc_definition_id = active_obj.data.BIMMeshProperties.ifc_definition_id
|
||||||
|
tool.Geometry.change_object_data(active_obj, mesh, is_global=True)
|
||||||
|
tool.Ifc.edit(active_obj)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_obj_origin_to_bboxcenter(cls, obj):
|
def set_obj_origin_to_bboxcenter(cls, obj):
|
||||||
@@ -422,11 +563,50 @@ class Spatial(blenderbim.core.tool.Spatial):
|
|||||||
vert.co = inverted @ aux_vector
|
vert.co = inverted @ aux_vector
|
||||||
obj.location = newLoc
|
obj.location = newLoc
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_obj_origin_to_cursor_position(cls, obj):
|
||||||
|
mat = obj.matrix_world
|
||||||
|
inverted = mat.inverted()
|
||||||
|
|
||||||
|
|
||||||
|
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||||
|
collection_obj = collection.BIMCollectionProperties.obj
|
||||||
|
x, y = bpy.context.scene.cursor.location.xy
|
||||||
|
z = collection_obj.matrix_world.translation.z
|
||||||
|
|
||||||
|
oldLoc = obj.location
|
||||||
|
newLoc = Vector((x,y,z))
|
||||||
|
diff = newLoc - oldLoc
|
||||||
|
for vert in obj.data.vertices:
|
||||||
|
aux_vector = mat @ vert.co
|
||||||
|
aux_vector = aux_vector - diff
|
||||||
|
vert.co = inverted @ aux_vector
|
||||||
|
obj.location = newLoc
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_selected_objects(cls):
|
||||||
|
return bpy.context.selected_objects
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_active_obj(cls):
|
||||||
|
return bpy.context.active_object
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_active_obj_z(cls):
|
def get_active_obj_z(cls):
|
||||||
x, y, z = bpy.context.active_object.matrix_world.translation.xyz
|
x, y, z = bpy.context.active_object.matrix_world.translation.xyz
|
||||||
return z
|
return z
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_active_obj_height(cls):
|
||||||
|
height = bpy.context.active_object.dimensions.z
|
||||||
|
return height
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_relating_type_id(cls):
|
||||||
|
props = bpy.context.scene.BIMModelProperties
|
||||||
|
relating_type_id = props.relating_type_id
|
||||||
|
return relating_type_id
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def traslate_obj_to_z_location(cls, obj, z):
|
def traslate_obj_to_z_location(cls, obj, z):
|
||||||
if z != 0:
|
if z != 0:
|
||||||
@@ -468,6 +648,10 @@ class Spatial(blenderbim.core.tool.Spatial):
|
|||||||
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def assign_ifcspace_class_to_obj(cls, obj):
|
||||||
|
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcSpace")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def assign_type_to_obj(cls, obj):
|
def assign_type_to_obj(cls, obj):
|
||||||
relating_type_id = bpy.context.scene.BIMModelProperties.relating_type_id
|
relating_type_id = bpy.context.scene.BIMModelProperties.relating_type_id
|
||||||
@@ -478,6 +662,10 @@ class Spatial(blenderbim.core.tool.Spatial):
|
|||||||
element = tool.Ifc.get_entity(obj)
|
element = tool.Ifc.get_entity(obj)
|
||||||
blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type)
|
blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def assign_relating_type_to_element(cls, ifc, Type, element, relating_type):
|
||||||
|
blenderbim.core.type.assign_type(ifc, Type, element=element, type=relating_type)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def assign_container_to_obj(cls, obj):
|
def assign_container_to_obj(cls, obj):
|
||||||
active_obj = bpy.context.active_object
|
active_obj = bpy.context.active_object
|
||||||
|
|||||||
@@ -116,17 +116,6 @@ Scenario: Execute generate spaces from walls
|
|||||||
When I press "bim.generate_spaces_from_walls"
|
When I press "bim.generate_spaces_from_walls"
|
||||||
Then nothing happens
|
Then nothing happens
|
||||||
|
|
||||||
Scenario: Execute generate flooring coverings from walls
|
|
||||||
Given an empty IFC project
|
|
||||||
And I load the demo construction library
|
|
||||||
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
|
||||||
And the variable "element_type" is "[e for e in {ifc}.by_type('IfcWallType') if e.Name == 'WAL100'][0].id()"
|
|
||||||
And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}"
|
|
||||||
And I press "bim.add_constr_type_instance"
|
|
||||||
And the object "IfcWall/Wall" is selected
|
|
||||||
When I press "bim.generate_flooring_coverings_from_walls"
|
|
||||||
Then nothing happens
|
|
||||||
|
|
||||||
Scenario: Execute toggle space visibility
|
Scenario: Execute toggle space visibility
|
||||||
Given an empty IFC project
|
Given an empty IFC project
|
||||||
And I add a cube
|
And I add a cube
|
||||||
|
|||||||
Reference in New Issue
Block a user