diff options
author | Stephen Leger <stephen@3dservices.ch> | 2017-07-22 14:25:28 +0300 |
---|---|---|
committer | Stephen Leger <stephen@3dservices.ch> | 2017-07-22 14:26:04 +0300 |
commit | c1ab9b4b9c6c0226f8d7789b92efda9b0f33cfd1 (patch) | |
tree | 37d5a97c758fa9af48d1dfb5428edd72072d882a /archipack/archipack_door.py | |
parent | 5638a8783502138500912061dde0e8ee476d7fca (diff) |
archipack: T52120 release to official
Diffstat (limited to 'archipack/archipack_door.py')
-rw-r--r-- | archipack/archipack_door.py | 1847 |
1 files changed, 1847 insertions, 0 deletions
diff --git a/archipack/archipack_door.py b/archipack/archipack_door.py new file mode 100644 index 00000000..f29c44d1 --- /dev/null +++ b/archipack/archipack_door.py @@ -0,0 +1,1847 @@ +# -*- 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 ##### + +# <pep8 compliant> + +# ---------------------------------------------------------- +# Author: Stephen Leger (s-leger) +# +# ---------------------------------------------------------- + +# noinspection PyUnresolvedReferences +import bpy +# noinspection PyUnresolvedReferences +from bpy.types import Operator, PropertyGroup, Mesh, Panel +from bpy.props import ( + FloatProperty, IntProperty, CollectionProperty, + EnumProperty, BoolProperty, StringProperty + ) +from mathutils import Vector +# door component objects (panels, handles ..) +from .bmesh_utils import BmeshEdit as bmed +from .panel import Panel as DoorPanel +from .materialutils import MaterialUtils +from .archipack_handle import create_handle, door_handle_horizontal_01 +from .archipack_manipulator import Manipulable +from .archipack_preset import ArchipackPreset, PresetMenuOperator +from .archipack_object import ArchipackObject, ArchipackCreateTool, ArchpackDrawTool +from .archipack_gl import FeedbackPanel +from .archipack_keymaps import Keymaps + + +SPACING = 0.005 +BATTUE = 0.01 +BOTTOM_HOLE_MARGIN = 0.001 +FRONT_HOLE_MARGIN = 0.1 + + +def update(self, context): + self.update(context) + + +def update_childs(self, context): + self.update(context, childs_only=True) + + +class archipack_door_panel(ArchipackObject, PropertyGroup): + x = FloatProperty( + name='width', + min=0.25, + default=100.0, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='Width' + ) + y = FloatProperty( + name='Depth', + min=0.001, + default=0.02, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='depth' + ) + z = FloatProperty( + name='height', + min=0.1, + default=2.0, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='height' + ) + direction = IntProperty( + name="Direction", + min=0, + max=1, + description="open direction" + ) + model = IntProperty( + name="model", + min=0, + max=3, + default=0, + description="Model" + ) + chanfer = FloatProperty( + name='chanfer', + min=0.001, + default=0.005, precision=3, + unit='LENGTH', subtype='DISTANCE', + description='chanfer' + ) + panel_spacing = FloatProperty( + name='spacing', + min=0.001, + default=0.1, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='distance between panels' + ) + panel_bottom = FloatProperty( + name='bottom', + min=0.0, + default=0.0, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='distance from bottom' + ) + panel_border = FloatProperty( + name='border', + min=0.001, + default=0.2, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='distance from border' + ) + panels_x = IntProperty( + name="panels h", + min=1, + max=50, + default=1, + description="panels h" + ) + panels_y = IntProperty( + name="panels v", + min=1, + max=50, + default=1, + description="panels v" + ) + panels_distrib = EnumProperty( + name='distribution', + items=( + ('REGULAR', 'Regular', '', 0), + ('ONE_THIRD', '1/3 2/3', '', 1) + ), + default='REGULAR' + ) + handle = EnumProperty( + name='Shape', + items=( + ('NONE', 'No handle', '', 0), + ('BOTH', 'Inside and outside', '', 1) + ), + default='BOTH' + ) + + @property + def panels(self): + + # subdivide side to weld panels + subdiv_x = self.panels_x - 1 + + if self.panels_distrib == 'REGULAR': + subdiv_y = self.panels_y - 1 + else: + subdiv_y = 2 + + # __ y0 + # |__ y1 + # x0 x1 + y0 = -self.y + y1 = 0 + x0 = 0 + x1 = max(0.001, self.panel_border - 0.5 * self.panel_spacing) + + side = DoorPanel( + False, # profil closed + [1, 0, 0, 1], # x index + [x0, x1], + [y0, y0, y1, y1], + [0, 1, 1, 1], # material index + closed_path=True, # + subdiv_x=subdiv_x, + subdiv_y=subdiv_y + ) + + face = None + back = None + + if self.model == 1: + # / y2-y3 + # __/ y1-y0 + # x2 x3 + x2 = 0.5 * self.panel_spacing + x3 = x2 + self.chanfer + y2 = y1 + self.chanfer + y3 = y0 - self.chanfer + + face = DoorPanel( + False, # profil closed + [0, 1, 2], # x index + [0, x2, x3], + [y1, y1, y2], + [1, 1, 1], # material index + side_cap_front=2, # cap index + closed_path=True + ) + + back = DoorPanel( + False, # profil closed + [0, 1, 2], # x index + [x3, x2, 0], + [y3, y0, y0], + [0, 0, 0], # material index + side_cap_back=0, # cap index + closed_path=True + ) + + elif self.model == 2: + # / y2-y3 + # ___ _____/ y1-y0 + # \ / + # \/ y4-y5 + # 0 x2 x4 x5 x6 x3 + x2 = 0.5 * self.panel_spacing + x4 = x2 + self.chanfer + x5 = x4 + self.chanfer + x6 = x5 + 4 * self.chanfer + x3 = x6 + self.chanfer + y2 = y1 - self.chanfer + y4 = y1 + self.chanfer + y3 = y0 + self.chanfer + y5 = y0 - self.chanfer + face = DoorPanel( + False, # profil closed + [0, 1, 2, 3, 4, 5], # x index + [0, x2, x4, x5, x6, x3], + [y1, y1, y4, y1, y1, y2], + [1, 1, 1, 1, 1, 1], # material index + side_cap_front=5, # cap index + closed_path=True + ) + + back = DoorPanel( + False, # profil closed + [0, 1, 2, 3, 4, 5], # x index + [x3, x6, x5, x4, x2, 0], + [y3, y0, y0, y5, y0, y0], + [0, 0, 0, 0, 0, 0], # material index + side_cap_back=0, # cap index + closed_path=True + ) + + elif self.model == 3: + # _____ y2-y3 + # / \ y4-y5 + # __/ y1-y0 + # 0 x2 x3 x4 x5 + x2 = 0.5 * self.panel_spacing + x3 = x2 + self.chanfer + x4 = x3 + 4 * self.chanfer + x5 = x4 + 2 * self.chanfer + y2 = y1 - self.chanfer + y3 = y0 + self.chanfer + y4 = y2 + self.chanfer + y5 = y3 - self.chanfer + face = DoorPanel( + False, # profil closed + [0, 1, 2, 3, 4], # x index + [0, x2, x3, x4, x5], + [y1, y1, y2, y2, y4], + [1, 1, 1, 1, 1], # material index + side_cap_front=4, # cap index + closed_path=True + ) + + back = DoorPanel( + False, # profil closed + [0, 1, 2, 3, 4], # x index + [x5, x4, x3, x2, 0], + [y5, y3, y3, y0, y0], + [0, 0, 0, 0, 0], # material index + side_cap_back=0, # cap index + closed_path=True + ) + + else: + side.side_cap_front = 3 + side.side_cap_back = 0 + + return side, face, back + + @property + def verts(self): + if self.panels_distrib == 'REGULAR': + subdiv_y = self.panels_y - 1 + else: + subdiv_y = 2 + + radius = Vector((0.8, 0.5, 0)) + center = Vector((0, self.z - radius.x, 0)) + + if self.direction == 0: + pivot = 1 + else: + pivot = -1 + + path_type = 'RECTANGLE' + curve_steps = 16 + side, face, back = self.panels + + x1 = max(0.001, self.panel_border - 0.5 * self.panel_spacing) + bottom_z = self.panel_bottom + shape_z = [0, bottom_z, bottom_z, 0] + origin = Vector((-pivot * 0.5 * self.x, 0, 0)) + offset = Vector((0, 0, 0)) + size = Vector((self.x, self.z, 0)) + verts = side.vertices(curve_steps, offset, center, origin, + size, radius, 0, pivot, shape_z=shape_z, path_type=path_type) + if face is not None: + p_radius = radius.copy() + p_radius.x -= x1 + p_radius.y -= x1 + if self.panels_distrib == 'REGULAR': + p_size = Vector(((self.x - 2 * x1) / self.panels_x, + (self.z - 2 * x1 - bottom_z) / self.panels_y, 0)) + for i in range(self.panels_x): + for j in range(self.panels_y): + if j < subdiv_y: + shape = 'RECTANGLE' + else: + shape = path_type + offset = Vector(((pivot * 0.5 * self.x) + p_size.x * (i + 0.5) - 0.5 * size.x + x1, + bottom_z + p_size.y * j + x1, 0)) + origin = Vector((p_size.x * (i + 0.5) - 0.5 * size.x + x1, bottom_z + p_size.y * j + x1, 0)) + verts += face.vertices(curve_steps, offset, center, origin, + p_size, p_radius, 0, 0, shape_z=None, path_type=shape) + if back is not None: + verts += back.vertices(curve_steps, offset, center, origin, + p_size, p_radius, 0, 0, shape_z=None, path_type=shape) + else: + #################################### + # Ratio vertical panels 1/3 - 2/3 + #################################### + p_size = Vector(((self.x - 2 * x1) / self.panels_x, (self.z - 2 * x1 - bottom_z) / 3, 0)) + p_size_2x = Vector((p_size.x, p_size.y * 2, 0)) + for i in range(self.panels_x): + j = 0 + offset = Vector(((pivot * 0.5 * self.x) + p_size.x * (i + 0.5) - 0.5 * size.x + x1, + bottom_z + p_size.y * j + x1, 0)) + origin = Vector((p_size.x * (i + 0.5) - 0.5 * size.x + x1, bottom_z + p_size.y * j + x1, 0)) + shape = 'RECTANGLE' + face.subdiv_y = 0 + verts += face.vertices(curve_steps, offset, center, origin, + p_size, p_radius, 0, 0, shape_z=None, path_type=shape) + if back is not None: + back.subdiv_y = 0 + verts += back.vertices(curve_steps, offset, center, origin, + p_size, p_radius, 0, 0, shape_z=None, path_type=shape) + j = 1 + offset = Vector(((pivot * 0.5 * self.x) + p_size.x * (i + 0.5) - 0.5 * size.x + x1, + bottom_z + p_size.y * j + x1, 0)) + origin = Vector((p_size.x * (i + 0.5) - 0.5 * size.x + x1, + bottom_z + p_size.y * j + x1, 0)) + shape = path_type + face.subdiv_y = 1 + verts += face.vertices(curve_steps, offset, center, origin, + p_size_2x, p_radius, 0, 0, shape_z=None, path_type=path_type) + if back is not None: + back.subdiv_y = 1 + verts += back.vertices(curve_steps, offset, center, origin, + p_size_2x, p_radius, 0, 0, shape_z=None, path_type=path_type) + + return verts + + @property + def faces(self): + if self.panels_distrib == 'REGULAR': + subdiv_y = self.panels_y - 1 + else: + subdiv_y = 2 + + path_type = 'RECTANGLE' + curve_steps = 16 + side, face, back = self.panels + + faces = side.faces(curve_steps, path_type=path_type) + faces_offset = side.n_verts(curve_steps, path_type=path_type) + + if face is not None: + if self.panels_distrib == 'REGULAR': + for i in range(self.panels_x): + for j in range(self.panels_y): + if j < subdiv_y: + shape = 'RECTANGLE' + else: + shape = path_type + faces += face.faces(curve_steps, path_type=shape, offset=faces_offset) + faces_offset += face.n_verts(curve_steps, path_type=shape) + if back is not None: + faces += back.faces(curve_steps, path_type=shape, offset=faces_offset) + faces_offset += back.n_verts(curve_steps, path_type=shape) + else: + #################################### + # Ratio vertical panels 1/3 - 2/3 + #################################### + for i in range(self.panels_x): + j = 0 + shape = 'RECTANGLE' + face.subdiv_y = 0 + faces += face.faces(curve_steps, path_type=shape, offset=faces_offset) + faces_offset += face.n_verts(curve_steps, path_type=shape) + if back is not None: + back.subdiv_y = 0 + faces += back.faces(curve_steps, path_type=shape, offset=faces_offset) + faces_offset += back.n_verts(curve_steps, path_type=shape) + j = 1 + shape = path_type + face.subdiv_y = 1 + faces += face.faces(curve_steps, path_type=path_type, offset=faces_offset) + faces_offset += face.n_verts(curve_steps, path_type=path_type) + if back is not None: + back.subdiv_y = 1 + faces += back.faces(curve_steps, path_type=path_type, offset=faces_offset) + faces_offset += back.n_verts(curve_steps, path_type=path_type) + + return faces + + @property + def uvs(self): + if self.panels_distrib == 'REGULAR': + subdiv_y = self.panels_y - 1 + else: + subdiv_y = 2 + + radius = Vector((0.8, 0.5, 0)) + center = Vector((0, self.z - radius.x, 0)) + + if self.direction == 0: + pivot = 1 + else: + pivot = -1 + + path_type = 'RECTANGLE' + curve_steps = 16 + side, face, back = self.panels + + x1 = max(0.001, self.panel_border - 0.5 * self.panel_spacing) + bottom_z = self.panel_bottom + origin = Vector((-pivot * 0.5 * self.x, 0, 0)) + size = Vector((self.x, self.z, 0)) + uvs = side.uv(curve_steps, center, origin, size, radius, 0, pivot, 0, self.panel_border, path_type=path_type) + if face is not None: + p_radius = radius.copy() + p_radius.x -= x1 + p_radius.y -= x1 + if self.panels_distrib == 'REGULAR': + p_size = Vector(((self.x - 2 * x1) / self.panels_x, (self.z - 2 * x1 - bottom_z) / self.panels_y, 0)) + for i in range(self.panels_x): + for j in range(self.panels_y): + if j < subdiv_y: + shape = 'RECTANGLE' + else: + shape = path_type + origin = Vector((p_size.x * (i + 0.5) - 0.5 * size.x + x1, bottom_z + p_size.y * j + x1, 0)) + uvs += face.uv(curve_steps, center, origin, p_size, p_radius, 0, 0, 0, 0, path_type=shape) + if back is not None: + uvs += back.uv(curve_steps, center, origin, + p_size, p_radius, 0, 0, 0, 0, path_type=shape) + else: + #################################### + # Ratio vertical panels 1/3 - 2/3 + #################################### + p_size = Vector(((self.x - 2 * x1) / self.panels_x, (self.z - 2 * x1 - bottom_z) / 3, 0)) + p_size_2x = Vector((p_size.x, p_size.y * 2, 0)) + for i in range(self.panels_x): + j = 0 + origin = Vector((p_size.x * (i + 0.5) - 0.5 * size.x + x1, bottom_z + p_size.y * j + x1, 0)) + shape = 'RECTANGLE' + face.subdiv_y = 0 + uvs += face.uv(curve_steps, center, origin, p_size, p_radius, 0, 0, 0, 0, path_type=shape) + if back is not None: + back.subdiv_y = 0 + uvs += back.uv(curve_steps, center, origin, p_size, p_radius, 0, 0, 0, 0, path_type=shape) + j = 1 + origin = Vector((p_size.x * (i + 0.5) - 0.5 * size.x + x1, bottom_z + p_size.y * j + x1, 0)) + shape = path_type + face.subdiv_y = 1 + uvs += face.uv(curve_steps, center, origin, p_size_2x, p_radius, 0, 0, 0, 0, path_type=path_type) + if back is not None: + back.subdiv_y = 1 + uvs += back.uv(curve_steps, center, origin, + p_size_2x, p_radius, 0, 0, 0, 0, path_type=path_type) + return uvs + + @property + def matids(self): + if self.panels_distrib == 'REGULAR': + subdiv_y = self.panels_y - 1 + else: + subdiv_y = 2 + + path_type = 'RECTANGLE' + curve_steps = 16 + side, face, back = self.panels + + mat = side.mat(curve_steps, 1, 0, path_type=path_type) + + if face is not None: + if self.panels_distrib == 'REGULAR': + for i in range(self.panels_x): + for j in range(self.panels_y): + if j < subdiv_y: + shape = 'RECTANGLE' + else: + shape = path_type + mat += face.mat(curve_steps, 1, 1, path_type=shape) + if back is not None: + mat += back.mat(curve_steps, 0, 0, path_type=shape) + else: + #################################### + # Ratio vertical panels 1/3 - 2/3 + #################################### + for i in range(self.panels_x): + j = 0 + shape = 'RECTANGLE' + face.subdiv_y = 0 + mat += face.mat(curve_steps, 1, 1, path_type=shape) + if back is not None: + back.subdiv_y = 0 + mat += back.mat(curve_steps, 0, 0, path_type=shape) + j = 1 + shape = path_type + face.subdiv_y = 1 + mat += face.mat(curve_steps, 1, 1, path_type=shape) + if back is not None: + back.subdiv_y = 1 + mat += back.mat(curve_steps, 0, 0, path_type=shape) + return mat + + def find_handle(self, o): + for child in o.children: + if 'archipack_handle' in child: + return child + return None + + def update_handle(self, context, o): + handle = self.find_handle(o) + if handle is None: + m = bpy.data.meshes.new("Handle") + handle = create_handle(context, o, m) + MaterialUtils.add_handle_materials(handle) + verts, faces = door_handle_horizontal_01(self.direction, 1) + b_verts, b_faces = door_handle_horizontal_01(self.direction, 0, offset=len(verts)) + b_verts = [(v[0], v[1] - self.y, v[2]) for v in b_verts] + handle_y = 0.07 + handle.location = ((1 - self.direction * 2) * (self.x - handle_y), 0, 0.5 * self.z) + bmed.buildmesh(context, handle, verts + b_verts, faces + b_faces) + + def remove_handle(self, context, o): + handle = self.find_handle(o) + if handle is not None: + context.scene.objects.unlink(handle) + bpy.data.objects.remove(handle, do_unlink=True) + + def update(self, context): + o = self.find_in_selection(context) + + if o is None: + return + + bmed.buildmesh(context, o, self.verts, self.faces, matids=self.matids, uvs=self.uvs, weld=True) + + if self.handle == 'NONE': + self.remove_handle(context, o) + else: + self.update_handle(context, o) + + self.restore_context(context) + + +class ARCHIPACK_PT_door_panel(Panel): + bl_idname = "ARCHIPACK_PT_door_panel" + bl_label = "Door" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + # bl_context = 'object' + bl_category = 'ArchiPack' + + @classmethod + def poll(cls, context): + return archipack_door_panel.filter(context.active_object) + + def draw(self, context): + layout = self.layout + layout.operator("archipack.select_parent") + + +# ------------------------------------------------------------------ +# Define operator class to create object +# ------------------------------------------------------------------ + + +class ARCHIPACK_OT_door_panel(Operator): + bl_idname = "archipack.door_panel" + bl_label = "Door model 1" + bl_description = "Door model 1" + bl_category = 'Archipack' + bl_options = {'REGISTER', 'UNDO'} + x = FloatProperty( + name='width', + min=0.1, + default=0.80, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='Width' + ) + z = FloatProperty( + name='height', + min=0.1, + default=2.0, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='height' + ) + y = FloatProperty( + name='depth', + min=0.001, + default=0.02, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='Depth' + ) + direction = IntProperty( + name="direction", + min=0, + max=1, + description="open direction" + ) + model = IntProperty( + name="model", + min=0, + max=3, + description="panel type" + ) + chanfer = FloatProperty( + name='chanfer', + min=0.001, + default=0.005, precision=3, + unit='LENGTH', subtype='DISTANCE', + description='chanfer' + ) + panel_spacing = FloatProperty( + name='spacing', + min=0.001, + default=0.1, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='distance between panels' + ) + panel_bottom = FloatProperty( + name='bottom', + min=0.0, + default=0.0, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='distance from bottom' + ) + panel_border = FloatProperty( + name='border', + min=0.001, + default=0.2, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='distance from border' + ) + panels_x = IntProperty( + name="panels h", + min=1, + max=50, + default=1, + description="panels h" + ) + panels_y = IntProperty( + name="panels v", + min=1, + max=50, + default=1, + description="panels v" + ) + panels_distrib = EnumProperty( + name='distribution', + items=( + ('REGULAR', 'Regular', '', 0), + ('ONE_THIRD', '1/3 2/3', '', 1) + ), + default='REGULAR' + ) + handle = EnumProperty( + name='Shape', + items=( + ('NONE', 'No handle', '', 0), + ('BOTH', 'Inside and outside', '', 1) + ), + default='BOTH' + ) + + def draw(self, context): + layout = self.layout + row = layout.row() + row.label("Use Properties panel (N) to define parms", icon='INFO') + + def create(self, context): + """ + expose only basic params in operator + use object property for other params + """ + m = bpy.data.meshes.new("Panel") + o = bpy.data.objects.new("Panel", m) + d = m.archipack_door_panel.add() + d.x = self.x + d.y = self.y + d.z = self.z + d.model = self.model + d.direction = self.direction + d.chanfer = self.chanfer + d.panel_border = self.panel_border + d.panel_bottom = self.panel_bottom + d.panel_spacing = self.panel_spacing + d.panels_distrib = self.panels_distrib + d.panels_x = self.panels_x + d.panels_y = self.panels_y + d.handle = self.handle + context.scene.objects.link(o) + o.lock_location[0] = True + o.lock_location[1] = True + o.lock_location[2] = True + o.lock_rotation[0] = True + o.lock_rotation[1] = True + o.lock_scale[0] = True + o.lock_scale[1] = True + o.lock_scale[2] = True + o.select = True + context.scene.objects.active = o + d.update(context) + MaterialUtils.add_door_materials(o) + o.lock_rotation[0] = True + o.lock_rotation[1] = True + return o + + def execute(self, context): + if context.mode == "OBJECT": + bpy.ops.object.select_all(action="DESELECT") + o = self.create(context) + o.select = True + context.scene.objects.active = o + return {'FINISHED'} + else: + self.report({'WARNING'}, "Archipack: Option only valid in Object mode") + return {'CANCELLED'} + + +class ARCHIPACK_OT_select_parent(Operator): + bl_idname = "archipack.select_parent" + bl_label = "Edit parameters" + bl_description = "Edit parameters located on parent" + bl_category = 'Archipack' + bl_options = {'REGISTER', 'UNDO'} + + def draw(self, context): + layout = self.layout + row = layout.row() + row.label("Use Properties panel (N) to define parms", icon='INFO') + + def execute(self, context): + if context.mode == "OBJECT": + if context.active_object is not None and context.active_object.parent is not None: + bpy.ops.object.select_all(action="DESELECT") + context.active_object.parent.select = True + context.scene.objects.active = context.active_object.parent + return {'FINISHED'} + else: + self.report({'WARNING'}, "Archipack: Option only valid in Object mode") + return {'CANCELLED'} + + +class archipack_door(ArchipackObject, Manipulable, PropertyGroup): + """ + The frame is the door main object + parent parametric object + create/remove/update her own childs + """ + x = FloatProperty( + name='width', + min=0.25, + default=100.0, precision=2, step=1, + unit='LENGTH', subtype='DISTANCE', + description='Width', update=update, + ) + y = FloatProperty( + name='depth', + min=0.1, + default=0.20, precision=2, step=1, + unit='LENGTH', subtype='DISTANCE', + description='Depth', update=update, + ) + z = FloatProperty( + name='height', + min=0.1, + default=2.0, precision=2, step=1, + unit='LENGTH', subtype='DISTANCE', + description='height', update=update, + ) + frame_x = FloatProperty( + name='Width', + min=0, + default=0.1, precision=2, step=1, + unit='LENGTH', subtype='DISTANCE', + description='frame width', update=update, + ) + frame_y = FloatProperty( + name='Depth', + default=0.03, precision=2, step=1, + unit='LENGTH', subtype='DISTANCE', + description='frame depth', update=update, + ) + direction = IntProperty( + name="Direction", + min=0, + max=1, + description="open direction", update=update, + ) + door_y = FloatProperty( + name='Depth', + min=0.001, + default=0.02, precision=2, step=1, + unit='LENGTH', subtype='DISTANCE', + description='depth', update=update, + ) + door_offset = FloatProperty( + name='Offset', + min=0, + default=0, precision=2, step=1, + unit='LENGTH', subtype='DISTANCE', + description='offset', update=update, + ) + model = IntProperty( + name="Model", + min=0, + max=3, + default=0, + description="Model", update=update, + ) + n_panels = IntProperty( + name="Panels", + min=1, + max=2, + default=1, + description="number of panels", update=update + ) + chanfer = FloatProperty( + name='chanfer', + min=0.001, + default=0.005, precision=3, step=0.01, + unit='LENGTH', subtype='DISTANCE', + description='chanfer', update=update_childs, + ) + panel_spacing = FloatProperty( + name='spacing', + min=0.001, + default=0.1, precision=2, step=1, + unit='LENGTH', subtype='DISTANCE', + description='distance between panels', update=update_childs, + ) + panel_bottom = FloatProperty( + name='bottom', + min=0.0, + default=0.0, precision=2, step=1, + unit='LENGTH', subtype='DISTANCE', + description='distance from bottom', update=update_childs, + ) + panel_border = FloatProperty( + name='border', + min=0.001, + default=0.2, precision=2, step=1, + unit='LENGTH', subtype='DISTANCE', + description='distance from border', update=update_childs, + ) + panels_x = IntProperty( + name="panels h", + min=1, + max=50, + default=1, + description="panels h", update=update_childs, + ) + panels_y = IntProperty( + name="panels v", + min=1, + max=50, + default=1, + description="panels v", update=update_childs, + ) + panels_distrib = EnumProperty( + name='distribution', + items=( + ('REGULAR', 'Regular', '', 0), + ('ONE_THIRD', '1/3 2/3', '', 1) + ), + default='REGULAR', update=update_childs, + ) + handle = EnumProperty( + name='Handle', + items=( + ('NONE', 'No handle', '', 0), + ('BOTH', 'Inside and outside', '', 1) + ), + default='BOTH', update=update_childs, + ) + hole_margin = FloatProperty( + name='hole margin', + min=0.0, + default=0.1, precision=2, step=1, + unit='LENGTH', subtype='DISTANCE', + description='how much hole surround wall' + ) + flip = BoolProperty( + default=False, + update=update, + description='flip outside and outside material of hole' + ) + auto_update = BoolProperty( + options={'SKIP_SAVE'}, + default=True, + update=update + ) + + @property + def frame(self): + + # + # _____ y0 + # | |___ y1 + # x | y3 + # | | + # |_________| y2 + # + # x2 x1 x0 + x0 = 0 + x1 = -BATTUE + x2 = -self.frame_x + y0 = max(0.25 * self.door_y + 0.0005, self.y / 2 + self.frame_y) + y1 = max(y0 - 0.5 * self.door_y - self.door_offset, -y0 + 0.001) + y2 = -y0 + y3 = 0 + return DoorPanel( + True, # closed + [0, 0, 0, 1, 1, 2, 2], # x index + [x2, x1, x0], + [y2, y3, y0, y0, y1, y1, y2], + [0, 1, 1, 1, 1, 0, 0], # material index + closed_path=False + ) + + @property + def hole(self): + # + # _____ y0 + # | + # x y2 + # | + # |_____ y1 + # + # x0 + x0 = 0 + y0 = self.y / 2 + self.hole_margin + y1 = -y0 + y2 = 0 + outside_mat = 0 + inside_mat = 1 + if self.flip: + outside_mat, inside_mat = inside_mat, outside_mat + return DoorPanel( + False, # closed + [0, 0, 0], # x index + [x0], + [y1, y2, y0], + [outside_mat, inside_mat, inside_mat], # material index + closed_path=True, + side_cap_front=2, + side_cap_back=0 # cap index + ) + + @property + def verts(self): + # door inner space + v = Vector((0, 0, 0)) + size = Vector((self.x, self.z, self.y)) + return self.frame.vertices(16, v, v, v, size, v, 0, 0, shape_z=None, path_type='RECTANGLE') + + @property + def faces(self): + return self.frame.faces(16, path_type='RECTANGLE') + + @property + def matids(self): + return self.frame.mat(16, 0, 0, path_type='RECTANGLE') + + @property + def uvs(self): + v = Vector((0, 0, 0)) + size = Vector((self.x, self.z, self.y)) + return self.frame.uv(16, v, v, size, v, 0, 0, 0, 0, path_type='RECTANGLE') + + def setup_manipulators(self): + if len(self.manipulators) == 3: + return + s = self.manipulators.add() + s.prop1_name = "x" + s.prop2_name = "x" + s.type_key = "SNAP_SIZE_LOC" + s = self.manipulators.add() + s.prop1_name = "y" + s.prop2_name = "y" + s.type_key = "SNAP_SIZE_LOC" + s = self.manipulators.add() + s.prop1_name = "z" + s.normal = Vector((0, 1, 0)) + + def remove_childs(self, context, o, to_remove): + for child in o.children: + if to_remove < 1: + return + if archipack_door_panel.filter(child): + self.remove_handle(context, child) + to_remove -= 1 + context.scene.objects.unlink(child) + bpy.data.objects.remove(child, do_unlink=True) + + def remove_handle(self, context, o): + handle = self.find_handle(o) + if handle is not None: + context.scene.objects.unlink(handle) + bpy.data.objects.remove(handle, do_unlink=True) + + def create_childs(self, context, o): + + n_childs = 0 + for child in o.children: + if archipack_door_panel.filter(child): + n_childs += 1 + + # remove child + if n_childs > self.n_panels: + self.remove_childs(context, o, n_childs - self.n_panels) + + if n_childs < 1: + # create one door panel + bpy.ops.archipack.door_panel(x=self.x, z=self.z, door_y=self.door_y, + n_panels=self.n_panels, direction=self.direction) + child = context.active_object + child.parent = o + child.matrix_world = o.matrix_world.copy() + location = self.x / 2 + BATTUE - SPACING + if self.direction == 0: + location = -location + child.location.x = location + child.location.y = self.door_y + + if self.n_panels == 2 and n_childs < 2: + # create 2nth door panel + bpy.ops.archipack.door_panel(x=self.x, z=self.z, door_y=self.door_y, + n_panels=self.n_panels, direction=1 - self.direction) + child = context.active_object + child.parent = o + child.matrix_world = o.matrix_world.copy() + location = self.x / 2 + BATTUE - SPACING + if self.direction == 1: + location = -location + child.location.x = location + child.location.y = self.door_y + + def find_handle(self, o): + for handle in o.children: + if 'archipack_handle' in handle: + return handle + return None + + def get_childs_panels(self, context, o): + return [child for child in o.children if archipack_door_panel.filter(child)] + + def _synch_childs(self, context, o, linked, childs): + """ + sub synch childs nodes of linked object + """ + # remove childs not found on source + l_childs = self.get_childs_panels(context, linked) + c_names = [c.data.name for c in childs] + for c in l_childs: + try: + id = c_names.index(c.data.name) + except: + self.remove_handle(context, c) + context.scene.objects.unlink(c) + bpy.data.objects.remove(c, do_unlink=True) + + # children ordering may not be the same, so get the right l_childs order + l_childs = self.get_childs_panels(context, linked) + l_names = [c.data.name for c in l_childs] + order = [] + for c in childs: + try: + id = l_names.index(c.data.name) + except: + id = -1 + order.append(id) + + # add missing childs and update other ones + for i, child in enumerate(childs): + if order[i] < 0: + p = bpy.data.objects.new("DoorPanel", child.data) + context.scene.objects.link(p) + p.lock_location[0] = True + p.lock_location[1] = True + p.lock_location[2] = True + p.lock_rotation[0] = True + p.lock_rotation[1] = True + p.lock_scale[0] = True + p.lock_scale[1] = True + p.lock_scale[2] = True + p.parent = linked + p.matrix_world = linked.matrix_world.copy() + p.location = child.location.copy() + else: + p = l_childs[order[i]] + + p.location = child.location.copy() + + # update handle + handle = self.find_handle(child) + h = self.find_handle(p) + if handle is not None: + if h is None: + h = create_handle(context, p, handle.data) + MaterialUtils.add_handle_materials(h) + h.location = handle.location.copy() + elif h is not None: + context.scene.objects.unlink(h) + bpy.data.objects.remove(h, do_unlink=True) + + def _synch_hole(self, context, linked, hole): + l_hole = self.find_hole(linked) + if l_hole is None: + l_hole = bpy.data.objects.new("hole", hole.data) + l_hole['archipack_hole'] = True + context.scene.objects.link(l_hole) + l_hole.parent = linked + l_hole.matrix_world = linked.matrix_world.copy() + l_hole.location = hole.location.copy() + else: + l_hole.data = hole.data + + def synch_childs(self, context, o): + """ + synch childs nodes of linked objects + """ + bpy.ops.object.select_all(action='DESELECT') + o.select = True + context.scene.objects.active = o + childs = self.get_childs_panels(context, o) + hole = self.find_hole(o) + bpy.ops.object.select_linked(type='OBDATA') + for linked in context.selected_objects: + if linked != o: + self._synch_childs(context, o, linked, childs) + if hole is not None: + self._synch_hole(context, linked, hole) + + def update_childs(self, context, o): + """ + pass params to childrens + """ + childs = self.get_childs_panels(context, o) + n_childs = len(childs) + self.remove_childs(context, o, n_childs - self.n_panels) + + childs = self.get_childs_panels(context, o) + n_childs = len(childs) + child_n = 0 + + # location_y = self.y / 2 + self.frame_y - SPACING + # location_y = min(max(self.door_offset, - location_y), location_y) + self.door_y + + location_y = max(0.25 * self.door_y + 0.0005, self.y / 2 + self.frame_y) + location_y = max(location_y - self.door_offset + 0.5 * self.door_y, -location_y + self.door_y + 0.001) + + x = self.x / self.n_panels + (3 - self.n_panels) * (BATTUE - SPACING) + y = self.door_y + z = self.z + BATTUE - SPACING + + if self.n_panels < 2: + direction = self.direction + else: + direction = 0 + + for panel in range(self.n_panels): + child_n += 1 + + if child_n == 1: + handle = self.handle + else: + handle = 'NONE' + + if child_n > 1: + direction = 1 - direction + + location_x = (2 * direction - 1) * (self.x / 2 + BATTUE - SPACING) + + if child_n > n_childs: + bpy.ops.archipack.door_panel( + x=x, + y=y, + z=z, + model=self.model, + direction=direction, + chanfer=self.chanfer, + panel_border=self.panel_border, + panel_bottom=self.panel_bottom, + panel_spacing=self.panel_spacing, + panels_distrib=self.panels_distrib, + panels_x=self.panels_x, + panels_y=self.panels_y, + handle=handle + ) + child = context.active_object + # parenting at 0, 0, 0 before set object matrix_world + # so location remains local from frame + child.parent = o + child.matrix_world = o.matrix_world.copy() + else: + child = childs[child_n - 1] + child.select = True + context.scene.objects.active = child + props = archipack_door_panel.datablock(child) + if props is not None: + props.x = x + props.y = y + props.z = z + props.model = self.model + props.direction = direction + props.chanfer = self.chanfer + props.panel_border = self.panel_border + props.panel_bottom = self.panel_bottom + props.panel_spacing = self.panel_spacing + props.panels_distrib = self.panels_distrib + props.panels_x = self.panels_x + props.panels_y = self.panels_y + props.handle = handle + props.update(context) + child.location = Vector((location_x, location_y, 0)) + + def update(self, context, childs_only=False): + + # support for "copy to selected" + o = self.find_in_selection(context, self.auto_update) + + if o is None: + return + + self.setup_manipulators() + + if childs_only is False: + bmed.buildmesh(context, o, self.verts, self.faces, self.matids, self.uvs) + + self.update_childs(context, o) + + if childs_only is False and self.find_hole(o) is not None: + self.interactive_hole(context, o) + + # support for instances childs, update at object level + self.synch_childs(context, o) + + # setup 3d points for gl manipulators + x, y = 0.5 * self.x, 0.5 * self.y + self.manipulators[0].set_pts([(-x, -y, 0), (x, -y, 0), (1, 0, 0)]) + self.manipulators[1].set_pts([(-x, -y, 0), (-x, y, 0), (-1, 0, 0)]) + self.manipulators[2].set_pts([(x, -y, 0), (x, -y, self.z), (-1, 0, 0)]) + + # restore context + self.restore_context(context) + + def find_hole(self, o): + for child in o.children: + if 'archipack_hole' in child: + return child + return None + + def interactive_hole(self, context, o): + hole_obj = self.find_hole(o) + if hole_obj is None: + m = bpy.data.meshes.new("hole") + hole_obj = bpy.data.objects.new("hole", m) + context.scene.objects.link(hole_obj) + hole_obj['archipack_hole'] = True + hole_obj.parent = o + hole_obj.matrix_world = o.matrix_world.copy() + MaterialUtils.add_wall2_materials(hole_obj) + hole = self.hole + v = Vector((0, 0, 0)) + offset = Vector((0, -0.001, 0)) + size = Vector((self.x + 2 * self.frame_x, self.z + self.frame_x + 0.001, self.y)) + verts = hole.vertices(16, offset, v, v, size, v, 0, 0, shape_z=None, path_type='RECTANGLE') + faces = hole.faces(16, path_type='RECTANGLE') + matids = hole.mat(16, 0, 1, path_type='RECTANGLE') + uvs = hole.uv(16, v, v, size, v, 0, 0, 0, 0, path_type='RECTANGLE') + bmed.buildmesh(context, hole_obj, verts, faces, matids=matids, uvs=uvs) + return hole_obj + + def robust_hole(self, context, tM): + hole = self.hole + m = bpy.data.meshes.new("hole") + o = bpy.data.objects.new("hole", m) + o['archipack_robusthole'] = True + context.scene.objects.link(o) + v = Vector((0, 0, 0)) + offset = Vector((0, -0.001, 0)) + size = Vector((self.x + 2 * self.frame_x, self.z + self.frame_x + 0.001, self.y)) + verts = hole.vertices(16, offset, v, v, size, v, 0, 0, shape_z=None, path_type='RECTANGLE') + verts = [tM * Vector(v) for v in verts] + faces = hole.faces(16, path_type='RECTANGLE') + matids = hole.mat(16, 0, 1, path_type='RECTANGLE') + uvs = hole.uv(16, v, v, size, v, 0, 0, 0, 0, path_type='RECTANGLE') + bmed.buildmesh(context, o, verts, faces, matids=matids, uvs=uvs) + MaterialUtils.add_wall2_materials(o) + o.select = True + context.scene.objects.active = o + return o + + +class ARCHIPACK_PT_door(Panel): + bl_idname = "ARCHIPACK_PT_door" + bl_label = "Door" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = 'ArchiPack' + + @classmethod + def poll(cls, context): + return archipack_door.filter(context.active_object) + + def draw(self, context): + o = context.active_object + if not archipack_door.filter(o): + return + layout = self.layout + layout.operator('archipack.door_manipulate', icon='HAND') + props = archipack_door.datablock(o) + row = layout.row(align=True) + row.operator('archipack.door', text="Refresh", icon='FILE_REFRESH').mode = 'REFRESH' + if o.data.users > 1: + row.operator('archipack.door', text="Make unique", icon='UNLINKED').mode = 'UNIQUE' + row.operator('archipack.door', text="Delete", icon='ERROR').mode = 'DELETE' + box = layout.box() + # box.label(text="Styles") + row = box.row(align=True) + row.operator("archipack.door_preset_menu", text=bpy.types.ARCHIPACK_OT_door_preset_menu.bl_label) + row.operator("archipack.door_preset", text="", icon='ZOOMIN') + row.operator("archipack.door_preset", text="", icon='ZOOMOUT').remove_active = True + row = layout.row() + box = row.box() + box.label(text="Size") + box.prop(props, 'x') + box.prop(props, 'y') + box.prop(props, 'z') + box.prop(props, 'door_offset') + row = layout.row() + box = row.box() + row = box.row() + row.label(text="Door") + box.prop(props, 'direction') + box.prop(props, 'n_panels') + box.prop(props, 'door_y') + box.prop(props, 'handle') + row = layout.row() + box = row.box() + row = box.row() + row.label(text="Frame") + row = box.row(align=True) + row.prop(props, 'frame_x') + row.prop(props, 'frame_y') + row = layout.row() + box = row.box() + row = box.row() + row.label(text="Panels") + box.prop(props, 'model') + if props.model > 0: + box.prop(props, 'panels_distrib', text="") + row = box.row(align=True) + row.prop(props, 'panels_x') + if props.panels_distrib == 'REGULAR': + row.prop(props, 'panels_y') + box.prop(props, 'panel_bottom') + box.prop(props, 'panel_spacing') + box.prop(props, 'panel_border') + box.prop(props, 'chanfer') + + +# ------------------------------------------------------------------ +# Define operator class to create object +# ------------------------------------------------------------------ + + +class ARCHIPACK_OT_door(ArchipackCreateTool, Operator): + bl_idname = "archipack.door" + bl_label = "Door" + bl_description = "Door" + bl_category = 'Archipack' + bl_options = {'REGISTER', 'UNDO'} + x = FloatProperty( + name='width', + min=0.1, + default=0.80, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='Width' + ) + y = FloatProperty( + name='depth', + min=0.1, + default=0.20, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='Depth' + ) + z = FloatProperty( + name='height', + min=0.1, + default=2.0, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='height' + ) + direction = IntProperty( + name="direction", + min=0, + max=1, + description="open direction" + ) + n_panels = IntProperty( + name="panels", + min=1, + max=2, + default=1, + description="number of panels" + ) + chanfer = FloatProperty( + name='chanfer', + min=0.001, + default=0.005, precision=3, + unit='LENGTH', subtype='DISTANCE', + description='chanfer' + ) + panel_spacing = FloatProperty( + name='spacing', + min=0.001, + default=0.1, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='distance between panels' + ) + panel_bottom = FloatProperty( + name='bottom', + default=0.0, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='distance from bottom' + ) + panel_border = FloatProperty( + name='border', + min=0.001, + default=0.2, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='distance from border' + ) + panels_x = IntProperty( + name="panels h", + min=1, + max=50, + default=1, + description="panels h" + ) + panels_y = IntProperty( + name="panels v", + min=1, + max=50, + default=1, + description="panels v" + ) + panels_distrib = EnumProperty( + name='distribution', + items=( + ('REGULAR', 'Regular', '', 0), + ('ONE_THIRD', '1/3 2/3', '', 1) + ), + default='REGULAR' + ) + handle = EnumProperty( + name='Shape', + items=( + ('NONE', 'No handle', '', 0), + ('BOTH', 'Inside and outside', '', 1) + ), + default='BOTH' + ) + mode = EnumProperty( + items=( + ('CREATE', 'Create', '', 0), + ('DELETE', 'Delete', '', 1), + ('REFRESH', 'Refresh', '', 2), + ('UNIQUE', 'Make unique', '', 3), + ), + default='CREATE' + ) + + def create(self, context): + """ + expose only basic params in operator + use object property for other params + """ + m = bpy.data.meshes.new("Door") + o = bpy.data.objects.new("Door", m) + d = m.archipack_door.add() + d.x = self.x + d.y = self.y + d.z = self.z + d.direction = self.direction + d.n_panels = self.n_panels + d.chanfer = self.chanfer + d.panel_border = self.panel_border + d.panel_bottom = self.panel_bottom + d.panel_spacing = self.panel_spacing + d.panels_distrib = self.panels_distrib + d.panels_x = self.panels_x + d.panels_y = self.panels_y + d.handle = self.handle + context.scene.objects.link(o) + o.select = True + context.scene.objects.active = o + self.load_preset(d) + self.add_material(o) + o.select = True + context.scene.objects.active = o + return o + + def delete(self, context): + o = context.active_object + if archipack_door.filter(o): + bpy.ops.archipack.disable_manipulate() + for child in o.children: + if 'archipack_hole' in child: + context.scene.objects.unlink(child) + bpy.data.objects.remove(child, do_unlink=True) + elif child.data is not None and 'archipack_door_panel' in child.data: + for handle in child.children: + if 'archipack_handle' in handle: + context.scene.objects.unlink(handle) + bpy.data.objects.remove(handle, do_unlink=True) + context.scene.objects.unlink(child) + bpy.data.objects.remove(child, do_unlink=True) + context.scene.objects.unlink(o) + bpy.data.objects.remove(o, do_unlink=True) + + def update(self, context): + o = context.active_object + d = archipack_door.datablock(o) + if d is not None: + d.update(context) + bpy.ops.object.select_linked(type='OBDATA') + for linked in context.selected_objects: + if linked != o: + archipack_door.datablock(linked).update(context) + bpy.ops.object.select_all(action="DESELECT") + o.select = True + context.scene.objects.active = o + + def unique(self, context): + act = context.active_object + sel = [o for o in context.selected_objects] + bpy.ops.object.select_all(action="DESELECT") + for o in sel: + if archipack_door.filter(o): + o.select = True + for child in o.children: + if 'archipack_hole' in child or (child.data is not None and + 'archipack_door_panel' in child.data): + child.hide_select = False + child.select = True + if len(context.selected_objects) > 0: + bpy.ops.object.make_single_user(type='SELECTED_OBJECTS', object=True, + obdata=True, material=False, texture=False, animation=False) + for child in context.selected_objects: + if 'archipack_hole' in child: + child.hide_select = True + bpy.ops.object.select_all(action="DESELECT") + context.scene.objects.active = act + for o in sel: + o.select = True + + def execute(self, context): + if context.mode == "OBJECT": + if self.mode == 'CREATE': + bpy.ops.object.select_all(action="DESELECT") + o = self.create(context) + o.location = bpy.context.scene.cursor_location + o.select = True + context.scene.objects.active = o + self.manipulate() + elif self.mode == 'DELETE': + self.delete(context) + elif self.mode == 'REFRESH': + self.update(context) + elif self.mode == 'UNIQUE': + self.unique(context) + return {'FINISHED'} + else: + self.report({'WARNING'}, "Archipack: Option only valid in Object mode") + return {'CANCELLED'} + + +class ARCHIPACK_OT_door_draw(ArchpackDrawTool, Operator): + bl_idname = "archipack.door_draw" + bl_label = "Draw Doors" + bl_description = "Draw Doors over walls" + bl_category = 'Archipack' + bl_options = {'REGISTER', 'UNDO'} + + filepath = StringProperty(default="") + feedback = None + stack = [] + + @classmethod + def poll(cls, context): + return True + + def draw(self, context): + layout = self.layout + row = layout.row() + row.label("Use Properties panel (N) to define parms", icon='INFO') + + def draw_callback(self, _self, context): + self.feedback.draw(context) + + def add_object(self, context, event): + o = context.active_object + bpy.ops.object.select_all(action="DESELECT") + + if archipack_door.filter(o): + + o.select = True + context.scene.objects.active = o + + if event.shift: + bpy.ops.archipack.door(mode="UNIQUE") + + new_w = o.copy() + new_w.data = o.data + context.scene.objects.link(new_w) + + o = new_w + o.select = True + context.scene.objects.active = o + + # synch subs from parent instance + bpy.ops.archipack.door(mode="REFRESH") + + else: + bpy.ops.archipack.door(auto_manipulate=False, filepath=self.filepath) + o = context.active_object + + bpy.ops.archipack.generate_hole('INVOKE_DEFAULT') + o.select = True + context.scene.objects.active = o + + def modal(self, context, event): + + context.area.tag_redraw() + o = context.active_object + d = archipack_door.datablock(o) + hole = None + + if d is not None: + hole = d.find_hole(o) + + # hide hole from raycast + if hole is not None: + o.hide = True + hole.hide = True + + res, tM, wall, y = self.mouse_hover_wall(context, event) + + if hole is not None: + o.hide = False + hole.hide = False + + if res and d is not None: + o.matrix_world = tM + if d.y != wall.data.archipack_wall2[0].width: + d.y = wall.data.archipack_wall2[0].width + + if event.value == 'PRESS': + if event.type in {'LEFTMOUSE', 'RET', 'NUMPAD_ENTER', 'SPACE'}: + if wall is not None: + context.scene.objects.active = wall + wall.select = True + if bpy.ops.archipack.single_boolean.poll(): + bpy.ops.archipack.single_boolean() + wall.select = False + # o must be a door here + if d is not None: + context.scene.objects.active = o + self.stack.append(o) + self.add_object(context, event) + context.active_object.matrix_world = tM + return {'RUNNING_MODAL'} + + # prevent selection of other object + if event.type in {'RIGHTMOUSE'}: + return {'RUNNING_MODAL'} + + if self.keymap.check(event, self.keymap.undo) or ( + event.type in {'BACK_SPACE'} and event.value == 'RELEASE' + ): + if len(self.stack) > 0: + last = self.stack.pop() + context.scene.objects.active = last + bpy.ops.archipack.door(mode="DELETE") + context.scene.objects.active = o + return {'RUNNING_MODAL'} + + if event.value == 'RELEASE': + + if event.type in {'ESC', 'RIGHTMOUSE'}: + bpy.ops.archipack.door(mode='DELETE') + self.feedback.disable() + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + return {'FINISHED'} + + return {'PASS_THROUGH'} + + def invoke(self, context, event): + + if context.mode == "OBJECT": + o = None + self.stack = [] + self.keymap = Keymaps(context) + # exit manipulate_mode if any + bpy.ops.archipack.disable_manipulate() + # invoke with alt pressed will use current object as basis for linked copy + if self.filepath == '' and archipack_door.filter(context.active_object): + o = context.active_object + context.scene.objects.active = None + bpy.ops.object.select_all(action="DESELECT") + if o is not None: + o.select = True + context.scene.objects.active = o + self.add_object(context, event) + self.feedback = FeedbackPanel() + self.feedback.instructions(context, "Draw a door", "Click & Drag over a wall", [ + ('LEFTCLICK, RET, SPACE, ENTER', 'Create a door'), + ('BACKSPACE, CTRL+Z', 'undo last'), + ('SHIFT', 'Make independant copy'), + ('RIGHTCLICK or ESC', 'exit') + ]) + self.feedback.enable() + args = (self, context) + + self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback, args, 'WINDOW', 'POST_PIXEL') + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} + else: + self.report({'WARNING'}, "Archipack: Option only valid in Object mode") + return {'CANCELLED'} + + +# ------------------------------------------------------------------ +# Define operator class to manipulate object +# ------------------------------------------------------------------ + + +class ARCHIPACK_OT_door_manipulate(Operator): + bl_idname = "archipack.door_manipulate" + bl_label = "Manipulate" + bl_description = "Manipulate" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + return archipack_door.filter(context.active_object) + + def invoke(self, context, event): + d = archipack_door.datablock(context.active_object) + d.manipulable_invoke(context) + return {'FINISHED'} + + +# ------------------------------------------------------------------ +# Define operator class to load / save presets +# ------------------------------------------------------------------ + + +class ARCHIPACK_OT_door_preset_menu(PresetMenuOperator, Operator): + bl_description = "Show Doors presets" + bl_idname = "archipack.door_preset_menu" + bl_label = "Door Presets" + preset_subdir = "archipack_door" + + +class ARCHIPACK_OT_door_preset(ArchipackPreset, Operator): + """Add a Door Preset""" + bl_idname = "archipack.door_preset" + bl_label = "Add Door Preset" + preset_menu = "ARCHIPACK_OT_door_preset_menu" + + @property + def blacklist(self): + # 'x', 'y', 'z', 'direction', + return ['manipulators'] + + +def register(): + bpy.utils.register_class(archipack_door_panel) + Mesh.archipack_door_panel = CollectionProperty(type=archipack_door_panel) + bpy.utils.register_class(ARCHIPACK_PT_door_panel) + bpy.utils.register_class(ARCHIPACK_OT_door_panel) + bpy.utils.register_class(ARCHIPACK_OT_select_parent) + bpy.utils.register_class(archipack_door) + Mesh.archipack_door = CollectionProperty(type=archipack_door) + bpy.utils.register_class(ARCHIPACK_OT_door_preset_menu) + bpy.utils.register_class(ARCHIPACK_PT_door) + bpy.utils.register_class(ARCHIPACK_OT_door) + bpy.utils.register_class(ARCHIPACK_OT_door_preset) + bpy.utils.register_class(ARCHIPACK_OT_door_draw) + bpy.utils.register_class(ARCHIPACK_OT_door_manipulate) + + +def unregister(): + bpy.utils.unregister_class(archipack_door_panel) + del Mesh.archipack_door_panel + bpy.utils.unregister_class(ARCHIPACK_PT_door_panel) + bpy.utils.unregister_class(ARCHIPACK_OT_door_panel) + bpy.utils.unregister_class(ARCHIPACK_OT_select_parent) + bpy.utils.unregister_class(archipack_door) + del Mesh.archipack_door + bpy.utils.unregister_class(ARCHIPACK_OT_door_preset_menu) + bpy.utils.unregister_class(ARCHIPACK_PT_door) + bpy.utils.unregister_class(ARCHIPACK_OT_door) + bpy.utils.unregister_class(ARCHIPACK_OT_door_preset) + bpy.utils.unregister_class(ARCHIPACK_OT_door_draw) + bpy.utils.unregister_class(ARCHIPACK_OT_door_manipulate) |