diff --git a/src/blenderbim/blenderbim/bim/module/model/__init__.py b/src/blenderbim/blenderbim/bim/module/model/__init__.py index 98cb2da069..d23386d63f 100644 --- a/src/blenderbim/blenderbim/bim/module/model/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/model/__init__.py @@ -3,7 +3,8 @@ from . import handler, prop, ui, grid, product, wall, slab, stair, door, window, classes = ( product.AddTypeInstance, - workspace.HotkeyE, + product.AlignProduct, + workspace.Hotkey, wall.JoinWall, wall.AlignWall, wall.FlipWall, diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 3e84c223ee..5b137c891e 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -1,4 +1,5 @@ import bpy +import mathutils import ifcopenshell import ifcopenshell.api import ifcopenshell.util.element @@ -6,7 +7,7 @@ import ifcopenshell.util.representation from . import wall, slab, column from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.pset.data import Data as PsetData -from mathutils import Vector +from mathutils import Vector, Matrix class AddTypeInstance(bpy.types.Operator): @@ -73,6 +74,52 @@ class AddTypeInstance(bpy.types.Operator): return {"FINISHED"} +class AlignProduct(bpy.types.Operator): + bl_idname = "bim.align_product" + bl_label = "Align Wall" + bl_options = {"REGISTER", "UNDO"} + align_type: bpy.props.StringProperty() + + def execute(self, context): + selected_objs = context.selected_objects + if len(selected_objs) < 2 or not context.active_object: + return {"FINISHED"} + if self.align_type == "CENTERLINE": + point = context.active_object.matrix_world @ ( + Vector(context.active_object.bound_box[0]) + (context.active_object.dimensions / 2) + ) + elif self.align_type == "POSITIVE": + point = context.active_object.matrix_world @ Vector(context.active_object.bound_box[6]) + elif self.align_type == "NEGATIVE": + point = context.active_object.matrix_world @ Vector(context.active_object.bound_box[0]) + + active_x_axis = context.active_object.matrix_world.to_quaternion() @ Vector((1, 0, 0)) + active_y_axis = context.active_object.matrix_world.to_quaternion() @ Vector((0, 1, 0)) + active_z_axis = context.active_object.matrix_world.to_quaternion() @ Vector((0, 0, 1)) + + x_distances = self.get_axis_distances(point, active_x_axis) + y_distances = self.get_axis_distances(point, active_y_axis) + if abs(sum(x_distances)) < abs(sum(y_distances)): + for i, obj in enumerate(selected_objs): + obj.matrix_world = Matrix.Translation(active_x_axis * -x_distances[i]) @ obj.matrix_world + else: + for i, obj in enumerate(selected_objs): + obj.matrix_world = Matrix.Translation(active_y_axis * -y_distances[i]) @ obj.matrix_world + return {"FINISHED"} + + def get_axis_distances(self, point, axis): + results = [] + for obj in bpy.context.selected_objects: + if self.align_type == "CENTERLINE": + obj_point = obj.matrix_world @ (Vector(obj.bound_box[0]) + (obj.dimensions / 2)) + elif self.align_type == "POSITIVE": + obj_point = obj.matrix_world @ Vector(obj.bound_box[6]) + elif self.align_type == "NEGATIVE": + obj_point = obj.matrix_world @ Vector(obj.bound_box[0]) + results.append(mathutils.geometry.distance_point_to_plane(obj_point, point, axis)) + return results + + def generate_box(usecase_path, ifc_file, settings): box_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Box", "MODEL_VIEW") if not box_context: diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index cc58461146..f14eaf5c35 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -18,26 +18,14 @@ class BimTool(WorkSpaceTool): # ("bim.wall_tool_op", {"type": 'MOUSEMOVE', "value": 'ANY'}, {"properties": []}), # ("mesh.add_wall", {"type": 'LEFTMOUSE', "value": 'PRESS'}, {"properties": []}), ("bim.add_type_instance", {"type": "A", "value": "PRESS", "shift": True}, {"properties": []}), - ("bim.hotkey_e", {"type": "E", "value": "PRESS", "shift": True}, {"properties": []}), + ("bim.hotkey", {"type": "E", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "E")]}), ("bim.join_wall", {"type": "T", "value": "PRESS", "shift": True}, {"properties": [("join_type", "L")]}), ("bim.join_wall", {"type": "Y", "value": "PRESS", "shift": True}, {"properties": [("join_type", "V")]}), ("bim.flip_wall", {"type": "F", "value": "PRESS", "shift": True}, {"properties": []}), ("bim.split_wall", {"type": "S", "value": "PRESS", "shift": True}, {"properties": []}), - ( - "bim.align_wall", - {"type": "X", "value": "PRESS", "shift": True}, - {"properties": [("align_type", "EXTERIOR")]}, - ), - ( - "bim.align_wall", - {"type": "C", "value": "PRESS", "shift": True}, - {"properties": [("align_type", "CENTERLINE")]}, - ), - ( - "bim.align_wall", - {"type": "V", "value": "PRESS", "shift": True}, - {"properties": [("align_type", "INTERIOR")]}, - ), + ("bim.hotkey", {"type": "X", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "X")]}), + ("bim.hotkey", {"type": "C", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "C")]}), + ("bim.hotkey", {"type": "V", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "V")]}), ) def draw_settings(context, layout, tool): @@ -57,22 +45,45 @@ class BimTool(WorkSpaceTool): row.label(text="Mitre", icon="EVENT_Y") row.label(text="Flip", icon="EVENT_F") row.label(text="Split", icon="EVENT_S") - row.label(text="", icon="EVENT_X") - row.label(text="", icon="EVENT_C") - row.label(text="", icon="EVENT_V") - row.label(text="Align") + + row.label(text="", icon="EVENT_X") + row.label(text="", icon="EVENT_C") + row.label(text="", icon="EVENT_V") + row.label(text="Align") -class HotkeyE(bpy.types.Operator): - bl_idname = "bim.hotkey_e" - bl_label = "Hotkey E" +class Hotkey(bpy.types.Operator): + bl_idname = "bim.hotkey" + bl_label = "Hotkey" bl_options = {"REGISTER", "UNDO"} + hotkey: bpy.props.StringProperty() def execute(self, context): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): - props = context.scene.BIMTypeProperties - if props.ifc_class == "IfcWallType": - bpy.ops.bim.join_wall(join_type="T") + self.props = context.scene.BIMTypeProperties + getattr(self, f"hotkey_{self.hotkey}")() return {"FINISHED"} + + def hotkey_C(self): + if self.props.ifc_class == "IfcWallType": + bpy.ops.bim.align_wall(align_type="CENTERLINE") + else: + bpy.ops.bim.align_product(align_type="CENTERLINE") + + def hotkey_E(self): + if self.props.ifc_class == "IfcWallType": + bpy.ops.bim.join_wall(join_type="T") + + def hotkey_V(self): + if self.props.ifc_class == "IfcWallType": + bpy.ops.bim.align_wall(align_type="INTERIOR") + else: + bpy.ops.bim.align_product(align_type="POSITIVE") + + def hotkey_X(self): + if self.props.ifc_class == "IfcWallType": + bpy.ops.bim.align_wall(align_type="EXTERIOR") + else: + bpy.ops.bim.align_product(align_type="NEGATIVE")