Merge pull request #3838 from maxfb87/covering_tool

Covering tool (see #3754)
This commit is contained in:
Massimo Fabbro
2023-10-07 14:20:27 +02:00
committed by GitHub
10 changed files with 332 additions and 216 deletions
@@ -38,3 +38,4 @@ import ifcopenshell
class BIMCoveringProperties(PropertyGroup):
pass
# 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_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.covering")
bl_widget = None
ifc_element_type = "IfcCoveringType"
bl_keymap = tool.Blender.get_default_selection_keypmap() + (
("bim.covering_hotkey", {"type": "A", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_A")]}),
)
@classmethod
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):
args = ["covering", layout, text, hotkey, description]
@@ -71,7 +72,6 @@ class CoveringToolUI:
elif AuthoringData.data["ifc_element_type"] != ifc_element_type:
AuthoringData.load(ifc_element_type)
if context.region.type == "TOOL_HEADER":
cls.draw_header_interface()
elif context.region.type in ("UI", "WINDOW"):
@@ -93,8 +93,33 @@ class CoveringToolUI:
row.label(text="", icon="EVENT_A")
active_obj = bpy.context.active_object
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:
op = row.operator("bim.add_constr_type_instance", text="Add")
op.from_invoke = True
@@ -159,8 +184,11 @@ class Hotkey(bpy.types.Operator, Operator):
def hotkey_S_A(self):
active_obj = bpy.context.active_object
element = tool.Ifc.get_entity(active_obj)
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
bpy.ops.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"):
bpy.ops.bim.add_instance_flooring_coverings_from_walls()
else:
bpy.ops.bim.add_constr_type_instance()
else:
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):
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_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_classes"] = cls.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()
# 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 []
@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
def selected_material_usages(cls):
selected_usages = {}
@@ -79,6 +79,7 @@ def update_type_class(self, context):
def update_relating_type_id(self, context):
AuthoringData.data["relating_type_id"] = AuthoringData.relating_type_id()
AuthoringData.data["type_thumbnail"] = AuthoringData.type_thumbnail()
AuthoringData.data["predefined_type"] = AuthoringData.predefined_type()
def update_type_page(self, context):
@@ -50,13 +50,7 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
def msg(self, context):
self.layout.label(text="NO ACTIVE STOREY")
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
# props = context.scene.BIMModelProperties
collection = context.view_layer.active_layer_collection.collection
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")
return
active_obj = bpy.context.active_object
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
core.generate_space(tool.Ifc, tool.Spatial, tool.Model, tool.Type)
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.
# In order to run, the active object must be a wall and
# 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)
class ToggleSpaceVisibility(bpy.types.Operator, tool.Ifc.Operator):
+54 -40
View File
@@ -16,8 +16,6 @@
# 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/>.
import blenderbim.core
def reference_structure(ifc, spatial, structure=None, element=None):
if spatial.can_reference(structure, element):
@@ -128,35 +126,57 @@ def select_decomposed_elements(spatial):
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):
import bpy
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
z = spatial.get_active_obj_z()
h = spatial.get_active_obj_height()
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)
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.matrix_world = mat
obj = spatial.get_named_obj_from_bmesh(name, bmesh = bm)
spatial.set_obj_origin_to_bboxcenter(obj)
spatial.traslate_obj_to_z_location(obj, z)
if z != 0:
obj.location = obj.location + Vector((0, 0, z))
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.link_obj_to_active_collection(obj)
spatial.assign_ifcspace_class_to_obj(obj)
spatial.assign_container_to_obj(obj)
def toggle_space_visibility(ifc, spatial):
model = ifc.get()
+18 -1
View File
@@ -809,6 +809,13 @@ class Spatial:
def set_relative_object_matrix(cls, target_obj, relative_to_obj, matrix): pass
def show_scene_objects(cls): pass
#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_boundary_elements(cls, selected_objects): 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_bmesh_from_polygon(cls, poly, h): 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 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 link_obj_to_active_collection(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 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_relating_type_to_element(cls, ifc, Type, element, relating_type): pass
def regen_obj_representation(cls, ifc, geometry, obj, body): pass
def toggle_spaces_visibility_wired_and_textured(cls, spaces): pass
+193 -5
View File
@@ -20,6 +20,7 @@ import bpy
import bmesh
import shapely
import ifcopenshell
import blenderbim.core.type
import blenderbim.core.tool
import blenderbim.core.root
import blenderbim.core.spatial
@@ -270,6 +271,123 @@ class Spatial(blenderbim.core.tool.Spatial):
#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
def get_union_shape_from_selected_objects(cls):
selected_objects = bpy.context.selected_objects
@@ -369,7 +487,11 @@ class Spatial(blenderbim.core.tool.Spatial):
@classmethod
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.verts.index_update()
bm.edges.index_update()
@@ -398,13 +520,32 @@ class Spatial(blenderbim.core.tool.Spatial):
@classmethod
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)
bmesh.to_mesh(mesh)
bmesh.free()
obj = bpy.data.objects.new(name, mesh)
mat = bpy.context.active_object.matrix_world
obj.matrix_world = mat
return obj
return mesh
@classmethod
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
def set_obj_origin_to_bboxcenter(cls, obj):
@@ -422,11 +563,50 @@ class Spatial(blenderbim.core.tool.Spatial):
vert.co = inverted @ aux_vector
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
def get_active_obj_z(cls):
x, y, z = bpy.context.active_object.matrix_world.translation.xyz
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
def traslate_obj_to_z_location(cls, obj, z):
if z != 0:
@@ -468,6 +648,10 @@ class Spatial(blenderbim.core.tool.Spatial):
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
return body
@classmethod
def assign_ifcspace_class_to_obj(cls, obj):
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcSpace")
@classmethod
def assign_type_to_obj(cls, obj):
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)
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
def assign_container_to_obj(cls, obj):
active_obj = bpy.context.active_object
@@ -116,17 +116,6 @@ Scenario: Execute generate spaces from walls
When I press "bim.generate_spaces_from_walls"
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
Given an empty IFC project
And I add a cube