3 # ##### BEGIN GPL LICENSE BLOCK #####
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 # ##### END GPL LICENSE BLOCK #####
23 # ----------------------------------------------------------
24 # Author: Stephen Leger (s-leger)
26 # ----------------------------------------------------------
28 # noinspection PyUnresolvedReferences
30 # noinspection PyUnresolvedReferences
31 from bpy
.types
import Operator
, PropertyGroup
, Mesh
, Panel
32 from bpy
.props
import (
33 FloatProperty
, IntProperty
, CollectionProperty
,
34 EnumProperty
, BoolProperty
, StringProperty
36 from mathutils
import Vector
37 # door component objects (panels, handles ..)
38 from .bmesh_utils
import BmeshEdit
as bmed
39 from .panel
import Panel
as DoorPanel
40 from .archipack_handle
import create_handle
, door_handle_horizontal_01
41 from .archipack_manipulator
import Manipulable
42 from .archipack_preset
import ArchipackPreset
, PresetMenuOperator
43 from .archipack_object
import ArchipackObject
, ArchipackCreateTool
, ArchipackDrawTool
, ArchipackCollectionManager
44 from .archipack_gl
import FeedbackPanel
45 from .archipack_keymaps
import Keymaps
50 BOTTOM_HOLE_MARGIN
= 0.001
51 FRONT_HOLE_MARGIN
= 0.1
54 def update(self
, context
):
58 def update_childs(self
, context
):
59 self
.update(context
, childs_only
=True)
62 class archipack_door_panel(ArchipackObject
, PropertyGroup
):
66 default
=100.0, precision
=2,
67 unit
='LENGTH', subtype
='DISTANCE',
73 default
=0.02, precision
=2,
74 unit
='LENGTH', subtype
='DISTANCE',
80 default
=2.0, precision
=2,
81 unit
='LENGTH', subtype
='DISTANCE',
84 direction
: IntProperty(
88 description
="open direction"
97 chanfer
: FloatProperty(
100 default
=0.005, precision
=3,
101 unit
='LENGTH', subtype
='DISTANCE',
102 description
='chanfer'
104 panel_spacing
: FloatProperty(
107 default
=0.1, precision
=2,
108 unit
='LENGTH', subtype
='DISTANCE',
109 description
='distance between panels'
111 panel_bottom
: FloatProperty(
114 default
=0.0, precision
=2,
115 unit
='LENGTH', subtype
='DISTANCE',
116 description
='distance from bottom'
118 panel_border
: FloatProperty(
121 default
=0.2, precision
=2,
122 unit
='LENGTH', subtype
='DISTANCE',
123 description
='distance from border'
125 panels_x
: IntProperty(
130 description
="panels h"
132 panels_y
: IntProperty(
137 description
="panels v"
139 panels_distrib
: EnumProperty(
142 ('REGULAR', 'Regular', '', 0),
143 ('ONE_THIRD', '1/3 2/3', '', 1)
147 handle
: EnumProperty(
150 ('NONE', 'No handle', '', 0),
151 ('BOTH', 'Inside and outside', '', 1)
159 # subdivide side to weld panels
160 subdiv_x
= self
.panels_x
- 1
162 if self
.panels_distrib
== 'REGULAR':
163 subdiv_y
= self
.panels_y
- 1
173 x1
= max(0.001, self
.panel_border
- 0.5 * self
.panel_spacing
)
176 False, # profil closed
177 [1, 0, 0, 1], # x index
180 [0, 1, 1, 1], # material index
193 x2
= 0.5 * self
.panel_spacing
194 x3
= x2
+ self
.chanfer
195 y2
= y1
+ self
.chanfer
196 y3
= y0
- self
.chanfer
199 False, # profil closed
203 [1, 1, 1], # material index
204 side_cap_front
=2, # cap index
209 False, # profil closed
213 [0, 0, 0], # material index
214 side_cap_back
=0, # cap index
218 elif self
.model
== 2:
224 x2
= 0.5 * self
.panel_spacing
225 x4
= x2
+ self
.chanfer
226 x5
= x4
+ self
.chanfer
227 x6
= x5
+ 4 * self
.chanfer
228 x3
= x6
+ self
.chanfer
229 y2
= y1
- self
.chanfer
230 y4
= y1
+ self
.chanfer
231 y3
= y0
+ self
.chanfer
232 y5
= y0
- self
.chanfer
234 False, # profil closed
235 [0, 1, 2, 3, 4, 5], # x index
236 [0, x2
, x4
, x5
, x6
, x3
],
237 [y1
, y1
, y4
, y1
, y1
, y2
],
238 [1, 1, 1, 1, 1, 1], # material index
239 side_cap_front
=5, # cap index
244 False, # profil closed
245 [0, 1, 2, 3, 4, 5], # x index
246 [x3
, x6
, x5
, x4
, x2
, 0],
247 [y3
, y0
, y0
, y5
, y0
, y0
],
248 [0, 0, 0, 0, 0, 0], # material index
249 side_cap_back
=0, # cap index
253 elif self
.model
== 3:
258 x2
= 0.5 * self
.panel_spacing
259 x3
= x2
+ self
.chanfer
260 x4
= x3
+ 4 * self
.chanfer
261 x5
= x4
+ 2 * self
.chanfer
262 y2
= y1
- self
.chanfer
263 y3
= y0
+ self
.chanfer
264 y4
= y2
+ self
.chanfer
265 y5
= y3
- self
.chanfer
267 False, # profil closed
268 [0, 1, 2, 3, 4], # x index
270 [y1
, y1
, y2
, y2
, y4
],
271 [1, 1, 1, 1, 1], # material index
272 side_cap_front
=4, # cap index
277 False, # profil closed
278 [0, 1, 2, 3, 4], # x index
280 [y5
, y3
, y3
, y0
, y0
],
281 [0, 0, 0, 0, 0], # material index
282 side_cap_back
=0, # cap index
287 side
.side_cap_front
= 3
288 side
.side_cap_back
= 0
290 return side
, face
, back
294 if self
.panels_distrib
== 'REGULAR':
295 subdiv_y
= self
.panels_y
- 1
299 radius
= Vector((0.8, 0.5, 0))
300 center
= Vector((0, self
.z
- radius
.x
, 0))
302 if self
.direction
== 0:
307 path_type
= 'RECTANGLE'
309 side
, face
, back
= self
.panels
311 x1
= max(0.001, self
.panel_border
- 0.5 * self
.panel_spacing
)
312 bottom_z
= self
.panel_bottom
313 shape_z
= [0, bottom_z
, bottom_z
, 0]
314 origin
= Vector((-pivot
* 0.5 * self
.x
, 0, 0))
315 offset
= Vector((0, 0, 0))
316 size
= Vector((self
.x
, self
.z
, 0))
317 verts
= side
.vertices(curve_steps
, offset
, center
, origin
,
318 size
, radius
, 0, pivot
, shape_z
=shape_z
, path_type
=path_type
)
320 p_radius
= radius
.copy()
323 if self
.panels_distrib
== 'REGULAR':
324 p_size
= Vector(((self
.x
- 2 * x1
) / self
.panels_x
,
325 (self
.z
- 2 * x1
- bottom_z
) / self
.panels_y
, 0))
326 for i
in range(self
.panels_x
):
327 for j
in range(self
.panels_y
):
332 offset
= Vector(((pivot
* 0.5 * self
.x
) + p_size
.x
* (i
+ 0.5) - 0.5 * size
.x
+ x1
,
333 bottom_z
+ p_size
.y
* j
+ x1
, 0))
334 origin
= Vector((p_size
.x
* (i
+ 0.5) - 0.5 * size
.x
+ x1
, bottom_z
+ p_size
.y
* j
+ x1
, 0))
335 verts
+= face
.vertices(curve_steps
, offset
, center
, origin
,
336 p_size
, p_radius
, 0, 0, shape_z
=None, path_type
=shape
)
338 verts
+= back
.vertices(curve_steps
, offset
, center
, origin
,
339 p_size
, p_radius
, 0, 0, shape_z
=None, path_type
=shape
)
341 ####################################
342 # Ratio vertical panels 1/3 - 2/3
343 ####################################
344 p_size
= Vector(((self
.x
- 2 * x1
) / self
.panels_x
, (self
.z
- 2 * x1
- bottom_z
) / 3, 0))
345 p_size_2x
= Vector((p_size
.x
, p_size
.y
* 2, 0))
346 for i
in range(self
.panels_x
):
348 offset
= Vector(((pivot
* 0.5 * self
.x
) + p_size
.x
* (i
+ 0.5) - 0.5 * size
.x
+ x1
,
349 bottom_z
+ p_size
.y
* j
+ x1
, 0))
350 origin
= Vector((p_size
.x
* (i
+ 0.5) - 0.5 * size
.x
+ x1
, bottom_z
+ p_size
.y
* j
+ x1
, 0))
353 verts
+= face
.vertices(curve_steps
, offset
, center
, origin
,
354 p_size
, p_radius
, 0, 0, shape_z
=None, path_type
=shape
)
357 verts
+= back
.vertices(curve_steps
, offset
, center
, origin
,
358 p_size
, p_radius
, 0, 0, shape_z
=None, path_type
=shape
)
360 offset
= Vector(((pivot
* 0.5 * self
.x
) + p_size
.x
* (i
+ 0.5) - 0.5 * size
.x
+ x1
,
361 bottom_z
+ p_size
.y
* j
+ x1
, 0))
362 origin
= Vector((p_size
.x
* (i
+ 0.5) - 0.5 * size
.x
+ x1
,
363 bottom_z
+ p_size
.y
* j
+ x1
, 0))
366 verts
+= face
.vertices(curve_steps
, offset
, center
, origin
,
367 p_size_2x
, p_radius
, 0, 0, shape_z
=None, path_type
=path_type
)
370 verts
+= back
.vertices(curve_steps
, offset
, center
, origin
,
371 p_size_2x
, p_radius
, 0, 0, shape_z
=None, path_type
=path_type
)
377 if self
.panels_distrib
== 'REGULAR':
378 subdiv_y
= self
.panels_y
- 1
382 path_type
= 'RECTANGLE'
384 side
, face
, back
= self
.panels
386 faces
= side
.faces(curve_steps
, path_type
=path_type
)
387 faces_offset
= side
.n_verts(curve_steps
, path_type
=path_type
)
390 if self
.panels_distrib
== 'REGULAR':
391 for i
in range(self
.panels_x
):
392 for j
in range(self
.panels_y
):
397 faces
+= face
.faces(curve_steps
, path_type
=shape
, offset
=faces_offset
)
398 faces_offset
+= face
.n_verts(curve_steps
, path_type
=shape
)
400 faces
+= back
.faces(curve_steps
, path_type
=shape
, offset
=faces_offset
)
401 faces_offset
+= back
.n_verts(curve_steps
, path_type
=shape
)
403 ####################################
404 # Ratio vertical panels 1/3 - 2/3
405 ####################################
406 for i
in range(self
.panels_x
):
410 faces
+= face
.faces(curve_steps
, path_type
=shape
, offset
=faces_offset
)
411 faces_offset
+= face
.n_verts(curve_steps
, path_type
=shape
)
414 faces
+= back
.faces(curve_steps
, path_type
=shape
, offset
=faces_offset
)
415 faces_offset
+= back
.n_verts(curve_steps
, path_type
=shape
)
419 faces
+= face
.faces(curve_steps
, path_type
=path_type
, offset
=faces_offset
)
420 faces_offset
+= face
.n_verts(curve_steps
, path_type
=path_type
)
423 faces
+= back
.faces(curve_steps
, path_type
=path_type
, offset
=faces_offset
)
424 faces_offset
+= back
.n_verts(curve_steps
, path_type
=path_type
)
430 if self
.panels_distrib
== 'REGULAR':
431 subdiv_y
= self
.panels_y
- 1
435 radius
= Vector((0.8, 0.5, 0))
436 center
= Vector((0, self
.z
- radius
.x
, 0))
438 if self
.direction
== 0:
443 path_type
= 'RECTANGLE'
445 side
, face
, back
= self
.panels
447 x1
= max(0.001, self
.panel_border
- 0.5 * self
.panel_spacing
)
448 bottom_z
= self
.panel_bottom
449 origin
= Vector((-pivot
* 0.5 * self
.x
, 0, 0))
450 size
= Vector((self
.x
, self
.z
, 0))
451 uvs
= side
.uv(curve_steps
, center
, origin
, size
, radius
, 0, pivot
, 0, self
.panel_border
, path_type
=path_type
)
453 p_radius
= radius
.copy()
456 if self
.panels_distrib
== 'REGULAR':
457 p_size
= Vector(((self
.x
- 2 * x1
) / self
.panels_x
, (self
.z
- 2 * x1
- bottom_z
) / self
.panels_y
, 0))
458 for i
in range(self
.panels_x
):
459 for j
in range(self
.panels_y
):
464 origin
= Vector((p_size
.x
* (i
+ 0.5) - 0.5 * size
.x
+ x1
, bottom_z
+ p_size
.y
* j
+ x1
, 0))
465 uvs
+= face
.uv(curve_steps
, center
, origin
, p_size
, p_radius
, 0, 0, 0, 0, path_type
=shape
)
467 uvs
+= back
.uv(curve_steps
, center
, origin
,
468 p_size
, p_radius
, 0, 0, 0, 0, path_type
=shape
)
470 ####################################
471 # Ratio vertical panels 1/3 - 2/3
472 ####################################
473 p_size
= Vector(((self
.x
- 2 * x1
) / self
.panels_x
, (self
.z
- 2 * x1
- bottom_z
) / 3, 0))
474 p_size_2x
= Vector((p_size
.x
, p_size
.y
* 2, 0))
475 for i
in range(self
.panels_x
):
477 origin
= Vector((p_size
.x
* (i
+ 0.5) - 0.5 * size
.x
+ x1
, bottom_z
+ p_size
.y
* j
+ x1
, 0))
480 uvs
+= face
.uv(curve_steps
, center
, origin
, p_size
, p_radius
, 0, 0, 0, 0, path_type
=shape
)
483 uvs
+= back
.uv(curve_steps
, center
, origin
, p_size
, p_radius
, 0, 0, 0, 0, path_type
=shape
)
485 origin
= Vector((p_size
.x
* (i
+ 0.5) - 0.5 * size
.x
+ x1
, bottom_z
+ p_size
.y
* j
+ x1
, 0))
488 uvs
+= face
.uv(curve_steps
, center
, origin
, p_size_2x
, p_radius
, 0, 0, 0, 0, path_type
=path_type
)
491 uvs
+= back
.uv(curve_steps
, center
, origin
,
492 p_size_2x
, p_radius
, 0, 0, 0, 0, path_type
=path_type
)
497 if self
.panels_distrib
== 'REGULAR':
498 subdiv_y
= self
.panels_y
- 1
502 path_type
= 'RECTANGLE'
504 side
, face
, back
= self
.panels
506 mat
= side
.mat(curve_steps
, 1, 0, path_type
=path_type
)
509 if self
.panels_distrib
== 'REGULAR':
510 for i
in range(self
.panels_x
):
511 for j
in range(self
.panels_y
):
516 mat
+= face
.mat(curve_steps
, 1, 1, path_type
=shape
)
518 mat
+= back
.mat(curve_steps
, 0, 0, path_type
=shape
)
520 ####################################
521 # Ratio vertical panels 1/3 - 2/3
522 ####################################
523 for i
in range(self
.panels_x
):
527 mat
+= face
.mat(curve_steps
, 1, 1, path_type
=shape
)
530 mat
+= back
.mat(curve_steps
, 0, 0, path_type
=shape
)
534 mat
+= face
.mat(curve_steps
, 1, 1, path_type
=shape
)
537 mat
+= back
.mat(curve_steps
, 0, 0, path_type
=shape
)
540 def find_handle(self
, o
):
541 for child
in o
.children
:
542 if 'archipack_handle' in child
:
546 def update_handle(self
, context
, o
):
547 handle
= self
.find_handle(o
)
549 m
= bpy
.data
.meshes
.new("Handle")
550 handle
= create_handle(context
, o
, m
)
552 verts
, faces
= door_handle_horizontal_01(self
.direction
, 1)
553 b_verts
, b_faces
= door_handle_horizontal_01(self
.direction
, 0, offset
=len(verts
))
554 b_verts
= [(v
[0], v
[1] - self
.y
, v
[2]) for v
in b_verts
]
556 handle
.location
= ((1 - self
.direction
* 2) * (self
.x
- handle_y
), 0, 0.5 * self
.z
)
557 bmed
.buildmesh(context
, handle
, verts
+ b_verts
, faces
+ b_faces
)
559 def remove_handle(self
, context
, o
):
560 handle
= self
.find_handle(o
)
561 if handle
is not None:
562 self
.unlink_object_from_scene(handle
)
563 bpy
.data
.objects
.remove(handle
, do_unlink
=True)
565 def update(self
, context
):
566 o
= self
.find_in_selection(context
)
571 bmed
.buildmesh(context
, o
, self
.verts
, self
.faces
, matids
=self
.matids
, uvs
=self
.uvs
, weld
=True)
573 if self
.handle
== 'NONE':
574 self
.remove_handle(context
, o
)
576 self
.update_handle(context
, o
)
578 self
.restore_context(context
)
581 class ARCHIPACK_PT_door_panel(Panel
):
582 bl_idname
= "ARCHIPACK_PT_door_panel"
584 bl_space_type
= 'VIEW_3D'
585 bl_region_type
= 'UI'
586 # bl_context = 'object'
587 bl_category
= 'Archipack'
590 def poll(cls
, context
):
591 return archipack_door_panel
.filter(context
.active_object
)
593 def draw(self
, context
):
595 layout
.operator("archipack.select_parent")
598 # ------------------------------------------------------------------
599 # Define operator class to create object
600 # ------------------------------------------------------------------
603 class ARCHIPACK_OT_door_panel(ArchipackCollectionManager
, Operator
):
604 bl_idname
= "archipack.door_panel"
605 bl_label
= "Door model 1"
606 bl_description
= "Door model 1"
607 bl_category
= 'Archipack'
608 bl_options
= {'REGISTER', 'UNDO'}
612 default
=0.80, precision
=2,
613 unit
='LENGTH', subtype
='DISTANCE',
619 default
=2.0, precision
=2,
620 unit
='LENGTH', subtype
='DISTANCE',
626 default
=0.02, precision
=2,
627 unit
='LENGTH', subtype
='DISTANCE',
630 direction
: IntProperty(
634 description
="open direction"
640 description
="panel type"
642 chanfer
: FloatProperty(
645 default
=0.005, precision
=3,
646 unit
='LENGTH', subtype
='DISTANCE',
647 description
='chanfer'
649 panel_spacing
: FloatProperty(
652 default
=0.1, precision
=2,
653 unit
='LENGTH', subtype
='DISTANCE',
654 description
='distance between panels'
656 panel_bottom
: FloatProperty(
659 default
=0.0, precision
=2,
660 unit
='LENGTH', subtype
='DISTANCE',
661 description
='distance from bottom'
663 panel_border
: FloatProperty(
666 default
=0.2, precision
=2,
667 unit
='LENGTH', subtype
='DISTANCE',
668 description
='distance from border'
670 panels_x
: IntProperty(
675 description
="panels h"
677 panels_y
: IntProperty(
682 description
="panels v"
684 panels_distrib
: EnumProperty(
687 ('REGULAR', 'Regular', '', 0),
688 ('ONE_THIRD', '1/3 2/3', '', 1)
692 handle
: EnumProperty(
695 ('NONE', 'No handle', '', 0),
696 ('BOTH', 'Inside and outside', '', 1)
700 material
: StringProperty(
704 def draw(self
, context
):
707 row
.label(text
="Use Properties panel (N) to define parms", icon
='INFO')
709 def create(self
, context
):
711 expose only basic params in operator
712 use object property for other params
714 m
= bpy
.data
.meshes
.new("Panel")
715 o
= bpy
.data
.objects
.new("Panel", m
)
716 d
= m
.archipack_door_panel
.add()
721 d
.direction
= self
.direction
722 d
.chanfer
= self
.chanfer
723 d
.panel_border
= self
.panel_border
724 d
.panel_bottom
= self
.panel_bottom
725 d
.panel_spacing
= self
.panel_spacing
726 d
.panels_distrib
= self
.panels_distrib
727 d
.panels_x
= self
.panels_x
728 d
.panels_y
= self
.panels_y
729 d
.handle
= self
.handle
730 self
.link_object_to_scene(context
, o
)
731 o
.lock_location
[0] = True
732 o
.lock_location
[1] = True
733 o
.lock_location
[2] = True
734 o
.lock_rotation
[0] = True
735 o
.lock_rotation
[1] = True
736 o
.lock_scale
[0] = True
737 o
.lock_scale
[1] = True
738 o
.lock_scale
[2] = True
739 o
.select_set(state
=True)
740 context
.view_layer
.objects
.active
= o
741 m
= o
.archipack_material
.add()
743 m
.material
= self
.material
745 # MaterialUtils.add_door_materials(o)
746 o
.lock_rotation
[0] = True
747 o
.lock_rotation
[1] = True
750 def execute(self
, context
):
751 if context
.mode
== "OBJECT":
752 bpy
.ops
.object.select_all(action
="DESELECT")
753 o
= self
.create(context
)
754 o
.select_set(state
=True)
755 context
.view_layer
.objects
.active
= o
758 self
.report({'WARNING'}, "Archipack: Option only valid in Object mode")
762 class ARCHIPACK_OT_select_parent(Operator
):
763 bl_idname
= "archipack.select_parent"
764 bl_label
= "Edit parameters"
765 bl_description
= "Edit parameters located on parent"
766 bl_category
= 'Archipack'
767 bl_options
= {'REGISTER', 'UNDO'}
769 def draw(self
, context
):
772 row
.label(text
="Use Properties panel (N) to define parms", icon
='INFO')
774 def execute(self
, context
):
775 if context
.mode
== "OBJECT":
776 if context
.active_object
is not None and context
.active_object
.parent
is not None:
777 bpy
.ops
.object.select_all(action
="DESELECT")
778 context
.active_object
.parent
.select_set(state
=True)
779 context
.view_layer
.objects
.active
= context
.active_object
.parent
782 self
.report({'WARNING'}, "Archipack: Option only valid in Object mode")
786 class archipack_door(ArchipackObject
, Manipulable
, PropertyGroup
):
788 The frame is the door main object
789 parent parametric object
790 create/remove/update her own childs
795 default
=100.0, precision
=2, step
=1,
796 unit
='LENGTH', subtype
='DISTANCE',
797 description
='Width', update
=update
,
802 default
=0.20, precision
=2, step
=1,
803 unit
='LENGTH', subtype
='DISTANCE',
804 description
='Depth', update
=update
,
809 default
=2.0, precision
=2, step
=1,
810 unit
='LENGTH', subtype
='DISTANCE',
811 description
='height', update
=update
,
813 frame_x
: FloatProperty(
816 default
=0.1, precision
=2, step
=1,
817 unit
='LENGTH', subtype
='DISTANCE',
818 description
='frame width', update
=update
,
820 frame_y
: FloatProperty(
822 default
=0.03, precision
=2, step
=1,
823 unit
='LENGTH', subtype
='DISTANCE',
824 description
='frame depth', update
=update
,
826 direction
: IntProperty(
830 description
="open direction", update
=update
,
832 door_y
: FloatProperty(
835 default
=0.02, precision
=2, step
=1,
836 unit
='LENGTH', subtype
='DISTANCE',
837 description
='depth', update
=update
,
839 door_offset
: FloatProperty(
842 default
=0, precision
=2, step
=1,
843 unit
='LENGTH', subtype
='DISTANCE',
844 description
='offset', update
=update
,
851 description
="Model", update
=update
,
853 n_panels
: IntProperty(
858 description
="number of panels", update
=update
860 chanfer
: FloatProperty(
863 default
=0.005, precision
=3, step
=0.01,
864 unit
='LENGTH', subtype
='DISTANCE',
865 description
='chanfer', update
=update_childs
,
867 panel_spacing
: FloatProperty(
870 default
=0.1, precision
=2, step
=1,
871 unit
='LENGTH', subtype
='DISTANCE',
872 description
='distance between panels', update
=update_childs
,
874 panel_bottom
: FloatProperty(
877 default
=0.0, precision
=2, step
=1,
878 unit
='LENGTH', subtype
='DISTANCE',
879 description
='distance from bottom', update
=update_childs
,
881 panel_border
: FloatProperty(
884 default
=0.2, precision
=2, step
=1,
885 unit
='LENGTH', subtype
='DISTANCE',
886 description
='distance from border', update
=update_childs
,
888 panels_x
: IntProperty(
893 description
="panels h", update
=update_childs
,
895 panels_y
: IntProperty(
900 description
="panels v", update
=update_childs
,
902 panels_distrib
: EnumProperty(
905 ('REGULAR', 'Regular', '', 0),
906 ('ONE_THIRD', '1/3 2/3', '', 1)
908 default
='REGULAR', update
=update_childs
,
910 handle
: EnumProperty(
913 ('NONE', 'No handle', '', 0),
914 ('BOTH', 'Inside and outside', '', 1)
916 default
='BOTH', update
=update_childs
,
918 hole_margin
: FloatProperty(
921 default
=0.1, precision
=2, step
=1,
922 unit
='LENGTH', subtype
='DISTANCE',
923 description
='how much hole surround wall'
928 description
='flip outside and outside material of hole'
930 auto_update
: BoolProperty(
931 options
={'SKIP_SAVE'},
950 y0
= max(0.25 * self
.door_y
+ 0.0005, self
.y
/ 2 + self
.frame_y
)
951 y1
= max(y0
- 0.5 * self
.door_y
- self
.door_offset
, -y0
+ 0.001)
956 [0, 0, 0, 1, 1, 2, 2], # x index
958 [y2
, y3
, y0
, y0
, y1
, y1
, y2
],
959 [0, 1, 1, 1, 1, 0, 0], # material index
974 y0
= self
.y
/ 2 + self
.hole_margin
980 outside_mat
, inside_mat
= inside_mat
, outside_mat
986 [outside_mat
, inside_mat
, inside_mat
], # material index
989 side_cap_back
=0 # cap index
995 v
= Vector((0, 0, 0))
996 size
= Vector((self
.x
, self
.z
, self
.y
))
997 return self
.frame
.vertices(16, v
, v
, v
, size
, v
, 0, 0, shape_z
=None, path_type
='RECTANGLE')
1001 return self
.frame
.faces(16, path_type
='RECTANGLE')
1005 return self
.frame
.mat(16, 0, 0, path_type
='RECTANGLE')
1009 v
= Vector((0, 0, 0))
1010 size
= Vector((self
.x
, self
.z
, self
.y
))
1011 return self
.frame
.uv(16, v
, v
, size
, v
, 0, 0, 0, 0, path_type
='RECTANGLE')
1013 def setup_manipulators(self
):
1014 if len(self
.manipulators
) == 3:
1016 s
= self
.manipulators
.add()
1019 s
.type_key
= "SNAP_SIZE_LOC"
1020 s
= self
.manipulators
.add()
1023 s
.type_key
= "SNAP_SIZE_LOC"
1024 s
= self
.manipulators
.add()
1026 s
.normal
= Vector((0, 1, 0))
1028 def remove_childs(self
, context
, o
, to_remove
):
1029 for child
in o
.children
:
1032 if archipack_door_panel
.filter(child
):
1033 self
.remove_handle(context
, child
)
1035 self
.unlink_object_from_scene(child
)
1036 bpy
.data
.objects
.remove(child
, do_unlink
=True)
1038 def remove_handle(self
, context
, o
):
1039 handle
= self
.find_handle(o
)
1040 if handle
is not None:
1041 self
.unlink_object_from_scene(handle
)
1042 bpy
.data
.objects
.remove(handle
, do_unlink
=True)
1044 def create_childs(self
, context
, o
):
1047 for child
in o
.children
:
1048 if archipack_door_panel
.filter(child
):
1052 if n_childs
> self
.n_panels
:
1053 self
.remove_childs(context
, o
, n_childs
- self
.n_panels
)
1056 # create one door panel
1057 bpy
.ops
.archipack
.door_panel(
1061 n_panels
=self
.n_panels
,
1062 direction
=self
.direction
,
1063 material
=o
.archipack_material
[0].material
1065 child
= context
.active_object
1067 child
.matrix_world
= o
.matrix_world
.copy()
1068 location
= self
.x
/ 2 + BATTUE
- SPACING
1069 if self
.direction
== 0:
1070 location
= -location
1071 child
.location
.x
= location
1072 child
.location
.y
= self
.door_y
1074 if self
.n_panels
== 2 and n_childs
< 2:
1075 # create 2nth door panel
1076 bpy
.ops
.archipack
.door_panel(
1080 n_panels
=self
.n_panels
,
1081 direction
=1 - self
.direction
,
1082 material
=o
.archipack_material
[0].material
1084 child
= context
.active_object
1087 child
.matrix_world
= o
.matrix_world
.copy()
1088 location
= self
.x
/ 2 + BATTUE
- SPACING
1089 if self
.direction
== 1:
1090 location
= -location
1091 child
.location
.x
= location
1092 child
.location
.y
= self
.door_y
1094 def find_handle(self
, o
):
1095 for handle
in o
.children
:
1096 if 'archipack_handle' in handle
:
1100 def get_childs_panels(self
, context
, o
):
1101 return [child
for child
in o
.children
if archipack_door_panel
.filter(child
)]
1103 def _synch_childs(self
, context
, o
, linked
, childs
):
1105 sub synch childs nodes of linked object
1107 # remove childs not found on source
1108 l_childs
= self
.get_childs_panels(context
, linked
)
1109 c_names
= [c
.data
.name
for c
in childs
]
1112 id = c_names
.index(c
.data
.name
)
1114 self
.remove_handle(context
, c
)
1115 self
.unlink_object_from_scene(c
)
1116 bpy
.data
.objects
.remove(c
, do_unlink
=True)
1118 # children ordering may not be the same, so get the right l_childs order
1119 l_childs
= self
.get_childs_panels(context
, linked
)
1120 l_names
= [c
.data
.name
for c
in l_childs
]
1124 id = l_names
.index(c
.data
.name
)
1129 # add missing childs and update other ones
1130 for i
, child
in enumerate(childs
):
1132 p
= bpy
.data
.objects
.new("DoorPanel", child
.data
)
1133 self
.link_object_to_scene(context
, p
)
1134 p
.lock_location
[0] = True
1135 p
.lock_location
[1] = True
1136 p
.lock_location
[2] = True
1137 p
.lock_rotation
[0] = True
1138 p
.lock_rotation
[1] = True
1139 p
.lock_scale
[0] = True
1140 p
.lock_scale
[1] = True
1141 p
.lock_scale
[2] = True
1143 p
.matrix_world
= linked
.matrix_world
.copy()
1144 m
= p
.archipack_material
.add()
1146 m
.material
= o
.archipack_material
[0].material
1148 p
= l_childs
[order
[i
]]
1150 p
.location
= child
.location
.copy()
1153 handle
= self
.find_handle(child
)
1154 h
= self
.find_handle(p
)
1155 if handle
is not None:
1157 h
= create_handle(context
, p
, handle
.data
)
1158 # MaterialUtils.add_handle_materials(h)
1159 h
.location
= handle
.location
.copy()
1161 self
.unlink_object_from_scene(h
)
1162 bpy
.data
.objects
.remove(h
, do_unlink
=True)
1164 def _synch_hole(self
, context
, linked
, hole
):
1165 l_hole
= self
.find_hole(linked
)
1167 l_hole
= bpy
.data
.objects
.new("hole", hole
.data
)
1168 l_hole
['archipack_hole'] = True
1169 self
.link_object_to_scene(context
, l_hole
)
1170 l_hole
.parent
= linked
1171 l_hole
.matrix_world
= linked
.matrix_world
.copy()
1172 l_hole
.location
= hole
.location
.copy()
1174 l_hole
.data
= hole
.data
1176 def synch_childs(self
, context
, o
):
1178 synch childs nodes of linked objects
1180 bpy
.ops
.object.select_all(action
='DESELECT')
1181 o
.select_set(state
=True)
1182 context
.view_layer
.objects
.active
= o
1183 childs
= self
.get_childs_panels(context
, o
)
1184 hole
= self
.find_hole(o
)
1185 bpy
.ops
.object.select_linked(type='OBDATA')
1186 for linked
in context
.selected_objects
:
1188 self
._synch
_childs
(context
, o
, linked
, childs
)
1189 if hole
is not None:
1190 self
._synch
_hole
(context
, linked
, hole
)
1192 def update_childs(self
, context
, o
):
1194 pass params to childrens
1196 childs
= self
.get_childs_panels(context
, o
)
1197 n_childs
= len(childs
)
1198 self
.remove_childs(context
, o
, n_childs
- self
.n_panels
)
1200 childs
= self
.get_childs_panels(context
, o
)
1201 n_childs
= len(childs
)
1204 # location_y = self.y / 2 + self.frame_y - SPACING
1205 # location_y = min(max(self.door_offset, - location_y), location_y) + self.door_y
1207 location_y
= max(0.25 * self
.door_y
+ 0.0005, self
.y
/ 2 + self
.frame_y
)
1208 location_y
= max(location_y
- self
.door_offset
+ 0.5 * self
.door_y
, -location_y
+ self
.door_y
+ 0.001)
1210 x
= self
.x
/ self
.n_panels
+ (3 - self
.n_panels
) * (BATTUE
- SPACING
)
1212 z
= self
.z
+ BATTUE
- SPACING
1214 if self
.n_panels
< 2:
1215 direction
= self
.direction
1219 for panel
in range(self
.n_panels
):
1223 handle
= self
.handle
1228 direction
= 1 - direction
1230 location_x
= (2 * direction
- 1) * (self
.x
/ 2 + BATTUE
- SPACING
)
1232 if child_n
> n_childs
:
1233 bpy
.ops
.archipack
.door_panel(
1238 direction
=direction
,
1239 chanfer
=self
.chanfer
,
1240 panel_border
=self
.panel_border
,
1241 panel_bottom
=self
.panel_bottom
,
1242 panel_spacing
=self
.panel_spacing
,
1243 panels_distrib
=self
.panels_distrib
,
1244 panels_x
=self
.panels_x
,
1245 panels_y
=self
.panels_y
,
1247 material
=o
.archipack_material
[0].material
1249 child
= context
.active_object
1250 # parenting at 0, 0, 0 before set object matrix_world
1251 # so location remains local from frame
1253 child
.matrix_world
= o
.matrix_world
.copy()
1255 child
= childs
[child_n
- 1]
1256 child
.select_set(state
=True)
1257 context
.view_layer
.objects
.active
= child
1258 props
= archipack_door_panel
.datablock(child
)
1259 if props
is not None:
1263 props
.model
= self
.model
1264 props
.direction
= direction
1265 props
.chanfer
= self
.chanfer
1266 props
.panel_border
= self
.panel_border
1267 props
.panel_bottom
= self
.panel_bottom
1268 props
.panel_spacing
= self
.panel_spacing
1269 props
.panels_distrib
= self
.panels_distrib
1270 props
.panels_x
= self
.panels_x
1271 props
.panels_y
= self
.panels_y
1272 props
.handle
= handle
1273 props
.update(context
)
1274 child
.location
= Vector((location_x
, location_y
, 0))
1276 def update(self
, context
, childs_only
=False):
1278 # support for "copy to selected"
1279 o
= self
.find_in_selection(context
, self
.auto_update
)
1284 self
.setup_manipulators()
1286 if childs_only
is False:
1287 bmed
.buildmesh(context
, o
, self
.verts
, self
.faces
, self
.matids
, self
.uvs
)
1289 self
.update_childs(context
, o
)
1291 if childs_only
is False and self
.find_hole(o
) is not None:
1292 self
.interactive_hole(context
, o
)
1294 # support for instances childs, update at object level
1295 self
.synch_childs(context
, o
)
1297 # setup 3d points for gl manipulators
1298 x
, y
= 0.5 * self
.x
, 0.5 * self
.y
1299 self
.manipulators
[0].set_pts([(-x
, -y
, 0), (x
, -y
, 0), (1, 0, 0)])
1300 self
.manipulators
[1].set_pts([(-x
, -y
, 0), (-x
, y
, 0), (-1, 0, 0)])
1301 self
.manipulators
[2].set_pts([(x
, -y
, 0), (x
, -y
, self
.z
), (-1, 0, 0)])
1304 self
.restore_context(context
)
1306 def find_hole(self
, o
):
1307 for child
in o
.children
:
1308 if 'archipack_hole' in child
:
1312 def interactive_hole(self
, context
, o
):
1313 hole_obj
= self
.find_hole(o
)
1314 if hole_obj
is None:
1315 m
= bpy
.data
.meshes
.new("hole")
1316 hole_obj
= bpy
.data
.objects
.new("hole", m
)
1317 self
.link_object_to_scene(context
, hole_obj
)
1318 hole_obj
['archipack_hole'] = True
1320 hole_obj
.matrix_world
= o
.matrix_world
.copy()
1322 hole_obj
.data
.materials
.clear()
1323 for mat
in o
.data
.materials
:
1324 hole_obj
.data
.materials
.append(mat
)
1327 v
= Vector((0, 0, 0))
1328 offset
= Vector((0, -0.001, 0))
1329 size
= Vector((self
.x
+ 2 * self
.frame_x
, self
.z
+ self
.frame_x
+ 0.001, self
.y
))
1330 verts
= hole
.vertices(16, offset
, v
, v
, size
, v
, 0, 0, shape_z
=None, path_type
='RECTANGLE')
1331 faces
= hole
.faces(16, path_type
='RECTANGLE')
1332 matids
= hole
.mat(16, 0, 1, path_type
='RECTANGLE')
1333 uvs
= hole
.uv(16, v
, v
, size
, v
, 0, 0, 0, 0, path_type
='RECTANGLE')
1334 bmed
.buildmesh(context
, hole_obj
, verts
, faces
, matids
=matids
, uvs
=uvs
)
1337 def robust_hole(self
, context
, tM
):
1339 m
= bpy
.data
.meshes
.new("hole")
1340 o
= bpy
.data
.objects
.new("hole", m
)
1341 o
['archipack_robusthole'] = True
1342 self
.link_object_to_scene(context
, o
)
1343 v
= Vector((0, 0, 0))
1344 offset
= Vector((0, -0.001, 0))
1345 size
= Vector((self
.x
+ 2 * self
.frame_x
, self
.z
+ self
.frame_x
+ 0.001, self
.y
))
1346 verts
= hole
.vertices(16, offset
, v
, v
, size
, v
, 0, 0, shape_z
=None, path_type
='RECTANGLE')
1347 verts
= [tM
@ Vector(v
) for v
in verts
]
1348 faces
= hole
.faces(16, path_type
='RECTANGLE')
1349 matids
= hole
.mat(16, 0, 1, path_type
='RECTANGLE')
1350 uvs
= hole
.uv(16, v
, v
, size
, v
, 0, 0, 0, 0, path_type
='RECTANGLE')
1351 bmed
.buildmesh(context
, o
, verts
, faces
, matids
=matids
, uvs
=uvs
)
1353 o
.select_set(state
=True)
1354 context
.view_layer
.objects
.active
= o
1358 class ARCHIPACK_PT_door(Panel
):
1359 bl_idname
= "ARCHIPACK_PT_door"
1361 bl_space_type
= 'VIEW_3D'
1362 bl_region_type
= 'UI'
1363 bl_category
= 'Archipack'
1366 def poll(cls
, context
):
1367 return archipack_door
.filter(context
.active_object
)
1369 def draw(self
, context
):
1370 o
= context
.active_object
1371 if not archipack_door
.filter(o
):
1373 layout
= self
.layout
1374 layout
.operator('archipack.door_manipulate', icon
='VIEW_PAN')
1375 props
= archipack_door
.datablock(o
)
1376 row
= layout
.row(align
=True)
1377 row
.operator('archipack.door', text
="Refresh", icon
='FILE_REFRESH').mode
= 'REFRESH'
1378 if o
.data
.users
> 1:
1379 row
.operator('archipack.door', text
="Make unique", icon
='UNLINKED').mode
= 'UNIQUE'
1380 row
.operator('archipack.door', text
="Delete", icon
='ERROR').mode
= 'DELETE'
1382 # box.label(text="Styles")
1383 row
= box
.row(align
=True)
1384 row
.operator("archipack.door_preset_menu", text
=bpy
.types
.ARCHIPACK_OT_door_preset_menu
.bl_label
)
1385 row
.operator("archipack.door_preset", text
="", icon
='ADD')
1386 row
.operator("archipack.door_preset", text
="", icon
='REMOVE').remove_active
= True
1389 box
.label(text
="Size")
1390 box
.prop(props
, 'x')
1391 box
.prop(props
, 'y')
1392 box
.prop(props
, 'z')
1393 box
.prop(props
, 'door_offset')
1397 row
.label(text
="Door")
1398 box
.prop(props
, 'direction')
1399 box
.prop(props
, 'n_panels')
1400 box
.prop(props
, 'door_y')
1401 box
.prop(props
, 'handle')
1405 row
.label(text
="Frame")
1406 row
= box
.row(align
=True)
1407 row
.prop(props
, 'frame_x')
1408 row
.prop(props
, 'frame_y')
1412 row
.label(text
="Panels")
1413 box
.prop(props
, 'model')
1415 box
.prop(props
, 'panels_distrib', text
="")
1416 row
= box
.row(align
=True)
1417 row
.prop(props
, 'panels_x')
1418 if props
.panels_distrib
== 'REGULAR':
1419 row
.prop(props
, 'panels_y')
1420 box
.prop(props
, 'panel_bottom')
1421 box
.prop(props
, 'panel_spacing')
1422 box
.prop(props
, 'panel_border')
1423 box
.prop(props
, 'chanfer')
1426 # ------------------------------------------------------------------
1427 # Define operator class to create object
1428 # ------------------------------------------------------------------
1431 class ARCHIPACK_OT_door(ArchipackCreateTool
, Operator
):
1432 bl_idname
= "archipack.door"
1434 bl_description
= "Door"
1435 bl_category
= 'Archipack'
1436 bl_options
= {'REGISTER', 'UNDO'}
1440 default
=0.80, precision
=2,
1441 unit
='LENGTH', subtype
='DISTANCE',
1447 default
=0.20, precision
=2,
1448 unit
='LENGTH', subtype
='DISTANCE',
1454 default
=2.0, precision
=2,
1455 unit
='LENGTH', subtype
='DISTANCE',
1456 description
='height'
1458 direction
: IntProperty(
1462 description
="open direction"
1464 n_panels
: IntProperty(
1469 description
="number of panels"
1471 chanfer
: FloatProperty(
1474 default
=0.005, precision
=3,
1475 unit
='LENGTH', subtype
='DISTANCE',
1476 description
='chanfer'
1478 panel_spacing
: FloatProperty(
1481 default
=0.1, precision
=2,
1482 unit
='LENGTH', subtype
='DISTANCE',
1483 description
='distance between panels'
1485 panel_bottom
: FloatProperty(
1487 default
=0.0, precision
=2,
1488 unit
='LENGTH', subtype
='DISTANCE',
1489 description
='distance from bottom'
1491 panel_border
: FloatProperty(
1494 default
=0.2, precision
=2,
1495 unit
='LENGTH', subtype
='DISTANCE',
1496 description
='distance from border'
1498 panels_x
: IntProperty(
1503 description
="panels h"
1505 panels_y
: IntProperty(
1510 description
="panels v"
1512 panels_distrib
: EnumProperty(
1513 name
='distribution',
1515 ('REGULAR', 'Regular', '', 0),
1516 ('ONE_THIRD', '1/3 2/3', '', 1)
1520 handle
: EnumProperty(
1523 ('NONE', 'No handle', '', 0),
1524 ('BOTH', 'Inside and outside', '', 1)
1528 mode
: EnumProperty(
1530 ('CREATE', 'Create', '', 0),
1531 ('DELETE', 'Delete', '', 1),
1532 ('REFRESH', 'Refresh', '', 2),
1533 ('UNIQUE', 'Make unique', '', 3),
1538 def create(self
, context
):
1540 expose only basic params in operator
1541 use object property for other params
1543 m
= bpy
.data
.meshes
.new("Door")
1544 o
= bpy
.data
.objects
.new("Door", m
)
1545 d
= m
.archipack_door
.add()
1549 d
.direction
= self
.direction
1550 d
.n_panels
= self
.n_panels
1551 d
.chanfer
= self
.chanfer
1552 d
.panel_border
= self
.panel_border
1553 d
.panel_bottom
= self
.panel_bottom
1554 d
.panel_spacing
= self
.panel_spacing
1555 d
.panels_distrib
= self
.panels_distrib
1556 d
.panels_x
= self
.panels_x
1557 d
.panels_y
= self
.panels_y
1558 d
.handle
= self
.handle
1559 self
.link_object_to_scene(context
, o
)
1560 o
.select_set(state
=True)
1561 context
.view_layer
.objects
.active
= o
1562 self
.add_material(o
)
1564 o
.select_set(state
=True)
1565 context
.view_layer
.objects
.active
= o
1568 def delete(self
, context
):
1569 o
= context
.active_object
1570 if archipack_door
.filter(o
):
1571 bpy
.ops
.archipack
.disable_manipulate()
1572 for child
in o
.children
:
1573 if 'archipack_hole' in child
:
1574 self
.unlink_object_from_scene(child
)
1575 bpy
.data
.objects
.remove(child
, do_unlink
=True)
1576 elif child
.data
is not None and 'archipack_door_panel' in child
.data
:
1577 for handle
in child
.children
:
1578 if 'archipack_handle' in handle
:
1579 self
.unlink_object_from_scene(handle
)
1580 bpy
.data
.objects
.remove(handle
, do_unlink
=True)
1581 self
.unlink_object_from_scene(child
)
1582 bpy
.data
.objects
.remove(child
, do_unlink
=True)
1583 self
.unlink_object_from_scene(o
)
1584 bpy
.data
.objects
.remove(o
, do_unlink
=True)
1586 def update(self
, context
):
1587 o
= context
.active_object
1588 d
= archipack_door
.datablock(o
)
1591 bpy
.ops
.object.select_linked(type='OBDATA')
1592 for linked
in context
.selected_objects
:
1594 archipack_door
.datablock(linked
).update(context
)
1595 bpy
.ops
.object.select_all(action
="DESELECT")
1596 o
.select_set(state
=True)
1597 context
.view_layer
.objects
.active
= o
1599 def unique(self
, context
):
1600 act
= context
.active_object
1601 sel
= context
.selected_objects
[:]
1602 bpy
.ops
.object.select_all(action
="DESELECT")
1604 if archipack_door
.filter(o
):
1605 o
.select_set(state
=True)
1606 for child
in o
.children
:
1607 if 'archipack_hole' in child
or (child
.data
is not None and
1608 'archipack_door_panel' in child
.data
):
1609 child
.hide_select
= False
1610 child
.select_set(state
=True)
1611 if len(context
.selected_objects
) > 0:
1612 bpy
.ops
.object.make_single_user(type='SELECTED_OBJECTS', object=True,
1613 obdata
=True, material
=False, texture
=False, animation
=False)
1614 for child
in context
.selected_objects
:
1615 if 'archipack_hole' in child
:
1616 child
.hide_select
= True
1617 bpy
.ops
.object.select_all(action
="DESELECT")
1618 context
.view_layer
.objects
.active
= act
1620 o
.select_set(state
=True)
1622 def execute(self
, context
):
1623 if context
.mode
== "OBJECT":
1624 if self
.mode
== 'CREATE':
1625 bpy
.ops
.object.select_all(action
="DESELECT")
1626 o
= self
.create(context
)
1627 o
.location
= bpy
.context
.scene
.cursor
.location
1628 o
.select_set(state
=True)
1629 context
.view_layer
.objects
.active
= o
1631 elif self
.mode
== 'DELETE':
1632 self
.delete(context
)
1633 elif self
.mode
== 'REFRESH':
1634 self
.update(context
)
1635 elif self
.mode
== 'UNIQUE':
1636 self
.unique(context
)
1639 self
.report({'WARNING'}, "Archipack: Option only valid in Object mode")
1640 return {'CANCELLED'}
1643 class ARCHIPACK_OT_door_draw(ArchipackDrawTool
, Operator
):
1644 bl_idname
= "archipack.door_draw"
1645 bl_label
= "Draw Doors"
1646 bl_description
= "Draw Doors over walls"
1647 bl_category
= 'Archipack'
1648 bl_options
= {'REGISTER', 'UNDO'}
1650 filepath
: StringProperty(default
="")
1656 def poll(cls
, context
):
1659 def draw(self
, context
):
1660 layout
= self
.layout
1662 row
.label(text
="Use Properties panel (N) to define parms", icon
='INFO')
1664 def draw_callback(self
, _self
, context
):
1665 self
.feedback
.draw(context
)
1667 def add_object(self
, context
, event
):
1668 o
= context
.active_object
1669 bpy
.ops
.object.select_all(action
="DESELECT")
1671 if archipack_door
.filter(o
):
1673 o
.select_set(state
=True)
1674 context
.view_layer
.objects
.active
= o
1677 bpy
.ops
.archipack
.door(mode
="UNIQUE")
1681 self
.link_object_to_scene(context
, new_w
)
1683 for child
in o
.children
:
1684 if "archipack_hole" not in child
:
1685 new_c
= child
.copy()
1686 new_c
.data
= child
.data
1687 new_c
.parent
= new_w
1688 self
.link_object_to_scene(context
, new_c
)
1690 for c
in child
.children
:
1693 new_h
.parent
= new_c
1694 self
.link_object_to_scene(context
, new_h
)
1697 o
.select_set(state
=True)
1698 context
.view_layer
.objects
.active
= o
1701 bpy
.ops
.archipack
.door(auto_manipulate
=False, filepath
=self
.filepath
)
1702 o
= context
.active_object
1704 self
.object_name
= o
.name
1706 bpy
.ops
.archipack
.generate_hole('INVOKE_DEFAULT')
1707 o
.select_set(state
=True)
1708 context
.view_layer
.objects
.active
= o
1710 def modal(self
, context
, event
):
1712 context
.area
.tag_redraw()
1713 o
= context
.scene
.objects
.get(self
.object_name
.strip())
1717 d
= archipack_door
.datablock(o
)
1721 hole
= d
.find_hole(o
)
1723 # hide hole from raycast
1724 if hole
is not None:
1725 o
.hide_viewport
= True
1726 hole
.hide_viewport
= True
1728 res
, tM
, wall
, width
, y
, z_offset
= self
.mouse_hover_wall(context
, event
)
1730 if hole
is not None:
1731 o
.hide_viewport
= False
1732 hole
.hide_viewport
= False
1734 if res
and d
is not None:
1736 if d
.y
!= wall
.data
.archipack_wall2
[0].width
:
1737 d
.y
= wall
.data
.archipack_wall2
[0].width
1739 if event
.value
== 'PRESS':
1741 if event
.type in {'C'}:
1742 bpy
.ops
.archipack
.door(mode
='DELETE')
1743 self
.feedback
.disable()
1744 bpy
.types
.SpaceView3D
.draw_handler_remove(self
._handle
, 'WINDOW')
1745 bpy
.ops
.archipack
.door_preset_menu(
1747 preset_operator
="archipack.door_draw")
1750 if event
.type in {'LEFTMOUSE', 'RET', 'NUMPAD_ENTER', 'SPACE'}:
1751 if wall
is not None:
1752 o
.select_set(state
=True)
1753 context
.view_layer
.objects
.active
= wall
1754 wall
.select_set(state
=True)
1755 if bpy
.ops
.archipack
.single_boolean
.poll():
1756 bpy
.ops
.archipack
.single_boolean()
1757 wall
.select_set(state
=False)
1758 # o must be a door here
1760 context
.view_layer
.objects
.active
= o
1761 self
.stack
.append(o
)
1762 self
.add_object(context
, event
)
1763 context
.active_object
.matrix_world
= tM
1764 return {'RUNNING_MODAL'}
1766 # prevent selection of other object
1767 if event
.type in {'RIGHTMOUSE'}:
1768 return {'RUNNING_MODAL'}
1770 if self
.keymap
.check(event
, self
.keymap
.undo
) or (
1771 event
.type in {'BACK_SPACE'} and event
.value
== 'RELEASE'
1773 if len(self
.stack
) > 0:
1774 last
= self
.stack
.pop()
1775 context
.view_layer
.objects
.active
= last
1776 bpy
.ops
.archipack
.door(mode
="DELETE")
1777 context
.view_layer
.objects
.active
= o
1778 return {'RUNNING_MODAL'}
1780 if event
.value
== 'RELEASE':
1782 if event
.type in {'ESC', 'RIGHTMOUSE'}:
1783 bpy
.ops
.archipack
.door(mode
='DELETE')
1784 self
.feedback
.disable()
1785 bpy
.types
.SpaceView3D
.draw_handler_remove(self
._handle
, 'WINDOW')
1788 return {'PASS_THROUGH'}
1790 def invoke(self
, context
, event
):
1792 if context
.mode
== "OBJECT":
1795 self
.keymap
= Keymaps(context
)
1796 # exit manipulate_mode if any
1797 bpy
.ops
.archipack
.disable_manipulate()
1798 # invoke with alt pressed will use current object as basis for linked copy
1799 if self
.filepath
== '' and archipack_door
.filter(context
.active_object
):
1800 o
= context
.active_object
1801 context
.view_layer
.objects
.active
= None
1802 bpy
.ops
.object.select_all(action
="DESELECT")
1804 o
.select_set(state
=True)
1805 context
.view_layer
.objects
.active
= o
1806 self
.add_object(context
, event
)
1807 self
.feedback
= FeedbackPanel()
1808 self
.feedback
.instructions(context
, "Draw a door", "Click & Drag over a wall", [
1809 ('LEFTCLICK, RET, SPACE, ENTER', 'Create a door'),
1810 ('BACKSPACE, CTRL+Z', 'undo last'),
1811 ('C', 'Choose another door'),
1812 ('SHIFT', 'Make independent copy'),
1813 ('RIGHTCLICK or ESC', 'exit')
1815 self
.feedback
.enable()
1816 args
= (self
, context
)
1818 self
._handle
= bpy
.types
.SpaceView3D
.draw_handler_add(self
.draw_callback
, args
, 'WINDOW', 'POST_PIXEL')
1819 context
.window_manager
.modal_handler_add(self
)
1820 return {'RUNNING_MODAL'}
1822 self
.report({'WARNING'}, "Archipack: Option only valid in Object mode")
1823 return {'CANCELLED'}
1826 # ------------------------------------------------------------------
1827 # Define operator class to manipulate object
1828 # ------------------------------------------------------------------
1831 class ARCHIPACK_OT_door_manipulate(Operator
):
1832 bl_idname
= "archipack.door_manipulate"
1833 bl_label
= "Manipulate"
1834 bl_description
= "Manipulate"
1835 bl_options
= {'REGISTER', 'UNDO'}
1838 def poll(self
, context
):
1839 return archipack_door
.filter(context
.active_object
)
1841 def invoke(self
, context
, event
):
1842 d
= archipack_door
.datablock(context
.active_object
)
1843 d
.manipulable_invoke(context
)
1847 # ------------------------------------------------------------------
1848 # Define operator class to load / save presets
1849 # ------------------------------------------------------------------
1852 class ARCHIPACK_OT_door_preset_menu(PresetMenuOperator
, Operator
):
1853 bl_description
= "Show Doors presets"
1854 bl_idname
= "archipack.door_preset_menu"
1855 bl_label
= "Door Presets"
1856 preset_subdir
= "archipack_door"
1859 class ARCHIPACK_OT_door_preset(ArchipackPreset
, Operator
):
1860 """Add a Door Preset"""
1861 bl_idname
= "archipack.door_preset"
1862 bl_label
= "Add Door Preset"
1863 preset_menu
= "ARCHIPACK_OT_door_preset_menu"
1866 def blacklist(self
):
1867 return ['manipulators']
1871 bpy
.utils
.register_class(archipack_door_panel
)
1872 Mesh
.archipack_door_panel
= CollectionProperty(type=archipack_door_panel
)
1873 bpy
.utils
.register_class(ARCHIPACK_PT_door_panel
)
1874 bpy
.utils
.register_class(ARCHIPACK_OT_door_panel
)
1875 bpy
.utils
.register_class(ARCHIPACK_OT_select_parent
)
1876 bpy
.utils
.register_class(archipack_door
)
1877 Mesh
.archipack_door
= CollectionProperty(type=archipack_door
)
1878 bpy
.utils
.register_class(ARCHIPACK_OT_door_preset_menu
)
1879 bpy
.utils
.register_class(ARCHIPACK_PT_door
)
1880 bpy
.utils
.register_class(ARCHIPACK_OT_door
)
1881 bpy
.utils
.register_class(ARCHIPACK_OT_door_preset
)
1882 bpy
.utils
.register_class(ARCHIPACK_OT_door_draw
)
1883 bpy
.utils
.register_class(ARCHIPACK_OT_door_manipulate
)
1887 bpy
.utils
.unregister_class(archipack_door_panel
)
1888 del Mesh
.archipack_door_panel
1889 bpy
.utils
.unregister_class(ARCHIPACK_PT_door_panel
)
1890 bpy
.utils
.unregister_class(ARCHIPACK_OT_door_panel
)
1891 bpy
.utils
.unregister_class(ARCHIPACK_OT_select_parent
)
1892 bpy
.utils
.unregister_class(archipack_door
)
1893 del Mesh
.archipack_door
1894 bpy
.utils
.unregister_class(ARCHIPACK_OT_door_preset_menu
)
1895 bpy
.utils
.unregister_class(ARCHIPACK_PT_door
)
1896 bpy
.utils
.unregister_class(ARCHIPACK_OT_door
)
1897 bpy
.utils
.unregister_class(ARCHIPACK_OT_door_preset
)
1898 bpy
.utils
.unregister_class(ARCHIPACK_OT_door_draw
)
1899 bpy
.utils
.unregister_class(ARCHIPACK_OT_door_manipulate
)