Extensions: change the constant for the complete status
[blender-addons-contrib.git] / np_station / np_point_distance.py
blob252a6916eee160d3c6e0812b2f798a03a4e7719b
2 # BEGIN GPL LICENSE BLOCK #####
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # END GPL LICENSE BLOCK #####
21 '''
22 DESCRIPTION
24 Measures distance using start and end points. Emulates the functionality of the standard 'distance' command in CAD applications. User is now able to change snap target while operating.
27 INSTALATION
29 Unzip and place .py file to scripts / addons_contrib folder. In User Preferences / Addons tab, search with Testing filter - NP Point Distance and check the box.
30 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.
33 SHORTCUTS
35 After successful installation of the addon, the NP Point Distance operator should be registered in your system. Enter User Preferences / Input, and under that, 3DView / Global mode. At the bottom of the list click the 'Add new' button. In the operator field type object.np_point_distance_xxx (xxx being the number of the version) and assign a key of yor prefference. At the moment i am using 'T' for 'tape measure'. I have my 'T' and 'N' keys free because of new 'Z' and 'X' keys assigned to toolshelf and properties (under the left hand, no need to look down).
38 USAGE
40 Run operator (spacebar search - NP Point Distance, or keystroke if you assigned it)
41 Select a point anywhere in the scene (holding CTRL enables snapping). This will be your start point.
42 Move your mouse anywhere in the scene, in relation to the start point (again CTRL - snap). The addon will show the distance between your start and end points.
43 Middle mouse button (MMB) enables axis constraint, numpad keys enable numerical input of distance, ENTER key changes snap target and RMB and ESC key interrupt the operation.
46 ADDON SETTINGS:
48 Below the addon name in the user preferences / addon tab, you can find a couple of settings that control the behavior of the addon:
50 Unit scale: Distance multiplier for various unit scenarios
51 Suffix: Unit abbreviation after the numerical distance
52 Custom colors: Default or custom colors for graphical elements
53 Mouse badge: Option to display a small cursor label
56 IMPORTANT PERFORMANCE NOTES
58 Now can start in all 3D modes, the operator temporarily exits the mode and enters the object mode, does the task and returns to original mode.
61 WISH LIST
63 X/Y/Z distance components
64 Custom colors, fonts and unit formats
65 Navigation enabled during use
66 Smarter code and faster performance
69 TO DO
71 PEP8
72 Custom colors, fonts and unit formats, custom colors for badge, aistance num on top
73 Navigation enabled during use
74 Smarter code and faster performance
77 WARNINGS
79 None so far
80 '''
83 bl_info = {
84 'name': 'NP 020 Point Distance',
85 'author': 'Okavango & the Blenderartists community',
86 'version': (0, 2, 0),
87 'blender': (2, 75, 0),
88 'location': 'View3D',
89 'warning': '',
90 'description': 'Measures distance between two snapped points',
91 'doc_url': '',
92 'category': '3D View'}
94 import bpy
95 import copy
96 import bgl
97 import blf
98 import mathutils
99 from bpy_extras import view3d_utils
100 from bpy.app.handlers import persistent
101 from mathutils import Vector, Matrix
102 from blf import ROTATION
103 from math import radians
105 from .utils_geometry import *
106 from .utils_graphics import *
107 from .utils_function import *
109 # Defining the main class - the macro:
111 class NP020PointDistance(bpy.types.Macro):
112 bl_idname = 'object.np_020_point_distance'
113 bl_label = 'NP 020 Point Distance'
114 bl_options = {'UNDO'}
117 # Defining the storage class that will serve as a variable-bank for
118 # exchange among the classes. Later, this bank will receive more variables
119 # with their values for safe keeping, as the program goes on:
121 class NP020PD:
123 startloc3d = (0.0, 0.0, 0.0)
124 endloc3d = (0.0, 0.0, 0.0)
125 phase = 0
126 start = None
127 end = None
128 dist = None
129 flag = 'TRANSLATE'
130 snap = 'VERTEX'
133 # Defining the first of the classes from the macro, that will gather the
134 # current system settings set by the user. Some of the system settings
135 # will be changed during the process, and will be restored when macro has
136 # completed. It also acquires the list of selected objects and storing them
137 # for later re-call (the addon doesn't need them for operation):
139 class NP020PDGetSelection(bpy.types.Operator):
140 bl_idname = 'object.np_pd_get_selection'
141 bl_label = 'NP PD Get Selection'
142 bl_options = {'INTERNAL'}
144 def execute(self, context):
146 # First, storing all of the system preferences set by the user, that
147 # will be changed during the process, in order to restore them when the
148 # operation is completed:
150 np_print('01_get_selection_START')
151 NP020PD.use_snap = bpy.context.tool_settings.use_snap
152 NP020PD.snap_element = bpy.context.tool_settings.snap_element
153 NP020PD.snap_target = bpy.context.tool_settings.snap_target
154 NP020PD.pivot_point = bpy.context.space_data.pivot_point
155 NP020PD.trans_orient = bpy.context.space_data.transform_orientation
156 NP020PD.show_manipulator = bpy.context.space_data.show_manipulator
157 NP020PD.acob = bpy.context.active_object
158 np_print('NP020PD.acob =', NP020PD.acob)
159 np_print(bpy.context.mode)
160 if bpy.context.mode == 'OBJECT':
161 NP020PD.edit_mode = 'OBJECT'
162 elif bpy.context.mode in ('EDIT_MESH', 'EDIT_CURVE', 'EDIT_SURFACE', 'EDIT_TEXT', 'EDIT_ARMATURE', 'EDIT_METABALL', 'EDIT_LATTICE'):
163 NP020PD.edit_mode = 'EDIT'
164 elif bpy.context.mode == 'POSE':
165 NP020PD.edit_mode = 'POSE'
166 elif bpy.context.mode == 'SCULPT':
167 NP020PD.edit_mode = 'SCULPT'
168 elif bpy.context.mode == 'PAINT_WEIGHT':
169 NP020PD.edit_mode = 'WEIGHT_PAINT'
170 elif bpy.context.mode == 'PAINT_TEXTURE':
171 NP020PD.edit_mode = 'TEXTURE_PAINT'
172 elif bpy.context.mode == 'PAINT_VERTEX':
173 NP020PD.edit_mode = 'VERTEX_PAINT'
174 elif bpy.context.mode == 'PARTICLE':
175 NP020PD.edit_mode = 'PARTICLE_EDIT'
176 NP020PD.phase = 0
177 # Reading and storing the selection:
178 selob = bpy.context.selected_objects
179 NP020PD.selob = selob
180 # De-selecting objects in prepare for other processes in the script:
181 for ob in selob:
182 ob.select_set(False)
183 np_print('01_get_selection_END')
184 return {'FINISHED'}
186 # Defining the operator that will read the mouse position in 3D when the
187 # command is activated and store it as a location for placing the start
188 # and end points under the mouse:
191 class NP020PDReadMouseLoc(bpy.types.Operator):
192 bl_idname = 'object.np_pd_read_mouse_loc'
193 bl_label = 'NP PD Read Mouse Loc'
194 bl_options = {'INTERNAL'}
196 def modal(self, context, event):
197 np_print('02_read_mouse_loc_START')
198 region = context.region
199 rv3d = context.region_data
200 co2d = ((event.mouse_region_x, event.mouse_region_y))
201 view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, co2d)
202 pointloc = view3d_utils.region_2d_to_origin_3d(
203 region, rv3d, co2d) + view_vector / 5
204 np_print(pointloc)
205 NP020PD.pointloc = pointloc
206 np_print('02_read_mouse_loc_END')
207 return{'FINISHED'}
209 def invoke(self, context, event):
210 np_print('02_read_mouse_loc_INVOKE_a')
211 args = (self, context)
212 context.window_manager.modal_handler_add(self)
213 np_print('02_read_mouse_loc_INVOKE_b')
214 return {'RUNNING_MODAL'}
217 # Defining the operator that will generate start and end points at the
218 # spot marked by mouse and select them, preparing for translation:
221 class NP020PDAddPoints(bpy.types.Operator):
222 bl_idname = 'object.np_pd_add_points'
223 bl_label = 'NP PD Add Points'
224 bl_options = {'INTERNAL'}
226 def execute(self, context):
227 np_print('03_add_points_START')
228 pointloc = NP020PD.pointloc
229 if bpy.context.mode not in ('OBJECT'):
230 bpy.ops.object.mode_set(mode='OBJECT')
231 bpy.context.space_data.show_manipulator = False
232 bpy.ops.object.add(type='MESH', location=pointloc)
233 start = bpy.context.object
234 start.name = 'NP_PD_start'
235 NP020PD.start = start
236 bpy.ops.object.add(type='MESH', location=pointloc)
237 end = bpy.context.object
238 end.name = 'NP_PD_end'
239 NP020PD.end = end
240 start.select_set(True)
241 end.select_set(True)
242 bpy.context.tool_settings.use_snap = False
243 bpy.context.tool_settings.snap_element = NP020PD.snap
244 bpy.context.tool_settings.snap_target = 'ACTIVE'
245 bpy.context.space_data.pivot_point = 'MEDIAN_POINT'
246 bpy.context.space_data.transform_orientation = 'GLOBAL'
247 np_print('03_add_points_END')
248 return{'FINISHED'}
251 # Defining the operator that will draw the OpenGL line across the screen
252 # together with the numerical distance and the on-screen instructions in
253 # normal, translation mode:
255 def draw_callback_px_TRANS(self, context):
257 np_print('04_callback_TRANS_START')
259 addon_prefs = context.preferences.addons[__package__].preferences
261 scale = addon_prefs.nppd_scale
262 badge = addon_prefs.nppd_badge
263 step = addon_prefs.nppd_step
264 info = addon_prefs.nppd_info
265 clip = addon_prefs.nppd_clip
266 xyz_lines = addon_prefs.nppd_xyz_lines
267 xyz_distances = addon_prefs.nppd_xyz_distances
268 xyz_backdrop = addon_prefs.nppd_xyz_backdrop
269 stereo_cage = addon_prefs.nppd_stereo_cage
270 gold = addon_prefs.nppd_gold
272 if addon_prefs.nppd_col_line_main_DEF == False:
273 col_line_main = addon_prefs.nppd_col_line_main
274 else:
275 col_line_main = (1.0, 1.0, 1.0, 1.0)
277 if addon_prefs.nppd_col_line_shadow_DEF == False:
278 col_line_shadow = addon_prefs.nppd_col_line_shadow
279 else:
280 col_line_shadow = (0.1, 0.1, 0.1, 0.25)
282 if addon_prefs.nppd_col_num_main_DEF == False:
283 col_num_main = addon_prefs.nppd_col_num_main
284 else:
285 col_num_main = (0.95, 0.95, 0.95, 1.0)
287 if addon_prefs.nppd_col_num_shadow_DEF == False:
288 col_num_shadow = addon_prefs.nppd_col_num_shadow
289 else:
290 col_num_shadow = (0.0, 0.0, 0.0, 0.75)
292 if addon_prefs.nppd_suffix == 'None':
293 suffix = None
295 elif addon_prefs.nppd_suffix == 'km':
296 suffix = ' km'
298 elif addon_prefs.nppd_suffix == 'm':
299 suffix = ' m'
301 elif addon_prefs.nppd_suffix == 'cm':
302 suffix = ' cm'
304 elif addon_prefs.nppd_suffix == 'mm':
305 suffix = ' mm'
307 elif addon_prefs.nppd_suffix == 'nm':
308 suffix = ' nm'
310 elif addon_prefs.nppd_suffix == "'":
311 suffix = "'"
313 elif addon_prefs.nppd_suffix == '"':
314 suffix = '"'
316 elif addon_prefs.nppd_suffix == 'thou':
317 suffix = ' thou'
318 # np_print('0')
319 # sel=bpy.context.selected_objects
320 phase = NP020PD.phase
321 start = NP020PD.start
322 end = NP020PD.end
323 startloc3d = start.location
324 endloc3d = end.location
325 endloc3dx = copy.deepcopy(startloc3d)
326 endloc3dx[0] = endloc3d[0]
327 endloc3dy = copy.deepcopy(startloc3d)
328 endloc3dy[1] = endloc3d[1]
329 endloc3dz = copy.deepcopy(startloc3d)
330 endloc3dz[2] = endloc3d[2]
331 region = context.region
332 rv3d = context.region_data
333 startloc2d = view3d_utils.location_3d_to_region_2d(
334 region, rv3d, startloc3d)
335 endloc2d = view3d_utils.location_3d_to_region_2d(region, rv3d, endloc3d)
336 endloc2dx = view3d_utils.location_3d_to_region_2d(region, rv3d, endloc3dx)
337 endloc2dy = view3d_utils.location_3d_to_region_2d(region, rv3d, endloc3dy)
338 endloc2dz = view3d_utils.location_3d_to_region_2d(region, rv3d, endloc3dz)
339 if startloc2d is None:
340 startloc2d = (0.0, 0.0)
341 endloc2d = (0.0, 0.0)
342 np_print(startloc2d, endloc2d)
343 # np_print('1')
344 dist = (mathutils.Vector(endloc3d) - mathutils.Vector(startloc3d))
345 distgold = dist / 1.6180339887
346 goldloc3d = mathutils.Vector(startloc3d) + distgold
347 goldloc2d = view3d_utils.location_3d_to_region_2d(
348 region, rv3d, goldloc3d)
349 distn = dist.length * scale
350 distn = str(abs(round(distn, 2)))
352 dist = dist.length * scale
354 if suffix is not None:
355 dist = str(abs(round(dist, 2))) + suffix
356 else:
357 dist = str(abs(round(dist, 2)))
358 NP020PD.dist = dist
359 # np_print('2')
360 # This is for correcting the position of the numerical on the screen if
361 # the endpoints are far out of screen:
362 numloc = []
363 run = 'IN'
364 startx = startloc2d[0]
365 starty = startloc2d[1]
366 endx = endloc2d[0]
367 endy = endloc2d[1]
368 if startx > region.width:
369 startx = region.width
370 run = 'OUT'
371 if startx < 0:
372 startx = 0
373 run = 'OUT'
374 if starty > region.height:
375 starty = region.height
376 run = 'OUT'
377 if starty < 0:
378 starty = 0
379 run = 'OUT'
380 if endx > region.width:
381 endx = region.width
382 run = 'OUT'
383 if endx < 0:
384 endx = 0
385 run = 'OUT'
386 if endy > region.height:
387 endy = region.height
388 run = 'OUT'
389 if endy < 0:
390 endy = 0
391 run = 'OUT'
392 numloc.append((startx + endx) / 2)
393 numloc.append((starty + endy) / 2)
394 # np_print('3')
395 if phase == 0:
396 instruct = 'select start point'
398 if phase == 1:
399 instruct = 'select end point'
401 if NP020PD.flag == 'HOLD':
402 instruct = 'inspect result'
404 # Drawing:
406 bgl.glEnable(bgl.GL_BLEND)
409 # ON-SCREEN INSTRUCTIONS:
412 if NP020PD.flag == 'HOLD':
413 keys_aff = 'LMB, ENT, SPACE - continue'
414 keys_nav = ''
415 keys_neg = 'ESC, RMB - quit'
417 else:
418 keys_aff = 'LMB - select, CTRL - snap, ENT - change snap, MMB - lock axis'
419 keys_nav = 'SPACE - change to navigate'
420 keys_neg = 'ESC, RMB - quit'
422 display_instructions(region, rv3d, instruct, keys_aff, keys_nav, keys_neg)
426 # LINES:
427 # cage lines:
428 if phase == 1 and stereo_cage:
430 startloc3dx = copy.deepcopy(endloc3d)
431 startloc3dx[0] = startloc3d[0]
432 startloc3dy = copy.deepcopy(endloc3d)
433 startloc3dy[1] = startloc3d[1]
434 startloc3dz = copy.deepcopy(endloc3d)
435 startloc3dz[2] = startloc3d[2]
436 startloc2dx = view3d_utils.location_3d_to_region_2d(
437 region, rv3d, startloc3dx)
438 startloc2dy = view3d_utils.location_3d_to_region_2d(
439 region, rv3d, startloc3dy)
440 startloc2dz = view3d_utils.location_3d_to_region_2d(
441 region, rv3d, startloc3dz)
443 bgl.glColor4f(0.5, 0.5, 0.5, 0.5)
444 bgl.glLineWidth(1)
445 bgl.glBegin(bgl.GL_LINE_STRIP)
446 bgl.glVertex2f(*endloc2d)
447 bgl.glVertex2f(*startloc2dx)
448 bgl.glEnd()
450 bgl.glBegin(bgl.GL_LINE_STRIP)
451 bgl.glVertex2f(*endloc2d)
452 bgl.glVertex2f(*startloc2dy)
453 bgl.glEnd()
455 bgl.glBegin(bgl.GL_LINE_STRIP)
456 bgl.glVertex2f(*endloc2d)
457 bgl.glVertex2f(*startloc2dz)
458 bgl.glEnd()
460 bgl.glBegin(bgl.GL_LINE_STRIP)
461 bgl.glVertex2f(*endloc2dy)
462 bgl.glVertex2f(*startloc2dx)
463 bgl.glVertex2f(*endloc2dz)
464 bgl.glVertex2f(*startloc2dy)
465 bgl.glVertex2f(*endloc2dx)
466 bgl.glVertex2f(*startloc2dz)
467 bgl.glVertex2f(*endloc2dy)
468 bgl.glEnd()
470 if phase == 1 and xyz_lines == False and stereo_cage == True:
472 bgl.glBegin(bgl.GL_LINE_STRIP)
473 bgl.glVertex2f(*startloc2d)
474 bgl.glVertex2f(*endloc2dx)
475 bgl.glEnd()
477 bgl.glBegin(bgl.GL_LINE_STRIP)
478 bgl.glVertex2f(*startloc2d)
479 bgl.glVertex2f(*endloc2dy)
480 bgl.glEnd()
482 bgl.glBegin(bgl.GL_LINE_STRIP)
483 bgl.glVertex2f(*startloc2d)
484 bgl.glVertex2f(*endloc2dz)
485 bgl.glEnd()
487 # main line:
489 bgl.glColor4f(*col_line_shadow)
490 bgl.glLineWidth(1.4)
491 bgl.glBegin(bgl.GL_LINE_STRIP)
492 bgl.glVertex2f((startloc2d[0] - 1), (startloc2d[1] - 1))
493 bgl.glVertex2f((endloc2d[0] - 1), (endloc2d[1] - 1))
494 bgl.glEnd()
496 bgl.glColor4f(*col_line_main)
497 bgl.glLineWidth(1.4)
498 bgl.glBegin(bgl.GL_LINE_STRIP)
499 bgl.glVertex2f(*startloc2d)
500 bgl.glVertex2f(*endloc2d)
501 bgl.glEnd()
503 # xyz lines:
505 if phase == 1 and xyz_lines:
507 bgl.glColor4f(1.0, 0.0, 0.0, 1.0)
508 bgl.glLineWidth(1.2)
509 bgl.glBegin(bgl.GL_LINE_STRIP)
510 bgl.glVertex2f(*startloc2d)
511 bgl.glVertex2f(*endloc2dx)
512 bgl.glEnd()
514 bgl.glColor4f(0.0, 0.75, 0.0, 1.0)
515 bgl.glLineWidth(1.2)
516 bgl.glBegin(bgl.GL_LINE_STRIP)
517 bgl.glVertex2f(*startloc2d)
518 bgl.glVertex2f(*endloc2dy)
519 bgl.glEnd()
521 bgl.glColor4f(0.0, 0.0, 1.0, 1.0)
522 bgl.glLineWidth(1.2)
523 bgl.glBegin(bgl.GL_LINE_STRIP)
524 bgl.glVertex2f(*startloc2d)
525 bgl.glVertex2f(*endloc2dz)
526 bgl.glEnd()
528 # bgl.glEnable(bgl.GL_BLEND)
530 bgl.glPointSize(2)
531 bgl.glColor4f(1.0, 0.0, 0.0, 1.0)
532 bgl.glBegin(bgl.GL_POINTS)
533 bgl.glVertex2f(*endloc2dx)
534 bgl.glEnd()
536 bgl.glPointSize(2)
537 bgl.glColor4f(0.0, 0.75, 0.0, 1.0)
538 bgl.glBegin(bgl.GL_POINTS)
539 bgl.glVertex2f(*endloc2dy)
540 bgl.glEnd()
542 bgl.glPointSize(2)
543 bgl.glColor4f(0.0, 0.0, 1.0, 1.0)
544 bgl.glBegin(bgl.GL_POINTS)
545 bgl.glVertex2f(*endloc2dz)
546 bgl.glEnd()
547 # np_print('4')
551 # Drawing the small badge near the cursor with the basic instructions:
552 size = 2
553 font_id = 0
554 end = NP020PD.end
555 endloc = end.location
556 mouseloc = view3d_utils.location_3d_to_region_2d(region, rv3d, endloc)
557 if badge and NP020PD.flag != 'HOLD':
558 square = [[17, 30], [17, 40], [27, 40], [27, 30]]
559 rectangle = [[27, 30], [27, 40], [67, 40], [67, 30]]
560 snapsquare = [[17, 30], [67, 30], [67, 20], [17, 20]]
561 arrow = [[20, 33], [18, 35], [20, 37], [18, 35],
562 [26, 35], [24, 33], [26, 35], [24, 37]]
563 dots1 = [[19, 31], [20, 31]]
564 dots2 = [[22, 31], [23, 31]]
565 dots3 = [[25, 31], [26, 31]]
566 ipx = 29
567 ipy = 33
568 for co in square:
569 co[0] = round((co[0] * size), 0) - 20 + mouseloc[0]
570 co[1] = round((co[1] * size), 0) - 50 + mouseloc[1]
571 for co in rectangle:
572 co[0] = round((co[0] * size), 0) - 20 + mouseloc[0]
573 co[1] = round((co[1] * size), 0) - 50 + mouseloc[1]
574 for co in snapsquare:
575 co[0] = round((co[0] * size), 0) - 20 + mouseloc[0]
576 co[1] = round((co[1] * size), 0) - 50 + mouseloc[1]
577 for co in arrow:
578 co[0] = round((co[0] * size), 0) - 20 + mouseloc[0]
579 co[1] = round((co[1] * size), 0) - 50 + mouseloc[1]
580 for co in dots1:
581 co[0] = round((co[0] * size), 0) - 20 + mouseloc[0]
582 co[1] = round((co[1] * size), 0) - 50 + mouseloc[1]
583 for co in dots2:
584 co[0] = round((co[0] * size), 0) - 20 + mouseloc[0]
585 co[1] = round((co[1] * size), 0) - 50 + mouseloc[1]
586 for co in dots3:
587 co[0] = round((co[0] * size), 0) - 20 + mouseloc[0]
588 co[1] = round((co[1] * size), 0) - 50 + mouseloc[1]
589 ipx = round((ipx * size), 0) - 20 + mouseloc[0]
590 ipy = round((ipy * size), 0) - 50 + mouseloc[1]
591 ipsize = 6 * size
592 bgl.glColor4f(0.0, 0.0, 0.0, 0.0)
593 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
594 for x, y in square:
595 bgl.glVertex2f(x, y)
596 bgl.glEnd()
597 bgl.glColor4f(1.0, 0.5, 0.0, 1.0)
598 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
599 for x, y in rectangle:
600 bgl.glVertex2f(x, y)
601 bgl.glEnd()
602 bgl.glColor4f(0.4, 0.15, 0.75, 1.0)
603 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
604 for x, y in snapsquare:
605 bgl.glVertex2f(x, y)
606 bgl.glEnd()
607 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
608 bgl.glBegin(bgl.GL_LINE_STRIP)
609 for x, y in arrow:
610 bgl.glVertex2f(x, y)
611 bgl.glEnd()
612 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
613 blf.position(font_id, ipx - (10 * size), ipy - (10 * size), 0)
614 blf.size(font_id, ipsize, 72)
615 blf.draw(font_id, NP020PD.snap)
616 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
617 blf.position(font_id, ipx, ipy, 0)
618 blf.size(font_id, ipsize, 72)
619 blf.draw(font_id, 'CTRL+SNAP')
620 if step == 'continuous':
621 bgl.glBegin(bgl.GL_LINE_STRIP)
622 for x, y in dots1:
623 bgl.glVertex2f(x, y)
624 bgl.glEnd()
625 bgl.glBegin(bgl.GL_LINE_STRIP)
626 for x, y in dots2:
627 bgl.glVertex2f(x, y)
628 bgl.glEnd()
629 bgl.glBegin(bgl.GL_LINE_STRIP)
630 for x, y in dots3:
631 bgl.glVertex2f(x, y)
632 bgl.glEnd()
634 if gold and phase != 0:
636 goldtriangle = [[0, 0], [-1, 1], [1, 1]]
637 for co in goldtriangle:
638 co[0] = round((co[0] * 10), 0) + goldloc2d[0]
639 co[1] = round((co[1] * 10), 0) + goldloc2d[1]
640 bgl.glColor4f(1.0, 0.5, 0.0, 1.0)
641 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
642 for x, y in goldtriangle:
643 bgl.glVertex2f(x, y)
644 bgl.glEnd()
646 goldvec1 = mathutils.Vector((1.0 , 0.0))
647 goldvec2 = endloc2d - startloc2d
648 ang = goldvec1.angle_signed(goldvec2, None)
649 if ang is not None:
650 coy = round(cos(ang), 8)
651 cox = round(sin(ang), 8)
652 goldtick = [[-cox, -coy], [0, 0], [cox, coy]]
653 for co in goldtick:
654 co[0] = round((co[0] * 10), 0) + goldloc2d[0]
655 co[1] = round((co[1] * 10), 0) + goldloc2d[1]
656 bgl.glColor4f(0.95, 0.55, 0.0, 1.0)
657 bgl.glLineWidth(2)
658 bgl.glBegin(bgl.GL_LINE_STRIP)
659 for x, y in goldtick:
660 bgl.glVertex2f(x, y)
661 bgl.glEnd()
662 bgl.glLineWidth(1)
663 if xyz_distances:
664 distgold_first = (goldloc3d - startloc3d).length * scale
665 distgold_first = str(abs(round(distgold_first, 2)))
666 distgold_sec = (endloc3d - goldloc3d).length * scale
667 distgold_sec = str(abs(round(distgold_sec, 2)))
668 goldloc_first = [((startloc2d[0] + goldloc2d[0]) / 2), ((startloc2d[1] + goldloc2d[1]) / 2)]
669 goldloc_sec = [((goldloc2d[0] + endloc2d[0]) / 2), ((goldloc2d[1] + endloc2d[1]) / 2)]
670 if xyz_backdrop:
671 bgl.glColor4f(1.0, 0.5, 0.0, 1.0)
672 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
673 bgl.glVertex2f(goldloc_first[0]-2, goldloc_first[1]-2)
674 bgl.glVertex2f(goldloc_first[0]-2, goldloc_first[1]+10)
675 bgl.glVertex2f(goldloc_first[0]+50, goldloc_first[1]+10)
676 bgl.glVertex2f(goldloc_first[0]+50, goldloc_first[1]-2)
677 bgl.glEnd()
678 bgl.glColor4f(0.95, 0.55, 0.0, 1.0)
679 if xyz_backdrop:
680 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
681 blf.position(font_id, goldloc_first[0], goldloc_first[1], 0)
682 blf.draw(font_id, distgold_first)
683 if xyz_backdrop:
684 bgl.glColor4f(1.0, 0.5, 0.0, 1.0)
685 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
686 bgl.glVertex2f(goldloc_sec[0]-2, goldloc_sec[1]-2)
687 bgl.glVertex2f(goldloc_sec[0]-2, goldloc_sec[1]+10)
688 bgl.glVertex2f(goldloc_sec[0]+50, goldloc_sec[1]+10)
689 bgl.glVertex2f(goldloc_sec[0]+50, goldloc_sec[1]-2)
690 bgl.glEnd()
691 bgl.glColor4f(0.95, 0.55, 0.0, 1.0)
692 if xyz_backdrop:
693 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
694 blf.position(font_id, goldloc_sec[0], goldloc_sec[1], 0)
695 blf.draw(font_id, distgold_sec)
698 # NUMERICAL DISTANCE:
700 square = [[17, 30], [17, 40], [27, 40], [27, 30]]
701 for co in square:
702 co[0] = round((co[0] * size), 0) - 20 + mouseloc[0]
703 co[1] = round((co[1] * size), 0) - 50 + mouseloc[1]
704 badgeloc = [(square[0][0] + 6), (square[0][1] - 8)]
705 numlocdistx = abs(badgeloc[0] - numloc[0])
706 numlocdisty = abs(badgeloc[1] - numloc[1])
707 if numlocdistx < 96 and numlocdisty < 30:
708 numloc[0] = square[0][0]
709 numloc[1] = square[0][1] - 40
711 if startloc3d[0] == endloc3d[0] and startloc3d[1] == endloc3d[1]:
712 col_num_main = (0.0, 0.0, 0.80, 0.75)
713 elif startloc3d[1] == endloc3d[1] and startloc3d[2] == endloc3d[2]:
714 col_num_main = (0.85, 0.0, 0.0, 0.75)
715 elif startloc3d[0] == endloc3d[0] and startloc3d[2] == endloc3d[2]:
716 col_num_main = (0.0, 0.60, 0.0, 0.75)
718 bgl.glColor4f(*col_num_shadow)
719 if phase == 1:
720 font_id = 0
721 blf.size(font_id, 20, 72)
722 blf.position(font_id, (numloc[0] - 1), (numloc[1] - 1), 0)
723 blf.draw(font_id, dist)
725 bgl.glColor4f(*col_num_main)
726 if phase == 1:
727 font_id = 0
728 blf.size(font_id, 20, 72)
729 blf.position(font_id, numloc[0], numloc[1], 0)
730 blf.draw(font_id, dist)
732 # xyz numericals:
734 if phase == 1 and xyz_distances:
736 badgeloc[0] = badgeloc[0] + 27
737 badgeloc[1] = badgeloc[1] - 15
738 font_id = 0
739 blf.size(font_id, 12, 72)
740 slide = 0
741 numloccen = [0, 0]
742 numloccen[0] = numloc[0] + 15
743 numloccen[1] = numloc[1] + 2
745 distx = (endloc3dx[0] - startloc3d[0]) * scale
746 distx = str(abs(round(distx, 2)))
747 if distx not in (dist, distn):
748 numlocx = [(
749 (startloc2d[0] + endloc2dx[0]) / 2),
750 ((startloc2d[1] + endloc2dx[1]) / 2)]
751 numlocbadge = (
752 mathutils.Vector(
753 numlocx) - mathutils.Vector(
754 badgeloc)).length
755 numlocdistx = abs(numlocx[0] - numloccen[0])
756 numlocdisty = abs(numlocx[1] - numloccen[1])
757 if numlocdistx < 60 and numlocdisty < 16:
758 numlocx[0] = numloc[0]
759 numlocx[1] = numloc[1] - 15
760 slide = slide + 1
761 elif run == 'OUT' or numlocbadge < 65:
762 numlocx[0] = numloc[0]
763 numlocx[1] = numloc[1] - 15
764 slide = slide + 1
765 # bgl.glColor4f(*col_num_shadow)
766 # blf.position(font_id, numlocx[0]-1, numlocx[1]-1, 0)
767 # blf.draw(font_id, distx)
768 if xyz_backdrop:
769 bgl.glColor4f(1.0, 0.0, 0.0, 1.0)
770 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
771 bgl.glVertex2f(numlocx[0]-2, numlocx[1]-2)
772 bgl.glVertex2f(numlocx[0]-2, numlocx[1]+10)
773 bgl.glVertex2f(numlocx[0]+50, numlocx[1]+10)
774 bgl.glVertex2f(numlocx[0]+50, numlocx[1]-2)
775 bgl.glEnd()
776 bgl.glColor4f(0.85, 0.0, 0.0, 1.0)
777 if xyz_backdrop:
778 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
779 blf.position(font_id, numlocx[0], numlocx[1], 0)
780 blf.draw(font_id, distx)
782 disty = (endloc3dy[1] - startloc3d[1]) * scale
783 disty = str(abs(round(disty, 2)))
784 if disty not in (dist, distn):
785 numlocy = [(
786 (startloc2d[0] + endloc2dy[0]) / 2),
787 ((startloc2d[1] + endloc2dy[1]) / 2)]
788 numlocbadge = (
789 mathutils.Vector(
790 numlocy) - mathutils.Vector(
791 badgeloc)).length
792 numlocdistx = abs(numlocy[0] - numloccen[0])
793 numlocdisty = abs(numlocy[1] - numloccen[1])
794 if numlocdistx < 60 and numlocdisty < 16:
795 numlocy[0] = numloc[0]
796 numlocy[1] = numloc[1] - ((slide * 12) + 15)
797 slide = slide + 1
798 elif run == 'OUT' or numlocbadge < 65:
799 numlocy[0] = numloc[0]
800 numlocy[1] = numloc[1] - ((slide * 12) + 15)
801 slide = slide + 1
802 if xyz_backdrop:
803 bgl.glColor4f(0.0 ,0.65 ,0.0 ,1.0)
804 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
805 bgl.glVertex2f(numlocy[0]-2, numlocy[1]-2)
806 bgl.glVertex2f(numlocy[0]-2, numlocy[1]+10)
807 bgl.glVertex2f(numlocy[0]+50, numlocy[1]+10)
808 bgl.glVertex2f(numlocy[0]+50, numlocy[1]-2)
809 bgl.glEnd()
810 bgl.glColor4f(0.0 ,0.75 ,0.0 ,1.0)
811 if xyz_backdrop:
812 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
813 blf.position(font_id, numlocy[0], numlocy[1], 0)
814 blf.draw(font_id, disty)
816 distz = (endloc3dz[2] - startloc3d[2]) * scale
817 distz = str(abs(round(distz, 2)))
818 if distz not in (dist, distn):
819 numlocz = [(
820 (startloc2d[0] + endloc2dz[0]) / 2),
821 ((startloc2d[1] + endloc2dz[1]) / 2)]
822 numlocbadge = (
823 mathutils.Vector(
824 numlocz) - mathutils.Vector(
825 badgeloc)).length
826 numlocdistx = abs(numlocz[0] - numloccen[0])
827 numlocdisty = abs(numlocz[1] - numloccen[1])
828 if numlocdistx < 60 and numlocdisty < 16:
829 numlocz[0] = numloc[0]
830 numlocz[1] = numloc[1] - ((slide * 12) + 15)
831 elif run == 'OUT' or numlocbadge < 65:
832 numlocz[0] = numloc[0]
833 numlocz[1] = numloc[1] - ((slide * 12) + 15)
834 if xyz_backdrop:
835 bgl.glColor4f(0.0, 0.0, 0.85, 1.0)
836 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
837 bgl.glVertex2f(numlocz[0]-2, numlocz[1]-2)
838 bgl.glVertex2f(numlocz[0]-2, numlocz[1]+10)
839 bgl.glVertex2f(numlocz[0]+50, numlocz[1]+10)
840 bgl.glVertex2f(numlocz[0]+50, numlocz[1]-2)
841 bgl.glEnd()
842 bgl.glColor4f(0.0, 0.0, 1.0, 1.0)
843 if xyz_backdrop:
844 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
845 blf.position(font_id, numlocz[0], numlocz[1], 0)
846 blf.draw(font_id, distz)
848 bgl.glLineWidth(1)
849 bgl.glDisable(bgl.GL_BLEND)
850 bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
852 # np_print('5')
854 # ENDING
856 bgl.glLineWidth(1)
857 bgl.glDisable(bgl.GL_BLEND)
858 bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
859 # np_print('7')
860 np_print('04_callback_TRANS_END')
863 def scene_update(context):
864 # np_print('00_scene_update_START')
865 # np_print('up1')
866 if bpy.data.objects.is_updated:
867 phase = NP020PD.phase
868 np_print('up')
869 start = NP020PD.start
870 end = NP020PD.end
871 if phase == 1:
872 # np_print('up2')
873 startloc3d = start.location
874 endloc3d = end.location
875 NP020PD.startloc3d = startloc3d
876 NP020PD.endloc3d = endloc3d
877 # np_print('up3')
878 # np_print('00_scene_update_END')
881 # Defining the operator that will let the user translate start and end to
882 # the desired point. It also uses some listening operators that clean up
883 # the leftovers should the user interrupt the command. Many thanks to
884 # CoDEmanX and lukas_t:
887 class NP020PDRunTranslate(bpy.types.Operator):
888 bl_idname = 'object.np_pd_run_translate'
889 bl_label = 'NP PD Run Translate'
890 bl_options = {'INTERNAL'}
892 np_print('04_run_TRANS_START')
893 count = 0
895 def modal(self, context, event):
896 context.area.tag_redraw()
897 self.count += 1
898 selob = NP020PD.selob
899 start = NP020PD.start
900 end = NP020PD.end
901 phase = NP020PD.phase
903 if self.count == 1:
904 bpy.ops.transform.translate('INVOKE_DEFAULT')
905 np_print('04_run_TRANS_count_1_INVOKE_DEFAULT')
907 elif event.type in ('LEFTMOUSE', 'NUMPAD_ENTER') and event.value == 'RELEASE':
908 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
909 NP020PD.flag = 'PASS'
910 np_print('04_run_TRANS_left_release_FINISHED')
911 return{'FINISHED'}
913 elif event.type == 'RET' and event.value == 'RELEASE':
914 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
915 if bpy.context.tool_settings.snap_element == 'VERTEX':
916 bpy.context.tool_settings.snap_element = 'EDGE'
917 NP020PD.snap = 'EDGE'
918 elif bpy.context.tool_settings.snap_element == 'EDGE':
919 bpy.context.tool_settings.snap_element = 'FACE'
920 NP020PD.snap = 'FACE'
921 elif bpy.context.tool_settings.snap_element == 'FACE':
922 bpy.context.tool_settings.snap_element = 'VERTEX'
923 NP020PD.snap = 'VERTEX'
924 NP020PD.flag = 'TRANSLATE'
925 np_print('04_run_TRANS_enter_PASS')
926 return{'FINISHED'}
928 elif event.type == 'SPACE':
929 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
930 start.hide = True
931 end.hide = True
932 NP020PD.flag = 'NAVIGATE'
933 np_print('04_run_TRANS_space_FINISHED_flag_NAVIGATE')
934 return{'FINISHED'}
936 elif event.type in ('ESC', 'RIGHTMOUSE'):
937 # this actually begins when user RELEASES esc or rightmouse, PRESS is
938 # taken by transform.translate operator
939 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
940 bpy.ops.object.select_all(action='DESELECT')
941 start.select_set(True)
942 end.select_set(True)
943 bpy.ops.object.delete('EXEC_DEFAULT')
944 for ob in selob:
945 ob.select_set(True)
946 NP020PD.startloc3d = (0.0, 0.0, 0.0)
947 NP020PD.endloc3d = (0.0, 0.0, 0.0)
948 NP020PD.phase = 0
949 NP020PD.start = None
950 NP020PD.end = None
951 NP020PD.dist = None
952 NP020PD.flag = 'TRANSLATE'
953 NP020PD.snap = 'VERTEX'
954 bpy.context.tool_settings.use_snap = NP020PD.use_snap
955 bpy.context.tool_settings.snap_element = NP020PD.snap_element
956 bpy.context.tool_settings.snap_target = NP020PD.snap_target
957 bpy.context.space_data.pivot_point = NP020PD.pivot_point
958 bpy.context.space_data.transform_orientation = NP020PD.trans_orient
959 bpy.context.space_data.show_manipulator = NP020PD.show_manipulator
960 if NP020PD.acob is not None:
961 bpy.context.view_layer.objects.active = NP020PD.acob
962 bpy.ops.object.mode_set(mode=NP020PD.edit_mode)
963 np_print('04_run_TRANS_esc_right_CANCELLED')
964 return{'CANCELLED'}
966 np_print('04_run_TRANS_PASS_THROUGH')
967 return{'PASS_THROUGH'}
969 def invoke(self, context, event):
970 flag = NP020PD.flag
971 np_print('04_run_TRANS_INVOKE_a')
972 np_print('flag=', flag)
973 if flag == 'TRANSLATE':
974 if context.area.type == 'VIEW_3D':
975 args = (self, context)
976 self._handle = bpy.types.SpaceView3D.draw_handler_add(
977 draw_callback_px_TRANS, args, 'WINDOW', 'POST_PIXEL')
978 context.window_manager.modal_handler_add(self)
979 np_print('04_run_TRANS_INVOKE_a_RUNNING_MODAL')
980 return {'RUNNING_MODAL'}
981 else:
982 self.report(
983 {'WARNING'},
984 "View3D not found, cannot run operator")
985 np_print('04_run_TRANS_INVOKE_a_CANCELLED')
986 return {'CANCELLED'}
987 else:
988 np_print('04_run_TRANS_INVOKE_a_FINISHED')
989 return {'FINISHED'}
992 # Defining the operator that will draw the graphicall reprezentation of
993 # distance in navigation mode if user calls it:
996 def draw_callback_px_NAV(self, context):
998 np_print('05_callback_NAV_START')
1000 addon_prefs = context.preferences.addons[__package__].preferences
1002 scale = addon_prefs.nppd_scale
1003 badge = addon_prefs.nppd_badge
1004 step = addon_prefs.nppd_step
1005 info = addon_prefs.nppd_info
1006 clip = addon_prefs.nppd_clip
1008 if addon_prefs.nppd_col_line_main_DEF == False:
1009 col_line_main = addon_prefs.nppd_col_line_main
1010 else:
1011 col_line_main = (1.0, 1.0, 1.0, 1.0)
1013 if addon_prefs.nppd_col_line_shadow_DEF == False:
1014 col_line_shadow = addon_prefs.nppd_col_line_shadow
1015 else:
1016 col_line_shadow = (0.1, 0.1, 0.1, 0.25)
1018 if addon_prefs.nppd_col_num_main_DEF == False:
1019 col_num_main = addon_prefs.nppd_col_num_main
1020 else:
1021 col_num_main = (0.95, 0.95, 0.95, 1.0)
1023 if addon_prefs.nppd_col_num_shadow_DEF == False:
1024 col_num_shadow = addon_prefs.nppd_col_num_shadow
1025 else:
1026 col_num_shadow = (0.0, 0.0, 0.0, 0.75)
1028 if addon_prefs.nppd_suffix == 'None':
1029 suffix = None
1031 elif addon_prefs.nppd_suffix == 'km':
1032 suffix = ' km'
1034 elif addon_prefs.nppd_suffix == 'm':
1035 suffix = ' m'
1037 elif addon_prefs.nppd_suffix == 'cm':
1038 suffix = ' cm'
1040 elif addon_prefs.nppd_suffix == 'mm':
1041 suffix = ' mm'
1043 elif addon_prefs.nppd_suffix == 'nm':
1044 suffix = ' nm'
1046 elif addon_prefs.nppd_suffix == "'":
1047 suffix = "'"
1049 elif addon_prefs.nppd_suffix == '"':
1050 suffix = '"'
1052 elif addon_prefs.nppd_suffix == 'thou':
1053 suffix = ' thou'
1055 # Calculating the 3d points for the graphical line while in NAVIGATE flag:
1056 phase = NP020PD.phase
1057 region = context.region
1058 rv3d = context.region_data
1059 co2d = self.co2d
1060 view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, co2d)
1061 pointloc = view3d_utils.region_2d_to_origin_3d(
1062 region, rv3d, co2d) + view_vector / 5
1064 np_print('phase=', phase)
1065 if phase == 0:
1066 startloc3d = (0.0, 0.0, 0.0)
1067 endloc3d = (0.0, 0.0, 0.0)
1069 if phase == 1:
1070 startloc3d = NP020PD.startloc3d
1071 endloc3d = pointloc
1073 # Calculating the 2D points for the graphical line while in NAVIGATE flag
1074 # from 3D points:
1076 startloc2d = view3d_utils.location_3d_to_region_2d(
1077 region, rv3d, startloc3d)
1078 endloc2d = view3d_utils.location_3d_to_region_2d(region, rv3d, endloc3d)
1080 if startloc2d is None:
1081 startloc2d = (0.0, 0.0)
1082 endloc2d = (0.0, 0.0)
1083 np_print(startloc2d, endloc2d)
1085 dist = (mathutils.Vector(endloc3d) - mathutils.Vector(startloc3d))
1086 dist = dist.length * scale
1087 if suffix is not None:
1088 dist = str(abs(round(dist, 2))) + suffix
1089 else:
1090 dist = str(abs(round(dist, 2)))
1092 NP020PD.dist = dist
1094 # This is for correcting the position of the numerical on the screen if
1095 # the endpoints are far out of screen:
1096 numloc = []
1097 startx = startloc2d[0]
1098 starty = startloc2d[1]
1099 endx = endloc2d[0]
1100 endy = endloc2d[1]
1101 if startx > region.width:
1102 startx = region.width
1103 if startx < 0:
1104 startx = 0
1105 if starty > region.height:
1106 starty = region.height
1107 if starty < 0:
1108 starty = 0
1109 if endx > region.width:
1110 endx = region.width
1111 if endx < 0:
1112 endx = 0
1113 if endy > region.height:
1114 endy = region.height
1115 if endy < 0:
1116 endy = 0
1117 numloc.append((startx + endx) / 2)
1118 numloc.append((starty + endy) / 2)
1120 if phase == 0:
1121 instruct = 'navigate for better placement of start point'
1123 if phase == 1:
1124 instruct = 'navigate for better placement of end point'
1126 # Drawing:
1128 bgl.glEnable(bgl.GL_BLEND)
1130 # ON-SCREEN INSTRUCTIONS:
1132 keys_aff = 'MMB, SCROLL - navigate'
1133 keys_nav = 'LMB, SPACE - leave navigate'
1134 keys_neg = 'ESC, RMB - quit'
1136 display_instructions(region, rv3d, instruct, keys_aff, keys_nav, keys_neg)
1139 # LINE:
1141 bgl.glColor4f(*col_line_shadow)
1142 bgl.glLineWidth(1.4)
1143 bgl.glBegin(bgl.GL_LINE_STRIP)
1144 bgl.glVertex2f((startloc2d[0] - 1), (startloc2d[1] - 1))
1145 bgl.glVertex2f((endloc2d[0] - 1), (endloc2d[1] - 1))
1146 bgl.glEnd()
1148 bgl.glColor4f(*col_line_main)
1149 bgl.glLineWidth(1.4)
1150 bgl.glBegin(bgl.GL_LINE_STRIP)
1151 bgl.glVertex2f(*startloc2d)
1152 bgl.glVertex2f(*endloc2d)
1153 bgl.glEnd()
1157 # Drawing the small badge near the cursor with the basic instructions:
1159 if badge:
1160 font_id = 0
1161 square = [[17, 30], [17, 40], [27, 40], [27, 30]]
1162 rectangle = [[27, 30], [27, 40], [67, 40], [67, 30]]
1163 arrow = [[20, 33], [18, 35], [20, 37], [18, 35],
1164 [26, 35], [24, 33], [26, 35], [24, 37]]
1165 dots1 = [[19, 31], [20, 31]]
1166 dots2 = [[22, 31], [23, 31]]
1167 dots3 = [[25, 31], [26, 31]]
1168 ipx = 29
1169 ipy = 33
1170 size = 2
1171 for co in square:
1172 co[0] = round((co[0] * size), 0) - 20 + co2d[0]
1173 co[1] = round((co[1] * size), 0) - 50 + co2d[1]
1174 for co in rectangle:
1175 co[0] = round((co[0] * size), 0) - 20 + co2d[0]
1176 co[1] = round((co[1] * size), 0) - 50 + co2d[1]
1177 for co in arrow:
1178 co[0] = round((co[0] * size), 0) - 20 + co2d[0]
1179 co[1] = round((co[1] * size), 0) - 50 + co2d[1]
1180 for co in dots1:
1181 co[0] = round((co[0] * size), 0) - 20 + co2d[0]
1182 co[1] = round((co[1] * size), 0) - 50 + co2d[1]
1183 for co in dots2:
1184 co[0] = round((co[0] * size), 0) - 20 + co2d[0]
1185 co[1] = round((co[1] * size), 0) - 50 + co2d[1]
1186 for co in dots3:
1187 co[0] = round((co[0] * size), 0) - 20 + co2d[0]
1188 co[1] = round((co[1] * size), 0) - 50 + co2d[1]
1189 ipx = round((ipx * size), 0) - 20 + co2d[0]
1190 ipy = round((ipy * size), 0) - 50 + co2d[1]
1191 ipsize = 6 * size
1192 bgl.glColor4f(0.0, 0.0, 0.0, 0.0)
1193 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
1194 for x, y in square:
1195 bgl.glVertex2f(x, y)
1196 bgl.glEnd()
1197 bgl.glColor4f(0.5, 0.75, 0.0, 1.0)
1198 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
1199 for x, y in rectangle:
1200 bgl.glVertex2f(x, y)
1201 bgl.glEnd()
1202 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
1203 bgl.glBegin(bgl.GL_LINE_STRIP)
1204 for x, y in arrow:
1205 bgl.glVertex2f(x, y)
1206 bgl.glEnd()
1207 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
1208 blf.position(font_id, ipx, ipy, 0)
1209 blf.size(font_id, ipsize, 72)
1210 blf.draw(font_id, 'NAVIGATE')
1211 if step == 'continuous':
1212 bgl.glBegin(bgl.GL_LINE_STRIP)
1213 for x, y in dots1:
1214 bgl.glVertex2f(x, y)
1215 bgl.glEnd()
1216 bgl.glBegin(bgl.GL_LINE_STRIP)
1217 for x, y in dots2:
1218 bgl.glVertex2f(x, y)
1219 bgl.glEnd()
1220 bgl.glBegin(bgl.GL_LINE_STRIP)
1221 for x, y in dots3:
1222 bgl.glVertex2f(x, y)
1223 bgl.glEnd()
1225 # NUMERICAL DISTANCE:
1227 bgl.glColor4f(*col_num_shadow)
1228 if phase == 1:
1229 font_id = 0
1230 blf.size(font_id, 20, 72)
1231 blf.position(font_id, (numloc[0] - 1), (numloc[1] - 1), 0)
1232 blf.draw(font_id, dist)
1234 bgl.glColor4f(*col_num_main)
1235 if phase == 1:
1236 font_id = 0
1237 blf.size(font_id, 20, 72)
1238 blf.position(font_id, numloc[0], numloc[1], 0)
1239 blf.draw(font_id, dist)
1241 bgl.glLineWidth(1)
1242 bgl.glDisable(bgl.GL_BLEND)
1243 bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
1245 # ENDING
1246 bgl.glLineWidth(1)
1247 bgl.glDisable(bgl.GL_BLEND)
1248 bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
1249 np_print('05_callback_NAV_END')
1252 # Defining the operator that will enable navigation if user calls it:
1254 class NP020PDRunNavigate(bpy.types.Operator):
1255 bl_idname = "object.np_pd_run_navigate"
1256 bl_label = "NP PD Run Navigate"
1257 bl_options = {'INTERNAL'}
1259 def modal(self, context, event):
1260 np_print('05_run_NAV_START')
1261 context.area.tag_redraw()
1262 selob = NP020PD.selob
1263 start = NP020PD.start
1264 end = NP020PD.end
1265 phase = NP020PD.phase
1267 if event.type == 'MOUSEMOVE':
1268 np_print('05_run_NAV_mousemove_a')
1269 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
1270 np_print('05_run_NAV_mousemove_b')
1272 elif event.type in {'LEFTMOUSE', 'SPACE'} and event.value == 'PRESS':
1273 np_print('05_run_NAV_left_space_press_START')
1274 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
1275 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
1276 phase = NP020PD.phase
1277 region = context.region
1278 rv3d = context.region_data
1279 co2d = self.co2d
1280 view_vector = view3d_utils.region_2d_to_vector_3d(
1281 region, rv3d, co2d)
1282 pointloc = view3d_utils.region_2d_to_origin_3d(
1283 region, rv3d, co2d) + view_vector / 5
1284 start = NP020PD.start
1285 end = NP020PD.end
1286 start.hide = False
1287 end.hide = False
1288 np_print('phase=', phase)
1289 if phase == 0:
1290 startloc3d = (0.0, 0.0, 0.0)
1291 endloc3d = (0.0, 0.0, 0.0)
1292 start.location = pointloc
1293 end.location = pointloc
1294 if phase == 1:
1295 startloc3d = NP020PD.startloc3d
1296 endloc3d = pointloc
1297 end.location = pointloc
1298 NP020PD.start = start
1299 NP020PD.end = end
1300 NP020PD.startloc3d = startloc3d
1301 NP020PD.endloc3d = endloc3d
1302 NP020PD.flag = 'TRANSLATE'
1303 np_print('05_run_NAV_left_space_press_FINISHED_flag_TRANSLATE')
1304 return {'FINISHED'}
1306 elif event.type in ('ESC', 'RIGHTMOUSE'):
1307 np_print('05_run_NAV_esc_right_any_START')
1308 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
1309 bpy.ops.object.select_all(action='DESELECT')
1310 start.hide = False
1311 end.hide = False
1312 start.select_set(True)
1313 end.select_set(True)
1314 bpy.ops.object.delete('EXEC_DEFAULT')
1315 for ob in selob:
1316 ob.select_set(True)
1317 NP020PD.startloc3d = (0.0, 0.0, 0.0)
1318 NP020PD.endloc3d = (0.0, 0.0, 0.0)
1319 NP020PD.phase = 0
1320 NP020PD.start = None
1321 NP020PD.end = None
1322 NP020PD.dist = None
1323 NP020PD.flag = 'TRANSLATE'
1324 NP020PD.snap = 'VERTEX'
1325 bpy.context.tool_settings.use_snap = NP020PD.use_snap
1326 bpy.context.tool_settings.snap_element = NP020PD.snap_element
1327 bpy.context.tool_settings.snap_target = NP020PD.snap_target
1328 bpy.context.space_data.pivot_point = NP020PD.pivot_point
1329 bpy.context.space_data.transform_orientation = NP020PD.trans_orient
1330 bpy.context.space_data.show_manipulator = NP020PD.show_manipulator
1331 if NP020PD.acob is not None:
1332 bpy.context.view_layer.objects.active = NP020PD.acob
1333 bpy.ops.object.mode_set(mode=NP020PD.edit_mode)
1334 np_print('05_run_NAV_esc_right_any_CANCELLED')
1335 return{'CANCELLED'}
1337 elif event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
1338 np_print('05_run_NAV_middle_wheel_any_PASS_THROUGH')
1339 return {'PASS_THROUGH'}
1340 np_print('05_run_NAV_RUNNING_MODAL')
1341 return {'RUNNING_MODAL'}
1343 def invoke(self, context, event):
1344 np_print('05_run_NAV_INVOKE_a')
1345 flag = NP020PD.flag
1346 np_print('flag=', flag)
1347 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
1348 if flag == 'NAVIGATE':
1349 args = (self, context)
1350 self._handle = bpy.types.SpaceView3D.draw_handler_add(
1351 draw_callback_px_NAV, args, 'WINDOW', 'POST_PIXEL')
1352 context.window_manager.modal_handler_add(self)
1353 np_print('05_run_NAV_INVOKE_a_RUNNING_MODAL')
1354 return {'RUNNING_MODAL'}
1355 else:
1356 np_print('05_run_NAV_INVOKE_a_FINISHED')
1357 return {'FINISHED'}
1360 # Changing the mode of operating and leaving start point at the first
1361 # snap, continuing with just the end point:
1363 class NP020PDChangePhase(bpy.types.Operator):
1364 bl_idname = "object.np_pd_change_phase"
1365 bl_label = "NP PD Change Phase"
1366 bl_options = {'INTERNAL'}
1368 def execute(self, context):
1369 np_print('06_change_phase_START')
1370 NP020PD.phase = 1
1371 np_print('NP020PD.phase=', NP020PD.phase)
1372 start = NP020PD.start
1373 end = NP020PD.end
1374 startloc3d = start.location
1375 endloc3d = end.location
1376 NP020PD.startloc3d = startloc3d
1377 NP020PD.endloc3d = endloc3d
1378 bpy.ops.object.select_all(action='DESELECT')
1379 end.select_set(True)
1380 bpy.context.tool_settings.use_snap = False
1381 bpy.context.tool_settings.snap_element = NP020PD.snap
1382 bpy.context.tool_settings.snap_target = 'ACTIVE'
1383 bpy.context.space_data.pivot_point = 'ACTIVE_ELEMENT'
1384 bpy.context.space_data.transform_orientation = 'GLOBAL'
1385 NP020PD.flag = 'TRANSLATE'
1386 np_print('06_change_phase_END_flag_TRANSLATE')
1387 return {'FINISHED'}
1390 # Defining the operator that will hold the end result in the viewport and let the user navigate around it for documentation
1392 class NP020PDHoldResult(bpy.types.Operator):
1393 bl_idname = 'object.np_pd_hold_result'
1394 bl_label = 'NP PD Hold Result'
1395 bl_options = {'INTERNAL'}
1397 np_print('07_HOLD_START')
1399 def modal(self, context, event):
1400 context.area.tag_redraw()
1401 selob = NP020PD.selob
1402 start = NP020PD.start
1403 end = NP020PD.end
1405 if event.type == 'MOUSEMOVE':
1406 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
1408 elif event.type in ('LEFTMOUSE', 'RET', 'NUMPAD_ENTER', 'SPACE') and event.value == 'PRESS':
1409 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
1410 NP020PD.flag = 'PASS'
1411 np_print('07_HOLD_left_release_FINISHED')
1412 return{'FINISHED'}
1414 elif event.type in ('ESC', 'RIGHTMOUSE'):
1415 # this actually begins when user RELEASES esc or rightmouse, PRESS is
1416 # taken by transform.translate operator
1417 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
1418 bpy.ops.object.select_all(action='DESELECT')
1419 start.select_set(True)
1420 end.select_set(True)
1421 bpy.ops.object.delete('EXEC_DEFAULT')
1422 for ob in selob:
1423 ob.select_set(True)
1424 NP020PD.startloc3d = (0.0, 0.0, 0.0)
1425 NP020PD.endloc3d = (0.0, 0.0, 0.0)
1426 NP020PD.phase = 0
1427 NP020PD.start = None
1428 NP020PD.end = None
1429 NP020PD.dist = None
1430 NP020PD.flag = 'TRANSLATE'
1431 NP020PD.snap = 'VERTEX'
1432 bpy.context.tool_settings.use_snap = NP020PD.use_snap
1433 bpy.context.tool_settings.snap_element = NP020PD.snap_element
1434 bpy.context.tool_settings.snap_target = NP020PD.snap_target
1435 bpy.context.space_data.pivot_point = NP020PD.pivot_point
1436 bpy.context.space_data.transform_orientation = NP020PD.trans_orient
1437 bpy.context.space_data.show_manipulator = NP020PD.show_manipulator
1438 if NP020PD.acob is not None:
1439 bpy.context.view_layer.objects.active = NP020PD.acob
1440 bpy.ops.object.mode_set(mode=NP020PD.edit_mode)
1441 np_print('07_HOLD_esc_right_CANCELLED')
1442 return{'CANCELLED'}
1444 np_print('07_HOLD_PASS_THROUGH')
1445 return{'PASS_THROUGH'}
1447 def invoke(self, context, event):
1448 flag = NP020PD.flag
1449 np_print('flag=', flag)
1450 np_print('07_HOLD_INVOKE_a')
1451 addon_prefs = context.preferences.addons[__package__].preferences
1452 hold = addon_prefs.nppd_hold
1453 if hold:
1454 if context.area.type == 'VIEW_3D':
1455 args = (self, context)
1456 self._handle = bpy.types.SpaceView3D.draw_handler_add(
1457 draw_callback_px_TRANS, args, 'WINDOW', 'POST_PIXEL')
1458 context.window_manager.modal_handler_add(self)
1459 NP020PD.flag = 'HOLD'
1460 np_print('07_HOLD_INVOKE_a_RUNNING_MODAL')
1461 return {'RUNNING_MODAL'}
1462 else:
1463 self.report(
1464 {'WARNING'},
1465 "View3D not found, cannot run operator")
1466 np_print('07_HOLD_INVOKE_a_CANCELLED')
1467 return {'CANCELLED'}
1468 else:
1469 np_print('07_HOLD_INVOKE_a_FINISHED')
1470 return {'FINISHED'}
1473 # Deleting the anchors after succesfull translation and reselecting
1474 # previously selected objects:
1476 class NP020PDDeletePoints(bpy.types.Operator):
1477 bl_idname = "object.np_pd_delete_points"
1478 bl_label = "NP PD Delete Points"
1479 bl_options = {'INTERNAL'}
1481 def execute(self, context):
1482 addon_prefs = context.preferences.addons[__package__].preferences
1483 dist = NP020PD.dist
1484 step = addon_prefs.nppd_step
1485 info = addon_prefs.nppd_info
1486 clip = addon_prefs.nppd_clip
1487 np_print('07_delete_points_START')
1488 if info:
1489 np_print('GO info')
1490 self.report({'INFO'}, dist)
1491 if clip:
1492 startloc3d = NP020PD.startloc3d
1493 endloc3d = NP020PD.endloc3d
1494 le = (
1495 mathutils.Vector(
1496 endloc3d) - mathutils.Vector(
1497 startloc3d)).length
1498 le = str(abs(round(le, 4)))
1499 bpy.context.window_manager.clipboard = le
1500 selob = NP020PD.selob
1501 start = NP020PD.start
1502 end = NP020PD.end
1503 bpy.ops.object.select_all(action='DESELECT')
1504 start.select_set(True)
1505 end.select_set(True)
1506 bpy.ops.object.delete('EXEC_DEFAULT')
1507 for ob in selob:
1508 ob.select_set(True)
1509 NP020PD.startloc3d = (0.0, 0.0, 0.0)
1510 NP020PD.endloc3d = (0.0, 0.0, 0.0)
1511 NP020PD.phase = 0
1512 if step == 'simple':
1513 NP020PD.snap = 'VERTEX'
1514 NP020PD.flag = 'TRANSLATE'
1515 NP020PD.start = None
1516 NP020PD.end = None
1517 NP020PD.dist = None
1518 bpy.context.tool_settings.use_snap = NP020PD.use_snap
1519 bpy.context.tool_settings.snap_element = NP020PD.snap_element
1520 bpy.context.tool_settings.snap_target = NP020PD.snap_target
1521 bpy.context.space_data.pivot_point = NP020PD.pivot_point
1522 bpy.context.space_data.transform_orientation = NP020PD.trans_orient
1523 bpy.context.space_data.show_manipulator = NP020PD.show_manipulator
1524 if NP020PD.acob is not None:
1525 bpy.context.view_layer.objects.active = NP020PD.acob
1526 bpy.ops.object.mode_set(mode=NP020PD.edit_mode)
1528 if step == 'simple':
1529 np_print('07_delete_points_END_cancelled')
1530 return {'CANCELLED'}
1531 if step == 'continuous':
1532 np_print('07_delete_points_END_FINISHED')
1533 return {'FINISHED'}
1537 # This is the actual addon process, the algorithm that defines the order
1538 # of operator activation inside the main macro:
1540 def register():
1542 #bpy.app.handlers.scene_update_post.append(scene_update)
1544 for i in range(1, 15):
1545 NP020PointDistance.define('OBJECT_OT_np_pd_get_selection')
1546 NP020PointDistance.define('OBJECT_OT_np_pd_read_mouse_loc')
1547 NP020PointDistance.define('OBJECT_OT_np_pd_add_points')
1548 for i in range(1, 15):
1549 NP020PointDistance.define('OBJECT_OT_np_pd_run_translate')
1550 NP020PointDistance.define('OBJECT_OT_np_pd_run_navigate')
1551 NP020PointDistance.define('OBJECT_OT_np_pd_change_phase')
1552 for i in range(1, 15):
1553 NP020PointDistance.define('OBJECT_OT_np_pd_run_translate')
1554 NP020PointDistance.define('OBJECT_OT_np_pd_run_navigate')
1555 NP020PointDistance.define('OBJECT_OT_np_pd_hold_result')
1556 NP020PointDistance.define('OBJECT_OT_np_pd_delete_points')
1559 def unregister():
1561 # bpy.app.handlers.scene_update_post.remove(scene_update)
1563 pass