From d7517a6f2a69071eab53c02a645f7651ccfffd45 Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Tue, 23 Nov 2021 09:24:55 +0100 Subject: Remove Archipack to reflect new key requirements. https://wiki.blender.org/wiki/Process/Addons --- archipack/archipack_autoboolean.py | 662 ------------------------------------- 1 file changed, 662 deletions(-) delete mode 100644 archipack/archipack_autoboolean.py (limited to 'archipack/archipack_autoboolean.py') diff --git a/archipack/archipack_autoboolean.py b/archipack/archipack_autoboolean.py deleted file mode 100644 index 3a424728..00000000 --- a/archipack/archipack_autoboolean.py +++ /dev/null @@ -1,662 +0,0 @@ -# -*- coding:utf-8 -*- - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program 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 2 -# of the License, or (at your option) any later version. -# -# This program 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 this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -# ---------------------------------------------------------- -# Author: Stephen Leger (s-leger) -# -# ---------------------------------------------------------- -import bpy -from bpy.types import Operator -from bpy.props import EnumProperty -from mathutils import Vector -from . archipack_object import ArchipackCollectionManager - -class ArchipackBoolManager(ArchipackCollectionManager): - """ - Handle three methods for booleans - - interactive: one modifier for each hole right on wall - - robust: one single modifier on wall and merge holes in one mesh - - mixed: merge holes with boolean and use result on wall - may be slow, but is robust - """ - def __init__(self, mode): - """ - mode in 'ROBUST', 'INTERACTIVE', 'HYBRID' - """ - self.mode = mode - # internal variables - self.itM = None - self.min_x = 0 - self.min_y = 0 - self.min_z = 0 - self.max_x = 0 - self.max_y = 0 - self.max_z = 0 - - def _get_bounding_box(self, wall): - self.itM = wall.matrix_world.inverted() - x, y, z = wall.bound_box[0] - self.min_x = x - self.min_y = y - self.min_z = z - x, y, z = wall.bound_box[6] - self.max_x = x - self.max_y = y - self.max_z = z - self.center = Vector(( - self.min_x + 0.5 * (self.max_x - self.min_x), - self.min_y + 0.5 * (self.max_y - self.min_y), - self.min_z + 0.5 * (self.max_z - self.min_z))) - - def _contains(self, pt): - p = self.itM @ pt - return (p.x >= self.min_x and p.x <= self.max_x and - p.y >= self.min_y and p.y <= self.max_y and - p.z >= self.min_z and p.z <= self.max_z) - - def filter_wall(self, wall): - d = wall.data - return (d is None or - 'archipack_window' in d or - 'archipack_window_panel' in d or - 'archipack_door' in d or - 'archipack_doorpanel' in d or - 'archipack_hole' in wall or - 'archipack_robusthole' in wall or - 'archipack_handle' in wall) - - def datablock(self, o): - """ - get datablock from windows and doors - return - datablock if found - None when not found - """ - d = None - if o.data is None: - return - if "archipack_window" in o.data: - d = o.data.archipack_window[0] - elif "archipack_door" in o.data: - d = o.data.archipack_door[0] - return d - - def prepare_hole(self, hole): - hole.lock_location = (True, True, True) - hole.lock_rotation = (True, True, True) - hole.lock_scale = (True, True, True) - hole.display_type = 'WIRE' - hole.hide_render = True - hole.hide_select = True - hole.select_set(state=True) - - def get_child_hole(self, o): - for hole in o.children: - if "archipack_hole" in hole: - return hole - return None - - def _generate_hole(self, context, o): - # use existing one - if self.mode != 'ROBUST': - hole = self.get_child_hole(o) - if hole is not None: - # print("_generate_hole Use existing hole %s" % (hole.name)) - return hole - # generate single hole from archipack primitives - d = self.datablock(o) - hole = None - if d is not None: - if (self.itM is not None and ( - self._contains(o.location) or - self._contains(o.matrix_world @ Vector((0, 0, 0.5 * d.z)))) - ): - if self.mode != 'ROBUST': - hole = d.interactive_hole(context, o) - else: - hole = d.robust_hole(context, o.matrix_world) - # print("_generate_hole Generate hole %s" % (hole.name)) - else: - hole = d.interactive_hole(context, o) - return hole - - def partition(self, array, begin, end): - pivot = begin - for i in range(begin + 1, end + 1): - if array[i][1] <= array[begin][1]: - pivot += 1 - array[i], array[pivot] = array[pivot], array[i] - array[pivot], array[begin] = array[begin], array[pivot] - return pivot - - def quicksort(self, array, begin=0, end=None): - if end is None: - end = len(array) - 1 - - def _quicksort(array, begin, end): - if begin >= end: - return - pivot = self.partition(array, begin, end) - _quicksort(array, begin, pivot - 1) - _quicksort(array, pivot + 1, end) - return _quicksort(array, begin, end) - - def sort_holes(self, wall, holes): - """ - sort hole from center to borders by distance from center - may improve nested booleans - """ - center = wall.matrix_world @ self.center - holes = [(o, (o.matrix_world.translation - center).length) for o in holes] - self.quicksort(holes) - return [o[0] for o in holes] - - def difference(self, basis, hole, solver=None): - # print("difference %s" % (hole.name)) - m = basis.modifiers.new('AutoBoolean', 'BOOLEAN') - m.operation = 'DIFFERENCE' - m.object = hole - - def union(self, basis, hole): - # print("union %s" % (hole.name)) - m = basis.modifiers.new('AutoMerge', 'BOOLEAN') - m.operation = 'UNION' - m.object = hole - - def remove_modif_and_object(self, context, o, to_delete): - # print("remove_modif_and_object removed:%s" % (len(to_delete))) - for m, h in to_delete: - if m is not None: - if m.object is not None: - m.object = None - o.modifiers.remove(m) - if h is not None: - self.unlink_object_from_scene(h) - bpy.data.objects.remove(h, do_unlink=True) - - # Mixed - def create_merge_basis(self, context, wall): - # print("create_merge_basis") - h = bpy.data.meshes.new("AutoBoolean") - hole_obj = bpy.data.objects.new("AutoBoolean", h) - self.link_object_to_scene(context, hole_obj) - hole_obj['archipack_hybridhole'] = True - if wall.parent is not None: - hole_obj.parent = wall.parent - hole_obj.matrix_world = wall.matrix_world.copy() - for mat in wall.data.materials: - hole_obj.data.materials.append(mat) - # MaterialUtils.add_wall2_materials(hole_obj) - return hole_obj - - def update_hybrid(self, context, wall, childs, holes): - """ - Update all holes modifiers - remove holes not found in childs - - robust -> mixed: - there is only one object tagged with "archipack_robusthole" - interactive -> mixed: - many modifisers on wall tagged with "archipack_hole" - keep objects - """ - existing = [] - to_delete = [] - - # robust/interactive -> mixed - for m in wall.modifiers: - if m.type == 'BOOLEAN': - if m.object is None: - to_delete.append([m, None]) - elif 'archipack_hole' in m.object: - h = m.object - if h in holes: - to_delete.append([m, None]) - else: - to_delete.append([m, h]) - elif 'archipack_robusthole' in m.object: - to_delete.append([m, m.object]) - - # remove modifier and holes not found in new list - self.remove_modif_and_object(context, wall, to_delete) - - m = wall.modifiers.get("AutoMixedBoolean") - if m is None: - m = wall.modifiers.new('AutoMixedBoolean', 'BOOLEAN') - m.operation = 'DIFFERENCE' - - if m.object is None: - hole_obj = self.create_merge_basis(context, wall) - else: - hole_obj = m.object - - m.object = hole_obj - self.prepare_hole(hole_obj) - - to_delete = [] - - # mixed-> mixed - for m in hole_obj.modifiers: - h = m.object - if h in holes: - existing.append(h) - else: - to_delete.append([m, h]) - - # remove modifier and holes not found in new list - self.remove_modif_and_object(context, hole_obj, to_delete) - - # add modifier and holes not found in existing - for h in holes: - if h not in existing: - self.union(hole_obj, h) - - # Interactive - def update_interactive(self, context, wall, childs, holes): - - existing = [] - - to_delete = [] - - hole_obj = None - - # mixed-> interactive - for m in wall.modifiers: - if m.type == 'BOOLEAN': - if m.object is not None and 'archipack_hybridhole' in m.object: - hole_obj = m.object - break - - if hole_obj is not None: - for m in hole_obj.modifiers: - h = m.object - if h not in holes: - to_delete.append([m, h]) - # remove modifier and holes not found in new list - self.remove_modif_and_object(context, hole_obj, to_delete) - self.unlink_object_from_scene(hole_obj) - bpy.data.objects.remove(hole_obj, do_unlink=True) - - to_delete = [] - - # interactive/robust -> interactive - for m in wall.modifiers: - if m.type == 'BOOLEAN': - if m.object is None: - to_delete.append([m, None]) - elif 'archipack_hole' in m.object: - h = m.object - if h in holes: - existing.append(h) - else: - to_delete.append([m, h]) - elif 'archipack_robusthole' in m.object: - to_delete.append([m, m.object]) - - # remove modifier and holes not found in new list - self.remove_modif_and_object(context, wall, to_delete) - - # add modifier and holes not found in existing - for h in holes: - if h not in existing: - self.difference(wall, h) - - # Robust - def update_robust(self, context, wall, childs): - - modif = None - - to_delete = [] - - # robust/interactive/mixed -> robust - for m in wall.modifiers: - if m.type == 'BOOLEAN': - if m.object is None: - to_delete.append([m, None]) - elif 'archipack_robusthole' in m.object: - modif = m - to_delete.append([None, m.object]) - elif 'archipack_hole' in m.object: - to_delete.append([m, m.object]) - elif 'archipack_hybridhole' in m.object: - to_delete.append([m, m.object]) - o = m.object - for m in o.modifiers: - to_delete.append([None, m.object]) - - # remove modifier and holes - self.remove_modif_and_object(context, wall, to_delete) - - if bool(len(context.selected_objects) > 0): - # more than one hole : join, result becomes context.object - if len(context.selected_objects) > 1: - bpy.ops.object.join() - context.object['archipack_robusthole'] = True - - hole = context.object - hole.name = 'AutoBoolean' - - childs.append(hole) - - if modif is None: - self.difference(wall, hole) - else: - modif.object = hole - elif modif is not None: - wall.modifiers.remove(modif) - - def autoboolean(self, context, wall): - """ - Entry point for multi-boolean operations like - in T panel autoBoolean and RobustBoolean buttons - """ - - if wall.data is not None and "archipack_wall2" in wall.data: - # ensure wall modifier is there before any boolean - # to support "revival" of applied modifiers - m = wall.modifiers.get("Wall") - if m is None: - wall.select_set(state=True) - context.view_layer.objects.active = wall - wall.data.archipack_wall2[0].update(context) - - bpy.ops.object.select_all(action='DESELECT') - context.view_layer.objects.active = None - childs = [] - holes = [] - # get wall bounds to find what's inside - self._get_bounding_box(wall) - - # either generate hole or get existing one - for o in context.scene.objects: - h = self._generate_hole(context, o) - if h is not None: - holes.append(h) - childs.append(o) - - self.sort_holes(wall, holes) - - # hole(s) are selected and active after this one - for hole in holes: - # copy wall material to hole - hole.data.materials.clear() - for mat in wall.data.materials: - hole.data.materials.append(mat) - - self.prepare_hole(hole) - - # update / remove / add boolean modifier - if self.mode == 'INTERACTIVE': - self.update_interactive(context, wall, childs, holes) - elif self.mode == 'ROBUST': - self.update_robust(context, wall, childs) - else: - self.update_hybrid(context, wall, childs, holes) - - bpy.ops.object.select_all(action='DESELECT') - # parenting childs to wall reference point - if wall.parent is None: - x, y, z = wall.bound_box[0] - context.scene.cursor.location = wall.matrix_world @ Vector((x, y, z)) - # fix issue #9 - context.view_layer.objects.active = wall - bpy.ops.archipack.reference_point() - else: - wall.parent.select_set(state=True) - context.view_layer.objects.active = wall.parent - - wall.select_set(state=True) - for o in childs: - if 'archipack_robusthole' in o: - o.hide_select = False - o.select_set(state=True) - - bpy.ops.archipack.parent_to_reference() - - for o in childs: - if 'archipack_robusthole' in o: - o.hide_select = True - - def detect_mode(self, context, wall): - for m in wall.modifiers: - if m.type == 'BOOLEAN' and m.object is not None: - if 'archipack_hole' in m.object: - self.mode = 'INTERACTIVE' - if 'archipack_hybridhole' in m.object: - self.mode = 'HYBRID' - if 'archipack_robusthole' in m.object: - self.mode = 'ROBUST' - - def singleboolean(self, context, wall, o): - """ - Entry point for single boolean operations - in use in draw door and windows over wall - o is either a window or a door - """ - - # generate holes for crossing window and doors - self.itM = wall.matrix_world.inverted() - d = self.datablock(o) - - hole = None - hole_obj = None - # default mode defined by __init__ - self.detect_mode(context, wall) - - if d is not None: - if self.mode != 'ROBUST': - hole = d.interactive_hole(context, o) - else: - hole = d.robust_hole(context, o.matrix_world) - if hole is None: - return - - hole.data.materials.clear() - for mat in wall.data.materials: - hole.data.materials.append(mat) - - self.prepare_hole(hole) - - if self.mode == 'INTERACTIVE': - # update / remove / add boolean modifier - self.difference(wall, hole) - - elif self.mode == 'HYBRID': - m = wall.modifiers.get('AutoMixedBoolean') - - if m is None: - m = wall.modifiers.new('AutoMixedBoolean', 'BOOLEAN') - m.operation = 'DIFFERENCE' - - if m.object is None: - hole_obj = self.create_merge_basis(context, wall) - m.object = hole_obj - else: - hole_obj = m.object - self.union(hole_obj, hole) - - bpy.ops.object.select_all(action='DESELECT') - - # parenting childs to wall reference point - if wall.parent is None: - x, y, z = wall.bound_box[0] - context.scene.cursor.location = wall.matrix_world @ Vector((x, y, z)) - # fix issue #9 - context.view_layer.objects.active = wall - bpy.ops.archipack.reference_point() - else: - context.view_layer.objects.active = wall.parent - - if hole_obj is not None: - hole_obj.select_set(state=True) - - wall.select_set(state=True) - o.select_set(state=True) - bpy.ops.archipack.parent_to_reference() - wall.select_set(state=True) - context.view_layer.objects.active = wall - if "archipack_wall2" in wall.data: - d = wall.data.archipack_wall2[0] - g = d.get_generator() - d.setup_childs(wall, g) - d.relocate_childs(context, wall, g) - elif "archipack_roof" in wall.data: - pass - if hole_obj is not None: - self.prepare_hole(hole_obj) - - -class ARCHIPACK_OT_single_boolean(Operator): - bl_idname = "archipack.single_boolean" - bl_label = "SingleBoolean" - bl_description = "Add single boolean for doors and windows" - bl_category = 'Archipack' - bl_options = {'REGISTER', 'UNDO'} - mode : EnumProperty( - name="Mode", - items=( - ('INTERACTIVE', 'INTERACTIVE', 'Interactive, fast but may fail', 0), - ('ROBUST', 'ROBUST', 'Not interactive, robust', 1), - ('HYBRID', 'HYBRID', 'Interactive, slow but robust', 2) - ), - default='HYBRID' - ) - """ - Wall must be active object - window or door must be selected - """ - - @classmethod - def poll(cls, context): - w = context.active_object - return (w is not None and w.data is not None and - ("archipack_wall2" in w.data or - "archipack_wall" in w.data or - "archipack_roof" in w.data) and - len(context.selected_objects) == 2 - ) - - def draw(self, context): - pass - - def execute(self, context): - if context.mode == "OBJECT": - wall = context.active_object - manager = ArchipackBoolManager(mode=self.mode) - for o in context.selected_objects: - if o != wall: - manager.singleboolean(context, wall, o) - o.select_set(state=False) - break - wall.select_set(state=True) - context.view_layer.objects.active = wall - return {'FINISHED'} - else: - self.report({'WARNING'}, "Archipack: Option only valid in Object mode") - return {'CANCELLED'} - - -class ARCHIPACK_OT_auto_boolean(Operator): - bl_idname = "archipack.auto_boolean" - bl_label = "AutoBoolean" - bl_description = "Automatic boolean for doors and windows" - bl_category = 'Archipack' - bl_options = {'REGISTER', 'UNDO'} - mode : EnumProperty( - name="Mode", - items=( - ('INTERACTIVE', 'INTERACTIVE', 'Interactive, fast but may fail', 0), - ('ROBUST', 'ROBUST', 'Not interactive, robust', 1), - ('HYBRID', 'HYBRID', 'Interactive, slow but robust', 2) - ), - default='HYBRID' - ) - - def draw(self, context): - layout = self.layout - row = layout.row() - row.prop(self, 'mode') - - def execute(self, context): - if context.mode == "OBJECT": - manager = ArchipackBoolManager(mode=self.mode) - active = context.view_layer.objects.active - walls = [wall for wall in context.selected_objects if not manager.filter_wall(wall)] - bpy.ops.object.select_all(action='DESELECT') - for wall in walls: - manager.autoboolean(context, wall) - bpy.ops.object.select_all(action='DESELECT') - wall.select_set(state=True) - context.view_layer.objects.active = wall - if wall.data is not None and 'archipack_wall2' in wall.data: - bpy.ops.archipack.wall2_manipulate('EXEC_DEFAULT') - # reselect walls - bpy.ops.object.select_all(action='DESELECT') - for wall in walls: - wall.select_set(state=True) - context.view_layer.objects.active = active - return {'FINISHED'} - else: - self.report({'WARNING'}, "Archipack: Option only valid in Object mode") - return {'CANCELLED'} - - -class ARCHIPACK_OT_generate_hole(Operator): - bl_idname = "archipack.generate_hole" - bl_label = "Generate hole" - bl_description = "Generate interactive hole for doors and windows" - bl_category = 'Archipack' - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - if context.mode == "OBJECT": - manager = ArchipackBoolManager(mode='HYBRID') - o = context.active_object - - d = manager.datablock(o) - if d is None: - self.report({'WARNING'}, "Archipack: active object must be a door, a window or a roof") - return {'CANCELLED'} - bpy.ops.object.select_all(action='DESELECT') - o.select_set(state=True) - context.view_layer.objects.active = o - hole = manager._generate_hole(context, o) - manager.prepare_hole(hole) - hole.select_set(state=False) - o.select_set(state=True) - context.view_layer.objects.active = o - return {'FINISHED'} - else: - self.report({'WARNING'}, "Archipack: Option only valid in Object mode") - return {'CANCELLED'} - - -def register(): - bpy.utils.register_class(ARCHIPACK_OT_generate_hole) - bpy.utils.register_class(ARCHIPACK_OT_single_boolean) - bpy.utils.register_class(ARCHIPACK_OT_auto_boolean) - - -def unregister(): - bpy.utils.unregister_class(ARCHIPACK_OT_generate_hole) - bpy.utils.unregister_class(ARCHIPACK_OT_single_boolean) - bpy.utils.unregister_class(ARCHIPACK_OT_auto_boolean) -- cgit v1.2.3