From 722dd8d651a4c98b11ecda8011e8e87f71cd2071 Mon Sep 17 00:00:00 2001 From: Tristan Strathearn Date: Thu, 31 Mar 2022 21:01:35 +1000 Subject: [PATCH] feat: add boolean slice operator --- booleans/__init__.py | 4 +++ booleans/boolean_slice.py | 55 ++++++++++++++++++++++++++++++++++++++ interface/__init__.py | 6 ++++- interface/boolean_menu.py | 31 +++++++++++++++++++++ interface/main_menu.py | 4 +-- interface/main_ui_panel.py | 4 +-- 6 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 booleans/boolean_slice.py create mode 100644 interface/boolean_menu.py diff --git a/booleans/__init__.py b/booleans/__init__.py index 531f7f3..e67130d 100644 --- a/booleans/__init__.py +++ b/booleans/__init__.py @@ -1,14 +1,18 @@ import importlib from . import vanilla +from . import boolean_slice def reload(): importlib.reload(vanilla) + importlib.reload(boolean_slice) def register(): vanilla.register() + boolean_slice.register() def unregister(): vanilla.unregister() + boolean_slice.unregister() diff --git a/booleans/boolean_slice.py b/booleans/boolean_slice.py new file mode 100644 index 0000000..509c800 --- /dev/null +++ b/booleans/boolean_slice.py @@ -0,0 +1,55 @@ +import bpy + + +class ND_OT_bool_slice(bpy.types.Operator): + bl_idname = "nd.bool_slice" + bl_label = "Slice" + bl_description = "Perform a boolean operation on the selected objects" + bl_options = {'UNDO'} + + + @classmethod + def poll(cls, context): + if context.mode == 'OBJECT': + return len(context.selected_objects) == 2 + + + def execute(self, context): + a, b = context.selected_objects + reference_obj = a if a.name != context.object.name else b + + difference_obj = context.object + + intersecting_obj = context.object.copy() + intersecting_obj.data = context.object.data.copy() + intersecting_obj.animation_data_clear() + context.collection.objects.link(intersecting_obj) + + boolean_diff = difference_obj.modifiers.new("Difference — ND Bool", 'BOOLEAN') + boolean_diff.operation = 'DIFFERENCE' + boolean_diff.object = reference_obj + boolean_diff.solver = 'EXACT' + + boolean_isect = intersecting_obj.modifiers.new("Intersection — ND Bool", 'BOOLEAN') + boolean_isect.operation = 'INTERSECT' + boolean_isect.object = reference_obj + boolean_isect.solver = 'EXACT' + + reference_obj.display_type = 'WIRE' + reference_obj.hide_render = True + + return {'FINISHED'} + + +def menu_func(self, context): + self.layout.operator(ND_OT_bool_slice.bl_idname, text=ND_OT_bool_slice.bl_label) + + +def register(): + bpy.utils.register_class(ND_OT_bool_slice) + bpy.types.VIEW3D_MT_object.append(menu_func) + + +def unregister(): + bpy.utils.unregister_class(ND_OT_bool_slice) + bpy.types.VIEW3D_MT_object.remove(menu_func) diff --git a/interface/__init__.py b/interface/__init__.py index 6c2b198..8dc1c4a 100644 --- a/interface/__init__.py +++ b/interface/__init__.py @@ -3,6 +3,7 @@ from . import utils_ui_panel from . import main_menu from . import utils_menu +from . import boolean_menu def reload(): @@ -10,6 +11,7 @@ def reload(): importlib.reload(utils_ui_panel) importlib.reload(main_menu) importlib.reload(utils_menu) + importlib.reload(boolean_menu) def register(): @@ -17,10 +19,12 @@ def register(): utils_ui_panel.register() main_menu.register() utils_menu.register() + boolean_menu.register() def unregister(): main_ui_panel.unregister() utils_ui_panel.unregister() main_menu.unregister() - utils_menu.unregister() \ No newline at end of file + utils_menu.unregister() + boolean_menu.unregister() \ No newline at end of file diff --git a/interface/boolean_menu.py b/interface/boolean_menu.py new file mode 100644 index 0000000..5503a82 --- /dev/null +++ b/interface/boolean_menu.py @@ -0,0 +1,31 @@ +import bpy +from .. import bl_info + + +class ND_MT_boolean_menu(bpy.types.Menu): + bl_label = "Booleans" + bl_idname = "ND_MT_boolean_menu" + + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_DEFAULT' + layout.operator("nd.bool_vanilla", text="Difference", icon='MOD_BOOLEAN').mode = 'DIFFERENCE' + layout.operator("nd.bool_vanilla", text="Union", icon='MOD_BOOLEAN').mode = 'UNION' + layout.operator("nd.bool_vanilla", text="Intersect", icon='MOD_BOOLEAN').mode = 'INTERSECT' + layout.operator("nd.bool_slice", icon='MOD_BOOLEAN') + + +def draw_item(self, context): + layout = self.layout + layout.menu(ND_MT_boolean_menu.bl_idname) + + +def register(): + bpy.utils.register_class(ND_MT_boolean_menu) + bpy.types.INFO_HT_header.append(draw_item) + + +def unregister(): + bpy.utils.unregister_class(ND_MT_boolean_menu) + bpy.types.INFO_HT_header.remove(draw_item) diff --git a/interface/main_menu.py b/interface/main_menu.py index 37b6883..565eef8 100644 --- a/interface/main_menu.py +++ b/interface/main_menu.py @@ -18,9 +18,7 @@ def draw(self, context): layout.operator("nd.blank_sketch", icon='GREASEPENCIL') layout.operator("nd.vertex_bevel", icon='MOD_BEVEL') layout.separator() - layout.operator("nd.bool_vanilla", text="Difference", icon='MOD_BOOLEAN').mode = 'DIFFERENCE' - layout.operator("nd.bool_vanilla", text="Union", icon='MOD_BOOLEAN').mode = 'UNION' - layout.operator("nd.bool_vanilla", text="Intersect", icon='MOD_BOOLEAN').mode = 'INTERSECT' + layout.menu("ND_MT_boolean_menu", icon='MOD_BOOLEAN') layout.operator("nd.weighted_normal_bevel", icon='MOD_BEVEL') layout.operator("nd.solidify", icon='MOD_SOLIDIFY') layout.operator("nd.screw", icon='MOD_SCREW') diff --git a/interface/main_ui_panel.py b/interface/main_ui_panel.py index b1c5978..38bb925 100644 --- a/interface/main_ui_panel.py +++ b/interface/main_ui_panel.py @@ -59,14 +59,12 @@ def draw(self, context): row = column.row(align=True) row.scale_y = 1.2 row.operator("nd.bool_vanilla", text="Difference", icon='MOD_BOOLEAN').mode = 'DIFFERENCE' - - row = column.row(align=True) - row.scale_y = 1.2 row.operator("nd.bool_vanilla", text="Union", icon='MOD_BOOLEAN').mode = 'UNION' row = column.row(align=True) row.scale_y = 1.2 row.operator("nd.bool_vanilla", text="Intersect", icon='MOD_BOOLEAN').mode = 'INTERSECT' + row.operator("nd.bool_slice", icon='MOD_BOOLEAN') row = column.row(align=True) row.scale_y = 1.2