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 from bpy
.types
import Operator
, PropertyGroup
, Object
, Panel
29 from bpy
.props
import (
35 from mathutils
import Vector
36 from .bmesh_utils
import BmeshEdit
as bmed
37 from .archipack_object
import ArchipackCollectionManager
40 def update(self
, context
):
44 class archipack_reference_point(PropertyGroup
):
45 location_2d
: FloatVectorProperty(
48 default
=Vector((0, 0, 0))
50 location_3d
: FloatVectorProperty(
53 default
=Vector((0, 0, 0))
55 symbol_scale
: FloatProperty(
60 symbol_type
: EnumProperty(
64 ('WALL', 'Wall', '', 0),
65 ('ROOF', 'Roof', '', 1)),
71 Filter object with this class in data
73 True when object contains this datablock
76 class_name.filter(object) from outside world
77 self.__class__.filter(object) from instance
80 return cls
.__name
__ in o
86 def datablock(cls
, o
):
88 Retrieve datablock from base object
93 class_name.datablock(object) from outside world
94 self.__class__.datablock(object) from instance
97 return getattr(o
, cls
.__name
__)[0]
102 def update(self
, context
):
104 o
= context
.active_object
106 if self
.datablock(o
) != self
:
109 s
= self
.symbol_scale
111 if self
.symbol_type
== 'WALL':
113 verts
= [(s
* x
, s
* y
, s
* z
) for x
, y
, z
in [
114 (-0.25, 0.25, 0.0), (0.25, 0.25, 0.0), (-0.25, -0.25, 0.0), (0.25, -0.25, 0.0),
115 (0.0, 0.0, 0.487), (-0.107, 0.107, 0.216), (0.108, 0.107, 0.216), (-0.107, -0.107, 0.216),
116 (0.108, -0.107, 0.216), (-0.05, 0.05, 0.5), (0.05, 0.05, 0.5), (0.05, -0.05, 0.5),
117 (-0.05, -0.05, 0.5), (-0.193, 0.193, 0.0), (0.193, 0.193, 0.0), (0.193, -0.193, 0.0),
118 (-0.193, -0.193, 0.0), (0.0, 0.0, 0.8), (0.0, 0.8, -0.0), (0.0, 0.0, -0.0),
119 (0.0, 0.0, 0.0), (0.05, 0.05, 0.674), (-0.05, 0.674, -0.05), (0.0, 0.8, -0.0),
120 (-0.05, -0.05, 0.674), (-0.05, 0.674, 0.05), (0.05, 0.674, -0.05), (-0.129, 0.129, 0.162),
121 (0.129, 0.129, 0.162), (-0.129, -0.129, 0.162), (0.129, -0.129, 0.162), (0.0, 0.0, 0.8),
122 (-0.05, 0.05, 0.674), (0.05, -0.05, 0.674), (0.05, 0.674, 0.05), (0.8, -0.0, -0.0),
123 (0.0, -0.0, -0.0), (0.674, 0.05, -0.05), (0.8, -0.0, -0.0), (0.674, 0.05, 0.05),
124 (0.674, -0.05, -0.05), (0.674, -0.05, 0.05)]]
126 edges
= [(1, 0), (0, 9), (9, 10), (10, 1), (3, 1), (10, 11),
127 (11, 3), (2, 3), (11, 12), (12, 2), (0, 2), (12, 9),
128 (6, 5), (8, 6), (7, 8), (5, 7), (17, 24), (17, 20),
129 (18, 25), (18, 19), (13, 14), (14, 15), (15, 16), (16, 13),
130 (4, 6), (15, 30), (17, 21), (26, 22), (23, 22), (23, 34),
131 (18, 26), (28, 27), (30, 28), (29, 30), (27, 29), (14, 28),
132 (13, 27), (16, 29), (4, 7), (4, 8), (4, 5), (31, 33),
133 (31, 32), (21, 32), (24, 32), (24, 33), (21, 33), (25, 22),
134 (25, 34), (26, 34), (35, 39), (35, 36), (40, 37), (38, 37),
135 (38, 41), (35, 40), (39, 37), (39, 41), (40, 41)]
137 elif self
.symbol_type
== 'ROOF':
139 verts
= [(s
* x
, s
* y
, s
* z
) for x
, y
, z
in [
140 (-0.25, 0.25, 0.0), (0.25, 0.25, 0.0), (-0.25, -0.25, 0.0), (0.25, -0.25, 0.0),
141 (0.0, 0.0, 0.487), (-0.107, 0.107, 0.216), (0.108, 0.107, 0.216), (-0.107, -0.107, 0.216),
142 (0.108, -0.107, 0.216), (-0.05, 0.05, 0.5), (0.05, 0.05, 0.5), (0.05, -0.05, 0.5),
143 (-0.05, -0.05, 0.5), (-0.193, 0.193, 0.0), (0.193, 0.193, 0.0), (0.193, -0.193, 0.0),
144 (-0.193, -0.193, 0.0), (0.0, 0.0, 0.8), (0.0, 0.8, -0.0), (0.0, 0.0, 0.0),
145 (0.05, 0.05, 0.673), (-0.05, 0.674, -0.05), (-0.05, -0.05, 0.673), (-0.05, 0.674, 0.05),
146 (0.05, 0.674, -0.05), (-0.129, 0.129, 0.162), (0.129, 0.129, 0.162), (-0.129, -0.129, 0.162),
147 (0.129, -0.129, 0.162), (-0.05, 0.05, 0.673), (0.05, -0.05, 0.673), (0.05, 0.674, 0.05),
148 (0.8, -0.0, -0.0), (0.674, 0.05, -0.05), (0.674, 0.05, 0.05), (0.674, -0.05, -0.05),
149 (0.674, -0.05, 0.05), (0.108, 0.0, 0.216), (0.09, 0.0, 0.261), (0.001, 0.107, 0.216),
150 (0.001, -0.107, 0.216), (-0.107, 0.0, 0.216), (0.0, -0.089, 0.261), (0.0, 0.089, 0.261),
151 (-0.089, 0.0, 0.261), (0.0, 0.042, 0.694), (-0.042, 0.0, 0.694), (0.0, -0.042, 0.694),
152 (0.042, 0.0, 0.694)]]
155 (1, 0), (0, 9), (10, 1), (3, 1), (11, 3), (2, 3), (12, 2), (0, 2),
156 (17, 22), (17, 19), (18, 23), (13, 14), (14, 15), (15, 16), (16, 13),
157 (15, 28), (17, 20), (24, 21), (18, 24), (14, 26), (13, 25), (16, 27),
158 (45, 29), (46, 29), (47, 30), (48, 30), (23, 21), (23, 31), (24, 31),
159 (32, 34), (35, 33), (32, 35), (34, 33), (34, 36), (35, 36), (28, 37),
160 (6, 38), (26, 37), (26, 39), (25, 39), (5, 43), (5, 44), (25, 41),
161 (27, 41), (7, 44), (8, 42), (28, 40), (27, 40), (20, 45), (22, 46),
162 (22, 47), (20, 48), (18, 19), (18, 21), (18, 31), (17, 30), (17, 29),
163 (32, 19), (32, 33), (32, 36), (4, 6), (4, 7), (4, 8), (4, 5), (8, 38),
164 (6, 43), (7, 42), (9, 10), (10, 11), (11, 12), (12, 9)]
166 bm
= bmed
._start
(context
, o
)
170 bm
.verts
.ensure_lookup_table()
172 bm
.edges
.new((bm
.verts
[ed
[0]], bm
.verts
[ed
[1]]))
176 class ARCHIPACK_PT_reference_point(Panel
):
177 bl_idname
= "ARCHIPACK_PT_reference_point"
178 bl_label
= "Reference point"
179 bl_space_type
= 'VIEW_3D'
180 bl_region_type
= 'UI'
181 bl_category
= 'Archipack'
184 def poll(cls
, context
):
185 return archipack_reference_point
.filter(context
.active_object
)
187 def draw(self
, context
):
188 o
= context
.active_object
189 props
= archipack_reference_point
.datablock(o
)
193 if (o
.location
- props
.location_2d
).length
< 0.01:
194 layout
.operator('archipack.move_to_3d')
195 layout
.operator('archipack.move_2d_reference_to_cursor')
197 layout
.operator('archipack.move_to_2d')
198 layout
.prop(props
, 'symbol_scale')
200 layout
.operator('archipack.apply_holes')
203 class ARCHIPACK_OT_reference_point(ArchipackCollectionManager
, Operator
):
204 """Add reference point"""
205 bl_idname
= "archipack.reference_point"
206 bl_label
= "Reference point"
207 bl_description
= "Add reference point"
208 bl_category
= 'Archipack'
209 bl_options
= {'REGISTER', 'UNDO'}
210 location_3d
: FloatVectorProperty(
213 default
=Vector((0, 0, 0))
215 symbol_type
: EnumProperty(
219 ('WALL', 'Wall', '', 0),
220 ('ROOF', 'Roof', '', 1))
224 def poll(cls
, context
):
225 return context
.active_object
is not None
227 def draw(self
, context
):
230 row
.label(text
="Use Properties panel (N) to define parms", icon
='INFO')
232 def create(self
, context
):
233 x
, y
, z
= context
.scene
.cursor
.location
234 # bpy.ops.object.empty_add(type='ARROWS', radius=0.5, location=Vector((x, y, 0)))
235 m
= bpy
.data
.meshes
.new(name
="Reference")
236 o
= bpy
.data
.objects
.new("Reference", m
)
237 o
.location
= Vector((x
, y
, 0))
238 self
.link_object_to_scene(context
, o
)
239 d
= o
.archipack_reference_point
.add()
240 d
.location_2d
= Vector((x
, y
, 0))
241 d
.location_3d
= self
.location_3d
242 d
.symbol_type
= self
.symbol_type
243 o
.select_set(state
=True)
244 context
.view_layer
.objects
.active
= o
248 def execute(self
, context
):
249 if context
.mode
== "OBJECT":
250 o
= self
.create(context
)
251 o
.select_set(state
=True)
252 context
.view_layer
.objects
.active
= o
255 self
.report({'WARNING'}, "Archipack: Option only valid in Object mode")
259 class ARCHIPACK_OT_move_to_3d(Operator
):
260 bl_idname
= "archipack.move_to_3d"
261 bl_label
= "Move to 3d"
262 bl_description
= "Move point to 3d position"
263 bl_category
= 'Archipack'
264 bl_options
= {'REGISTER', 'UNDO'}
267 def poll(cls
, context
):
268 return archipack_reference_point
.filter(context
.active_object
)
270 def execute(self
, context
):
271 if context
.mode
== "OBJECT":
272 o
= context
.active_object
273 props
= archipack_reference_point
.datablock(o
)
276 o
.location
= props
.location_3d
279 self
.report({'WARNING'}, "Archipack: Option only valid in Object mode")
283 class ARCHIPACK_OT_apply_holes(Operator
):
284 bl_idname
= "archipack.apply_holes"
285 bl_label
= "Apply holes"
286 bl_description
= "Apply and remove holes from scene"
287 bl_category
= 'Archipack'
288 bl_options
= {'REGISTER', 'UNDO'}
291 def poll(cls
, context
):
292 return archipack_reference_point
.filter(context
.active_object
)
294 def apply_boolean(self
, context
, o
):
295 # mods = [m for m in o.modifiers if m.type == 'BOOLEAN']
296 ctx
= bpy
.context
.copy()
298 for mod
in o
.modifiers
[:]:
299 ctx
['modifier'] = mod
301 bpy
.ops
.object.modifier_apply(ctx
, apply_as
='DATA',
306 def execute(self
, context
):
307 if context
.mode
== "OBJECT":
308 o
= context
.active_object
312 if 'archipack_hybridhole' in c
:
313 self
.apply_boolean(context
, c
)
317 if c
.data
is not None and "archipack_wall2" in c
.data
:
318 self
.apply_boolean(context
, c
)
321 if c
.data
is not None and (
322 "archipack_window" in c
.data
or
323 "archipack_door" in c
.data
):
325 if "archipack_hole" in h
:
328 bpy
.ops
.object.select_all(action
="DESELECT")
330 r
.hide_select
= False
331 r
.select_set(state
=True)
332 context
.view_layer
.objects
.active
= r
333 bpy
.ops
.object.delete(use_global
=False)
335 o
.select_set(state
=True)
336 context
.view_layer
.objects
.active
= o
340 self
.report({'WARNING'}, "Archipack: Option only valid in Object mode")
344 class ARCHIPACK_OT_move_to_2d(Operator
):
345 bl_idname
= "archipack.move_to_2d"
346 bl_label
= "Move to 2d"
347 bl_description
= "Move point to 2d position"
348 bl_category
= 'Archipack'
349 bl_options
= {'REGISTER', 'UNDO'}
352 def poll(cls
, context
):
353 return archipack_reference_point
.filter(context
.active_object
)
355 def execute(self
, context
):
356 if context
.mode
== "OBJECT":
357 o
= context
.active_object
358 props
= archipack_reference_point
.datablock(o
)
361 props
.location_3d
= o
.location
362 o
.location
= props
.location_2d
365 self
.report({'WARNING'}, "Archipack: Option only valid in Object mode")
369 class ARCHIPACK_OT_store_2d_reference(Operator
):
370 bl_idname
= "archipack.store_2d_reference"
372 bl_description
= "Set 2d reference position"
373 bl_category
= 'Archipack'
374 bl_options
= {'REGISTER', 'UNDO'}
377 def poll(cls
, context
):
378 return archipack_reference_point
.filter(context
.active_object
)
380 def execute(self
, context
):
381 if context
.mode
== "OBJECT":
382 o
= context
.active_object
383 props
= archipack_reference_point
.datablock(o
)
387 props
.location_2d
= Vector((x
, y
, 0))
390 self
.report({'WARNING'}, "Archipack: Option only valid in Object mode")
394 class ARCHIPACK_OT_move_2d_reference_to_cursor(Operator
):
395 bl_idname
= "archipack.move_2d_reference_to_cursor"
396 bl_label
= "Change 2d"
397 bl_description
= "Change 2d reference position to cursor location without moving childs"
398 bl_category
= 'Archipack'
399 bl_options
= {'REGISTER', 'UNDO'}
402 def poll(cls
, context
):
403 return archipack_reference_point
.filter(context
.active_object
)
405 def execute(self
, context
):
406 if context
.mode
== "OBJECT":
407 o
= context
.active_object
408 props
= archipack_reference_point
.datablock(o
)
411 bpy
.ops
.object.select_all(action
="DESELECT")
412 bpy
.ops
.archipack
.reference_point(location_3d
=props
.location_3d
)
413 for child
in o
.children
:
414 child
.select_set(state
=True)
415 bpy
.ops
.archipack
.parent_to_reference()
416 self
.unlink_object_from_scene(o
)
419 self
.report({'WARNING'}, "Archipack: Option only valid in Object mode")
423 class ARCHIPACK_OT_parent_to_reference(Operator
):
424 bl_idname
= "archipack.parent_to_reference"
426 bl_description
= "Make selected object childs of parent reference point"
427 bl_category
= 'Archipack'
428 bl_options
= {'REGISTER', 'UNDO'}
431 def poll(cls
, context
):
432 return archipack_reference_point
.filter(context
.active_object
)
434 def execute(self
, context
):
435 if context
.mode
== "OBJECT":
436 o
= context
.active_object
437 props
= archipack_reference_point
.datablock(o
)
440 sel
= [obj
for obj
in context
.selected_objects
if obj
!= o
and obj
.parent
!= o
]
441 itM
= o
.matrix_world
.inverted()
442 # print("parent_to_reference parenting:%s objects" % (len(sel)))
444 rs
= child
.matrix_world
.to_3x3().to_4x4()
445 loc
= itM
@ child
.matrix_world
.translation
447 child
.matrix_parent_inverse
.identity()
448 child
.location
= Vector((0, 0, 0))
450 child
.matrix_world
= rs
454 self
.report({'WARNING'}, "Archipack: Option only valid in Object mode")
459 bpy
.utils
.register_class(archipack_reference_point
)
460 Object
.archipack_reference_point
= CollectionProperty(type=archipack_reference_point
)
461 bpy
.utils
.register_class(ARCHIPACK_PT_reference_point
)
462 bpy
.utils
.register_class(ARCHIPACK_OT_reference_point
)
463 bpy
.utils
.register_class(ARCHIPACK_OT_move_to_3d
)
464 bpy
.utils
.register_class(ARCHIPACK_OT_move_to_2d
)
465 bpy
.utils
.register_class(ARCHIPACK_OT_store_2d_reference
)
466 bpy
.utils
.register_class(ARCHIPACK_OT_move_2d_reference_to_cursor
)
467 bpy
.utils
.register_class(ARCHIPACK_OT_parent_to_reference
)
468 bpy
.utils
.register_class(ARCHIPACK_OT_apply_holes
)
472 bpy
.utils
.unregister_class(archipack_reference_point
)
473 del Object
.archipack_reference_point
474 bpy
.utils
.unregister_class(ARCHIPACK_PT_reference_point
)
475 bpy
.utils
.unregister_class(ARCHIPACK_OT_reference_point
)
476 bpy
.utils
.unregister_class(ARCHIPACK_OT_move_to_3d
)
477 bpy
.utils
.unregister_class(ARCHIPACK_OT_move_to_2d
)
478 bpy
.utils
.unregister_class(ARCHIPACK_OT_store_2d_reference
)
479 bpy
.utils
.unregister_class(ARCHIPACK_OT_move_2d_reference_to_cursor
)
480 bpy
.utils
.unregister_class(ARCHIPACK_OT_parent_to_reference
)
481 bpy
.utils
.unregister_class(ARCHIPACK_OT_apply_holes
)