Update for API change: scene.cursor_location -> scene.cursor.location
[blender-addons-contrib.git] / np_station / np_point_instance.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 Instance',
66     'author':'Okavango & the Blenderartists community',
67     'version': (0, 2, 0),
68     'blender': (2, 75, 0),
69     'location': 'View3D',
70     'warning': '',
71     'description': 'Duplicates selected objects as linked instances 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
93 # Defining the main class - the macro:
94
95 class NP020PointInstance(bpy.types.Macro):
96     bl_idname = 'object.np_020_point_instance'
97     bl_label = 'NP 020 Point Instance'
98     bl_options = {'UNDO'}
99
100
101 # 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:
102
103 class NP020PI:
104
105     take = None
106     place = None
107     takeloc3d = (0.0,0.0,0.0)
108     placeloc3d = (0.0,0.0,0.0)
109     dist = None
110     mode = 'MOVE'
111     flag = 'NONE'
112     deltavec = Vector ((0, 0, 0))
113     deltavec_safe = Vector ((0, 0, 0))
114
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 NPPI_scene_update(context):
120
121     #np_print('00_SceneUpdate_START')
122     if bpy.data.objects.is_updated:
123         np_print('NPPI_update1')
124         mode = NP020PI.mode
125         flag = NP020PI.flag
126         #np_print(mode, flag)
127         take = NP020PI.take
128         place = NP020PI.place
129         if flag in ('RUNTRANSZERO', 'RUNTRANSFIRST','RUNTRANSNEXT', 'NAVTRANSZERO', 'NAVTRANSFIRST', 'NAVTRANSNEXT'):
130             np_print('NPPI_update2')
131             NP020PI.takeloc3d = take.location
132             NP020PI.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 NPPIGetContext(bpy.types.Operator):
140     bl_idname = 'object.np_pi_get_context'
141     bl_label = 'NP PI 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         NP020PI.use_snap = copy.deepcopy(bpy.context.tool_settings.use_snap)
149         NP020PI.snap_element = copy.deepcopy(bpy.context.tool_settings.snap_element)
150         NP020PI.snap_target = copy.deepcopy(bpy.context.tool_settings.snap_target)
151         NP020PI.pivot_point = copy.deepcopy(bpy.context.space_data.pivot_point)
152         NP020PI.trans_orient = copy.deepcopy(bpy.context.space_data.transform_orientation)
153         NP020PI.curloc = copy.deepcopy(bpy.context.scene.cursor.location)
154         NP020PI.acob = bpy.context.active_object
155         if bpy.context.mode == 'OBJECT':
156             NP020PI.edit_mode = 'OBJECT'
157         elif bpy.context.mode in ('EDIT_MESH', 'EDIT_CURVE', 'EDIT_SURFACE', 'EDIT_TEXT', 'EDIT_ARMATURE', 'EDIT_METABALL', 'EDIT_LATTICE'):
158             NP020PI.edit_mode = 'EDIT'
159         elif bpy.context.mode == 'POSE':
160             NP020PI.edit_mode = 'POSE'
161         elif bpy.context.mode == 'SCULPT':
162             NP020PI.edit_mode = 'SCULPT'
163         elif bpy.context.mode == 'PAINT_WEIGHT':
164             NP020PI.edit_mode = 'WEIGHT_PAINT'
165         elif bpy.context.mode == 'PAINT_TEXTURE':
166             NP020PI.edit_mode = 'TEXTURE_PAINT'
167         elif bpy.context.mode == 'PAINT_VERTEX':
168             NP020PI.edit_mode = 'VERTEX_PAINT'
169         elif bpy.context.mode == 'PARTICLE':
170             NP020PI.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 = ', NP020PI.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 NPPIGetSelection(bpy.types.Operator):
184     bl_idname = 'object.np_pi_get_selection'
185     bl_label = 'NP PI Get Selection'
186     bl_options = {'INTERNAL'}
187
188     def execute(self, context):
189         # Reading and storing the selection:
190         NP020PI.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 NPPIGetMouseloc(bpy.types.Operator):
197     bl_idname = 'object.np_pi_get_mouseloc'
198     bl_label = 'NP PI 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         NP020PI.enterloc = copy.deepcopy(enterloc)
208         #np_print('02_RadMouseloc_FINISHED', ';', 'flag = ', NP020PI.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 = ', NP020PI.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 NPPIAddHelpers(bpy.types.Operator):
221     bl_idname = 'object.np_pi_add_helpers'
222     bl_label = 'NP PI Add Helpers'
223     bl_options = {'INTERNAL'}
224
225     def execute(self, context):
226         np_print('03_AddHelpers_START', ';', 'flag = ', NP020PI.flag)
227         enterloc = NP020PI.enterloc
228         bpy.ops.object.add(type = 'MESH',location = enterloc)
229         take = bpy.context.active_object
230         take.name = 'NP_PI_take'
231         NP020PI.take = take
232         bpy.ops.object.add(type = 'MESH',location = enterloc)
233         place = bpy.context.active_object
234         place.name = 'NP_PI_place'
235         NP020PI.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 NPPIPrepareContext(bpy.types.Operator):
242     bl_idname = 'object.np_pi_prepare_context'
243     bl_label = 'NP PI Prepare Context'
244     bl_options = {'INTERNAL'}
245
246     def execute(self, context):
247         take = NP020PI.take
248         place = NP020PI.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         NP020PI.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 NPPIRunTranslate(bpy.types.Operator):
264     bl_idname = 'object.np_pi_run_translate'
265     bl_label = 'NP PI Run Translate'
266     bl_options = {'INTERNAL'}
267
268     #np_print('04_RunTrans_START',';','NP020PI.flag = ', NP020PI.flag)
269     count = 0
270
271     def modal(self,context,event):
272         context.area.tag_redraw()
273         flag = NP020PI.flag
274         take = NP020PI.take
275         place = NP020PI.place
276         selob = NP020PI.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 = ', NP020PI.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                 NP020PI.firsttake3d = copy.deepcopy(take.location)
289                 for ob in selob:
290                    ob.select_set(True)
291                 bpy.ops.object.duplicate(linked = True)
292                 NP020PI.nextob = bpy.context.selected_objects
293                 NP020PI.prevob = selob
294                 place.select_set(True)
295                 NP020PI.flag = 'RUNTRANSFIRST_break'
296             elif flag == 'RUNTRANSFIRST':
297                 NP020PI.deltavec_safe = copy.deepcopy(NP020PI.deltavec)
298                 np_print('deltavec_safe = ', NP020PI.deltavec_safe)
299                 NP020PI.ar13d = copy.deepcopy(take.location)
300                 NP020PI.ar23d = copy.deepcopy(place.location)
301                 place.select_set(False)
302                 bpy.ops.object.delete('EXEC_DEFAULT')
303                 for ob in NP020PI.selob:
304                     ob.select_set(True)
305                 bpy.ops.object.duplicate(linked = True)
306                 value = (place.location - NP020PI.firsttake3d).to_tuple(4)
307                 bpy.ops.transform.translate(value = value)
308                 bpy.ops.object.duplicate()
309                 prevob = NP020PI.prevob
310                 nextob = NP020PI.nextob
311                 NP020PI.arob = prevob
312                 NP020PI.prevob = nextob
313                 NP020PI.nextob = bpy.context.selected_objects
314                 take.location = copy.deepcopy(place.location)
315                 place.select_set(True)
316                 bpy.context.view_layer.objects.active = place
317                 NP020PI.flag = 'RUNTRANSNEXT_break'
318             elif flag == 'RUNTRANSNEXT':
319                 NP020PI.deltavec_safe = copy.deepcopy(NP020PI.deltavec)
320                 np_print('deltavec_safe = ', NP020PI.deltavec_safe)
321                 NP020PI.ar13d = copy.deepcopy(take.location)
322                 NP020PI.ar23d = copy.deepcopy(place.location)
323                 place.select_set(False)
324                 bpy.ops.object.delete('EXEC_DEFAULT')
325                 for ob in NP020PI.selob:
326                     ob.select_set(True)
327                 bpy.ops.object.duplicate(linked = True)
328                 value = (place.location - NP020PI.firsttake3d).to_tuple(4)
329                 bpy.ops.transform.translate(value = value)
330                 bpy.ops.object.duplicate()
331                 prevob = NP020PI.prevob
332                 nextob = NP020PI.nextob
333                 NP020PI.arob = prevob
334                 NP020PI.prevob = nextob
335                 NP020PI.nextob = bpy.context.selected_objects
336                 take.location = copy.deepcopy(place.location)
337                 place.select_set(True)
338                 NP020PI.flag = 'RUNTRANSNEXT_break'
339             else:
340                 np_print('UNKNOWN FLAG')
341                 NP020PI.flag = 'EXIT'
342             np_print('04_RunTrans_alt_left_FINISHED',';','flag = ', NP020PI.flag)
343             return{'FINISHED'}
344
345         elif event.type == 'SPACE' and event.value == 'RELEASE':
346             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
347             take.hide = True
348             place.hide = True
349             self.co2d = ((event.mouse_region_x, event.mouse_region_y))
350             co2d = self.co2d
351             region = context.region
352             rv3d = context.region_data
353             away = view3d_utils.region_2d_to_origin_3d(region, rv3d, co2d) - place.location
354             away = away.length
355             placeloc3d = NP020PI.placeloc3d
356             awayloc = copy.deepcopy(placeloc3d)
357             NP020PI.awayloc = awayloc
358             NP020PI.away = copy.deepcopy(away)
359             if flag == 'RUNTRANSZERO':
360                 NP020PI.flag = 'NAVTRANSZERO'
361             elif flag == 'RUNTRANSFIRST':
362                 nextob = NP020PI.nextob
363                 for ob in nextob:
364                     ob.hide = True
365                 NP020PI.flag = 'NAVTRANSFIRST'
366             elif flag == 'RUNTRANSNEXT':
367                 nextob = NP020PI.nextob
368                 for ob in nextob:
369                     ob.hide = True
370                 NP020PI.flag = 'NAVTRANSNEXT'
371             else:
372                 np_print('UNKNOWN FLAG')
373                 NP020PI.flag = 'EXIT'
374             np_print('04_RunTrans_space_FINISHED',';','flag = ', NP020PI.flag)
375             return{'FINISHED'}
376
377         elif event.type == 'RIGHTMOUSE':
378             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
379             if flag == 'RUNTRANSZERO':
380                 NP020PI.flag = 'EXIT'
381             elif flag == 'RUNTRANSFIRST':
382                 place.select_set(False)
383                 prevob = NP020PI.prevob
384                 nextob = NP020PI.nextob
385                 bpy.ops.object.delete('EXEC_DEFAULT')
386                 for ob in prevob:
387                     ob.select_set(True)
388                 NP020PI.selob = prevob
389                 NP020PI.flag = 'EXIT'
390             elif flag == 'RUNTRANSNEXT':
391                 place.select_set(False)
392                 prevob = NP020PI.prevob
393                 nextob = NP020PI.nextob
394                 bpy.ops.object.delete('EXEC_DEFAULT')
395                 for ob in prevob:
396                     ob.select_set(True)
397                 NP020PI.selob = prevob
398                 NP020PI.flag = 'EXIT'
399             else:
400                 np_print('UNKNOWN FLAG')
401                 NP020PI.flag = 'EXIT'
402             np_print('04_RunTrans_rmb_FINISHED',';','flag = ', NP020PI.flag)
403             return{'FINISHED'}
404
405         elif event.type == 'ESC':
406             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
407             if flag == 'RUNTRANSZERO':
408                 NP020PI.flag = 'EXIT'
409             elif flag == 'RUNTRANSFIRST':
410                 place.select_set(False)
411                 prevob = NP020PI.prevob
412                 nextob = NP020PI.nextob
413                 bpy.ops.object.delete('EXEC_DEFAULT')
414                 for ob in prevob:
415                     ob.select_set(True)
416                 NP020PI.flag = 'EXIT'
417             elif flag == 'RUNTRANSNEXT':
418                 place.select_set(False)
419                 prevob = NP020PI.prevob
420                 nextob = NP020PI.nextob
421                 NP020PI.selob = prevob
422                 bpy.ops.object.delete('EXEC_DEFAULT')
423                 for ob in prevob:
424                     ob.select_set(True)
425                 NP020PI.flag = 'EXIT'
426             else:
427                 np_print('UNKNOWN FLAG')
428                 NP020PI.flag = 'EXIT'
429             np_print('04_RunTrans_rmb_FINISHED',';','flag = ', NP020PI.flag)
430             return{'FINISHED'}
431
432         np_print('04_RunTrans_count_PASS_THROUGH',';','flag = ', NP020PI.flag)
433         return{'PASS_THROUGH'}
434
435     def invoke(self, context, event):
436         #np_print('04_RunTrans_INVOKE_START')
437         flag = NP020PI.flag
438         selob = NP020PI.selob
439         #np_print('flag = ', flag)
440         if context.area.type == 'VIEW_3D':
441             if flag in ('RUNTRANSZERO', 'RUNTRANSFIRST', 'RUNTRANSNEXT'):
442                 args = (self, context)
443                 self._handle = bpy.types.SpaceView3D.draw_handler_add(DRAW_RunTranslate, args, 'WINDOW', 'POST_PIXEL')
444                 context.window_manager.modal_handler_add(self)
445                 np_print('04_RunTrans_INVOKED_RUNNING_MODAL',';','flag = ', NP020PI.flag)
446                 return {'RUNNING_MODAL'}
447             else:
448                 #np_print('04_RunTrans_INVOKE_DECLINED_FINISHED',';','flag = ', flag)
449                 return {'FINISHED'}
450         else:
451             self.report({'WARNING'}, "View3D not found, cannot run operator")
452             flag = 'WARNING3D'
453             NP020PI.flag = flag
454             np_print('04_RunTrans_INVOKE_DECLINED_FINISHED',';','flag = ', NP020PI.flag)
455             return {'CANCELLED'}
456
457
458 # Defining the set of instructions that will draw the OpenGL elements on the screen during the execution of RunTranslate operator:
459
460 def DRAW_RunTranslate(self, context):
461
462     np_print('04_DRAW_RunTrans_START',';','flag = ', NP020PI.flag)
463
464     addon_prefs = context.preferences.addons[__package__].preferences
465
466
467     flag = NP020PI.flag
468     takeloc3d = NP020PI.takeloc3d
469     placeloc3d = NP020PI.placeloc3d
470
471     region = context.region
472     rv3d = context.region_data
473
474     if flag in ('RUNTRANSZERO', 'RUNTRANSFIRST', 'RUNTRANSNEXT'):
475         takeloc2d = view3d_utils.location_3d_to_region_2d(region, rv3d, takeloc3d)
476         placeloc2d = view3d_utils.location_3d_to_region_2d(region, rv3d, placeloc3d)
477     if flag == 'NAVTRANSZERO':
478         takeloc2d = self.co2d
479         placeloc2d = self.co2d
480     if flag in ('NAVTRANSFIRST', 'NAVTRANSNEXT'):
481         takeloc2d = view3d_utils.location_3d_to_region_2d(region, rv3d, takeloc3d)
482         placeloc2d = self.co2d
483
484     '''
485     if flag in ('RUNTRANSNEXT', 'NAVTRANSNEXT'):
486         ardist_num = NP020PI.ar23d - NP020PI.ar13d
487         ardist_num = ardist_num.length * dist_scale
488         ar12d = view3d_utils.location_3d_to_region_2d(region, rv3d, NP020PI.ar13d)
489         ar22d = view3d_utils.location_3d_to_region_2d(region, rv3d, NP020PI.ar23d)
490         ardist_num = abs(round(ardist_num,2))
491         if suffix is not None:
492             ardist = str(ardist_num)+suffix
493         else:
494             ardist = str(ardist_num)
495         NP020PI.ardist = ardist
496         ardist_loc = (ar12d + ar22d) /2
497     '''
498
499     # DRAWING START:
500     bgl.glEnable(bgl.GL_BLEND)
501
502     if flag == 'RUNTRANSZERO':
503         instruct = 'select the take point'
504         keys_aff = 'LMB - confirm, CTRL - snap, MMB - lock axis, NUMPAD - value'
505         keys_nav = 'SPACE - navigate'
506         keys_neg = 'ESC / RMB - cancel copy'
507
508         badge_mode = 'RUN'
509         message_main = 'CTRL+SNAP'
510         message_aux = None
511         aux_num = None
512         aux_str = None
513
514     elif flag == 'RUNTRANSFIRST':
515         instruct = 'select the placement point'
516         keys_aff = 'LMB - confirm, CTRL - snap, MMB - lock axis, NUMPAD - value'
517         keys_nav = 'SPACE - navigate'
518         keys_neg = 'ESC / RMB - cancel copy'
519
520         badge_mode = 'RUN'
521         message_main = 'CTRL+SNAP'
522         message_aux = None
523         aux_num = None
524         aux_str = None
525
526     elif flag == 'RUNTRANSNEXT':
527         instruct = 'select the placement point'
528         keys_aff = 'LMB - confirm, CTRL - snap, MMB - lock axis, NUMPAD - value'
529         keys_nav = 'SPACE - navigate'
530         keys_neg = 'ESC / RMB - cancel current'
531
532         badge_mode = 'RUN'
533         message_main = 'CTRL+SNAP'
534         message_aux = None
535         aux_num = None
536         aux_str = None
537
538     elif flag == 'NAVTRANSZERO':
539         instruct = 'navigate for better placement of take point'
540         keys_aff = 'MMB / SCROLL - navigate'
541         keys_nav = 'LMB / SPACE - leave navigate'
542         keys_neg = 'ESC / RMB - cancel copy'
543
544         badge_mode = 'NAV'
545         message_main = 'NAVIGATE'
546         message_aux = None
547         aux_num = None
548         aux_str = None
549
550     elif flag == 'NAVTRANSFIRST':
551         instruct = 'navigate for better selection of placement point'
552         keys_aff = 'MMB / SCROLL - navigate'
553         keys_nav = 'LMB / SPACE - leave navigate'
554         keys_neg = 'ESC / RMB - cancel copy'
555
556         badge_mode = 'NAV'
557         message_main = 'NAVIGATE'
558         message_aux = None
559         aux_num = None
560         aux_str = None
561
562     elif flag == 'NAVTRANSNEXT':
563         instruct = 'navigate for better selection of placement point'
564         keys_aff = 'MMB / SCROLL - navigate'
565         keys_nav = 'LMB / SPACE - leave navigate'
566         keys_neg = 'ESC / RMB - cancel current'
567
568         badge_mode = 'NAV'
569         message_main = 'NAVIGATE'
570         message_aux = None
571         aux_num = None
572         aux_str = None
573
574     # ON-SCREEN INSTRUCTIONS:
575
576     display_instructions(region, rv3d, instruct, keys_aff, keys_nav, keys_neg)
577
578     # MOUSE BADGE:
579
580     co2d = placeloc2d
581
582     symbol = [[23, 34], [23, 32], [19, 32], [19, 36], [21, 36], [21, 38], [25, 38], [25, 34], [23, 34], [23, 36], [21, 36]]
583
584     display_cursor_badge(co2d, symbol, badge_mode, message_main, message_aux, aux_num, aux_str)
585
586     # LINE:
587
588     display_line_between_two_points(region, rv3d, takeloc3d, placeloc3d)
589
590     # DISTANCE:
591
592     display_distance_between_two_points(region, rv3d, takeloc3d, placeloc3d)
593     NP020PI.deltavec = copy.deepcopy(display_distance_between_two_points(region, rv3d, takeloc3d, placeloc3d)[0])
594
595     #DRAWING END:
596     bgl.glLineWidth(1)
597     bgl.glDisable(bgl.GL_BLEND)
598     bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
599     np_print('04_DRAW_RunTrans_FINISHED',';','flag = ', NP020PI.flag)
600
601
602 # Defining the operator that will enable navigation if user calls it:
603
604 class NPPINavTranslate(bpy.types.Operator):
605     bl_idname = "object.np_pi_nav_translate"
606     bl_label = "NP PI Nav Translate"
607     bl_options = {'INTERNAL'}
608
609     np_print('04a_NavTrans_START',';','flag = ', NP020PI.flag)
610
611     def modal(self,context,event):
612         context.area.tag_redraw()
613         flag = NP020PI.flag
614         take = NP020PI.take
615         place = NP020PI.place
616
617         if event.type == 'MOUSEMOVE':
618             self.co2d = ((event.mouse_region_x, event.mouse_region_y))
619             region = context.region
620             rv3d = context.region_data
621             co2d = self.co2d
622             view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, co2d)
623             pointloc = view3d_utils.region_2d_to_origin_3d(region, rv3d, co2d) + view_vector * NP020PI.away
624             NP020PI.placeloc3d = copy.deepcopy(pointloc)
625             np_print('04a_NavTrans_mousemove',';','flag = ', NP020PI.flag)
626
627         elif event.type in {'LEFTMOUSE', 'SPACE'} and event.value == 'PRESS':
628             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
629             self.co2d = ((event.mouse_region_x, event.mouse_region_y))
630             region = context.region
631             rv3d = context.region_data
632             co2d = self.co2d
633             view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, co2d)
634             enterloc = view3d_utils.region_2d_to_origin_3d(region, rv3d, co2d) + view_vector*NP020PI.away
635             placeloc3d = NP020PI.placeloc3d
636             navdelta = enterloc - NP020PI.awayloc
637             take.hide = False
638             place.hide = False
639             np_print('flag = ', flag)
640             if flag == 'NAVTRANSZERO':
641                 takeloc3d = enterloc
642                 placeloc3d = enterloc
643                 take.location = enterloc
644                 place.location = enterloc
645                 NP020PI.flag = 'RUNTRANSZERO'
646             elif flag == 'NAVTRANSFIRST':
647                 takeloc3d = NP020PI.takeloc3d
648                 placeloc3d = enterloc
649                 place.location = enterloc
650                 nextob = NP020PI.nextob
651                 for ob in nextob:
652                     ob.hide = False
653                 place.select_set(False)
654                 bpy.ops.transform.translate(value = navdelta)
655                 place.select_set(True)
656                 NP020PI.flag = 'RUNTRANSFIRST'
657             elif flag == 'NAVTRANSNEXT':
658                 takeloc3d = NP020PI.takeloc3d
659                 placeloc3d = enterloc
660                 place.location = enterloc
661                 nextob = NP020PI.nextob
662                 for ob in nextob:
663                     ob.hide = False
664                 place.select_set(False)
665                 bpy.ops.transform.translate(value = navdelta)
666                 place.select_set(True)
667                 NP020PI.flag = 'RUNTRANSNEXT'
668             else:
669                 np_print('UNKNOWN FLAG')
670                 NP020PI.flag = 'EXIT'
671             NP020PI.take = take
672             NP020PI.place = place
673             NP020PI.takeloc3d = takeloc3d
674             NP020PI.placeloc3d = placeloc3d
675             np_print('04a_NavTrans_left_space_FINISHED',';','flag = ', NP020PI.flag)
676             return {'FINISHED'}
677
678         elif event.type == 'RIGHTMOUSE':
679             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
680             take.hide = False
681             place.hide = False
682             if flag == 'NAVTRANSZERO':
683                 place.select_set(False)
684                 NP020PI.flag = 'EXIT'
685             elif flag == 'NAVTRANSFIRST':
686                 place.select_set(False)
687                 prevob = NP020PI.prevob
688                 nextob = NP020PI.nextob
689                 for ob in nextob:
690                     ob.hide = False
691                 bpy.ops.object.delete('EXEC_DEFAULT')
692                 for ob in prevob:
693                     ob.select_set(True)
694                 NP020PI.flag = 'EXIT'
695             elif flag == 'NAVTRANSNEXT':
696                 place.select_set(False)
697                 prevob = NP020PI.prevob
698                 nextob = NP020PI.nextob
699                 for ob in nextob:
700                     ob.hide = False
701                 bpy.ops.object.delete('EXEC_DEFAULT')
702                 for ob in prevob:
703                     ob.select_set(True)
704                 bpy.ops.object.delete('EXEC_DEFAULT')
705                 NP020PI.flag = 'ARRAYTRANS'
706             else:
707                 np_print('UNKNOWN FLAG')
708                 NP020PI.flag = 'EXIT'
709             np_print('04a_NavTrans_rmb_FINISHED',';','flag = ', NP020PI.flag)
710             return{'FINISHED'}
711
712         elif event.type == 'ESC':
713             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
714             take.hide = False
715             place.hide = False
716             if flag == 'NAVTRANSZERO':
717                 place.select_set(False)
718                 NP020PI.flag = 'EXIT'
719             elif flag == 'NAVTRANSFIRST':
720                 place.select_set(False)
721                 prevob = NP020PI.prevob
722                 nextob = NP020PI.nextob
723                 for ob in nextob:
724                     ob.hide = False
725                 bpy.ops.object.delete('EXEC_DEFAULT')
726                 for ob in prevob:
727                     ob.select_set(True)
728                 NP020PI.flag = 'EXIT'
729             elif flag == 'NAVTRANSNEXT':
730                 place.select_set(False)
731                 prevob = NP020PI.prevob
732                 nextob = NP020PI.nextob
733                 NP020PI.selob = prevob
734                 for ob in nextob:
735                     ob.hide = False
736                 bpy.ops.object.delete('EXEC_DEFAULT')
737                 for ob in prevob:
738                     ob.select_set(True)
739                 NP020PI.flag = 'EXIT'
740             else:
741                 np_print('UNKNOWN FLAG')
742                 NP020PI.flag = 'EXIT'
743             np_print('04a_NavTrans_esc_FINISHED',';','flag = ', NP020PI.flag)
744             return{'FINISHED'}
745
746         elif event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
747             np_print('04a_NavTrans_middle_wheel_any_PASS_THROUGH')
748             return {'PASS_THROUGH'}
749
750         np_print('04a_NavTrans_INVOKED_RUNNING_MODAL',';','flag = ', NP020PI.flag)
751         return {'RUNNING_MODAL'}
752
753     def invoke(self, context, event):
754         #np_print('04a_NavTrans_INVOKE_START')
755         flag = NP020PI.flag
756         #np_print('flag = ', flag)
757         self.co2d = ((event.mouse_region_x, event.mouse_region_y))
758         if flag in ('NAVTRANSZERO', 'NAVTRANSFIRST', 'NAVTRANSNEXT'):
759             args = (self, context)
760             self._handle = bpy.types.SpaceView3D.draw_handler_add(DRAW_RunTranslate, args, 'WINDOW', 'POST_PIXEL')
761             context.window_manager.modal_handler_add(self)
762             np_print('04a_run_NAV_INVOKE_a_RUNNING_MODAL',';','flag = ', NP020PI.flag)
763             return {'RUNNING_MODAL'}
764         else:
765             #np_print('04a_run_NAV_INVOKE_a_FINISHED',';','flag = ', flag)
766             return {'FINISHED'}
767
768
769 # Defining the operator that will enable the return to RunTrans cycle by reseting the 'break' flag:
770
771 class NPPIPrepareNext(bpy.types.Operator):
772     bl_idname = 'object.np_pi_prepare_next'
773     bl_label = 'NP PI Prepare Next'
774     bl_options = {'INTERNAL'}
775
776     def execute(self, context):
777         np_print('05_PrepareNext_START',';','flag = ', NP020PI.flag)
778         if NP020PI.flag == 'RUNTRANSFIRST_break':
779             NP020PI.flag = 'RUNTRANSFIRST'
780         if NP020PI.flag == 'RUNTRANSNEXT_break':
781             NP020PI.flag = 'RUNTRANSNEXT'
782         np_print('05_PrepareNext_FINISHED',';','flag = ', NP020PI.flag)
783         return{'FINISHED'}
784
785 '''
786 # Defining the operator that will collect the necessary data and the generate the array with an input dialogue for number of items:
787
788 class NPPIArrayTranslate(bpy.types.Operator):
789     bl_idname = "object.np_pi_array_translate"
790     bl_label = "NP PI Array Translate"
791     bl_options = {'INTERNAL'}
792
793     np_print('06_ArrayTrans_START',';','flag = ', NP020PI.flag)
794
795     def modal(self,context,event):
796         np_print('06_ArrayTrans_START',';','flag = ', NP020PI.flag)
797         context.area.tag_redraw()
798         flag = NP020PI.flag
799         ardict = NP020PI.ardict
800         arob = NP020PI.arob
801         np_print('ardict = ', ardict)
802
803         if event.type == 'MOUSEMOVE':
804             self.co2d = ((event.mouse_region_x, event.mouse_region_y))
805             np_print('04a_NavTrans_mousemove',';','flag = ', NP020PI.flag)
806
807         elif event.type in ('LEFTMOUSE', 'RIGHTMOUSE') and event.value == 'PRESS':
808             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
809             NP020PI.flag = 'EXIT'
810             np_print('06_ArrayTrans_rmb_FINISHED',';','flag = ', NP020PI.flag)
811             return{'FINISHED'}
812
813         elif event.ctrl and event.type == 'WHEELUPMOUSE' or event.type == 'UP_ARROW' and event.value == 'PRESS':
814             for ob in arob:
815                 ar = ardict[ob][0]
816                 deltavec_start = Vector(ardict[ob][1])
817                 count = ardict[ob][2]
818                 if ar.fit_type == 'FIXED_COUNT':
819                     ar.count = ar.count+1
820                     count = count + 1
821                 elif ar.fit_type == 'FIT_LENGTH' and count == 3:
822                     ar.fit_type = 'FIXED_COUNT'
823                     ar.constant_offset_displace = deltavec_start
824                     ar.count = 2
825                     count = 2
826                 elif ar.fit_type == 'FIT_LENGTH' and count >3:
827                     count = count - 1
828                     ar.constant_offset_displace.length = ar.fit_length/(count-1)
829                 ardict[ob][2] = count
830             NP020PI.fit_type = ar.fit_type
831             NP020PI.count = count
832             bpy.context.scene.update()
833
834         elif event.ctrl and event.type == 'WHEELDOWNMOUSE' or event.type == 'DOWN_ARROW' and event.value == 'PRESS':
835             for ob in arob:
836                 ar = ardict[ob][0]
837                 deltavec_start = Vector(ardict[ob][1])
838                 count = ardict[ob][2]
839                 if ar.fit_type == 'FIXED_COUNT' and count > 2:
840                     ar.count = ar.count-1
841                     count = count - 1
842                 elif ar.fit_type == 'FIXED_COUNT' and count == 2:
843                     ar.fit_type = 'FIT_LENGTH'
844                     ar.fit_length = deltavec_start.length
845                     ar.constant_offset_displace.length = ar.fit_length/2
846                     count = 3
847                 elif ar.fit_type == 'FIT_LENGTH':
848                     count = count + 1
849                     ar.constant_offset_displace.length = ar.fit_length/(count-1)
850                 ardict[ob][2] = count
851             NP020PI.fit_type = ar.fit_type
852             NP020PI.count = count
853             bpy.context.scene.update()
854
855         elif event.type in ('RET', 'NUMPAD_ENTER') and event.value == 'PRESS':
856             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
857             selob = bpy.context.selected_objects
858             bpy.ops.object.select_all(action='DESELECT')
859             for ob in arob:
860                 ob.select = True
861                 bpy.ops.object.modifier_apply(modifier = ardict[ob][0].name)
862                 ob.select = False
863             for ob in selob:
864                 ob.select = True
865             NP020PI.flag = 'EXIT'
866             np_print('06_ArrayTrans_enter_FINISHED',';','flag = ', NP020PI.flag)
867             return{'FINISHED'}
868
869         elif event.ctrl and event.type == 'TAB' and event.value == 'PRESS':
870             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
871             if NP020PI.fit_type == 'FIXED_COUNT':
872                 value = NP020PI.ar23d - NP020PI.ar13d
873             else:
874                 value = (NP020PI.ar23d - NP020PI.ar13d)/(NP020PI.count - 1)
875             selob = bpy.context.selected_objects
876             bpy.ops.object.select_all(action='DESELECT')
877             for ob in arob:
878                 ob.select = True
879                 ob.modifiers.remove(ardict[ob][0])
880                 np_print('NP020PI.count', NP020PI.count)
881                 for i in range(1, NP020PI.count):
882                     bpy.ops.object.duplicate(linked = True)
883                     bpy.ops.transform.translate(value = value)
884                 bpy.ops.object.select_all(action='DESELECT')
885             for ob in selob:
886                 ob.select = True
887             NP020PI.flag = 'EXIT'
888             np_print('06_ArrayTrans_ctrl_tab_FINISHED',';','flag = ', NP020PI.flag)
889             return{'FINISHED'}
890
891         elif event.type == 'TAB' and event.value == 'PRESS':
892             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
893             if NP020PI.fit_type == 'FIXED_COUNT':
894                 value = NP020PI.ar23d - NP020PI.ar13d
895             else:
896                 value = (NP020PI.ar23d - NP020PI.ar13d)/(NP020PI.count - 1)
897             selob = bpy.context.selected_objects
898             bpy.ops.object.select_all(action='DESELECT')
899             for ob in arob:
900                 ob.select = True
901                 ob.modifiers.remove(ardict[ob][0])
902                 np_print('NP020PI.count', NP020PI.count)
903                 for i in range(1, NP020PI.count):
904                     bpy.ops.object.duplicate()
905                     bpy.ops.transform.translate(value = value)
906                 bpy.ops.object.select_all(action='DESELECT')
907             for ob in selob:
908                 ob.select = True
909             NP020PI.flag = 'EXIT'
910             np_print('06_ArrayTrans_tab_FINISHED',';','flag = ', NP020PI.flag)
911             return{'FINISHED'}
912
913         elif event.type == 'ESC' and event.value == 'PRESS':
914             bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
915             for ob in arob:
916                 ob.modifiers.remove(ardict[ob][0])
917             NP020PI.flag = 'EXIT'
918             np_print('06_ArrayTrans_esc_FINISHED',';','flag = ', NP020PI.flag)
919             return{'FINISHED'}
920
921         elif event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
922             np_print('06_ArrayTrans_middle_wheel_any_PASS_THROUGH')
923             return {'PASS_THROUGH'}
924
925         np_print('06_ArrayTrans_INVOKED_RUNNING_MODAL',';','flag = ', NP020PI.flag)
926         return {'RUNNING_MODAL'}
927
928     def invoke(self, context, event):
929         np_print('06_ArrayTrans_INVOKE_START')
930         flag = NP020PI.flag
931         self.co2d = ((event.mouse_region_x, event.mouse_region_y))
932         if flag == 'ARRAYTRANS':
933             arob = NP020PI.arob
934             np_print('deltavec_safe = ', NP020PI.deltavec_safe)
935             ardict = {}
936             for ob in arob:
937                 deltavec = copy.deepiopy(NP020PI.deltavec_safe)
938                 np_print('deltavec = ', deltavec)
939                 loc, rot, sca = ob.matrix_world.decompose()
940                 rot = ob.rotation_euler
941                 rot = rot.to_quaternion()
942                 sca = ob.scale
943                 np_print(loc, rot, sca, ob.matrix_world)
944                 np_print('deltavec = ', deltavec)
945                 deltavec.rotate(rot.conjugated())
946                 np_print('sca.length', sca.length)
947                 deltavec[0] = deltavec[0] / sca[0]
948                 deltavec[1] = deltavec[1] / sca[1]
949                 deltavec[2] = deltavec[2] / sca[2]
950                 np_print('deltavec = ', deltavec)
951                 deltavec_trans = deltavec.to_tuple(4)
952                 arcur = ob.modifiers.new(name = '', type = 'ARRAY')
953                 arcur.fit_type = 'FIXED_COUNT'
954                 arcur.use_relative_offset = False
955                 arcur.use_constant_offset = True
956                 arcur.constant_offset_displace = deltavec_trans
957                 arcur.count = 5
958                 ardict[ob] = []
959                 ardict[ob].append(arcur)
960                 ardict[ob].append(deltavec_trans)
961                 ardict[ob].append(arcur.count)
962             NP020PI.selob = arob
963             NP020PI.ardict = ardict
964             NP020PI.count = 5
965             NP020PI.fit_type = 'FIXED_COUNT'
966             selob = NP020PI.selob
967             lenselob = len(selob)
968             for i, ob in enumerate(selob):
969                 ob.select = True
970                 if i == lenselob-1:
971                     bpy.context.scene.objects.active = ob
972             args = (self, context)
973             self._handle = bpy.types.SpaceView3D.draw_handler_add(DRAW_ArrayTrans, args, 'WINDOW', 'POST_PIXEL')
974             context.window_manager.modal_handler_add(self)
975             np_print('06_ArayTrans_INVOKE_a_RUNNING_MODAL',';','flag = ', NP020PI.flag)
976             return {'RUNNING_MODAL'}
977         else:
978             np_print('06_ArrayTrans_INVOKE_DENIED',';','flag = ', NP020PI.flag)
979             return {'FINISHED'}
980 '''
981
982 '''
983 # Defining the set of instructions that will draw the OpenGL elements on the screen during the execution of ArrayTrans operator:
984
985 def DRAW_ArrayTrans(self, context):
986
987     np_print('06a_DRAW_ArrayTrans_START',';','flag = ', NP020PI.flag)
988
989     addon_prefs = context.preferences.addons[__package__].preferences
990     badge = addon_prefs.nppi_badge
991     badge_size = addon_prefs.nppi_badge_size
992
993     # DRAWING START:
994     bgl.glEnable(bgl.GL_BLEND)
995
996     # MOUSE BADGE:
997     if badge == True:
998         square = [[17, 30], [17, 40], [27, 40], [27, 30]]
999         rectangle = [[27, 30], [27, 40], [67, 40], [67, 30]]
1000         icon = copy.deepcopy(NP020PI.icon)
1001         np_print('icon', icon)
1002         ipx = 29
1003         ipy = 33
1004         for co in square:
1005             co[0] = round((co[0] * badge_size),0) -(badge_size*10) + self.co2d[0]
1006             co[1] = round((co[1] * badge_size),0) -(badge_size*25) + self.co2d[1]
1007         for co in rectangle:
1008             co[0] = round((co[0] * badge_size),0) -(badge_size*10) + self.co2d[0]
1009             co[1] = round((co[1] * badge_size),0) -(badge_size*25) + self.co2d[1]
1010         for co in icon:
1011             co[0] = round((co[0] * badge_size),0) -(badge_size*10) + self.co2d[0]
1012             co[1] = round((co[1] * badge_size),0) -(badge_size*25) + self.co2d[1]
1013         ipx = round((ipx * badge_size),0) -(badge_size*10) + self.co2d[0]
1014         ipy = round((ipy * badge_size),0) -(badge_size*25) + self.co2d[1]
1015         ipsize = int(6* badge_size)
1016         bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
1017         bgl.glBegin(bgl.GL_TRIANGLE_FAN)
1018         for x,y in square:
1019             bgl.glVertex2f(x,y)
1020         bgl.glEnd()
1021         bgl.glColor4f(0.5, 0.75, 0.0, 1.0)
1022         bgl.glBegin(bgl.GL_TRIANGLE_FAN)
1023         for x,y in rectangle:
1024             bgl.glVertex2f(x,y)
1025         bgl.glEnd()
1026         bgl.glColor4f(0.2, 0.15, 0.55, 1.0)
1027         bgl.glBegin(bgl.GL_TRIANGLE_FAN)
1028         for x,y in rectangle:
1029             bgl.glVertex2f(x,(y-(badge_size*35)))
1030         bgl.glEnd()
1031         bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
1032         font_id = 0
1033         blf.position(font_id,ipx,ipy,0)
1034         blf.size(font_id,ipsize,72)
1035         blf.draw(font_id,'NAVIGATE')
1036         blf.position(font_id,ipx,(ipy-(badge_size*35)),0)
1037         blf.size(font_id,ipsize,72)
1038         blf.draw(font_id,'CTRL+SCRL')
1039         bgl.glColor4f(1,1,1,1)
1040         blf.position(font_id,ipx,(int(ipy-badge_size*25)),0)
1041         blf.size(font_id,(int(badge_size*24)),72)
1042         if NP020PI.fit_type == 'FIT_LENGTH':
1043             blf.draw(font_id,'/' + str(NP020PI.count))
1044         else:
1045             blf.draw(font_id,str(NP020PI.count))
1046         bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
1047         bgl.glBegin(bgl.GL_LINE_STRIP)
1048         for x,y in icon:
1049             bgl.glVertex2f(x,y)
1050         bgl.glEnd()
1051
1052     # ON-SCREEN INSTRUCTIONS:
1053     font_id = 0
1054     bgl.glColor4f(1,1,1,0.35)
1055     blf.size(font_id,88,72)
1056     blf.position(font_id,5,74,0)
1057     blf.draw(font_id,'N')
1058     blf.size(font_id,28,72)
1059     blf.position(font_id,22,74,0)
1060     blf.draw(font_id,'P')
1061     blf.enable(font_id, ROTATION)
1062     bgl.glColor4f(1,1,1,0.40)
1063     ang = radians(90)
1064     blf.size(font_id,19,72)
1065     blf.rotation(font_id,ang)
1066     blf.position(font_id,78,73,0)
1067     blf.draw(font_id,'PI 002')
1068     blf.disable(font_id, ROTATION)
1069
1070     main='SPECIFY NUMBER OF ITEMS IN ARRAY'
1071     bgl.glColor4f(0,0.5,0,1)
1072     blf.size(font_id,11,72)
1073     blf.position(font_id,93,105,0)
1074     blf.draw(font_id,'MMB, SCROLL - navigate')
1075     blf.position(font_id,93,90,0)
1076     blf.draw(font_id,'CTRL+SCROLL, UPARROW / DOWNARROW - number of items')
1077     blf.position(font_id,93,75,0)
1078     blf.draw(font_id,'LMB, RMB - confirm and keep array, ENTER - apply as one, TAB - apply as separate, CTRL+TAB - apply as instanced')
1079     bgl.glColor4f(1,0,0,1)
1080     blf.position(font_id,93,60,0)
1081     blf.draw(font_id,'ESC - cancel array')
1082     bgl.glColor4f(0.0, 0.0, 0.0, 0.5)
1083     blf.position(font_id,93,124,0)
1084     blf.size(font_id,16,72)
1085     blf.draw(font_id,main)
1086     bgl.glColor4f(1,1,1,1)
1087     blf.position(font_id,94,125,0)
1088     blf.size(font_id,16,72)
1089     blf.draw(font_id,main)
1090
1091
1092     region = context.region
1093     rv3d = context.region_data
1094     ar12d = view3d_utils.location_3d_to_region_2d(region, rv3d, NP020PI.ar13d)
1095     ar22d = view3d_utils.location_3d_to_region_2d(region, rv3d, NP020PI.ar23d)
1096     ardist = NP020PI.ardist
1097     ardist_loc = (ar12d + ar22d) /2
1098
1099     # ARMARKERS:
1100     markersize = badge_size*2.5
1101     triangle = [[0, 0], [-1, 1], [1, 1]]
1102     triangle = [[0, 0], [-1, 1], [1, 1]]
1103     for co in triangle:
1104         co[0] = int(co[0] * markersize * 3) + ar12d[0]
1105         co[1] = int(co[1] * markersize * 3) + ar12d[1]
1106     bgl.glColor4f(0.4, 0.15, 0.75, 1.0)
1107     bgl.glBegin(bgl.GL_TRIANGLE_FAN)
1108     for x,y in triangle:
1109         bgl.glVertex2f(x,y)
1110     bgl.glEnd()
1111     triangle = [[0, 0], [-1, 1], [1, 1]]
1112     for co in triangle:
1113         co[0] = int(co[0] * markersize * 3) + ar22d[0]
1114         co[1] = int(co[1] * markersize * 3) + ar22d[1]
1115     bgl.glColor4f(0.4, 0.15, 0.75, 1.0)
1116     bgl.glBegin(bgl.GL_TRIANGLE_FAN)
1117     for x,y in triangle:
1118         bgl.glVertex2f(x,y)
1119     bgl.glEnd()
1120
1121     # AR NUMERICAL DISTANCE:
1122     np_print('ardist = ', ardist, 'ardist_loc = ', ardist_loc)
1123     bgl.glColor4f(0.4, 0.15, 0.75, 1.0)
1124     font_id = 0
1125     blf.size(font_id, 20, 72)
1126     blf.position(font_id, ardist_loc[0], ardist_loc[1], 0)
1127     blf.draw(font_id, ardist)
1128
1129     #DRAWING END:
1130     bgl.glLineWidth(1)
1131     bgl.glDisable(bgl.GL_BLEND)
1132     bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
1133     np_print('06a_DRAW_ArrayTrans_FINISHED',';','flag = ', NP020PI.flag)
1134 '''
1135
1136 # 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:
1137
1138 class NPPIRestoreContext(bpy.types.Operator):
1139     bl_idname = "object.np_pi_restore_context"
1140     bl_label = "NP PI Restore Context"
1141     bl_options = {'INTERNAL'}
1142
1143     def execute(self, context):
1144         np_print('07_CleanExit_START',';','flag = ', NP020PI.flag)
1145         flag = NP020PI.flag
1146         selob = NP020PI.selob
1147         take = NP020PI.take
1148         place = NP020PI.place
1149         bpy.ops.object.select_all(action='DESELECT')
1150         take.select_set(True)
1151         place.select_set(True)
1152         bpy.ops.object.delete('EXEC_DEFAULT')
1153         lenselob = len(selob)
1154         for i, ob in enumerate(selob):
1155             ob.select_set(True)
1156             if i == lenselob-1:
1157                 bpy.context.view_layer.objects.active = ob
1158         NP020PI.take = None
1159         NP020PI.place = None
1160         NP020PI.takeloc3d = (0.0,0.0,0.0)
1161         NP020PI.placeloc3d = (0.0,0.0,0.0)
1162         NP020PI.dist = None
1163         NP020PI.mode = 'MOVE'
1164         NP020PI.flag = 'NONE'
1165         NP020PI.ardict = {}
1166         NP020PI.deltavec = Vector ((0, 0, 0))
1167         NP020PI.deltavec_safe = Vector ((0, 0, 0))
1168         bpy.context.tool_settings.use_snap = NP020PI.use_snap
1169         bpy.context.tool_settings.snap_element = NP020PI.snap_element
1170         bpy.context.tool_settings.snap_target = NP020PI.snap_target
1171         bpy.context.space_data.pivot_point = NP020PI.pivot_point
1172         bpy.context.space_data.transform_orientation = NP020PI.trans_orient
1173         #if NP020PI.acob is not None:
1174             #bpy.context.scene.objects.active = NP020PI.acob
1175             #bpy.ops.object.mode_set(mode = NP020PI.edit_mode)
1176
1177         np_print('07_CleanExit_FINISHED',';','flag = ', NP020PI.flag)
1178         return {'FINISHED'}
1179
1180
1181 '''
1182 # Defining the settings of the addon in the User preferences / addons tab:
1183
1184 class NPPIPreferences(bpy.types.AddonPreferences):
1185     # this must match the addon name, use '__package__'
1186     # when defining this in a submodule of a python package.
1187     bl_idname = __name__
1188
1189     dist_scale = bpy.props.FloatProperty(
1190             name='Unit scale',
1191             description='Distance multiplier (for example, for cm use 100)',
1192             default=100,
1193             min=0,
1194             step=1,
1195             precision=3)
1196
1197     suffix = bpy.props.EnumProperty(
1198         name='Unit suffix',
1199         items=(("'","'",''), ('"','"',''), ('thou','thou',''), ('km','km',''), ('m','m',''), ('cm','cm',''), ('mm','mm',''), ('nm','nm',''), ('None','None','')),
1200         default='cm',
1201         description='Add a unit extension after the numerical distance ')
1202
1203     badge = bpy.props.BoolProperty(
1204             name='Mouse badge',
1205             description='Use the graphical badge near the mouse cursor',
1206             default=True)
1207
1208     badge_size = bpy.props.FloatProperty(
1209             name='size',
1210             description='Size of the mouse badge, the default is 2.0',
1211             default=2,
1212             min=0.5,
1213             step=10,
1214             precision=1)
1215
1216     col_line_main_DEF = bpy.props.BoolProperty(
1217             name='Default',
1218             description='Use the default color',
1219             default=True)
1220
1221     col_line_shadow_DEF = bpy.props.BoolProperty(
1222             name='Default',
1223             description='Use the default color',
1224             default=True)
1225
1226     col_num_main_DEF = bpy.props.BoolProperty(
1227             name='Default',
1228             description='Use the default color',
1229             default=True)
1230
1231     col_num_shadow_DEF = bpy.props.BoolProperty(
1232             name='Default',
1233             description='Use the default color',
1234             default=True)
1235
1236     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')
1237
1238     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')
1239
1240     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')
1241
1242     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')
1243
1244     def draw(self, context):
1245         layout = self.layout
1246         split = layout.split()
1247         col = split.column()
1248         col.prop(self, "dist_scale")
1249         col = split.column()
1250         col.prop(self, "suffix")
1251         split = layout.split()
1252         col = split.column()
1253         col.label(text='Line Main COLOR')
1254         col.prop(self, "col_line_main_DEF")
1255         if self.col_line_main_DEF == False:
1256             col.prop(self, "col_line_main")
1257         col = split.column()
1258         col.label(text='Line Shadow COLOR')
1259         col.prop(self, "col_line_shadow_DEF")
1260         if self.col_line_shadow_DEF == False:
1261             col.prop(self, "col_line_shadow")
1262         col = split.column()
1263         col.label(text='Numerical Main COLOR')
1264         col.prop(self, "col_num_main_DEF")
1265         if self.col_num_main_DEF == False:
1266             col.prop(self, "col_num_main")
1267         col = split.column()
1268         col.label(text='Numerical Shadow COLOR')
1269         col.prop(self, "col_num_shadow_DEF")
1270         if self.col_num_shadow_DEF == False:
1271             col.prop(self, "col_num_shadow")
1272         split = layout.split()
1273         col = split.column()
1274         col.prop(self, "badge")
1275         col = split.column()
1276         if self.badge == True:
1277             col.prop(self, "badge_size")
1278         col = split.column()
1279         col = split.column()
1280 '''
1281
1282 # This is the actual addon process, the algorithm that defines the order of operator activation inside the main macro:
1283
1284 def register():
1285
1286     #bpy.utils.register_class(NPPIPreferences)
1287     #bpy.utils.register_module(__name__)
1288     bpy.app.handlers.scene_update_post.append(NPPI_scene_update)
1289
1290     NP020PointInstance.define('OBJECT_OT_np_pi_get_context')
1291     NP020PointInstance.define('OBJECT_OT_np_pi_get_selection')
1292     NP020PointInstance.define('OBJECT_OT_np_pi_get_mouseloc')
1293     NP020PointInstance.define('OBJECT_OT_np_pi_add_helpers')
1294     NP020PointInstance.define('OBJECT_OT_np_pi_prepare_context')
1295     for i in range(1, 50):
1296         for i in range(1, 10):
1297             NP020PointInstance.define('OBJECT_OT_np_pi_run_translate')
1298             NP020PointInstance.define('OBJECT_OT_np_pi_nav_translate')
1299         NP020PointInstance.define('OBJECT_OT_np_pi_prepare_next')
1300     #NP020PointInstance.define('OBJECT_OT_np_pi_array_translate')
1301     NP020PointInstance.define('OBJECT_OT_np_pi_restore_context')
1302
1303 def unregister():
1304     #pass
1305     #bpy.utils.unregister_class(NPPIPreferences)
1306     #bpy.utils.unregister_module(__name__)
1307     bpy.app.handlers.scene_update_post.remove(NPPI_scene_update)