# -*- 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, PropertyGroup, Object, Panel from bpy.props import ( FloatVectorProperty, CollectionProperty, FloatProperty, EnumProperty ) from mathutils import Vector from .bmesh_utils import BmeshEdit as bmed from .archipack_object import ArchipackCollectionManager def update(self, context): self.update(context) class archipack_reference_point(PropertyGroup): location_2d : FloatVectorProperty( subtype='XYZ', name="position 2d", default=Vector((0, 0, 0)) ) location_3d : FloatVectorProperty( subtype='XYZ', name="position 3d", default=Vector((0, 0, 0)) ) symbol_scale : FloatProperty( name="Screen scale", default=1, min=0.01, update=update) symbol_type : EnumProperty( name="Symbol type", default='WALL', items=( ('WALL', 'Wall', '', 0), ('ROOF', 'Roof', '', 1)), update=update) @classmethod def filter(cls, o): """ Filter object with this class in data return True when object contains this datablock False otherwise usage: class_name.filter(object) from outside world self.__class__.filter(object) from instance """ try: return cls.__name__ in o except: pass return False @classmethod def datablock(cls, o): """ Retrieve datablock from base object return datablock when found None when not found usage: class_name.datablock(object) from outside world self.__class__.datablock(object) from instance """ try: return getattr(o, cls.__name__)[0] except: pass return None def update(self, context): o = context.active_object if self.datablock(o) != self: return s = self.symbol_scale if self.symbol_type == 'WALL': verts = [(s * x, s * y, s * z) for x, y, z in [ (-0.25, 0.25, 0.0), (0.25, 0.25, 0.0), (-0.25, -0.25, 0.0), (0.25, -0.25, 0.0), (0.0, 0.0, 0.487), (-0.107, 0.107, 0.216), (0.108, 0.107, 0.216), (-0.107, -0.107, 0.216), (0.108, -0.107, 0.216), (-0.05, 0.05, 0.5), (0.05, 0.05, 0.5), (0.05, -0.05, 0.5), (-0.05, -0.05, 0.5), (-0.193, 0.193, 0.0), (0.193, 0.193, 0.0), (0.193, -0.193, 0.0), (-0.193, -0.193, 0.0), (0.0, 0.0, 0.8), (0.0, 0.8, -0.0), (0.0, 0.0, -0.0), (0.0, 0.0, 0.0), (0.05, 0.05, 0.674), (-0.05, 0.674, -0.05), (0.0, 0.8, -0.0), (-0.05, -0.05, 0.674), (-0.05, 0.674, 0.05), (0.05, 0.674, -0.05), (-0.129, 0.129, 0.162), (0.129, 0.129, 0.162), (-0.129, -0.129, 0.162), (0.129, -0.129, 0.162), (0.0, 0.0, 0.8), (-0.05, 0.05, 0.674), (0.05, -0.05, 0.674), (0.05, 0.674, 0.05), (0.8, -0.0, -0.0), (0.0, -0.0, -0.0), (0.674, 0.05, -0.05), (0.8, -0.0, -0.0), (0.674, 0.05, 0.05), (0.674, -0.05, -0.05), (0.674, -0.05, 0.05)]] edges = [(1, 0), (0, 9), (9, 10), (10, 1), (3, 1), (10, 11), (11, 3), (2, 3), (11, 12), (12, 2), (0, 2), (12, 9), (6, 5), (8, 6), (7, 8), (5, 7), (17, 24), (17, 20), (18, 25), (18, 19), (13, 14), (14, 15), (15, 16), (16, 13), (4, 6), (15, 30), (17, 21), (26, 22), (23, 22), (23, 34), (18, 26), (28, 27), (30, 28), (29, 30), (27, 29), (14, 28), (13, 27), (16, 29), (4, 7), (4, 8), (4, 5), (31, 33), (31, 32), (21, 32), (24, 32), (24, 33), (21, 33), (25, 22), (25, 34), (26, 34), (35, 39), (35, 36), (40, 37), (38, 37), (38, 41), (35, 40), (39, 37), (39, 41), (40, 41)] elif self.symbol_type == 'ROOF': verts = [(s * x, s * y, s * z) for x, y, z in [ (-0.25, 0.25, 0.0), (0.25, 0.25, 0.0), (-0.25, -0.25, 0.0), (0.25, -0.25, 0.0), (0.0, 0.0, 0.487), (-0.107, 0.107, 0.216), (0.108, 0.107, 0.216), (-0.107, -0.107, 0.216), (0.108, -0.107, 0.216), (-0.05, 0.05, 0.5), (0.05, 0.05, 0.5), (0.05, -0.05, 0.5), (-0.05, -0.05, 0.5), (-0.193, 0.193, 0.0), (0.193, 0.193, 0.0), (0.193, -0.193, 0.0), (-0.193, -0.193, 0.0), (0.0, 0.0, 0.8), (0.0, 0.8, -0.0), (0.0, 0.0, 0.0), (0.05, 0.05, 0.673), (-0.05, 0.674, -0.05), (-0.05, -0.05, 0.673), (-0.05, 0.674, 0.05), (0.05, 0.674, -0.05), (-0.129, 0.129, 0.162), (0.129, 0.129, 0.162), (-0.129, -0.129, 0.162), (0.129, -0.129, 0.162), (-0.05, 0.05, 0.673), (0.05, -0.05, 0.673), (0.05, 0.674, 0.05), (0.8, -0.0, -0.0), (0.674, 0.05, -0.05), (0.674, 0.05, 0.05), (0.674, -0.05, -0.05), (0.674, -0.05, 0.05), (0.108, 0.0, 0.216), (0.09, 0.0, 0.261), (0.001, 0.107, 0.216), (0.001, -0.107, 0.216), (-0.107, 0.0, 0.216), (0.0, -0.089, 0.261), (0.0, 0.089, 0.261), (-0.089, 0.0, 0.261), (0.0, 0.042, 0.694), (-0.042, 0.0, 0.694), (0.0, -0.042, 0.694), (0.042, 0.0, 0.694)]] edges = [ (1, 0), (0, 9), (10, 1), (3, 1), (11, 3), (2, 3), (12, 2), (0, 2), (17, 22), (17, 19), (18, 23), (13, 14), (14, 15), (15, 16), (16, 13), (15, 28), (17, 20), (24, 21), (18, 24), (14, 26), (13, 25), (16, 27), (45, 29), (46, 29), (47, 30), (48, 30), (23, 21), (23, 31), (24, 31), (32, 34), (35, 33), (32, 35), (34, 33), (34, 36), (35, 36), (28, 37), (6, 38), (26, 37), (26, 39), (25, 39), (5, 43), (5, 44), (25, 41), (27, 41), (7, 44), (8, 42), (28, 40), (27, 40), (20, 45), (22, 46), (22, 47), (20, 48), (18, 19), (18, 21), (18, 31), (17, 30), (17, 29), (32, 19), (32, 33), (32, 36), (4, 6), (4, 7), (4, 8), (4, 5), (8, 38), (6, 43), (7, 42), (9, 10), (10, 11), (11, 12), (12, 9)] bm = bmed._start(context, o) bm.clear() for v in verts: bm.verts.new(v) bm.verts.ensure_lookup_table() for ed in edges: bm.edges.new((bm.verts[ed[0]], bm.verts[ed[1]])) bmed._end(bm, o) class ARCHIPACK_PT_reference_point(Panel): bl_idname = "ARCHIPACK_PT_reference_point" bl_label = "Reference point" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Archipack' @classmethod def poll(cls, context): return archipack_reference_point.filter(context.active_object) def draw(self, context): o = context.active_object props = archipack_reference_point.datablock(o) if props is None: return layout = self.layout if (o.location - props.location_2d).length < 0.01: layout.operator('archipack.move_to_3d') layout.operator('archipack.move_2d_reference_to_cursor') else: layout.operator('archipack.move_to_2d') layout.prop(props, 'symbol_scale') layout.separator() layout.operator('archipack.apply_holes') class ARCHIPACK_OT_reference_point(ArchipackCollectionManager, Operator): """Add reference point""" bl_idname = "archipack.reference_point" bl_label = "Reference point" bl_description = "Add reference point" bl_category = 'Archipack' bl_options = {'REGISTER', 'UNDO'} location_3d : FloatVectorProperty( subtype='XYZ', name="position 3d", default=Vector((0, 0, 0)) ) symbol_type : EnumProperty( name="Symbol type", default='WALL', items=( ('WALL', 'Wall', '', 0), ('ROOF', 'Roof', '', 1)) ) @classmethod def poll(cls, context): return context.active_object is not None def draw(self, context): layout = self.layout row = layout.row() row.label(text="Use Properties panel (N) to define parms", icon='INFO') def create(self, context): x, y, z = context.scene.cursor.location # bpy.ops.object.empty_add(type='ARROWS', radius=0.5, location=Vector((x, y, 0))) m = bpy.data.meshes.new(name="Reference") o = bpy.data.objects.new("Reference", m) o.location = Vector((x, y, 0)) self.link_object_to_scene(context, o) d = o.archipack_reference_point.add() d.location_2d = Vector((x, y, 0)) d.location_3d = self.location_3d d.symbol_type = self.symbol_type o.select_set(state=True) context.view_layer.objects.active = o d.update(context) return o def execute(self, context): if context.mode == "OBJECT": o = self.create(context) 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'} class ARCHIPACK_OT_move_to_3d(Operator): bl_idname = "archipack.move_to_3d" bl_label = "Move to 3d" bl_description = "Move point to 3d position" bl_category = 'Archipack' bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): return archipack_reference_point.filter(context.active_object) def execute(self, context): if context.mode == "OBJECT": o = context.active_object props = archipack_reference_point.datablock(o) if props is None: return {'CANCELLED'} o.location = props.location_3d return {'FINISHED'} else: self.report({'WARNING'}, "Archipack: Option only valid in Object mode") return {'CANCELLED'} class ARCHIPACK_OT_apply_holes(Operator): bl_idname = "archipack.apply_holes" bl_label = "Apply holes" bl_description = "Apply and remove holes from scene" bl_category = 'Archipack' bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): return archipack_reference_point.filter(context.active_object) def apply_boolean(self, context, o): # mods = [m for m in o.modifiers if m.type == 'BOOLEAN'] ctx = bpy.context.copy() ctx['object'] = o for mod in o.modifiers[:]: ctx['modifier'] = mod try: bpy.ops.object.modifier_apply(ctx, modifier=mod.name) except: pass def execute(self, context): if context.mode == "OBJECT": o = context.active_object to_remove = [] for c in o.children: if 'archipack_hybridhole' in c: self.apply_boolean(context, c) to_remove.append(c) for c in o.children: if c.data is not None and "archipack_wall2" in c.data: self.apply_boolean(context, c) for c in o.children: if c.data is not None and ( "archipack_window" in c.data or "archipack_door" in c.data): for h in c.children: if "archipack_hole" in h: to_remove.append(h) bpy.ops.object.select_all(action="DESELECT") for r in to_remove: r.hide_select = False r.select_set(state=True) context.view_layer.objects.active = r bpy.ops.object.delete(use_global=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'} class ARCHIPACK_OT_move_to_2d(Operator): bl_idname = "archipack.move_to_2d" bl_label = "Move to 2d" bl_description = "Move point to 2d position" bl_category = 'Archipack' bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): return archipack_reference_point.filter(context.active_object) def execute(self, context): if context.mode == "OBJECT": o = context.active_object props = archipack_reference_point.datablock(o) if props is None: return {'CANCELLED'} props.location_3d = o.location o.location = props.location_2d return {'FINISHED'} else: self.report({'WARNING'}, "Archipack: Option only valid in Object mode") return {'CANCELLED'} class ARCHIPACK_OT_store_2d_reference(Operator): bl_idname = "archipack.store_2d_reference" bl_label = "Set 2d" bl_description = "Set 2d reference position" bl_category = 'Archipack' bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): return archipack_reference_point.filter(context.active_object) def execute(self, context): if context.mode == "OBJECT": o = context.active_object props = archipack_reference_point.datablock(o) if props is None: return {'CANCELLED'} x, y, z = o.location props.location_2d = Vector((x, y, 0)) return {'FINISHED'} else: self.report({'WARNING'}, "Archipack: Option only valid in Object mode") return {'CANCELLED'} class ARCHIPACK_OT_move_2d_reference_to_cursor(Operator): bl_idname = "archipack.move_2d_reference_to_cursor" bl_label = "Change 2d" bl_description = "Change 2d reference position to cursor location without moving childs" bl_category = 'Archipack' bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): return archipack_reference_point.filter(context.active_object) def execute(self, context): if context.mode == "OBJECT": o = context.active_object props = archipack_reference_point.datablock(o) if props is None: return {'CANCELLED'} bpy.ops.object.select_all(action="DESELECT") bpy.ops.archipack.reference_point(location_3d=props.location_3d) for child in o.children: child.select_set(state=True) bpy.ops.archipack.parent_to_reference() self.unlink_object_from_scene(o) return {'FINISHED'} else: self.report({'WARNING'}, "Archipack: Option only valid in Object mode") return {'CANCELLED'} class ARCHIPACK_OT_parent_to_reference(Operator): bl_idname = "archipack.parent_to_reference" bl_label = "Parent" bl_description = "Make selected object childs of parent reference point" bl_category = 'Archipack' bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): return archipack_reference_point.filter(context.active_object) def execute(self, context): if context.mode == "OBJECT": o = context.active_object props = archipack_reference_point.datablock(o) if props is None: return {'CANCELLED'} sel = [obj for obj in context.selected_objects if obj != o and obj.parent != o] itM = o.matrix_world.inverted() # print("parent_to_reference parenting:%s objects" % (len(sel))) for child in sel: rs = child.matrix_world.to_3x3().to_4x4() loc = itM @ child.matrix_world.translation child.parent = None child.matrix_parent_inverse.identity() child.location = Vector((0, 0, 0)) child.parent = o child.matrix_world = rs child.location = loc return {'FINISHED'} else: self.report({'WARNING'}, "Archipack: Option only valid in Object mode") return {'CANCELLED'} def register(): bpy.utils.register_class(archipack_reference_point) Object.archipack_reference_point = CollectionProperty(type=archipack_reference_point) bpy.utils.register_class(ARCHIPACK_PT_reference_point) bpy.utils.register_class(ARCHIPACK_OT_reference_point) bpy.utils.register_class(ARCHIPACK_OT_move_to_3d) bpy.utils.register_class(ARCHIPACK_OT_move_to_2d) bpy.utils.register_class(ARCHIPACK_OT_store_2d_reference) bpy.utils.register_class(ARCHIPACK_OT_move_2d_reference_to_cursor) bpy.utils.register_class(ARCHIPACK_OT_parent_to_reference) bpy.utils.register_class(ARCHIPACK_OT_apply_holes) def unregister(): bpy.utils.unregister_class(archipack_reference_point) del Object.archipack_reference_point bpy.utils.unregister_class(ARCHIPACK_PT_reference_point) bpy.utils.unregister_class(ARCHIPACK_OT_reference_point) bpy.utils.unregister_class(ARCHIPACK_OT_move_to_3d) bpy.utils.unregister_class(ARCHIPACK_OT_move_to_2d) bpy.utils.unregister_class(ARCHIPACK_OT_store_2d_reference) bpy.utils.unregister_class(ARCHIPACK_OT_move_2d_reference_to_cursor) bpy.utils.unregister_class(ARCHIPACK_OT_parent_to_reference) bpy.utils.unregister_class(ARCHIPACK_OT_apply_holes)