diff --git a/src/blenderbim/blenderbim/bim/__init__.py b/src/blenderbim/blenderbim/bim/__init__.py index 2bfccf20e8..b85043ee71 100644 --- a/src/blenderbim/blenderbim/bim/__init__.py +++ b/src/blenderbim/blenderbim/bim/__init__.py @@ -78,6 +78,7 @@ modules = { "augin": None, "debug": None, "ifcgit": None, + "covering": None, # Uncomment this line to enable loading of the demo module. Happy hacking! # The name "demo" must correlate to a folder name in `bim/module/`. # "demo": None, diff --git a/src/blenderbim/blenderbim/bim/module/covering/__init__.py b/src/blenderbim/blenderbim/bim/module/covering/__init__.py new file mode 100644 index 0000000000..73c39a93af --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/covering/__init__.py @@ -0,0 +1,41 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2020, 2021 Dion Moult +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +import bpy +from . import prop, workspace + +classes = ( + prop.BIMCoveringProperties, + workspace.Hotkey, +) + + +def register(): + if not bpy.app.background: + bpy.utils.register_tool(workspace.CoveringTool, after={"bim.structural_tool"}, separator=False, group=False) + bpy.types.Scene.BIMCoveringProperties = bpy.props.PointerProperty(type=prop.BIMCoveringProperties) +# bpy.types.Object.BIMObjectSpatialProperties = bpy.props.PointerProperty(type=prop.BIMObjectSpatialProperties) +# bpy.types.Scene.BIMSpatialManagerProperties = bpy.props.PointerProperty(type=prop.BIMSpatialManagerProperties) + + +def unregister(): + if not bpy.app.background: + bpy.utils.unregister_tool(workspace.CoveringTool) + del bpy.types.Scene.BIMCoveringProperties +# del bpy.types.Object.BIMObjectSpatialProperties +# del bpy.types.Scene.BIMSpatialManagerProperties diff --git a/src/blenderbim/blenderbim/bim/module/covering/ops.authoring.covering.dat b/src/blenderbim/blenderbim/bim/module/covering/ops.authoring.covering.dat new file mode 100644 index 0000000000..8a860d3149 Binary files /dev/null and b/src/blenderbim/blenderbim/bim/module/covering/ops.authoring.covering.dat differ diff --git a/src/blenderbim/blenderbim/bim/module/covering/prop.py b/src/blenderbim/blenderbim/bim/module/covering/prop.py new file mode 100644 index 0000000000..d66aac26cd --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/covering/prop.py @@ -0,0 +1,40 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2020, 2021 Dion Moult +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +import bpy +from blenderbim.bim.prop import StrProperty, Attribute +#from blenderbim.bim.module.spatial.data import SpatialData +from bpy.types import PropertyGroup +from bpy.props import ( + PointerProperty, + StringProperty, + EnumProperty, + BoolProperty, + IntProperty, + FloatProperty, + FloatVectorProperty, + CollectionProperty, +) +import blenderbim.tool as tool +import blenderbim.core.geometry +import ifcopenshell + + +class BIMCoveringProperties(PropertyGroup): + pass +# depth: bpy.props.FloatProperty(name="Depth", default=0.1, subtype="DISTANCE", description="Flooring depth") diff --git a/src/blenderbim/blenderbim/bim/module/covering/workspace.py b/src/blenderbim/blenderbim/bim/module/covering/workspace.py new file mode 100644 index 0000000000..336d3bc2ef --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/covering/workspace.py @@ -0,0 +1,166 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2023 @Andrej730 +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + + +import os +import bpy +import blenderbim.tool as tool +from blenderbim.bim.helper import prop_with_search +from blenderbim.bim.module.model.data import AuthoringData +from bpy.types import WorkSpaceTool +from blenderbim.bim.ifc import IfcStore +import blenderbim.bim.handler + + +# declaring it here to avoid circular import problems +class Operator: + def execute(self, context): + IfcStore.execute_ifc_operator(self, context) + blenderbim.bim.handler.refresh_ui_data() + return {"FINISHED"} + +class CoveringTool(WorkSpaceTool): + bl_space_type = "VIEW_3D" + bl_context_mode = "OBJECT" + bl_idname = "bim.covering_tool" + bl_label = "Covering Tool" + bl_description = "Create and edit coverings" + bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.covering") + bl_widget = None + 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") + +def add_layout_hotkey(layout, text, hotkey, description): + args = ["covering", layout, text, hotkey, description] + tool.Blender.add_layout_hotkey_operator(*args) + +class CoveringToolUI: + @classmethod + def draw(cls, context, layout, ifc_element_type = None): + cls.layout = layout + cls.props = context.scene.BIMModelProperties + cls.covering_props = context.scene.BIMCoveringProperties + + row = cls.layout.row(align=True) + if not tool.Ifc.get(): + row.label(text="No IFC Project", icon="ERROR") + return + + if not AuthoringData.is_loaded: + AuthoringData.load(ifc_element_type) + 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"): + cls.draw_basic_bim_tool_interface() + + cls.draw_default_interface() + + + + @classmethod + def draw_header_interface(cls): + cls.draw_type_selection_interface() + + @classmethod + def draw_default_interface(cls): + if AuthoringData.data["ifc_classes"]: + row = cls.layout.row(align=True) + row.label(text="", icon="EVENT_SHIFT") + 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") + else: + 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) + + @classmethod + def draw_type_selection_interface(cls): + # shared by both sidebar and header + row = cls.layout.row(align=True) + if AuthoringData.data["ifc_classes"]: + row = cls.layout.row(align=True) + row.label(text="", icon="FILE_3D") + prop_with_search(row, cls.props, "relating_type_id", text="") + row.operator("bim.launch_type_manager", icon="LIGHTPROBE_GRID", text="") + else: + row.label(text=f"No {AuthoringData.data['ifc_element_type']} Found", icon="ERROR") + row = cls.layout.row() + row.operator("bim.launch_type_manager", icon="LIGHTPROBE_GRID", text="Launch Type Manager") + + @classmethod + def draw_basic_bim_tool_interface(cls): + cls.draw_type_selection_interface() + + if AuthoringData.data["ifc_classes"]: + if cls.props.ifc_class: + box = cls.layout.box() + if AuthoringData.data["type_thumbnail"]: + box.template_icon(icon_value=AuthoringData.data["type_thumbnail"], scale=5) + else: + op = box.operator("bim.load_type_thumbnails", text="Load Thumbnails", icon="FILE_REFRESH") + op.ifc_class = cls.props.ifc_class + + +class Hotkey(bpy.types.Operator, Operator): + bl_idname = "bim.covering_hotkey" + bl_label = "Hotkey" + bl_options = {"REGISTER", "UNDO"} + hotkey: bpy.props.StringProperty() + description: bpy.props.StringProperty() + + @classmethod + def poll(cls, context): + return tool.Ifc.get() + + @classmethod + def description(cls, context, operator): + return operator.description or "" + + def _execute(self, context): +# self.props = context.scene.BIMCoveringProperties + getattr(self, f"hotkey_{self.hotkey}")() + + def invoke(self, context, event): + # https://blender.stackexchange.com/questions/276035/how-do-i-make-operators-remember-their-property-values-when-called-from-a-hotkey + # self.props = context.scene.BIMSpatialProperties + return self.execute(context) + + def draw(self, context): + pass + + 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() + else: + bpy.ops.bim.add_constr_type_instance() + diff --git a/src/blenderbim/blenderbim/bim/module/model/__init__.py b/src/blenderbim/blenderbim/bim/module/model/__init__.py index 8220be3df9..7786a0d8e5 100644 --- a/src/blenderbim/blenderbim/bim/module/model/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/model/__init__.py @@ -28,6 +28,7 @@ from . import ( roof, slab, space, + covering, stair, window, opening, @@ -106,7 +107,7 @@ classes = ( slab.SetArcIndex, space.GenerateSpace, space.GenerateSpacesFromWalls, - space.GenerateFlooringCoveringsFromWalls, + covering.AddInstanceFlooringCoveringsFromWalls, space.ToggleSpaceVisibility, mep.FitFlowSegments, mep.RegenerateDistributionElement, diff --git a/src/blenderbim/blenderbim/bim/module/model/covering.py b/src/blenderbim/blenderbim/bim/module/model/covering.py new file mode 100644 index 0000000000..da5f09cb06 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/model/covering.py @@ -0,0 +1,64 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2023 Dion Moult +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + + +import bpy +import ifcopenshell +import blenderbim.tool as tool +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_options = {"REGISTER", "UNDO"} + bl_description = "Add instance flooring coverings from selected walls. The active object must be a wall and layered vertically" + + @classmethod + def poll(cls, context): + active_obj = bpy.context.active_object + element = tool.Ifc.get_entity(active_obj) + if element: + if element.is_a("IfcWall") and tool.Model.get_usage_type(element) == "LAYER2": + return context.selected_objects + + def _execute(self, context): + # This only works based on a 2D plan only considering the standard + # 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 + 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.") + + container = ifcopenshell.util.element.get_container(element) + 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.add_instance_flooring_coverings_from_walls(tool.Ifc, tool.Spatial, tool.Collector, tool.Geometry) + diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index ab2534cfa8..2723e7904e 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -132,7 +132,7 @@ class BIMModelProperties(PropertyGroup): y: bpy.props.FloatProperty(name="Y", default=0.5, subtype="DISTANCE", description="Size by Y axis for the opening") z: bpy.props.FloatProperty(name="Z", default=0.5, subtype="DISTANCE", description="Size by Z axis for the opening") # Used for things like walls, doors, flooring, skirting, etc - rl1: bpy.props.FloatProperty(name="RL", default=1, subtype="DISTANCE", description="Z offset for walls") + rl1: bpy.props.FloatProperty(name="RL", default=1, subtype="DISTANCE", description="Z offset for walls") # Used for things like windows, other hosted furniture, and MEP rl2: bpy.props.FloatProperty(name="RL", default=1, subtype="DISTANCE", description="Z offset for windows") # Used for plan calculation points such as in room generation diff --git a/src/blenderbim/blenderbim/bim/module/model/space.py b/src/blenderbim/blenderbim/bim/module/model/space.py index efbcd9bdca..5f0e8edaf3 100644 --- a/src/blenderbim/blenderbim/bim/module/model/space.py +++ b/src/blenderbim/blenderbim/bim/module/model/space.py @@ -235,26 +235,6 @@ class GenerateSpacesFromWalls(bpy.types.Operator, tool.Ifc.Operator): # there must be selected walls core.generate_spaces_from_walls(tool.Ifc, tool.Spatial, tool.Collector) -class GenerateFlooringCoveringsFromWalls(bpy.types.Operator, tool.Ifc.Operator): - bl_idname = "bim.generate_flooring_coverings_from_walls" - bl_label = "Generate Flooring Coverings From Walls" - bl_options = {"REGISTER", "UNDO"} - bl_description = "Generate flooring coverings from selected walls. The active object must be a wall" - - @classmethod - def poll(cls, context): - active_obj = bpy.context.active_object - element = tool.Ifc.get_entity(active_obj) - if element: - return context.selected_objects and element.is_a("IfcWall") - - def _execute(self, context): - # This only works based on a 2D plan only considering the standard - # 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 - core.generate_flooring_coverings_from_walls(tool.Ifc, tool.Spatial, tool.Collector) - class ToggleSpaceVisibility(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.toggle_space_visibility" bl_label = "Toggle Space Visibility" diff --git a/src/blenderbim/blenderbim/core/covering.py b/src/blenderbim/blenderbim/core/covering.py new file mode 100644 index 0000000000..61aadfcd7a --- /dev/null +++ b/src/blenderbim/blenderbim/core/covering.py @@ -0,0 +1,42 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 Dion Moult +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + + +def add_instance_flooring_coverings_from_walls(ifc, spatial, collector, geometry): + z = spatial.get_active_obj_z() + union = spatial.get_union_shape_from_selected_objects() + for i, linear_ring in enumerate(union.interiors): + poly = spatial.get_buffered_poly_from_linear_ring(linear_ring) + bm = spatial.get_bmesh_from_polygon(poly, h=0) + + name = "Covering" + str(i) + 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) + spatial.link_obj_to_active_collection(obj) + + points = spatial.get_2d_vertices_from_obj(obj) + + spatial.assign_type_to_obj(obj) + spatial.assign_container_to_obj(obj) + + spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices = points) + body = spatial.get_body_representation(obj) + spatial.regen_obj_representation(ifc, geometry, obj, body) + diff --git a/src/blenderbim/blenderbim/core/spatial.py b/src/blenderbim/blenderbim/core/spatial.py index 7864ad0845..7f2beb3aba 100644 --- a/src/blenderbim/blenderbim/core/spatial.py +++ b/src/blenderbim/blenderbim/core/spatial.py @@ -18,7 +18,6 @@ import blenderbim.core import bpy -import ifcopenshell def reference_structure(ifc, spatial, structure=None, element=None): @@ -126,7 +125,6 @@ def select_decomposed_elements(spatial): #HERE STARTS SPATIAL TOOL def generate_spaces_from_walls(ifc, spatial, collector): - #props = bpy.context.scene.BIMModelProperties container, active_obj = spatial.get_container_and_active_obj() if not active_obj: @@ -176,9 +174,6 @@ def generate_spaces_from_walls(ifc, spatial, collector): ifc, collector, spatial, structure_obj=container_obj, element_obj=obj ) -def generate_flooring_coverings_from_walls(ifc, spatial, collector): - pass - def toggle_space_visibility(ifc, spatial): model = ifc.get() spaces = model.by_type("IfcSpace") diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index b249df13f3..df3f4793ce 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -811,7 +811,7 @@ 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 get_container_and_active_obj(cls): pass +# def get_container_and_active_obj(cls): pass def get_union_shape_from_selected_objects(cls, selected_objects): pass def get_boundary_elements(cls, selected_objects): pass def get_polygons(cls, boundary_elements): pass @@ -820,8 +820,17 @@ class Spatial: def get_purged_inner_holes_poly(cls, union_geom, min_area): pass def get_poly_valid_interior_list(cls, poly, min_area, interiors_list): pass def get_buffered_poly_from_linear_ring(cls, linear_ring): pass - def get_bmesh_from_polygon(cls, poly, mat, h): pass + def get_bmesh_from_polygon(cls, poly, h): pass + def get_named_obj_from_bmesh(cls, name, bmesh): pass def set_obj_origin_to_bboxcenter(cls, obj): pass + def get_active_obj_z(cls, obj): 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_type_to_obj(cls, obj): pass + def regen_obj_representation(cls, ifc, geometry, obj, body): pass def toggle_spaces_visibility_wired_and_textured(cls, spaces): pass diff --git a/src/blenderbim/blenderbim/tool/covering.py b/src/blenderbim/blenderbim/tool/covering.py new file mode 100644 index 0000000000..cfbae2f807 --- /dev/null +++ b/src/blenderbim/blenderbim/tool/covering.py @@ -0,0 +1,49 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 Dion Moult +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +import bpy +import bmesh +import shapely +import ifcopenshell +import blenderbim.core.tool +import blenderbim.core.root +import blenderbim.core.spatial +import blenderbim.core.geometry +import blenderbim.tool as tool +import json +from math import pi +from mathutils import Vector, Matrix +from shapely import Polygon, MultiPolygon + +class Covering(blenderbim.core.tool.Covering): + @classmethod +# def toggle_spaces_visibility_wired_and_textured(cls, spaces): +# first_obj = tool.Ifc.get_object(spaces[0]) +# if bpy.data.objects[first_obj.name].display_type == "TEXTURED": +# for space in spaces: +# obj = tool.Ifc.get_object(space) +# bpy.data.objects[obj.name].show_wire = True +# bpy.data.objects[obj.name].display_type = "WIRE" +# return +# +# elif bpy.data.objects[first_obj.name].display_type == "WIRE": +# for space in spaces: +# obj = tool.Ifc.get_object(space) +# bpy.data.objects[obj.name].show_wire = False +# bpy.data.objects[obj.name].display_type = "TEXTURED" +# return diff --git a/src/blenderbim/blenderbim/tool/spatial.py b/src/blenderbim/blenderbim/tool/spatial.py index c6865c9c1d..f838703059 100644 --- a/src/blenderbim/blenderbim/tool/spatial.py +++ b/src/blenderbim/blenderbim/tool/spatial.py @@ -23,6 +23,7 @@ import ifcopenshell import blenderbim.core.tool import blenderbim.core.root import blenderbim.core.spatial +import blenderbim.core.geometry import blenderbim.tool as tool import json from math import pi @@ -270,14 +271,8 @@ class Spatial(blenderbim.core.tool.Spatial): #HERE STARTS SPATIAL TOOL @classmethod - def get_container_and_active_obj(cls): - active_obj = bpy.context.active_object - element = tool.Ifc.get_entity(active_obj) - container = ifcopenshell.util.element.get_container(element) - return container, active_obj - - @classmethod - def get_union_shape_from_selected_objects(cls, selected_objects): + def get_union_shape_from_selected_objects(cls): + selected_objects = bpy.context.selected_objects boundary_elements = cls.get_boundary_elements(selected_objects) polys = cls.get_polygons(boundary_elements) converted_tolerance = cls.get_converted_tolerance(tolerance=0.03) @@ -373,7 +368,8 @@ class Spatial(blenderbim.core.tool.Spatial): return poly @classmethod - def get_bmesh_from_polygon(cls, poly, mat, h): + def get_bmesh_from_polygon(cls, poly, h): + mat = bpy.context.active_object.matrix_world bm = bmesh.new() bm.verts.index_update() bm.edges.index_update() @@ -391,14 +387,25 @@ class Spatial(blenderbim.core.tool.Spatial): 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) + if h!=0: + 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) bmesh.ops.recalc_face_normals(bm, faces=bm.faces) return bm + @classmethod + def get_named_obj_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 + @classmethod def set_obj_origin_to_bboxcenter(cls, obj): mat = obj.matrix_world @@ -415,6 +422,84 @@ class Spatial(blenderbim.core.tool.Spatial): vert.co = inverted @ aux_vector obj.location = newLoc + @classmethod + def get_active_obj_z(cls): + x, y, z = bpy.context.active_object.matrix_world.translation.xyz + return z + + @classmethod + def traslate_obj_to_z_location(cls, obj, z): + if z != 0: + obj.location = obj.location + Vector((0, 0, z)) + + @classmethod + def link_obj_to_active_collection(cls, obj): + bpy.context.view_layer.active_layer_collection.collection.objects.link(obj) + + @classmethod + def get_2d_vertices_from_obj(cls, obj): + points = [] + vectors = [v.co for v in obj.data.vertices.values()] + for vector in vectors: + point = (vector[0], vector[1]) + points.append(point) + + points.append((vectors[0][0], vectors[0][1])) + return points + + @classmethod + def assign_swept_area_outer_curve_from_2d_vertices(cls, obj, vertices): + body = cls.get_body_representation(obj) + model = tool.Ifc.get() + extrusion = tool.Model.get_extrusion(body) + area = extrusion.SweptArea + old_area = area.OuterCurve + + builder = ifcopenshell.util.shape_builder.ShapeBuilder(model) + outer_curve = builder.polyline(vertices, closed = True) + + area.OuterCurve = outer_curve + ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_area) + + @classmethod + def get_body_representation(cls, obj): + element = tool.Ifc.get_entity(obj) + model = tool.Ifc.get() + body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") + return body + + @classmethod + def assign_type_to_obj(cls, obj): + relating_type_id = bpy.context.scene.BIMModelProperties.relating_type_id + relating_type = tool.Ifc.get().by_id(int(relating_type_id)) + ifc_class = relating_type.is_a() + instance_class = ifcopenshell.util.type.get_applicable_entities(ifc_class, tool.Ifc.get().schema)[0] + bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class) + element = tool.Ifc.get_entity(obj) + blenderbim.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type) + + @classmethod + def assign_container_to_obj(cls, obj): + active_obj = bpy.context.active_object + element = tool.Ifc.get_entity(active_obj) + container = ifcopenshell.util.element.get_container(element) + container_obj = tool.Ifc.get_object(container) + blenderbim.core.spatial.assign_container( + tool.Ifc, tool.Collector, tool.Spatial, structure_obj=container_obj, element_obj=obj + ) + + @classmethod + def regen_obj_representation(cls, ifc, geometry, obj, body): + blenderbim.core.geometry.switch_representation( + ifc, + geometry, + obj=obj, + representation=body, + should_reload=True, + is_global=True, + should_sync_changes_first=False, + ) + @classmethod def toggle_spaces_visibility_wired_and_textured(cls, spaces): first_obj = tool.Ifc.get_object(spaces[0]) diff --git a/src/blenderbim/blenderbim_icons.blend b/src/blenderbim/blenderbim_icons.blend index df16936ee6..387e6815f1 100644 Binary files a/src/blenderbim/blenderbim_icons.blend and b/src/blenderbim/blenderbim_icons.blend differ diff --git a/src/blenderbim/pytest.ini b/src/blenderbim/pytest.ini index 4b4d9ae52c..2f55059a87 100644 --- a/src/blenderbim/pytest.ini +++ b/src/blenderbim/pytest.ini @@ -7,6 +7,7 @@ markers = classification context cost + covering debug demo document diff --git a/src/blenderbim/test/bim/feature/covering.feature b/src/blenderbim/test/bim/feature/covering.feature new file mode 100644 index 0000000000..bb7bdaf45d --- /dev/null +++ b/src/blenderbim/test/bim/feature/covering.feature @@ -0,0 +1,15 @@ +@covering +Feature: Covering + Covers covering tool. + +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.add_instance_flooring_coverings_from_walls" + Then nothing happens +