From eff68dda38d9d12db5b628fa1ceef8dcd480e1b4 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 20 Oct 2023 12:28:18 +1100 Subject: [PATCH] Fix #3903. Bug where stairs started off with an empty mesh and had a representation added, resulting in an invalid empty point cloud. Now stairs have should_add_representation=False when creating the IfcStairFlight, and the representation is updated explicitly, similar to how parametric doors and windows etc have their representations updated. --- .../blenderbim/bim/module/model/stair.py | 109 ++++++++++-------- .../blenderbim/bim/module/model/ui.py | 4 +- .../blenderbim/bim/module/type/operator.py | 2 +- 3 files changed, 62 insertions(+), 53 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/stair.py b/src/blenderbim/blenderbim/bim/module/model/stair.py index a444f510fc..1f82c4ee44 100644 --- a/src/blenderbim/blenderbim/bim/module/model/stair.py +++ b/src/blenderbim/blenderbim/bim/module/model/stair.py @@ -17,21 +17,17 @@ # along with BlenderBIM Add-on. If not, see . import bpy +import json +import bmesh +import ifcopenshell +import blenderbim +import blenderbim.tool as tool +from mathutils import Vector +from bmesh.types import BMVert from bpy.types import Operator from bpy.props import FloatProperty, IntProperty from bpy_extras.object_utils import AddObjectHelper, object_data_add - -import bmesh -from bmesh.types import BMVert - -import ifcopenshell from ifcopenshell.util.shape_builder import V, ShapeBuilder -import blenderbim -import blenderbim.tool as tool - -from mathutils import Vector -from pprint import pprint -import json def add_dumb_stair_object(self, context): @@ -106,8 +102,7 @@ def generate_stair_2d_profile( if stair_type == "WOOD/STEEL": builder = ShapeBuilder(None) tread_shape = builder.get_rectangle_coords( - size=V(tread_run, 0, tread_depth), - position=V(0, 0, -(tread_depth-tread_rise)) + size=V(tread_run, 0, tread_depth), position=V(0, 0, -(tread_depth - tread_rise)) ) tread_offset = V(tread_run, 0, tread_rise) @@ -116,12 +111,14 @@ def generate_stair_2d_profile( vertices.extend(cur_trade_shape) cur_vertex = i * 4 - edges.extend([ - (cur_vertex, cur_vertex+1), - (cur_vertex+1, cur_vertex+2), - (cur_vertex+2, cur_vertex+3), - (cur_vertex+3, cur_vertex), - ]) + edges.extend( + [ + (cur_vertex, cur_vertex + 1), + (cur_vertex + 1, cur_vertex + 2), + (cur_vertex + 2, cur_vertex + 3), + (cur_vertex + 3, cur_vertex), + ] + ) faces.append(list(range(cur_vertex, cur_vertex + 1))) return (vertices, edges, faces) @@ -129,36 +126,26 @@ def generate_stair_2d_profile( elif stair_type == "GENERIC": vertices.append(Vector([0, 0, 0])) - tread_verts = [ - Vector([0, 0, tread_rise]), - Vector([tread_run, 0, tread_rise]) - ] + tread_verts = [Vector([0, 0, tread_rise]), Vector([tread_run, 0, tread_rise])] tread_offset = Vector([tread_run, 0, tread_rise]) for i in range(number_of_risers): current_tread_verts = [v + tread_offset * i for v in tread_verts] last_vert_i = len(vertices) - 1 - edges.extend([ - (last_vert_i, last_vert_i + 1), - (last_vert_i + 1, last_vert_i + 2) - ]) + edges.extend([(last_vert_i, last_vert_i + 1), (last_vert_i + 1, last_vert_i + 2)]) vertices.extend(current_tread_verts) last_vert_i = len(vertices) - vertices.append(vertices[-1] * V(1,0,0)) - edges.extend([ - (last_vert_i - 1, last_vert_i), - (last_vert_i, 0) - ]) + vertices.append(vertices[-1] * V(1, 0, 0)) + edges.extend([(last_vert_i - 1, last_vert_i), (last_vert_i, 0)]) return (vertices, edges, faces) elif stair_type == "CONCRETE": for i in range(number_of_risers): - vertices.extend([ - Vector((tread_run*i, 0, tread_rise*i)), - Vector((tread_run*i, 0, tread_rise*(i+1))) - ]) + vertices.extend( + [Vector((tread_run * i, 0, tread_rise * i)), Vector((tread_run * i, 0, tread_rise * (i + 1)))] + ) cur_vertex = i * 2 if i != 0: edges.append((cur_vertex - 1, cur_vertex)) @@ -179,11 +166,11 @@ def generate_stair_2d_profile( # top nib if has_top_nib: - vertices.append( vertices[number_of_risers * 2] + Vector((0, 0, -top_slab_depth)) ) - vertices.append( vertices[number_of_risers * 2] + Vector(((-top_slab_depth - b) / k, 0, -top_slab_depth)) ) + vertices.append(vertices[number_of_risers * 2] + Vector((0, 0, -top_slab_depth))) + vertices.append(vertices[number_of_risers * 2] + Vector(((-top_slab_depth - b) / k, 0, -top_slab_depth))) last_vertex_i = len(vertices) - 1 - edges.append( (number_of_risers * 2, last_vertex_i - 1) ) - edges.append( (last_vertex_i - 1, last_vertex_i) ) + edges.append((number_of_risers * 2, last_vertex_i - 1)) + edges.append((last_vertex_i - 1, last_vertex_i)) else: vertices.append(vertices[number_of_risers * 2] + depth_vector) last_vertex_i = len(vertices) - 1 @@ -200,17 +187,17 @@ def generate_stair_2d_profile( vertices.append(vertices[0] + Vector(((-base_slab_depth - b) / k, 0, -base_slab_depth))) vertices.append(vertices[0] + Vector((0, 0, -base_slab_depth))) last_vertex_i = len(vertices) - 1 - edges.append( (0, last_vertex_i) ) - edges.append( (last_vertex_i - 1, last_vertex_i) ) + edges.append((0, last_vertex_i)) + edges.append((last_vertex_i - 1, last_vertex_i)) bottom_nib_end = len(vertices) - 2 - edges.append( (bottom_nib_end, top_nib_end) ) + edges.append((bottom_nib_end, top_nib_end)) faces = [list(range(len(vertices)))] return (vertices, edges, faces) -def update_stair_modifier(context): +def regenerate_stair_mesh(context): obj = context.active_object props_kwargs = obj.BIMStairProperties.get_props_kwargs() vertices, edges, faces = generate_stair_2d_profile(**props_kwargs) @@ -221,7 +208,7 @@ def update_stair_modifier(context): bm.edges.index_update() new_verts = [bm.verts.new(v) for v in vertices] - new_edges = [bm.edges.new( (new_verts[e[0]], new_verts[e[1]]) ) for e in edges] + new_edges = [bm.edges.new((new_verts[e[0]], new_verts[e[1]])) for e in edges] bm.verts.index_update() bm.edges.index_update() @@ -242,6 +229,26 @@ def update_stair_modifier(context): obj.data.update() +def update_stair_representation(obj): + body = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW") + representation = ifcopenshell.api.run( + "geometry.add_representation", + tool.Ifc.get(), + context=body, + blender_object=obj, + geometry=obj.data, + coordinate_offset=tool.Geometry.get_cartesian_point_coordinate_offset(obj), + total_items=tool.Geometry.get_total_representation_items(obj), + should_force_faceted_brep=tool.Geometry.should_force_faceted_brep(), + should_force_triangulation=tool.Geometry.should_force_triangulation(), + should_generate_uvs=tool.Geometry.should_generate_uvs(obj), + ifc_representation_class=None, + profile_set_usage=None, + ) + tool.Model.replace_object_ifc_representation(body, obj, representation) + tool.Ifc.finish_edit(obj) + + def update_ifc_stair_props(obj): """should be called after new geometry settled since it's going to update ifc representation @@ -327,7 +334,7 @@ class BIM_OT_add_clever_stair(bpy.types.Operator, tool.Ifc.Operator): obj.location = spawn_location collection = context.view_layer.active_layer_collection.collection collection.objects.link(obj) - + body_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW") element = blenderbim.core.root.assign_class( tool.Ifc, @@ -335,7 +342,7 @@ class BIM_OT_add_clever_stair(bpy.types.Operator, tool.Ifc.Operator): tool.Root, obj=obj, ifc_class="IfcStairFlight", - should_add_representation=True, + should_add_representation=False, context=body_context, ) if tool.Ifc.get_schema() != "IFC2X3": @@ -372,8 +379,9 @@ class AddStair(bpy.types.Operator, tool.Ifc.Operator): pset=pset, properties={"Data": json.dumps(stair_data)}, ) - update_stair_modifier(context) + regenerate_stair_mesh(context) update_ifc_stair_props(obj) + update_stair_representation(obj) class CancelEditingStair(bpy.types.Operator, tool.Ifc.Operator): @@ -388,7 +396,7 @@ class CancelEditingStair(bpy.types.Operator, tool.Ifc.Operator): props = obj.BIMStairProperties # restore previous settings since editing was canceled props.set_props_kwargs_from_ifc_data(data) - update_stair_modifier(context) + regenerate_stair_mesh(context) props.is_editing = False @@ -407,7 +415,8 @@ class FinishEditingStair(bpy.types.Operator, tool.Ifc.Operator): data = props.get_props_kwargs(convert_to_project_units=True) props.is_editing = False - update_stair_modifier(context) + regenerate_stair_mesh(context) + update_stair_representation(obj) pset = tool.Pset.get_element_pset(element, "BBIM_Stair") data = json.dumps(data) diff --git a/src/blenderbim/blenderbim/bim/module/model/ui.py b/src/blenderbim/blenderbim/bim/module/model/ui.py index 07cd3ec4bd..c99104261f 100644 --- a/src/blenderbim/blenderbim/bim/module/model/ui.py +++ b/src/blenderbim/blenderbim/bim/module/model/ui.py @@ -30,7 +30,7 @@ from blenderbim.bim.module.model.data import ( RoofData, ) from blenderbim.bim.module.model.prop import get_ifc_class -from blenderbim.bim.module.model.stair import update_stair_modifier +from blenderbim.bim.module.model.stair import regenerate_stair_mesh from blenderbim.bim.module.model.window import update_window_modifier_bmesh from blenderbim.bim.module.model.door import update_door_modifier_bmesh from blenderbim.bim.module.model.railing import update_railing_modifier_bmesh @@ -267,7 +267,7 @@ class BIM_PT_stair(bpy.types.Panel): row = self.layout.row(align=True) for prop_name in props.get_props_kwargs(): self.layout.prop(props, prop_name) - update_stair_modifier(context) + regenerate_stair_mesh(context) else: row.operator("bim.enable_editing_stair", icon="GREASEPENCIL", text="") row.operator("bim.remove_stair", icon="X", text="") diff --git a/src/blenderbim/blenderbim/bim/module/type/operator.py b/src/blenderbim/blenderbim/bim/module/type/operator.py index 223ca0ce04..4f8720c782 100644 --- a/src/blenderbim/blenderbim/bim/module/type/operator.py +++ b/src/blenderbim/blenderbim/bim/module/type/operator.py @@ -410,7 +410,7 @@ class AddType(bpy.types.Operator, tool.Ifc.Operator): obj=obj, predefined_type=predefined_type, ifc_class="IfcStairFlightType", - should_add_representation=True, + should_add_representation=False, context=body, ) tool.Blender.select_and_activate_single_object(context, obj)