Extensions: change the constant for the complete status
[blender-addons-contrib.git] / np_station / np_point_copy.py
blobf75966831befe213be0e6082f1fd78c2b908ae1b
3 # ##### BEGIN GPL LICENSE BLOCK #####
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 # ##### END GPL LICENSE BLOCK #####
21 '''
23 DESCRIPTION:
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.
28 INSTALLATION:
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.
34 SHORTCUTS:
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.
39 USAGE:
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.
48 ADDON SETTINGS:
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:
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
58 IMPORTANT PERFORMANCE NOTES:
60 None so far.
62 '''
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 'doc_url': '',
73 'category': '3D View'}
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 *
88 from .utils_geometry import *
89 from .utils_graphics import *
90 from .utils_function import *
92 # Defining the main class - the macro:
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'}
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:
102 class NP020PC:
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))
116 # Defining the scene update algorithm that will track the state of the objects during modal transforms, which is otherwise impossible:
118 @persistent
119 def NPPC_scene_update(context):
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')
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.
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'}
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'}
181 # Defining the operator for aquiring the list of selected objects and storing them for later re-calls:
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'}
188 def execute(self, context):
189 # Reading and storing the selection:
190 NP020PC.selob = bpy.context.selected_objects
191 return {'FINISHED'}
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:
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'}
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'}
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'}
218 # Defining the operator that will generate 'take' and 'place' points at the spot marked by mouse, preparing for translation:
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'}
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'}
239 # Defining the operator that will change some of the system settings and prepare objects for the operation:
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'}
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'}
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:
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'}
268 #np_print('04_RunTrans_START',';','NP020PC.flag = ', NP020PC.flag)
269 count = 0
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
279 if self.count == 1:
280 bpy.ops.transform.translate('INVOKE_DEFAULT')
281 np_print('04_RunTrans_count_1_INVOKE_DEFAULT', ';', 'flag = ', NP020PC.flag)
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'}
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'}
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'}
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'}
421 np_print('04_RunTrans_count_PASS_THROUGH',';','flag = ', NP020PC.flag)
422 return{'PASS_THROUGH'}
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'}
447 # Defining the set of instructions that will draw the OpenGL elements on the screen during the execution of RunTranslate operator:
449 def DRAW_RunTranslate(self, context):
451 np_print('04_DRAW_RunTrans_START',';','flag = ', NP020PC.flag)
453 addon_prefs = context.preferences.addons[__package__].preferences
455 flag = NP020PC.flag
456 takeloc3d = NP020PC.takeloc3d
457 placeloc3d = NP020PC.placeloc3d
459 region = context.region
460 rv3d = context.region_data
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
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
487 # DRAWING START:
488 bgl.glEnable(bgl.GL_BLEND)
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'
496 badge_mode = 'RUN'
497 message_main = 'CTRL+SNAP'
498 message_aux = None
499 aux_num = None
500 aux_str = None
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'
508 badge_mode = 'RUN'
509 message_main = 'CTRL+SNAP'
510 message_aux = None
511 aux_num = None
512 aux_str = None
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'
520 badge_mode = 'RUN'
521 message_main = 'CTRL+SNAP'
522 message_aux = None
523 aux_num = None
524 aux_str = None
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'
532 badge_mode = 'NAV'
533 message_main = 'NAVIGATE'
534 message_aux = None
535 aux_num = None
536 aux_str = None
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'
544 badge_mode = 'NAV'
545 message_main = 'NAVIGATE'
546 message_aux = None
547 aux_num = None
548 aux_str = None
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'
556 badge_mode = 'NAV'
557 message_main = 'NAVIGATE'
558 message_aux = None
559 aux_num = None
560 aux_str = None
562 # ON-SCREEN INSTRUCTIONS:
564 display_instructions(region, rv3d, instruct, keys_aff, keys_nav, keys_neg)
568 # MOUSE BADGE:
570 co2d = placeloc2d
572 symbol = [[23, 34], [23, 32], [19, 32], [19, 36], [21, 36], [21, 38], [25, 38], [25, 34], [23, 34], [23, 36], [21, 36]]
574 display_cursor_badge(co2d, symbol, badge_mode, message_main, message_aux, aux_num, aux_str)
576 # LINE:
578 display_line_between_two_points(region, rv3d, takeloc3d, placeloc3d)
580 # DISTANCE:
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])
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)
592 # Defining the operator that will enable navigation if user calls it:
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'}
599 np_print('04a_NavTrans_START',';','flag = ', NP020PC.flag)
601 def modal(self,context,event):
602 context.area.tag_redraw()
603 flag = NP020PC.flag
604 take = NP020PC.take
605 place = NP020PC.place
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)
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'}
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'}
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'}
736 elif event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
737 np_print('04a_NavTrans_middle_wheel_any_PASS_THROUGH')
738 return {'PASS_THROUGH'}
740 np_print('04a_NavTrans_INVOKED_RUNNING_MODAL',';','flag = ', NP020PC.flag)
741 return {'RUNNING_MODAL'}
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'}
759 # Defining the operator that will enable the return to RunTrans cycle by reseting the 'break' flag:
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'}
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'}
776 # Defining the operator that will collect the necessary data and the generate the array with an input dialogue for number of items:
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'}
783 np_print('06_ArrayTrans_START',';','flag = ', NP020PC.flag)
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)
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)
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'}
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.view_layer.update()
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.view_layer.update()
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'}
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'}
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'}
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'}
911 elif event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
912 np_print('06_ArrayTrans_middle_wheel_any_PASS_THROUGH')
913 return {'PASS_THROUGH'}
915 np_print('06_ArrayTrans_INVOKED_RUNNING_MODAL',';','flag = ', NP020PC.flag)
916 return {'RUNNING_MODAL'}
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'}
973 # Defining the set of instructions that will draw the OpenGL elements on the screen during the execution of ArrayTrans operator:
975 def DRAW_ArrayTrans(self, context):
977 np_print('06a_DRAW_ArrayTrans_START',';','flag = ', NP020PC.flag)
979 addon_prefs = context.preferences.addons[__package__].preferences
980 badge = addon_prefs.nppc_badge
981 badge_size = addon_prefs.nppc_badge_size
983 # DRAWING START:
984 bgl.glEnable(bgl.GL_BLEND)
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()
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)
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)
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
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()
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)
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)
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:
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'}
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)
1167 np_print('07_CleanExit_FINISHED',';','flag = ', NP020PC.flag)
1168 return {'FINISHED'}
1172 # Defining the settings of the addon in the User preferences / addons tab:
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__
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)
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 ')
1193 badge = bpy.props.BoolProperty(
1194 name='Mouse badge',
1195 description='Use the graphical badge near the mouse cursor',
1196 default=True)
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)
1206 col_line_main_DEF = bpy.props.BoolProperty(
1207 name='Default',
1208 description='Use the default color',
1209 default=True)
1211 col_line_shadow_DEF = bpy.props.BoolProperty(
1212 name='Default',
1213 description='Use the default color',
1214 default=True)
1216 col_num_main_DEF = bpy.props.BoolProperty(
1217 name='Default',
1218 description='Use the default color',
1219 default=True)
1221 col_num_shadow_DEF = bpy.props.BoolProperty(
1222 name='Default',
1223 description='Use the default color',
1224 default=True)
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')
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')
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')
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')
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()
1272 # This is the actual addon process, the algorithm that defines the order of operator activation inside the main macro:
1274 def register():
1276 #bpy.utils.register_class(NPPCPreferences)
1277 #bpy.utils.register_module(__name__)
1278 bpy.app.handlers.scene_update_post.append(NPPC_scene_update)
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')
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)