Cleanup: move 'check_existing' to the bottom of properties
[blender-addons.git] / archipack / archipack_reference_point.py
blob99acf9d86c41b5f5c057ed88b497ba9107a7ce41
1 # -*- coding:utf-8 -*-
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 #####
21 # <pep8 compliant>
23 # ----------------------------------------------------------
24 # Author: Stephen Leger (s-leger)
26 # ----------------------------------------------------------
27 import bpy
28 from bpy.types import Operator, PropertyGroup, Object, Panel
29 from bpy.props import (
30 FloatVectorProperty,
31 CollectionProperty,
32 FloatProperty,
33 EnumProperty
35 from mathutils import Vector
36 from .bmesh_utils import BmeshEdit as bmed
37 from .archipack_object import ArchipackCollectionManager
40 def update(self, context):
41 self.update(context)
44 class archipack_reference_point(PropertyGroup):
45 location_2d : FloatVectorProperty(
46 subtype='XYZ',
47 name="position 2d",
48 default=Vector((0, 0, 0))
50 location_3d : FloatVectorProperty(
51 subtype='XYZ',
52 name="position 3d",
53 default=Vector((0, 0, 0))
55 symbol_scale : FloatProperty(
56 name="Screen scale",
57 default=1,
58 min=0.01,
59 update=update)
60 symbol_type : EnumProperty(
61 name="Symbol type",
62 default='WALL',
63 items=(
64 ('WALL', 'Wall', '', 0),
65 ('ROOF', 'Roof', '', 1)),
66 update=update)
68 @classmethod
69 def filter(cls, o):
70 """
71 Filter object with this class in data
72 return
73 True when object contains this datablock
74 False otherwise
75 usage:
76 class_name.filter(object) from outside world
77 self.__class__.filter(object) from instance
78 """
79 try:
80 return cls.__name__ in o
81 except:
82 pass
83 return False
85 @classmethod
86 def datablock(cls, o):
87 """
88 Retrieve datablock from base object
89 return
90 datablock when found
91 None when not found
92 usage:
93 class_name.datablock(object) from outside world
94 self.__class__.datablock(object) from instance
95 """
96 try:
97 return getattr(o, cls.__name__)[0]
98 except:
99 pass
100 return None
102 def update(self, context):
104 o = context.active_object
106 if self.datablock(o) != self:
107 return
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)]]
154 edges = [
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)
167 bm.clear()
168 for v in verts:
169 bm.verts.new(v)
170 bm.verts.ensure_lookup_table()
171 for ed in edges:
172 bm.edges.new((bm.verts[ed[0]], bm.verts[ed[1]]))
173 bmed._end(bm, o)
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'
183 @classmethod
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)
190 if props is None:
191 return
192 layout = self.layout
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')
196 else:
197 layout.operator('archipack.move_to_2d')
198 layout.prop(props, 'symbol_scale')
199 layout.separator()
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(
211 subtype='XYZ',
212 name="position 3d",
213 default=Vector((0, 0, 0))
215 symbol_type : EnumProperty(
216 name="Symbol type",
217 default='WALL',
218 items=(
219 ('WALL', 'Wall', '', 0),
220 ('ROOF', 'Roof', '', 1))
223 @classmethod
224 def poll(cls, context):
225 return context.active_object is not None
227 def draw(self, context):
228 layout = self.layout
229 row = layout.row()
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
245 d.update(context)
246 return 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
253 return {'FINISHED'}
254 else:
255 self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
256 return {'CANCELLED'}
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'}
266 @classmethod
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)
274 if props is None:
275 return {'CANCELLED'}
276 o.location = props.location_3d
277 return {'FINISHED'}
278 else:
279 self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
280 return {'CANCELLED'}
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'}
290 @classmethod
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()
297 ctx['object'] = o
298 for mod in o.modifiers[:]:
299 ctx['modifier'] = mod
300 try:
301 bpy.ops.object.modifier_apply(ctx, apply_as='DATA',
302 modifier=mod.name)
303 except:
304 pass
306 def execute(self, context):
307 if context.mode == "OBJECT":
308 o = context.active_object
309 to_remove = []
311 for c in o.children:
312 if 'archipack_hybridhole' in c:
313 self.apply_boolean(context, c)
314 to_remove.append(c)
316 for c in o.children:
317 if c.data is not None and "archipack_wall2" in c.data:
318 self.apply_boolean(context, c)
320 for c in o.children:
321 if c.data is not None and (
322 "archipack_window" in c.data or
323 "archipack_door" in c.data):
324 for h in c.children:
325 if "archipack_hole" in h:
326 to_remove.append(h)
328 bpy.ops.object.select_all(action="DESELECT")
329 for r in to_remove:
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
338 return {'FINISHED'}
339 else:
340 self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
341 return {'CANCELLED'}
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'}
351 @classmethod
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)
359 if props is None:
360 return {'CANCELLED'}
361 props.location_3d = o.location
362 o.location = props.location_2d
363 return {'FINISHED'}
364 else:
365 self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
366 return {'CANCELLED'}
369 class ARCHIPACK_OT_store_2d_reference(Operator):
370 bl_idname = "archipack.store_2d_reference"
371 bl_label = "Set 2d"
372 bl_description = "Set 2d reference position"
373 bl_category = 'Archipack'
374 bl_options = {'REGISTER', 'UNDO'}
376 @classmethod
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)
384 if props is None:
385 return {'CANCELLED'}
386 x, y, z = o.location
387 props.location_2d = Vector((x, y, 0))
388 return {'FINISHED'}
389 else:
390 self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
391 return {'CANCELLED'}
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'}
401 @classmethod
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)
409 if props is None:
410 return {'CANCELLED'}
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)
417 return {'FINISHED'}
418 else:
419 self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
420 return {'CANCELLED'}
423 class ARCHIPACK_OT_parent_to_reference(Operator):
424 bl_idname = "archipack.parent_to_reference"
425 bl_label = "Parent"
426 bl_description = "Make selected object childs of parent reference point"
427 bl_category = 'Archipack'
428 bl_options = {'REGISTER', 'UNDO'}
430 @classmethod
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)
438 if props is None:
439 return {'CANCELLED'}
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)))
443 for child in sel:
444 rs = child.matrix_world.to_3x3().to_4x4()
445 loc = itM @ child.matrix_world.translation
446 child.parent = None
447 child.matrix_parent_inverse.identity()
448 child.location = Vector((0, 0, 0))
449 child.parent = o
450 child.matrix_world = rs
451 child.location = loc
452 return {'FINISHED'}
453 else:
454 self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
455 return {'CANCELLED'}
458 def register():
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)
471 def unregister():
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)