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