Update for API change: scene.cursor_location -> scene.cursor.location
[blender-addons-contrib.git] / np_station / np_point_array.py
1
2
3 # ##### BEGIN GPL LICENSE BLOCK #####
4 #
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.
9 #
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.
14 #
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.
18 #
19 # ##### END GPL LICENSE BLOCK #####
20
21 '''
22
23 DESCRIPTION:
24
25 Makes a copy (duplicate) of objects using snap points. Emulates the functionality of the standard 'copy' command in CAD applications, with vertex snapping. Optionally, using selected objects and last distance used, makes an array of selected objects.
26
27
28 INSTALLATION:
29
30 Unzip and place .py file to scripts / addons_contrib folder. In User Preferences / Addons tab, search with Testing filter - NP Point Copy and check the box.
31 Now you have the operator in your system. If you press Save User Preferences, you will have it at your disposal every time you run Blender.
32
33
34 SHORTCUTS:
35
36 After successful installation of the addon, the NP Point Copy operator should be registered in your system. Enter User Preferences / Input, and under that, 3DView / Object mode. At the bottom of the list click the 'Add new' button. In the operator field type object.np_point_copy_xxx (xxx being the number of the version) and assign a shortcut key of your preference. At the moment i am using 'C' for 'copy', as in standard CAD applications. I rarely use circle selection so letter 'C' is free.
37
38
39 USAGE:
40
41 You can run the operator with spacebar search - NP Point Copy, or shortcut key if you assigned it.
42 Select a point anywhere in the scene (holding CTRL enables snapping). This will be your 'take' point.
43 Move your mouse and click to a point anywhere in the scene with the left mouse button (LMB), in relation to the 'take' point and the operator will duplicate the selected objects at that position (again CTRL - snap enables snapping to objects around the scene). You can continue duplicating objects in the same way. When you want to finish the process, press ESC or RMB. If you want to make an array of the copied objects in relation to the last pair, press the 'ENTER' button (ENT). The command will automatically read the direction and the distance between the last pair of copied objects and present an interface to specify the number of arrayed copies. You specify the number with CTRL + mouse scroll, with the possibility to go below the amount of 2 which changes the mode of array to division.  You confirm the array with RMB / ENTER / TAB key or cancel it with ESC. Pressing RMB at the end will confirm the array and keep it as a modifier in the modifier stack, ENTER will apply the modifier as a single object and remove the modifier, while TAB would apply the modifier as an array of separate individual objects and remove the modifier from the modifier stack.
44 If at any point you lose sight of the next point you want to snap to, you can press SPACE to go to NAVIGATION mode in which you can change the point of view. When your next point is clearly in your field of view, you return to normal mode by pressing SPACE again or LMB.
45 Middle mouse button (MMB) enables axis constraint during snapping, while numpad keys enable numerical input for the copy distance.
46
47
48 ADDON SETTINGS:
49
50 Below the addon name in the user preferences / addon tab, you can find a couple of settings that control the behavior of the addon:
51
52 Unit scale: Distance multiplier for various unit scenarios
53 Suffix: Unit abbreviation after the numerical distance
54 Custom colors: Default or custom colors for graphical elements
55 Mouse badge: Option to display a small cursor label
56
57
58 IMPORTANT PERFORMANCE NOTES:
59
60 None so far.
61
62 '''
63
64 bl_info = {
65     'name':'NP 020 Point Array',
66     'author':'Okavango & the Blenderartists community',
67     'version': (0, 2, 0),
68     'blender': (2, 75, 0),
69     'location': 'View3D',
70     'warning': '',
71     'description': 'Makes a directional array of selected objects using "take" and "place" snap points',
72     'wiki_url': '',
73     'category': '3D View'}
74
75 import bpy
76 import copy
77 import bmesh
78 import bgl
79 import blf
80 import mathutils
81 from bpy_extras import view3d_utils
82 from bpy.app.handlers import persistent
83 from mathutils import Vector, Matrix
84 from blf import ROTATION
85 from math import radians
86 from bpy.props import *
87
88 from .utils_geometry import *
89 from .utils_graphics import *
90 from .utils_function import *
91
92 # Defining the main class - the macro:
93
94 class NP020PointArray(bpy.types.Macro):
95     bl_idname = 'object.np_020_point_array'
96     bl_label = 'NP 020 Point Array'
97     bl_options = {'UNDO'}
98
99
100 # Defining the storage class that will serve as a variable bank for exchange among the classes. Later, this bank will receive more variables with their values for safe keeping, as the program goes on:
101
102 class NP020PA:
103
104     take = None
105     place = None
106     takeloc3d = (0.0,0.0,0.0)
107     placeloc3d = (0.0,0.0,0.0)
108     dist = None
109     mode = 'MOVE'
110     flag = 'NONE'
111     deltavec = Vector ((0, 0, 0))
112     deltavec_safe = Vector ((0, 0, 0))
113     prevob = None
114     nextob = None
115
116 # Defining the scene update algorithm that will track the state of the objects during modal transforms, which is otherwise impossible:
117
118 @persistent
119 def NPPA_scene_update(context):
120
121     #np_print('00_SceneUpdate_START')
122     if bpy.data.objects.is_updated:
123         np_print('NPPA_update1')
124         mode = NP020PA.mode
125         flag = NP020PA.flag
126         #np_print(mode, flag)
127         take = NP020PA.take
128         place = NP020PA.place
129         if flag in ('RUNTRANSZERO', 'RUNTRANSFIRST','RUNTRANSNEXT', 'NAVTRANSZERO', 'NAVTRANSFIRST', 'NAVTRANSNEXT'):
130             np_print('NPPA_update2')
131             NP020PA.takeloc3d = take.location
132             NP020PA.placeloc3d = place.location
133     #np_print('up3')
134     #np_print('00_SceneUpdate_FINISHED')
135
136
137 # Defining the first of the classes from the macro, that will gather the current system settings set by the user. Some of the system settings will be changed during the process, and will be restored when macro has completed.
138
139 class NPPAGetContext(bpy.types.Operator):
140     bl_idname = 'object.np_pa_get_context'
141     bl_label = 'NP PA Get Context'
142     bl_options = {'INTERNAL'}
143
144     def execute(self, context):
145         if bpy.context.selected_objects == []:
146             self.report({'WARNING'}, "Please select objects first")
147             return {'CANCELLED'}
148         NP020PA.use_snap = copy.deepcopy(bpy.context.tool_settings.use_snap)
149         NP020PA.snap_element = copy.deepcopy(bpy.context.tool_settings.snap_element)
150         NP020PA.snap_target = copy.deepcopy(bpy.context.tool_settings.snap_target)
151         NP020PA.pivot_point = copy.deepcopy(bpy.context.space_data.pivot_point)
152         NP020PA.trans_orient = copy.deepcopy(bpy.context.space_data.transform_orientation)
153         NP020PA.curloc = copy.deepcopy(bpy.context.scene.cursor.location)
154         NP020PA.acob = bpy.context.active_object
155         if bpy.context.mode == 'OBJECT':
156             NP020PA.edit_mode = 'OBJECT'
157         elif bpy.context.mode in ('EDIT_MESH', 'EDIT_CURVE', 'EDIT_SURFACE', 'EDIT_TEXT', 'EDIT_ARMATURE', 'EDIT_METABALL', 'EDIT_LATTICE'):
158             NP020PA.edit_mode = 'EDIT'
159         elif bpy.context.mode == 'POSE':
160             NP020PA.edit_mode = 'POSE'
161         elif bpy.context.mode == 'SCULPT':
162             NP020PA.edit_mode = 'SCULPT'
163         elif bpy.context.mode == 'PAINT_WEIGHT':
164             NP020PA.edit_mode = 'WEIGHT_PAINT'
165         elif bpy.context.mode == 'PAINT_TEXTURE':
166             NP020PA.edit_mode = 'TEXTURE_PAINT'
167         elif bpy.context.mode == 'PAINT_VERTEX':
168             NP020PA.edit_mode = 'VERTEX_PAINT'
169         elif bpy.context.mode == 'PARTICLE':
170             NP020PA.edit_mode = 'PARTICLE_EDIT'
171         return {'FINISHED'}
172         # Changing to OBJECT mode which will be the context for the procedure:
173         if bpy.context.mode not in ('OBJECT'):
174             bpy.ops.object.mode_set(mode = 'OBJECT')
175         # De-selecting objects in prepare for other processes in the script:
176         bpy.ops.object.select_all(action = 'DESELECT')
177         np_print('01_ReadContext_FINISHED', ';', 'flag = ', NP020PA.flag)
178         return {'FINISHED'}
179
180
181 # Defining the operator for aquiring the list of selected objects and storing them for later re-calls:
182
183 class NPPAGetSelection(bpy.types.Operator):
184     bl_idname = 'object.np_pa_get_selection'
185     bl_label = 'NP PA Get Selection'
186     bl_options = {'INTERNAL'}
187
188     def execute(self, context):
189         # Reading and storing the selection:
190         NP020PA.selob = bpy.context.selected_objects
191         return {'FINISHED'}
192
193
194 # Defining the operator that will read the mouse position in 3D when the command is activated and store it as a location for placing the 'take' and 'place' points under the mouse:
195
196 class NPPAGetMouseloc(bpy.types.Operator):
197     bl_idname = 'object.np_pa_get_mouseloc'
198     bl_label = 'NP PA Get Mouseloc'
199     bl_options = {'INTERNAL'}
200
201     def modal(self, context, event):
202         region = context.region
203         rv3d = context.region_data
204         co2d = ((event.mouse_region_x, event.mouse_region_y))
205         view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, co2d)
206         enterloc = view3d_utils.region_2d_to_origin_3d(region, rv3d, co2d) + view_vector/5
207         NP020PA.enterloc = copy.deepcopy(enterloc)
208         np_print('02_RadMouseloc_FINISHED', ';', 'flag = ', NP020PA.flag)
209         return{'FINISHED'}
210
211     def invoke(self,context,event):
212         args = (self,context)
213         context.window_manager.modal_handler_add(self)
214         np_print('02_ReadMouseloc_INVOKED_FINISHED', ';', 'flag = ', NP020PA.flag)
215         return {'RUNNING_MODAL'}
216
217
218 # Defining the operator that will generate 'take' and 'place' points at the spot marked by mouse, preparing for translation:
219
220 class NPPAAddHelpers(bpy.types.Operator):
221     bl_idname = 'object.np_pa_add_helpers'
222     bl_label = 'NP PA Add Helpers'
223     bl_options = {'INTERNAL'}
224
225     def execute(self, context):
226         np_print('03_AddHelpers_START', ';', 'flag = ', NP020PA.flag)
227         enterloc = NP020PA.enterloc
228         bpy.ops.object.add(type = 'MESH',location = enterloc)
229         take = bpy.context.active_object
230         take.name = 'NP_PA_take'
231         NP020PA.take = take
232         bpy.ops.object.add(type = 'MESH',location = enterloc)
233         place = bpy.context.active_object
234         place.name = 'NP_PA_place'
235         NP020PA.place = place
236         return{'FINISHED'}
237
238
239 # Defining the operator that will change some of the system settings and prepare objects for the operation:
240
241 class NPPAPrepareContext(bpy.types.Operator):
242     bl_idname = 'object.np_pa_prepare_context'
243     bl_label = 'NP PA Prepare Context'
244     bl_options = {'INTERNAL'}
245
246     def execute(self, context):
247         take = NP020PA.take
248         place = NP020PA.place
249         take.select_set(True)
250         place.select_set(True)
251         bpy.context.view_layer.objects.active = place
252         bpy.context.tool_settings.use_snap = False
253         bpy.context.tool_settings.snap_element = 'VERTEX'
254         bpy.context.tool_settings.snap_target = 'ACTIVE'
255         bpy.context.space_data.pivot_point = 'ACTIVE_ELEMENT'
256         bpy.context.space_data.transform_orientation = 'GLOBAL'
257         NP020PA.flag = 'RUNTRANSZERO'
258         return{'FINISHED'}
259
260
261 # Defining the operator that will let the user translate take and place points to the desired 'take' location. It also uses some listening operators that clean up the leftovers should the user interrupt the command. Many thanks to CoDEmanX and lukas_t:
262
263 class NPPARunTranslate(bpy.types.Operator):
264     bl_idname = 'object.np_pa_run_translate'
265     bl_label = 'NP PA Run Translate'
266     bl_options = {'INTERNAL'}
267
268     np_print('04_RunTrans_START',';','NP020PA.flag = ', NP020PA.flag)
269     count = 0
270
271     def modal(self,context,event):
272         context.area.tag_redraw()
273         flag = NP020PA.flag
274         take = NP020PA.take
275         place = NP020PA.place
276         selob = NP020PA.selob
277         self.count += 1
278
279         if self.count == 1:
280             bpy.ops.transform.translate('INVOKE_DEFAULT')
281             np_print('04_RunTrans_count_1_INVOKE_DEFAULT', ';', 'flag = ', NP020PA.flag)
282
283         elif event.type in ('LEFTMOUSE','RET','NUMPAD_ENTER') and event.value == 'RELEASE':
284             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
285             if flag == 'RUNTRANSZERO':
286                 take.select_set(False)
287                 place.select_set(False)
288                 NP020PA.ar13d = copy.deepcopy(take.location)
289                 NP020PA.ar23d = copy.deepcopy(place.location)
290                 NP020PA.firsttake3d = copy.deepcopy(take.location)
291                 for ob in selob:
292                    ob.select_set(True)
293                 bpy.ops.object.duplicate()
294                 NP020PA.nextob = bpy.context.selected_objects
295                 NP020PA.prevob = selob
296                 place.select_set(True)
297                 NP020PA.flag = 'RUNTRANSFIRST_break'
298             elif flag == 'RUNTRANSFIRST':
299                 NP020PA.deltavec_safe = copy.deepcopy(NP020PA.deltavec)
300                 np_print('deltavec_safe = ', NP020PA.deltavec_safe)
301                 NP020PA.ar13d = copy.deepcopy(take.location)
302                 NP020PA.ar23d = copy.deepcopy(place.location)
303                 place.select_set(False)
304                 bpy.ops.object.duplicate()
305                 prevob = NP020PA.prevob
306                 nextob = NP020PA.nextob
307                 NP020PA.arob = prevob
308                 NP020PA.prevob = nextob
309                 NP020PA.nextob = bpy.context.selected_objects
310                 take.location = copy.deepcopy(place.location)
311                 place.select_set(True)
312                 NP020PA.flag = 'ARRAYTRANS'
313
314             else:
315                 np_print('UNKNOWN FLAG')
316                 NP020PA.flag = 'EXIT'
317             np_print('04_RunTrans_left_FINISHED',';','flag = ', NP020PA.flag)
318             return{'FINISHED'}
319
320         elif event.type == 'SPACE' and event.value == 'RELEASE':
321             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
322             take.hide = True
323             place.hide = True
324             self.co2d = ((event.mouse_region_x, event.mouse_region_y))
325             co2d = self.co2d
326             region = context.region
327             rv3d = context.region_data
328             away = view3d_utils.region_2d_to_origin_3d(region, rv3d, co2d) - place.location
329             away = away.length
330             placeloc3d = NP020PA.placeloc3d
331             awayloc = copy.deepcopy(placeloc3d)
332             NP020PA.awayloc = awayloc
333             NP020PA.away = copy.deepcopy(away)
334             if flag == 'RUNTRANSZERO':
335                 NP020PA.flag = 'NAVTRANSZERO'
336             elif flag == 'RUNTRANSFIRST':
337                 nextob = NP020PA.nextob
338                 for ob in nextob:
339                     ob.hide = True
340                 NP020PA.flag = 'NAVTRANSFIRST'
341             else:
342                 np_print('UNKNOWN FLAG')
343                 NP020PA.flag = 'EXIT'
344             np_print('04_RunTrans_space_FINISHED',';','flag = ', NP020PA.flag)
345             return{'FINISHED'}
346
347         elif event.type == 'RIGHTMOUSE':
348             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
349             if flag == 'RUNTRANSZERO':
350                 NP020PA.flag = 'EXIT'
351             elif flag == 'RUNTRANSFIRST':
352                 NP020PA.deltavec_safe = copy.deepcopy(NP020PA.deltavec)
353                 np_print('deltavec_safe = ', NP020PA.deltavec_safe)
354                 NP020PA.ar13d = copy.deepcopy(take.location)
355                 NP020PA.ar23d = copy.deepcopy(place.location)
356                 place.select_set(False)
357                 bpy.ops.object.duplicate()
358                 prevob = NP020PA.prevob
359                 nextob = NP020PA.nextob
360                 NP020PA.arob = prevob
361                 NP020PA.prevob = nextob
362                 NP020PA.nextob = bpy.context.selected_objects
363                 take.location = copy.deepcopy(place.location)
364                 place.select_set(True)
365                 NP020PA.flag = 'ARRAYTRANS'
366             else:
367                 np_print('UNKNOWN FLAG')
368                 NP020PA.flag = 'EXIT'
369             np_print('04_RunTrans_rmb_FINISHED',';','flag = ', NP020PA.flag)
370             return{'FINISHED'}
371
372         elif event.type == 'ESC':
373             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
374             if flag == 'RUNTRANSZERO':
375                 NP020PA.flag = 'EXIT'
376             elif flag == 'RUNTRANSFIRST':
377                 place.select_set(False)
378                 prevob = NP020PA.prevob
379                 nextob = NP020PA.nextob
380                 bpy.ops.object.delete('EXEC_DEFAULT')
381                 for ob in prevob:
382                     ob.select_set(True)
383                 NP020PA.flag = 'EXIT'
384             elif flag == 'RUNTRANSNEXT':
385                 place.select_set(False)
386                 prevob = NP020PA.prevob
387                 nextob = NP020PA.nextob
388                 NP020PA.selob = prevob
389                 bpy.ops.object.delete('EXEC_DEFAULT')
390                 for ob in prevob:
391                     ob.select_set(True)
392                 NP020PA.flag = 'EXIT'
393             else:
394                 np_print('UNKNOWN FLAG')
395                 NP020PA.flag = 'EXIT'
396             np_print('04_RunTrans_rmb_FINISHED',';','flag = ', NP020PA.flag)
397             return{'FINISHED'}
398
399         np_print('04_RunTrans_count_PASS_THROUGH',';','flag = ', NP020PA.flag)
400         return{'PASS_THROUGH'}
401
402     def invoke(self, context, event):
403         np_print('04_RunTrans_INVOKE_START')
404         flag = NP020PA.flag
405         selob = NP020PA.selob
406         np_print('flag = ', flag)
407         if context.area.type == 'VIEW_3D':
408             if flag in ('RUNTRANSZERO', 'RUNTRANSFIRST'):
409                 args = (self, context)
410                 self._handle = bpy.types.SpaceView3D.draw_handler_add(DRAW_RunTranslate, args, 'WINDOW', 'POST_PIXEL')
411                 context.window_manager.modal_handler_add(self)
412                 np_print('04_RunTrans_INVOKED_RUNNING_MODAL',';','flag = ', NP020PA.flag)
413                 return {'RUNNING_MODAL'}
414             else:
415                 np_print('04_RunTrans_INVOKE_DECLINED_FINISHED',';','flag = ', flag)
416                 return {'FINISHED'}
417         else:
418             self.report({'WARNING'}, "View3D not found, cannot run operator")
419             flag = 'WARNING3D'
420             NP020PA.flag = flag
421             np_print('04_RunTrans_INVOKE_DECLINED_FINISHED',';','flag = ', NP020PA.flag)
422             return {'CANCELLED'}
423
424
425 # Defining the set of instructions that will draw the OpenGL elements on the screen during the execution of RunTranslate operator:
426
427 def DRAW_RunTranslate(self, context):
428
429     np_print('04_DRAW_RunTrans_START',';','flag = ', NP020PA.flag)
430
431     addon_prefs = context.preferences.addons[__package__].preferences
432
433     flag = NP020PA.flag
434     takeloc3d = NP020PA.takeloc3d
435     placeloc3d = NP020PA.placeloc3d
436
437     region = context.region
438     rv3d = context.region_data
439
440     if flag in ('RUNTRANSZERO', 'RUNTRANSFIRST'):
441         takeloc2d = view3d_utils.location_3d_to_region_2d(region, rv3d, takeloc3d)
442         placeloc2d = view3d_utils.location_3d_to_region_2d(region, rv3d, placeloc3d)
443     if flag == 'NAVTRANSZERO':
444         takeloc2d = self.co2d
445         placeloc2d = self.co2d
446     if flag == 'NAVTRANSFIRST':
447         takeloc2d = view3d_utils.location_3d_to_region_2d(region, rv3d, takeloc3d)
448         placeloc2d = self.co2d
449
450
451     # DRAWING START:
452     bgl.glEnable(bgl.GL_BLEND)
453
454     if flag == 'RUNTRANSZERO':
455         instruct = 'select the take point'
456         keys_aff = 'LMB - confirm, CTRL - snap, MMB - lock axis, NUMPAD - value'
457         keys_nav = 'SPACE - navigate'
458         keys_neg = 'ESC / RMB - cancel copy'
459
460         badge_mode = 'RUN'
461         message_main = 'CTRL+SNAP'
462         message_aux = None
463         aux_num = None
464         aux_str = None
465
466     elif flag == 'RUNTRANSFIRST':
467         instruct = 'select the placement point'
468         keys_aff = 'LMB - confirm, CTRL - snap, MMB - lock axis, NUMPAD - value'
469         keys_nav = 'SPACE - navigate'
470         keys_neg = 'ESC / RMB - cancel copy'
471
472         badge_mode = 'RUN'
473         message_main = 'CTRL+SNAP'
474         message_aux = None
475         aux_num = None
476         aux_str = None
477
478     elif flag == 'NAVTRANSZERO':
479         instruct = 'navigate for better placement of take point'
480         keys_aff = 'MMB / SCROLL - navigate'
481         keys_nav = 'LMB / SPACE - leave navigate'
482         keys_neg = 'ESC / RMB - cancel copy'
483
484         badge_mode = 'NAV'
485         message_main = 'NAVIGATE'
486         message_aux = None
487         aux_num = None
488         aux_str = None
489
490     elif flag == 'NAVTRANSFIRST':
491         instruct = 'navigate for better selection of placement point'
492         keys_aff = 'MMB / SCROLL - navigate'
493         keys_nav = 'LMB / SPACE - leave navigate'
494         keys_neg = 'ESC / RMB - cancel copy'
495
496         badge_mode = 'NAV'
497         message_main = 'NAVIGATE'
498         message_aux = None
499         aux_num = None
500         aux_str = None
501
502     # ON-SCREEN INSTRUCTIONS:
503
504     display_instructions(region, rv3d, instruct, keys_aff, keys_nav, keys_neg)
505
506     # MOUSE BADGE:
507
508     co2d = placeloc2d
509
510     symbol = [[23, 34], [23, 32], [19, 32], [19, 36], [21, 36], [21, 38], [25, 38], [25, 34], [23, 34], [23, 36], [21, 36]]
511
512     display_cursor_badge(co2d, symbol, badge_mode, message_main, message_aux, aux_num, aux_str)
513
514     # LINE:
515
516     display_line_between_two_points(region, rv3d, takeloc3d, placeloc3d)
517
518     # DISTANCE:
519
520     display_distance_between_two_points(region, rv3d, takeloc3d, placeloc3d)
521     dist_num = copy.deepcopy(display_distance_between_two_points(region, rv3d, takeloc3d, placeloc3d)[0])
522
523
524     if flag in ('RUNTRANSFIRST', 'NAVTRANSFIRST'):
525         #ardist_num = NP020PA.ar23d - NP020PA.ar13d
526         #ardist_num = ardist_num.length * dist_scale
527         ar12d = view3d_utils.location_3d_to_region_2d(region, rv3d, NP020PA.ar13d)
528         ar22d = view3d_utils.location_3d_to_region_2d(region, rv3d, NP020PA.ar23d)
529         NP020PA.deltavec = NP020PA.placeloc3d - NP020PA.takeloc3d
530         np_print('DAKLE', NP020PA.ar23d, NP020PA.ar13d, NP020PA.deltavec)
531         #ardist_num = abs(round(ardist_num,2))
532         #if suffix is not None:
533             #ardist = str(dist_num)+suffix
534         #else:
535             #ardist = str(dist_num)
536         #NP020PA.ardist = ardist
537         #ardist_loc = (ar12d + ar22d) /2
538         #np_print('First Calc - ardist =', ardist, ardist_loc)
539
540
541     #DRAWING END:
542     bgl.glLineWidth(1)
543     bgl.glDisable(bgl.GL_BLEND)
544     bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
545     np_print('04_DRAW_RunTrans_FINISHED',';','flag = ', NP020PA.flag)
546
547
548 # Defining the operator that will enable navigation if user calls it:
549
550 class NPPANavTranslate(bpy.types.Operator):
551     bl_idname = "object.np_pa_nav_translate"
552     bl_label = "NP PA Nav Translate"
553     bl_options = {'INTERNAL'}
554
555     np_print('04a_NavTrans_START',';','flag = ', NP020PA.flag)
556
557     def modal(self,context,event):
558         context.area.tag_redraw()
559         flag = NP020PA.flag
560         take = NP020PA.take
561         place = NP020PA.place
562
563         if event.type == 'MOUSEMOVE':
564             self.co2d = ((event.mouse_region_x, event.mouse_region_y))
565             region = context.region
566             rv3d = context.region_data
567             co2d = self.co2d
568             view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, co2d)
569             pointloc = view3d_utils.region_2d_to_origin_3d(region, rv3d, co2d) + view_vector * NP020PA.away
570             NP020PA.placeloc3d = copy.deepcopy(pointloc)
571             np_print('04a_NavTrans_mousemove',';','flag = ', NP020PA.flag)
572
573         elif event.type in {'LEFTMOUSE', 'SPACE'} and event.value == 'PRESS':
574             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
575             self.co2d = ((event.mouse_region_x, event.mouse_region_y))
576             region = context.region
577             rv3d = context.region_data
578             co2d = self.co2d
579             view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, co2d)
580             enterloc = view3d_utils.region_2d_to_origin_3d(region, rv3d, co2d) + view_vector*NP020PA.away
581             placeloc3d = NP020PA.placeloc3d
582             navdelta = enterloc - NP020PA.awayloc
583             take.hide = False
584             place.hide = False
585             np_print('flag = ', flag)
586             if flag == 'NAVTRANSZERO':
587                 takeloc3d = enterloc
588                 placeloc3d = enterloc
589                 take.location = enterloc
590                 place.location = enterloc
591                 NP020PA.flag = 'RUNTRANSZERO'
592             elif flag == 'NAVTRANSFIRST':
593                 takeloc3d = NP020PA.takeloc3d
594                 placeloc3d = enterloc
595                 place.location = enterloc
596                 nextob = NP020PA.nextob
597                 for ob in nextob:
598                     ob.hide = False
599                 place.select_set(False)
600                 bpy.ops.transform.translate(value = navdelta)
601                 place.select_set(True)
602                 NP020PA.flag = 'RUNTRANSFIRST'
603             else:
604                 np_print('UNKNOWN FLAG')
605                 NP020PA.flag = 'EXIT'
606             NP020PA.take = take
607             NP020PA.place = place
608             NP020PA.takeloc3d = takeloc3d
609             NP020PA.placeloc3d = placeloc3d
610             np_print('04a_NavTrans_left_space_FINISHED',';','flag = ', NP020PA.flag)
611             return {'FINISHED'}
612
613         elif event.type == 'RIGHTMOUSE':
614             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
615             take.hide = False
616             place.hide = False
617             if flag == 'NAVTRANSZERO':
618                 place.select_set(False)
619                 NP020PA.flag = 'EXIT'
620             elif flag == 'NAVTRANSFIRST':
621                 place.select_set(False)
622                 prevob = NP020PA.prevob
623                 nextob = NP020PA.nextob
624                 for ob in nextob:
625                     ob.hide = False
626                 bpy.ops.object.delete('EXEC_DEFAULT')
627                 for ob in prevob:
628                     ob.select_set(True)
629                 NP020PA.flag = 'ARRAYTRANS'
630             else:
631                 np_print('UNKNOWN FLAG')
632                 NP020PA.flag = 'EXIT'
633             np_print('04a_NavTrans_rmb_FINISHED',';','flag = ', NP020PA.flag)
634             return{'FINISHED'}
635
636         elif event.type == 'ESC':
637             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
638             take.hide = False
639             place.hide = False
640             if flag == 'NAVTRANSZERO':
641                 place.select_set(False)
642                 NP020PA.flag = 'EXIT'
643             elif flag == 'NAVTRANSFIRST':
644                 place.select_set(False)
645                 prevob = NP020PA.prevob
646                 nextob = NP020PA.nextob
647                 for ob in nextob:
648                     ob.hide = False
649                 bpy.ops.object.delete('EXEC_DEFAULT')
650                 for ob in prevob:
651                     ob.select_set(True)
652                 NP020PA.flag = 'EXIT'
653             else:
654                 np_print('UNKNOWN FLAG')
655                 NP020PA.flag = 'EXIT'
656             np_print('04a_NavTrans_esc_FINISHED',';','flag = ', NP020PA.flag)
657             return{'FINISHED'}
658
659         elif event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
660             np_print('04a_NavTrans_middle_wheel_any_PASS_THROUGH')
661             return {'PASS_THROUGH'}
662
663         np_print('04a_NavTrans_INVOKED_RUNNING_MODAL',';','flag = ', NP020PA.flag)
664         return {'RUNNING_MODAL'}
665
666     def invoke(self, context, event):
667         np_print('04a_NavTrans_INVOKE_START')
668         flag = NP020PA.flag
669         np_print('flag = ', flag)
670         self.co2d = ((event.mouse_region_x, event.mouse_region_y))
671         if flag in ('NAVTRANSZERO', 'NAVTRANSFIRST'):
672             args = (self, context)
673             self._handle = bpy.types.SpaceView3D.draw_handler_add(DRAW_RunTranslate, args, 'WINDOW', 'POST_PIXEL')
674             context.window_manager.modal_handler_add(self)
675             np_print('04a_run_NAV_INVOKE_a_RUNNING_MODAL',';','flag = ', NP020PA.flag)
676             return {'RUNNING_MODAL'}
677         else:
678             np_print('04a_run_NAV_INVOKE_a_FINISHED',';','flag = ', flag)
679             return {'FINISHED'}
680
681
682 # Defining the operator that will enable the return to RunTrans cycle by reseting the 'break' flag:
683
684 class NPPAPrepareNext(bpy.types.Operator):
685     bl_idname = 'object.np_pa_prepare_next'
686     bl_label = 'NP PA Prepare Next'
687     bl_options = {'INTERNAL'}
688
689     def execute(self, context):
690         np_print('05_PrepareNext_START',';','flag = ', NP020PA.flag)
691         if NP020PA.flag == 'RUNTRANSFIRST_break':
692             NP020PA.flag = 'RUNTRANSFIRST'
693         np_print('05_PrepareNext_FINISHED',';','flag = ', NP020PA.flag)
694         return{'FINISHED'}
695
696
697 # Defining the operator that will collect the necessary data and the generate the array with an input dialogue for number of items:
698
699 class NPPAArrayTranslate(bpy.types.Operator):
700     bl_idname = "object.np_pa_array_translate"
701     bl_label = "NP PA Array Translate"
702     bl_options = {'INTERNAL'}
703
704     np_print('06_ArrayTrans_START',';','flag = ', NP020PA.flag)
705
706     def modal(self,context,event):
707         np_print('06_ArrayTrans_START',';','flag = ', NP020PA.flag)
708         context.area.tag_redraw()
709         flag = NP020PA.flag
710         ardict = NP020PA.ardict
711         arob = NP020PA.arob
712         np_print('ardict = ', ardict)
713
714         if event.type == 'MOUSEMOVE':
715             self.co2d = ((event.mouse_region_x, event.mouse_region_y))
716             np_print('04a_NavTrans_mousemove',';','flag = ', NP020PA.flag)
717
718         elif event.type in ('LEFTMOUSE', 'RIGHTMOUSE') and event.value == 'PRESS':
719             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
720             NP020PA.flag = 'EXIT'
721             np_print('06_ArrayTrans_rmb_FINISHED',';','flag = ', NP020PA.flag)
722             return{'FINISHED'}
723
724         elif event.ctrl and event.type == 'WHEELUPMOUSE' or event.type == 'UP_ARROW' and event.value == 'PRESS':
725             for ob in arob:
726                 ar = ardict[ob][0]
727                 deltavec_start = Vector(ardict[ob][1])
728                 count = ardict[ob][2]
729                 if ar.fit_type == 'FIXED_COUNT':
730                     ar.count = ar.count+1
731                     count = count + 1
732                 elif ar.fit_type == 'FIT_LENGTH' and count == 3:
733                     ar.fit_type = 'FIXED_COUNT'
734                     ar.constant_offset_displace = deltavec_start
735                     ar.count = 2
736                     count = 2
737                 elif ar.fit_type == 'FIT_LENGTH' and count >3:
738                     count = count - 1
739                     ar.constant_offset_displace.length = ar.fit_length/(count-1)
740                 ardict[ob][2] = count
741             NP020PA.fit_type = ar.fit_type
742             NP020PA.count = count
743             bpy.context.scene.update()
744
745         elif event.ctrl and event.type == 'WHEELDOWNMOUSE' or event.type == 'DOWN_ARROW' and event.value == 'PRESS':
746             for ob in arob:
747                 ar = ardict[ob][0]
748                 deltavec_start = Vector(ardict[ob][1])
749                 count = ardict[ob][2]
750                 if ar.fit_type == 'FIXED_COUNT' and count > 2:
751                     ar.count = ar.count-1
752                     count = count - 1
753                 elif ar.fit_type == 'FIXED_COUNT' and count == 2:
754                     ar.fit_type = 'FIT_LENGTH'
755                     ar.fit_length = deltavec_start.length
756                     ar.constant_offset_displace.length = ar.fit_length/2
757                     count = 3
758                 elif ar.fit_type == 'FIT_LENGTH':
759                     count = count + 1
760                     ar.constant_offset_displace.length = ar.fit_length/(count-1)
761                 ardict[ob][2] = count
762             NP020PA.fit_type = ar.fit_type
763             NP020PA.count = count
764             bpy.context.scene.update()
765
766         elif event.type in ('RET', 'NUMPAD_ENTER') and event.value == 'PRESS':
767             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
768             selob = bpy.context.selected_objects
769             bpy.ops.object.select_all(action='DESELECT')
770             for ob in arob:
771                 ob.select_set(True)
772                 bpy.ops.object.modifier_apply(modifier = ardict[ob][0].name)
773                 ob.select_set(False)
774             for ob in selob:
775                 ob.select_set(True)
776             NP020PA.flag = 'EXIT'
777             np_print('06_ArrayTrans_enter_FINISHED',';','flag = ', NP020PA.flag)
778             return{'FINISHED'}
779
780         elif event.ctrl and event.type == 'TAB' and event.value == 'PRESS':
781             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
782             if NP020PA.fit_type == 'FIXED_COUNT':
783                 value = NP020PA.ar23d - NP020PA.ar13d
784             else:
785                 value = (NP020PA.ar23d - NP020PA.ar13d)/(NP020PA.count - 1)
786             selob = bpy.context.selected_objects
787             bpy.ops.object.select_all(action='DESELECT')
788             for ob in arob:
789                 ob.select_set(True)
790                 ob.modifiers.remove(ardict[ob][0])
791                 np_print('NP020PA.count', NP020PA.count)
792                 for i in range(1, NP020PA.count):
793                     bpy.ops.object.duplicate(linked = True)
794                     bpy.ops.transform.translate(value = value)
795                 bpy.ops.object.select_all(action='DESELECT')
796             for ob in selob:
797                 ob.select_set(True)
798             NP020PA.flag = 'EXIT'
799             np_print('06_ArrayTrans_ctrl_tab_FINISHED',';','flag = ', NP020PA.flag)
800             return{'FINISHED'}
801
802         elif event.type == 'TAB' and event.value == 'PRESS':
803             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
804             if NP020PA.fit_type == 'FIXED_COUNT':
805                 value = NP020PA.ar23d - NP020PA.ar13d
806             else:
807                 value = (NP020PA.ar23d - NP020PA.ar13d)/(NP020PA.count - 1)
808             selob = bpy.context.selected_objects
809             bpy.ops.object.select_all(action='DESELECT')
810             for ob in arob:
811                 ob.select_set(True)
812                 ob.modifiers.remove(ardict[ob][0])
813                 np_print('NP020PA.count', NP020PA.count)
814                 for i in range(1, NP020PA.count):
815                     bpy.ops.object.duplicate()
816                     bpy.ops.transform.translate(value = value)
817                 bpy.ops.object.select_all(action='DESELECT')
818             for ob in selob:
819                 ob.select_set(True)
820             NP020PA.flag = 'EXIT'
821             np_print('06_ArrayTrans_tab_FINISHED',';','flag = ', NP020PA.flag)
822             return{'FINISHED'}
823
824         elif event.type == 'ESC' and event.value == 'PRESS':
825             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
826             for ob in arob:
827                 ob.modifiers.remove(ardict[ob][0])
828             NP020PA.flag = 'EXIT'
829             np_print('06_ArrayTrans_esc_FINISHED',';','flag = ', NP020PA.flag)
830             return{'FINISHED'}
831
832         elif event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
833             np_print('06_ArrayTrans_middle_wheel_any_PASS_THROUGH')
834             return {'PASS_THROUGH'}
835
836         np_print('06_ArrayTrans_INVOKED_RUNNING_MODAL',';','flag = ', NP020PA.flag)
837         return {'RUNNING_MODAL'}
838
839     def invoke(self, context, event):
840         np_print('06_ArrayTrans_INVOKE_START')
841         flag = NP020PA.flag
842         self.co2d = ((event.mouse_region_x, event.mouse_region_y))
843         if flag == 'ARRAYTRANS':
844             arob = NP020PA.arob
845             np_print('deltavec_safe = ', NP020PA.deltavec_safe)
846             ardict = {}
847             for ob in arob:
848                 deltavec = copy.deepcopy(NP020PA.deltavec_safe)
849                 np_print('deltavec = ', deltavec)
850                 loc, rot, sca = ob.matrix_world.decompose()
851                 rot = ob.rotation_euler
852                 rot = rot.to_quaternion()
853                 sca = ob.scale
854                 np_print(loc, rot, sca, ob.matrix_world)
855                 np_print('deltavec = ', deltavec)
856                 deltavec.rotate(rot.conjugated())
857                 np_print('sca.length', sca.length)
858                 deltavec[0] = deltavec[0] / sca[0]
859                 deltavec[1] = deltavec[1] / sca[1]
860                 deltavec[2] = deltavec[2] / sca[2]
861                 np_print('deltavec = ', deltavec)
862                 deltavec_trans = deltavec.to_tuple(4)
863                 arcur = ob.modifiers.new(name = '', type = 'ARRAY')
864                 arcur.fit_type = 'FIXED_COUNT'
865                 arcur.use_relative_offset = False
866                 arcur.use_constant_offset = True
867                 arcur.constant_offset_displace = deltavec_trans
868                 arcur.count = 5
869                 ardict[ob] = []
870                 ardict[ob].append(arcur)
871                 ardict[ob].append(deltavec_trans)
872                 ardict[ob].append(arcur.count)
873             NP020PA.selob = arob
874             NP020PA.ardict = ardict
875             NP020PA.count = 5
876             NP020PA.fit_type = 'FIXED_COUNT'
877             selob = NP020PA.selob
878             lenselob = len(selob)
879             for i, ob in enumerate(selob):
880                 ob.select_set(True)
881                 if i == lenselob-1:
882                     bpy.context.view_layer.objects.active = ob
883             args = (self, context)
884             self._handle = bpy.types.SpaceView3D.draw_handler_add(DRAW_ArrayTranslate, args, 'WINDOW', 'POST_PIXEL')
885             context.window_manager.modal_handler_add(self)
886             np_print('06_ArayTrans_INVOKE_a_RUNNING_MODAL',';','flag = ', NP020PA.flag)
887             return {'RUNNING_MODAL'}
888         else:
889             np_print('06_ArrayTrans_INVOKE_DENIED',';','flag = ', NP020PA.flag)
890             return {'FINISHED'}
891
892
893 # Defining the set of instructions that will draw the OpenGL elements on the screen during the execution of ArrayTranslate operator:
894
895 def DRAW_ArrayTranslate(self, context):
896
897     np_print('06a_DRAW_ArrayTrans_START',';','flag = ', NP020PA.flag)
898
899     addon_prefs = context.preferences.addons[__package__].preferences
900
901
902
903     # MOUSE BADGE:
904     '''
905     if badge == True:
906         square = [[17, 30], [17, 40], [27, 40], [27, 30]]
907         rectangle = [[27, 30], [27, 40], [67, 40], [67, 30]]
908         icon = copy.deepcopy(NP020PA.icon)
909         np_print('icon', icon)
910         ipx = 29
911         ipy = 33
912         for co in square:
913             co[0] = round((co[0] * badge_size),0) -(badge_size*10) + self.co2d[0]
914             co[1] = round((co[1] * badge_size),0) -(badge_size*25) + self.co2d[1]
915         for co in rectangle:
916             co[0] = round((co[0] * badge_size),0) -(badge_size*10) + self.co2d[0]
917             co[1] = round((co[1] * badge_size),0) -(badge_size*25) + self.co2d[1]
918         for co in icon:
919             co[0] = round((co[0] * badge_size),0) -(badge_size*10) + self.co2d[0]
920             co[1] = round((co[1] * badge_size),0) -(badge_size*25) + self.co2d[1]
921         ipx = round((ipx * badge_size),0) -(badge_size*10) + self.co2d[0]
922         ipy = round((ipy * badge_size),0) -(badge_size*25) + self.co2d[1]
923         ipsize = int(6* badge_size)
924         bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
925         bgl.glBegin(bgl.GL_TRIANGLE_FAN)
926         for x,y in square:
927             bgl.glVertex2f(x,y)
928         bgl.glEnd()
929         bgl.glColor4f(0.5, 0.75, 0.0, 1.0)
930         bgl.glBegin(bgl.GL_TRIANGLE_FAN)
931         for x,y in rectangle:
932             bgl.glVertex2f(x,y)
933         bgl.glEnd()
934         bgl.glColor4f(0.2, 0.15, 0.55, 1.0)
935         bgl.glBegin(bgl.GL_TRIANGLE_FAN)
936         for x,y in rectangle:
937             bgl.glVertex2f(x,(y-(badge_size*35)))
938         bgl.glEnd()
939         bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
940         font_id = 0
941         blf.position(font_id,ipx,ipy,0)
942         blf.size(font_id,ipsize,72)
943         blf.draw(font_id,'NAVIGATE')
944         blf.position(font_id,ipx,(ipy-(badge_size*35)),0)
945         blf.size(font_id,ipsize,72)
946         blf.draw(font_id,'CTRL+SCRL')
947         bgl.glColor4f(1,1,1,1)
948         blf.position(font_id,ipx,(int(ipy-badge_size*25)),0)
949         blf.size(font_id,(int(badge_size*24)),72)
950         if NP020PA.fit_type == 'FIT_LENGTH':
951             blf.draw(font_id,'/' + str(NP020PA.count))
952         else:
953             blf.draw(font_id,str(NP020PA.count))
954         bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
955         bgl.glBegin(bgl.GL_LINE_STRIP)
956         for x,y in icon:
957             bgl.glVertex2f(x,y)
958         bgl.glEnd()
959     '''
960
961     region = context.region
962     rv3d = context.region_data
963     instruct = 'specify number of items in array'
964     keys_aff = 'CTRL+SCROLL, UPARROW / DOWNARROW - number of items, LMB / RMB - confirm and keep array, ENTER - apply as one, TAB - apply as separate, CTRL+TAB - apply as instanced'
965     keys_nav = 'MMB, SCROLL - navigate'
966     keys_neg = 'ESC - cancel array'
967
968     badge_mode = 'NAV'
969     message_main = 'NAVIGATE'
970     message_aux = 'CTRL+SCRL'
971
972     if NP020PA.fit_type == 'FIT_LENGTH':
973         aux_num = '/' + str(NP020PA.count)
974     else:
975         aux_num = str(NP020PA.count)
976     aux_str = None
977
978     # ON-SCREEN INSTRUCTIONS:
979
980     display_instructions(region, rv3d, instruct, keys_aff, keys_nav, keys_neg)
981
982
983
984     co2d = self.co2d
985
986     symbol = [[23, 34], [23, 32], [19, 32], [19, 36], [21, 36], [21, 38], [25, 38], [25, 34], [23, 34], [23, 36], [21, 36]]
987
988     display_cursor_badge(co2d, symbol, badge_mode, message_main, message_aux, aux_num, aux_str)
989
990
991
992
993
994
995     ar12d = view3d_utils.location_3d_to_region_2d(region, rv3d, NP020PA.ar13d)
996     ar22d = view3d_utils.location_3d_to_region_2d(region, rv3d, NP020PA.ar23d)
997
998     bgl.glEnable(bgl.GL_BLEND)
999     # ARMARKERS:
1000     font_id = 0
1001     markersize = 2 * 2.5
1002     triangle = [[0, 0], [-1, 1], [1, 1]]
1003     triangle = [[0, 0], [-1, 1], [1, 1]]
1004     for co in triangle:
1005         co[0] = int(co[0] * markersize * 3) + ar12d[0]
1006         co[1] = int(co[1] * markersize * 3) + ar12d[1]
1007     bgl.glColor4f(0.4, 0.15, 0.75, 1.0)
1008     bgl.glBegin(bgl.GL_TRIANGLE_FAN)
1009     for x,y in triangle:
1010         bgl.glVertex2f(x,y)
1011     bgl.glEnd()
1012     triangle = [[0, 0], [-1, 1], [1, 1]]
1013     for co in triangle:
1014         co[0] = int(co[0] * markersize * 3) + ar22d[0]
1015         co[1] = int(co[1] * markersize * 3) + ar22d[1]
1016     bgl.glColor4f(0.4, 0.15, 0.75, 1.0)
1017     bgl.glBegin(bgl.GL_TRIANGLE_FAN)
1018     for x,y in triangle:
1019         bgl.glVertex2f(x,y)
1020     bgl.glEnd()
1021
1022     # AR NUMERICAL DISTANCE:
1023     display_distance_between_two_points(region, rv3d, NP020PA.ar13d, NP020PA.ar23d)
1024
1025     #DRAWING END:
1026     bgl.glLineWidth(1)
1027     bgl.glDisable(bgl.GL_BLEND)
1028     bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
1029     np_print('06a_DRAW_ArrayTrans_FINISHED',';','flag = ', NP020PA.flag)
1030
1031
1032 # Restoring the object selection and system settings from before the operator activation. Deleting the helpers after successful translation, reseting all viewport options and reselecting previously selected objects:
1033
1034 class NPPARestoreContext(bpy.types.Operator):
1035     bl_idname = "object.np_pa_restore_context"
1036     bl_label = "NP PA Restore Context"
1037     bl_options = {'INTERNAL'}
1038
1039     def execute(self, context):
1040         np_print('07_CleanExit_START',';','flag = ', NP020PA.flag)
1041         flag = NP020PA.flag
1042         selob = NP020PA.selob
1043         take = NP020PA.take
1044         place = NP020PA.place
1045         bpy.ops.object.select_all(action='DESELECT')
1046         take.select_set(True)
1047         place.select_set(True)
1048         if NP020PA.prevob != None:
1049             prevob = NP020PA.prevob
1050             nextob = NP020PA.nextob
1051             if prevob is not selob:
1052                 for i, ob in enumerate(prevob):
1053                     ob.select_set(True)
1054             for i, ob in enumerate(nextob):
1055                 ob.select_set(True)
1056         bpy.ops.object.delete('EXEC_DEFAULT')
1057         lenselob = len(selob)
1058         for i, ob in enumerate(selob):
1059             ob.select_set(True)
1060             if i == lenselob-1:
1061                 bpy.context.view_layer.objects.active = ob
1062         NP020PA.take = None
1063         NP020PA.place = None
1064         NP020PA.takeloc3d = (0.0,0.0,0.0)
1065         NP020PA.placeloc3d = (0.0,0.0,0.0)
1066         NP020PA.prevob = None
1067         NP020PA.nextob = None
1068         NP020PA.dist = None
1069         NP020PA.mode = 'MOVE'
1070         NP020PA.flag = 'NONE'
1071         NP020PA.ardict = {}
1072         NP020PA.deltavec = Vector ((0, 0, 0))
1073         NP020PA.deltavec_safe = Vector ((0, 0, 0))
1074         bpy.context.tool_settings.use_snap = NP020PA.use_snap
1075         bpy.context.tool_settings.snap_element = NP020PA.snap_element
1076         bpy.context.tool_settings.snap_target = NP020PA.snap_target
1077         bpy.context.space_data.pivot_point = NP020PA.pivot_point
1078         bpy.context.space_data.transform_orientation = NP020PA.trans_orient
1079         if NP020PA.acob is not None:
1080             bpy.context.view_layer.objects.active = NP020PA.acob
1081             bpy.ops.object.mode_set(mode = NP020PA.edit_mode)
1082
1083         np_print('07_CleanExit_FINISHED',';','flag = ', NP020PA.flag)
1084         return {'FINISHED'}
1085
1086
1087 '''
1088 # Defining the settings of the addon in the User preferences / addons tab:
1089
1090 class NPPAPreferences(bpy.types.AddonPreferences):
1091     # this must match the addon name, use '__package__'
1092     # when defining this in a submodule of a python package.
1093     bl_idname = __name__
1094
1095     dist_scale = bpy.props.FloatProperty(
1096             name='Unit scale',
1097             description='Distance multiplier (for example, for cm use 100)',
1098             default=100,
1099             min=0,
1100             step=1,
1101             precision=3)
1102
1103     suffix = bpy.props.EnumProperty(
1104         name='Unit suffix',
1105         items=(("'","'",''), ('"','"',''), ('thou','thou',''), ('km','km',''), ('m','m',''), ('cm','cm',''), ('mm','mm',''), ('nm','nm',''), ('None','None','')),
1106         default='cm',
1107         description='Add a unit extension after the numerical distance ')
1108
1109     badge = bpy.props.BoolProperty(
1110             name='Mouse badge',
1111             description='Use the graphical badge near the mouse cursor',
1112             default=True)
1113
1114     badge_size = bpy.props.FloatProperty(
1115             name='size',
1116             description='Size of the mouse badge, the default is 2.0',
1117             default=2,
1118             min=0.5,
1119             step=10,
1120             precision=1)
1121
1122     col_line_main_DEF = bpy.props.BoolProperty(
1123             name='Default',
1124             description='Use the default color',
1125             default=True)
1126
1127     col_line_shadow_DEF = bpy.props.BoolProperty(
1128             name='Default',
1129             description='Use the default color',
1130             default=True)
1131
1132     col_num_main_DEF = bpy.props.BoolProperty(
1133             name='Default',
1134             description='Use the default color',
1135             default=True)
1136
1137     col_num_shadow_DEF = bpy.props.BoolProperty(
1138             name='Default',
1139             description='Use the default color',
1140             default=True)
1141
1142     col_line_main = bpy.props.FloatVectorProperty(name='', default=(1.0, 1.0, 1.0, 1.0), size=4, subtype="COLOR", min=0, max=1, description = 'Color of the measurement line, to disable it set alpha to 0.0')
1143
1144     col_line_shadow = bpy.props.FloatVectorProperty(name='', default=(0.1, 0.1, 0.1, 0.25), size=4, subtype="COLOR", min=0, max=1, description = 'Color of the line shadow, to disable it set alpha to 0.0')
1145
1146     col_num_main = bpy.props.FloatVectorProperty(name='', default=(0.1, 0.1, 0.1, 0.75), size=4, subtype="COLOR", min=0, max=1, description = 'Color of the number, to disable it set alpha to 0.0')
1147
1148     col_num_shadow = bpy.props.FloatVectorProperty(name='', default=(1.0, 1.0, 1.0, 1.0), size=4, subtype="COLOR", min=0, max=1, description = 'Color of the number shadow, to disable it set alpha to 0.0')
1149
1150     def draw(self, context):
1151         layout = self.layout
1152         split = layout.split()
1153         col = split.column()
1154         col.prop(self, "dist_scale")
1155         col = split.column()
1156         col.prop(self, "suffix")
1157         split = layout.split()
1158         col = split.column()
1159         col.label(text='Line Main COLOR')
1160         col.prop(self, "col_line_main_DEF")
1161         if self.col_line_main_DEF == False:
1162             col.prop(self, "col_line_main")
1163         col = split.column()
1164         col.label(text='Line Shadow COLOR')
1165         col.prop(self, "col_line_shadow_DEF")
1166         if self.col_line_shadow_DEF == False:
1167             col.prop(self, "col_line_shadow")
1168         col = split.column()
1169         col.label(text='Numerical Main COLOR')
1170         col.prop(self, "col_num_main_DEF")
1171         if self.col_num_main_DEF == False:
1172             col.prop(self, "col_num_main")
1173         col = split.column()
1174         col.label(text='Numerical Shadow COLOR')
1175         col.prop(self, "col_num_shadow_DEF")
1176         if self.col_num_shadow_DEF == False:
1177             col.prop(self, "col_num_shadow")
1178         split = layout.split()
1179         col = split.column()
1180         col.prop(self, "badge")
1181         col = split.column()
1182         if self.badge == True:
1183             col.prop(self, "badge_size")
1184         col = split.column()
1185         col = split.column()
1186 '''
1187
1188 # This is the actual addon process, the algorithm that defines the order of operator activation inside the main macro:
1189
1190 def register():
1191
1192     #bpy.utils.register_class(NPPAPreferences)
1193     #bpy.utils.register_module(__name__)
1194     bpy.app.handlers.scene_update_post.append(NPPA_scene_update)
1195
1196     NP020PointArray.define('OBJECT_OT_np_pa_get_context')
1197     NP020PointArray.define('OBJECT_OT_np_pa_get_selection')
1198     NP020PointArray.define('OBJECT_OT_np_pa_get_mouseloc')
1199     NP020PointArray.define('OBJECT_OT_np_pa_add_helpers')
1200     NP020PointArray.define('OBJECT_OT_np_pa_prepare_context')
1201     for i in range(1, 3):
1202         for i in range(1, 10):
1203             NP020PointArray.define('OBJECT_OT_np_pa_run_translate')
1204             NP020PointArray.define('OBJECT_OT_np_pa_nav_translate')
1205         NP020PointArray.define('OBJECT_OT_np_pa_prepare_next')
1206     NP020PointArray.define('OBJECT_OT_np_pa_array_translate')
1207     NP020PointArray.define('OBJECT_OT_np_pa_restore_context')
1208
1209 def unregister():
1210     #pass
1211     #bpy.utils.unregister_class(NPPAPreferences)
1212     #bpy.utils.unregister_module(__name__)
1213     bpy.app.handlers.scene_update_post.remove(NPPA_scene_update)