Extensions: refactor CommandBatch.exec_non_blocking return value
[blender-addons-contrib.git] / np_station / np_float_box.py
blob0cfe332298754ec6c9d45a5ef0474bb2a1cebbd4
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 Translates objects using anchor and target points.
26 Emulates the functionality of the standard 'box' command in CAD applications, with start and end points. This way, it does pretty much what the basic 'grab' function does, only it locks the snap ability to one designated point in selected group, giving more control and precision to the user.
28 INSTALATION
30 Two ways:
32 A. Paste the the .py file to text editor and run (ALT+P)
33 B. Unzip and place .py file to addons_contrib. In User Preferences / Addons tab search under Testing / NP Anchor Translate and check the box.
35 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 Bl.
37 SHORTCUTS
39 After succesful instalation of the addon, or it's activation from the text editor, the NP Float Box operator should be registered in your system. Enter User Preferences / Input, and under that, 3DView / Object Mode. Search for definition assigned to simple M key (provided that you don't use it for placing objects into layers, instead of now almost-standard 'Layer manager' addon) and instead object.move_to_layer, type object.np_xxx_float_box (xxx being the number of the version). I suggest asigning hotkey only for the Object Mode because the addon doesn't work in other modes. Also, this way the basic G command should still be available and at your disposal.
41 USAGE
43 Select one or more objects.
44 Run operator (spacebar search - NP Anchor Translate, or keystroke if you assigned it)
45 Select a point anywhere in the scene (holding CTRL enables snapping). This will be your anchor point.
46 Place objects anywhere in the scene, in relation to the anchor point (again CTRL - snap).
47 Middle mouse button (MMB) enables axis constraint, numpad keys enable numerical input of distance, and RMB and ESC key interrupt the operation.
49 IMPORTANT PERFORMANCE NOTES
51 Should be key-mapped only for Object Mode. Other modes are not supported and key definitions should not be replaced.
53 WISH LIST
55 Bgl overlay for snapping modes and eventualy the translate path
56 Blf instructions on screen, preferably interactive
57 Smarter code and faster performance
59 WARNINGS
61 None so far
62 '''
64 bl_info = {
65 'name': 'NP 020 Float Box',
66 'author': 'Okavango & the Blenderartists community',
67 'version': (0, 2, 0),
68 'blender': (2, 75, 0),
69 'location': 'View3D',
70 'warning': '',
71 'description': 'Draws a mesh box using snap points',
72 'doc_url': '',
73 'category': '3D View'}
75 import bpy
76 import copy
77 import bgl
78 import blf
79 import bmesh
80 import mathutils
81 from mathutils import *
82 from math import *
83 #from math import sin, cos, tan, atan, degrees, radians, asin, acos
84 from bpy_extras import view3d_utils
85 from bpy.app.handlers import persistent
87 from .utils_geometry import *
88 from .utils_graphics import *
89 from .utils_function import *
91 # Defining the main class - the macro:
93 class NP020FloatBox(bpy.types.Macro):
94 bl_idname = 'object.np_020_float_box'
95 bl_label = 'NP 020 Float Box'
96 bl_options = {'UNDO'}
99 # Defining the storage class that will serve as a variable bank for exchange among the classes. Later, this bank will receive more variables with their values for safe keeping, as the program goes on:
101 class NP020FB:
103 flag = 'RUNTRANS0'
104 boxob = None
106 # Defining the scene update algorithm that will track the state of the objects during modal transforms, which is otherwise impossible:
108 @persistent
109 def NPFB_scene_update(context):
111 if bpy.data.objects.is_updated:
112 region = bpy.context.region
113 rv3d = bpy.context.region_data
114 helper = NP020FB.helper
115 co = helper.location
118 # 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.
120 class NPFBGetContext(bpy.types.Operator):
121 bl_idname = 'object.np_fb_get_context'
122 bl_label = 'NP FB Get Context'
123 bl_options = {'INTERNAL'}
125 def execute(self, context):
126 NP020FB.use_snap = copy.deepcopy(bpy.context.tool_settings.use_snap)
127 NP020FB.snap_element = copy.deepcopy(bpy.context.tool_settings.snap_element)
128 NP020FB.snap_target = copy.deepcopy(bpy.context.tool_settings.snap_target)
129 NP020FB.pivot_point = copy.deepcopy(bpy.context.space_data.pivot_point)
130 NP020FB.trans_orient = copy.deepcopy(bpy.context.space_data.transform_orientation)
131 NP020FB.curloc = copy.deepcopy(bpy.context.scene.cursor.location)
132 NP020FB.acob = bpy.context.active_object
133 if bpy.context.mode == 'OBJECT':
134 NP020FB.edit_mode = 'OBJECT'
135 elif bpy.context.mode in ('EDIT_MESH', 'EDIT_CURVE', 'EDIT_SURFACE', 'EDIT_TEXT', 'EDIT_ARMATURE', 'EDIT_METABALL', 'EDIT_LATTICE'):
136 NP020FB.edit_mode = 'EDIT'
137 elif bpy.context.mode == 'POSE':
138 NP020FB.edit_mode = 'POSE'
139 elif bpy.context.mode == 'SCULPT':
140 NP020FB.edit_mode = 'SCULPT'
141 elif bpy.context.mode == 'PAINT_WEIGHT':
142 NP020FB.edit_mode = 'WEIGHT_PAINT'
143 elif bpy.context.mode == 'PAINT_TEXTURE':
144 NP020FB.edit_mode = 'TEXTURE_PAINT'
145 elif bpy.context.mode == 'PAINT_VERTEX':
146 NP020FB.edit_mode = 'VERTEX_PAINT'
147 elif bpy.context.mode == 'PARTICLE':
148 NP020FB.edit_mode = 'PARTICLE_EDIT'
149 return {'FINISHED'}
152 # Defining the operator for aquiring the list of selected objects and storing them for later re-calls:
154 class NPFBGetSelection(bpy.types.Operator):
155 bl_idname = 'object.np_fb_get_selection'
156 bl_label = 'NP FB Get Selection'
157 bl_options = {'INTERNAL'}
159 def execute(self, context):
160 # Reading and storing the selection:
161 NP020FB.selob = bpy.context.selected_objects
162 return {'FINISHED'}
165 # 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 helper object under the mouse:
167 class NPFBGetMouseloc(bpy.types.Operator):
168 bl_idname = 'object.np_fb_get_mouseloc'
169 bl_label = 'NP FB Get Mouseloc'
170 bl_options = {'INTERNAL'}
172 def modal(self, context, event):
173 region = context.region
174 rv3d = context.region_data
175 co2d = ((event.mouse_region_x, event.mouse_region_y))
176 view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, co2d)
177 enterloc = view3d_utils.region_2d_to_origin_3d(region, rv3d, co2d) + view_vector*100
178 NP020FB.enterloc = copy.deepcopy(enterloc)
179 np_print('02_GetMouseloc_FINISHED', ';', 'NP020FB.flag = ', NP020FB.flag)
180 return{'FINISHED'}
182 def invoke(self,context,event):
183 args = (self, context)
184 context.window_manager.modal_handler_add(self)
185 np_print('02_GetMouseloc_INVOKED_FINISHED', ';', 'NP020FB.flag = ', NP020FB.flag)
186 return {'RUNNING_MODAL'}
189 # Defining the operator that will generate the helper object at the spot marked by mouse, preparing for translation:
191 class NPFBAddHelper(bpy.types.Operator):
192 bl_idname = 'object.np_fb_add_helper'
193 bl_label = 'NP FB Add Helper'
194 bl_options = {'INTERNAL'}
196 def execute(self, context):
197 np_print('03_AddHelper_START', ';', 'NP020FB.flag = ', NP020FB.flag)
198 enterloc = NP020FB.enterloc
199 bpy.ops.object.add(type = 'MESH',location = enterloc)
200 helper = bpy.context.active_object
201 helper.name = 'NP_FR_helper'
202 NP020FB.helper = helper
203 np_print('03_AddHelper_FINISHED', ';', 'NP020FB.flag = ', NP020FB.flag)
204 return{'FINISHED'}
207 # Defining the operator that will change some of the system settings and prepare objects for the operation:
209 class NPFBPrepareContext(bpy.types.Operator):
210 bl_idname = 'object.np_fb_prepare_context'
211 bl_label = 'NP FB Prepare Context'
212 bl_options = {'INTERNAL'}
214 def execute(self, context):
216 flag = NP020FB.flag
217 np_print('prepare, NP020FB.flag = ', flag)
218 if flag == 'RUNTRANS0':
219 helper = NP020FB.helper
220 bpy.ops.object.select_all(action = 'DESELECT')
221 helper.select_set(True)
222 bpy.context.view_layer.objects.active = helper
223 bpy.context.tool_settings.use_snap = False
224 bpy.context.tool_settings.snap_element = 'VERTEX'
225 bpy.context.tool_settings.snap_target = 'ACTIVE'
226 bpy.context.space_data.pivot_point = 'ACTIVE_ELEMENT'
227 bpy.context.space_data.transform_orientation = 'GLOBAL'
228 NP020FB.corner_brush = False
229 NP020FB.constrain = False
230 NP020FB.trans_custom = False
231 NP020FB.qdef = None
232 NP020FB.ndef = Vector((0.0, 0.0, 1.0))
233 NP020FB.ro_hor_def = 0
235 elif flag == 'RUNTRANS1_break':
236 NP020FB.flag = 'RUNTRANS1'
237 corner_brush = NP020FB.corner_brush
238 helper = NP020FB.helper
239 pointloc = NP020FB.pointloc
240 loc = copy.deepcopy(helper.location)
241 ndef = NP020FB.ndef
243 matrix = NP020FB.matrix
244 np_print('matrix =' , matrix)
245 matrix_world = helper.matrix_world.to_3x3()
246 np_print('matrix_world =' , matrix_world)
247 matrix_world.rotate(matrix)
248 matrix_world.resize_4x4()
249 helper.matrix_world = matrix_world
250 helper.location = pointloc
251 bpy.ops.object.select_all(action = 'DESELECT')
252 helper.select = True
255 ro_hor_def = NP020FB.ro_hor_def
256 ang_hor = ro_hor_def.to_euler()
257 ang_hor = ang_hor[2]
258 v1 = helper.location
259 v2 = helper.location + Vector ((0.0, 0.0, 1.0))
260 v3 = helper.location + ndef
261 rot_axis = Vector((1.0, 0.0, 0.0))
262 rot_axis.rotate(ro_hor_def)
263 rot_ang = Vector((0.0, 0.0, 1.0)).angle(ndef)
264 np_print('rot_axis, rot_ang =', rot_axis, rot_ang)
266 bpy.ops.object.select_all(action = 'DESELECT')
267 if corner_brush == False: helper.location = pointloc
268 helper.select_set(True)
269 bpy.ops.transform.rotate(value = ang_hor ,axis = Vector((0.0, 0.0, 1.0)))
270 bpy.ops.transform.rotate(value = rot_ang ,axis = rot_axis)
271 NP020FB.trans_custom = True
272 bpy.ops.transform.create_orientation(use = True)
273 bpy.context.view_layer.objects.active = helper
274 bpy.context.tool_settings.use_snap = False
275 bpy.context.tool_settings.snap_element = 'VERTEX'
276 bpy.context.tool_settings.snap_target = 'ACTIVE'
277 bpy.context.space_data.pivot_point = 'ACTIVE_ELEMENT'
279 elif flag in ('RUNTRANS2', 'RUNTRANS3'):
280 helper = NP020FB.helper
281 bpy.context.view_layer.objects.active = helper
282 bpy.context.tool_settings.use_snap = False
283 bpy.context.tool_settings.snap_element = 'VERTEX'
284 bpy.context.tool_settings.snap_target = 'ACTIVE'
285 bpy.context.space_data.pivot_point = 'ACTIVE_ELEMENT'
287 return{'FINISHED'}
290 # Defining the operator that will let the user translate the helper to the desired point. It also uses some listening operators that clean up the leftovers should the user interrupt the command. Many thanks to CoDEmanX and lukas_t:
292 class NPFBRunTranslate(bpy.types.Operator):
293 bl_idname = 'object.np_fb_run_translate'
294 bl_label = 'NP FB Run Translate'
295 bl_options = {'INTERNAL'}
297 if NP020FB.flag == 'RUNTRANS0': np_print('04_RunTrans_START',';','NP020FB.flag = ', NP020FB.flag)
298 elif NP020FB.flag == 'RUNTRANS1': np_print('05_RunTrans_START',';','NP020FB.flag = ', NP020FB.flag)
299 elif NP020FB.flag == 'RUNTRANS2': np_print('06_RunTrans_START',';','NP020FB.flag = ', NP020FB.flag)
300 elif NP020FB.flag == 'RUNTRANS3': np_print('07_RunTrans_START',';','NP020FB.flag = ', NP020FB.flag)
302 def modal(self, context, event):
303 context.area.tag_redraw()
304 flag = NP020FB.flag
305 helper = NP020FB.helper
309 if event.ctrl and event.type in ('LEFTMOUSE', 'NUMPAD_ENTER', 'SPACE'):
310 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
311 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
312 if flag == 'RUNTRANS0':
313 NP020FB.p0 = copy.deepcopy(helper.location)
314 NP020FB.ndef = copy.deepcopy(NP020FB.n)
315 NP020FB.qdef = copy.deepcopy(NP020FB.q)
316 NP020FB.ro_hor_def = copy.deepcopy(NP020FB.ro_hor)
317 NP020FB.corner_brush = True
318 NP020FB.flag = 'RUNTRANS1_break'
319 elif flag == 'RUNTRANS1':
320 NP020FB.p1 = copy.deepcopy(helper.location)
321 NP020FB.flag = 'RUNTRANS2'
322 elif flag == 'RUNTRANS2':
323 NP020FB.p2 = copy.deepcopy(helper.location)
324 NP020FB.flag = 'RUNTRANS3'
325 elif flag == 'RUNTRANS3':
326 NP020FB.flag = 'GENERATE'
327 np_print('04_RunTrans_left_enter_FINISHED',';','NP020FB.flag = ', NP020FB.flag)
328 return{'FINISHED'}
330 elif event.type in ('LEFTMOUSE', 'NUMPAD_ENTER', 'SPACE') and event.value == 'RELEASE':
331 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
332 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
333 if flag == 'RUNTRANS0':
334 NP020FB.p0 = copy.deepcopy(NP020FB.pointloc)
335 NP020FB.ndef = copy.deepcopy(NP020FB.n)
336 NP020FB.qdef = copy.deepcopy(NP020FB.q)
337 NP020FB.ro_hor_def = copy.deepcopy(NP020FB.ro_hor)
338 NP020FB.flag = 'RUNTRANS1_break'
339 elif flag == 'RUNTRANS1':
340 NP020FB.p1 = copy.deepcopy(helper.location)
341 NP020FB.flag = 'RUNTRANS2'
342 elif flag == 'RUNTRANS2':
343 NP020FB.p2 = copy.deepcopy(helper.location)
344 NP020FB.flag = 'RUNTRANS3'
345 elif flag == 'RUNTRANS3':
346 NP020FB.flag = 'GENERATE'
347 np_print('04_RunTrans_left_enter_FINISHED',';','NP020FB.flag = ', NP020FB.flag)
348 return{'FINISHED'}
350 elif event.type == 'RET':
351 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
352 if flag == 'RUNTRANS0':
353 NP020FB.ndef = copy.deepcopy(NP020FB.n)
354 NP020FB.qdef = copy.deepcopy(NP020FB.q)
355 NP020FB.ro_hor_def = copy.deepcopy(NP020FB.ro_hor)
356 NP020FB.constrain = True
357 elif flag == 'RUNTRANS1':
358 NP020FB.p1 = copy.deepcopy(helper.location)
359 NP020FB.flag = 'RUNTRANS2'
360 elif flag == 'RUNTRANS2':
361 NP020FB.p2 = copy.deepcopy(helper.location)
362 NP020FB.flag = 'GENERATE'
363 np_print('04_RunTrans_right_FINISHED',';','NP020FB.flag = ', NP020FB.flag)
364 return{'FINISHED'}
369 elif event.type in ('ESC', 'RIGHTMOUSE'):
370 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
371 NP020FB.flag = 'EXIT'
372 np_print('04_RunTrans_esc_right_FINISHED',';','NP020FB.flag = ', NP020FB.flag)
373 return{'FINISHED'}
375 np_print('04_RunTrans_count_PASS_THROUGH',';','NP020FB.flag = ', NP020FB.flag)
376 return{'PASS_THROUGH'}
378 def invoke(self, context, event):
379 np_print('04_RunTrans_INVOKE_START')
380 helper = NP020FB.helper
381 flag = NP020FB.flag
382 selob = NP020FB.selob
384 if context.area.type == 'VIEW_3D':
385 if flag in ('RUNTRANS0', 'RUNTRANS1', 'RUNTRANS2', 'RUNTRANS3'):
386 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
387 args = (self, context)
388 self._handle = bpy.types.SpaceView3D.draw_handler_add(DRAW_Overlay, args, 'WINDOW', 'POST_PIXEL')
389 context.window_manager.modal_handler_add(self)
390 if flag == 'RUNTRANS0':
391 bpy.ops.transform.translate('INVOKE_DEFAULT')
392 np_print('04_RunTrans0_INVOKED_RUNNING_MODAL',';','NP020FB.flag = ', NP020FB.flag)
393 elif flag == 'RUNTRANS1':
394 bpy.ops.transform.translate('INVOKE_DEFAULT', constraint_axis=(True, False, False))
395 np_print('04_RunTrans1_INVOKED_RUNNING_MODAL',';','NP020FB.flag = ', NP020FB.flag)
396 elif flag == 'RUNTRANS2':
397 bpy.ops.transform.translate('INVOKE_DEFAULT', constraint_axis=(False, True, False))
398 np_print('04_RunTrans2_INVOKED_RUNNING_MODAL',';','NP020FB.flag = ', NP020FB.flag)
399 elif flag == 'RUNTRANS3':
400 bpy.ops.transform.translate('INVOKE_DEFAULT', constraint_axis=(False, False, True))
401 np_print('04_RunTrans3_INVOKED_RUNNING_MODAL',';','NP020FB.flag = ', NP020FB.flag)
402 return {'RUNNING_MODAL'}
403 else:
404 np_print('04_RunTrans_INVOKE_DECLINED_wrong_flag_FINISHED',';','NP020FB.flag = ', NP020FB.flag)
405 return {'FINISHED'}
406 else:
407 self.report({'WARNING'}, "View3D not found, cannot run operator")
408 NP020FB.flag = 'EXIT'
409 np_print('04_RunTrans_INVOKE_DECLINED_no_VIEW_3D_FINISHED',';','NP020FB.flag = ', NP020FB.flag)
410 return {'FINISHED'}
413 # Defining the set of instructions that will draw the OpenGL elements on the screen during the execution of RunTranslate operator:
415 def DRAW_Overlay(self, context):
417 np_print('DRAW_Overlay_START',';','NP020FB.flag = ', NP020FB.flag)
420 addon_prefs = context.preferences.addons[__package__].preferences
421 badge = addon_prefs.npfb_badge
422 badge_size = addon_prefs.npfb_badge_size
423 dist_scale = addon_prefs.npfb_dist_scale
425 flag = NP020FB.flag
426 helper = NP020FB.helper
427 matrix = helper.matrix_world.to_3x3()
428 region = bpy.context.region
429 rv3d = bpy.context.region_data
430 rw = region.width
431 rh = region.height
432 qdef = NP020FB.qdef
433 ndef = NP020FB.ndef
434 ro_hor_def = NP020FB.ro_hor_def
435 constrain = NP020FB.constrain
436 np_print('rw, rh', rw, rh)
437 rmin = int(min(rw, rh) / 50)
438 if rmin == 0: rmin = 1
439 co2d = view3d_utils.location_3d_to_region_2d(region, rv3d, helper.location)
440 if qdef != None and constrain == False:
441 q = qdef
442 n = ndef
443 pointloc = helper.location
444 ro_hor = ro_hor_def
446 elif qdef != None and constrain == True:
447 q = qdef
448 n = ndef
449 pointloc = get_ro_normal_from_vertical(region, rv3d, co2d)[2]
450 ro_hor = ro_hor_def
452 else:
453 q = get_ro_normal_from_vertical(region, rv3d, co2d)[1]
454 n = get_ro_normal_from_vertical(region, rv3d, co2d)[0]
455 pointloc = get_ro_normal_from_vertical(region, rv3d, co2d)[2]
456 ro_hor, isohipse = get_ro_x_from_iso(region, rv3d, co2d, helper.location)
458 if pointloc == Vector((0.0, 0.0, 0.0)): pointloc = helper.location
459 np_print('n / q', n, q)
460 NP020FB.q = q
461 NP020FB.n = n
462 NP020FB.pointloc = pointloc
463 NP020FB.ro_hor = ro_hor
464 np_print('co2d, n, q', co2d, n, q)
466 # Acquiring factor for graphics scaling in order to be space - independent
469 fac = get_fac_from_view_loc_plane(region, rv3d, rmin, helper.location, q)
470 NP020FB.fac = fac
473 symbol = [[18, 37], [21, 37], [23, 33], [26, 33]]
474 badge_mode = 'RUN'
477 if qdef != None:
478 matrix.rotate(ro_hor)
479 matrix.rotate(qdef)
480 NP020FB.matrix = matrix
482 if flag == 'RUNTRANS0':
483 instruct = 'choose plane / place corner point'
484 keys_aff = 'LMB - select, CTRL - snap, ENT - lock plane'
485 keys_nav = ''
486 keys_neg = 'ESC, RMB - quit'
487 box = [helper.location, helper.location, helper.location, helper.location, helper.location, helper.location, helper.location, helper.location]
489 message_main = 'CTRL+SNAP'
490 message_aux = None
491 aux_num = None
492 aux_str = None
494 elif flag == 'RUNTRANS1':
495 instruct = 'define the width of the box'
496 keys_aff = 'LMB - select, CTRL - snap, NUMPAD - value'
497 keys_nav = ''
498 keys_neg = 'ESC, RMB - quit'
499 p0 = NP020FB.p0
500 box = [p0, helper.location, helper.location, p0, p0, helper.location, helper.location, p0 ]
502 message_main = 'CTRL+SNAP'
503 message_aux = None
504 aux_num = None
505 aux_str = None
507 elif flag == 'RUNTRANS2':
508 instruct = 'define the length of the box'
509 keys_aff = 'LMB - select, CTRL - snap, NUMPAD - value'
510 keys_nav = ''
511 keys_neg = 'ESC, RMB - quit'
512 p0 = NP020FB.p0
513 p1 = NP020FB.p1
514 box = [p0, p1, helper.location, p0 + (helper.location-p1), p0, p1, helper.location, p0 + (helper.location-p1)]
516 message_main = 'CTRL+SNAP'
517 message_aux = None
518 aux_num = None
519 aux_str = None
521 elif flag == 'RUNTRANS3':
522 instruct = 'define the height of the box'
523 keys_aff = 'LMB - select, CTRL - snap, NUMPAD - value'
524 keys_nav = ''
525 keys_neg = 'ESC, RMB - quit'
526 p0 = NP020FB.p0
527 p1 = NP020FB.p1
528 p2 = NP020FB.p2
529 p3 = p0 + (p2 - p1)
530 h = helper.location - p2
531 box = [p0, p1, p2, p3, p0 + h, p1 + h, p2 + h, p3 + h]
533 message_main = 'CTRL+SNAP'
534 message_aux = None
535 aux_num = None
536 aux_str = None
538 NP020FB.box = box
541 # ON-SCREEN INSTRUCTIONS:
543 display_instructions(region, rv3d, instruct, keys_aff, keys_nav, keys_neg)
546 # drawing of box:
548 box_2d = []
549 for co in box:
550 co = view3d_utils.location_3d_to_region_2d(region, rv3d, co)
551 box_2d.append(co)
552 bgl.glEnable(bgl.GL_BLEND)
553 bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
554 bgl.glLineWidth(1)
555 bgl.glBegin(bgl.GL_LINE_STRIP)
556 for i in range (0, 4):
557 bgl.glVertex2f(*box_2d[i])
558 bgl.glVertex2f(*box_2d[0])
559 bgl.glVertex2f(*box_2d[4])
560 bgl.glVertex2f(*box_2d[7])
561 bgl.glVertex2f(*box_2d[3])
562 bgl.glVertex2f(*box_2d[7])
563 bgl.glVertex2f(*box_2d[6])
564 bgl.glVertex2f(*box_2d[2])
565 bgl.glVertex2f(*box_2d[6])
566 bgl.glVertex2f(*box_2d[5])
567 bgl.glVertex2f(*box_2d[1])
568 bgl.glVertex2f(*box_2d[5])
569 bgl.glVertex2f(*box_2d[4])
570 bgl.glEnd()
571 bgl.glColor4f(1.0, 1.0, 1.0, 0.25)
572 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
573 boxfaces = ((0, 1, 2, 3), (0, 1, 5, 4), (1, 2, 6, 5), (2, 3, 7, 6), (3, 0, 4, 7), (4, 5, 6, 7))
574 for face in boxfaces:
575 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
576 bgl.glVertex2f(*box_2d[face[0]])
577 bgl.glVertex2f(*box_2d[face[1]])
578 bgl.glVertex2f(*box_2d[face[2]])
579 bgl.glVertex2f(*box_2d[face[3]])
580 bgl.glEnd()
583 # Drawing the small badge near the cursor with the basic instructions:
589 display_cursor_badge(co2d, symbol, badge_mode, message_main, message_aux, aux_num, aux_str)
592 # writing the dots for boxwidget widget at center of scene:
594 geowidget_base = [(0.0 ,0.0 ,0.0), (5.0 ,0.0 ,0.0), (5.0 ,-3.0 ,0.0), (0.0, -3.0 ,0.0)]
595 geowidget_top = [(0.0 ,0.0 ,4.0), (5.0 ,0.0 ,4.0), (5.0 ,-3.0 ,4.0), (0.0, -3.0 ,4.0)]
596 geowidget_rest = [(0.0 ,0.0 ,0.0), (0.0 ,0.0 ,4.0), (5.0 ,0.0 ,4.0), (5.0 ,0.0 ,0.0), (5.0 ,-3.0 ,0.0), (5.0 ,-3.0 ,4.0), (0.0, -3.0 ,4.0), (0.0, -3.0 ,0.0)]
598 # ON-SCREEN DISPLAY OF GEOWIDGET:
600 display_geowidget(region, rv3d, fac, ro_hor, q, helper.location, n, qdef, geowidget_base, geowidget_top, geowidget_rest)
605 # ON-SCREEN DISTANCES AND OTHERS:
608 if addon_prefs.npfb_suffix == 'None':
609 suffix = None
611 elif addon_prefs.npfb_suffix == 'km':
612 suffix = ' km'
614 elif addon_prefs.npfb_suffix == 'm':
615 suffix = ' m'
617 elif addon_prefs.npfb_suffix == 'cm':
618 suffix = ' cm'
620 elif addon_prefs.npfb_suffix == 'mm':
621 suffix = ' mm'
623 elif addon_prefs.npfb_suffix == 'nm':
624 suffix = ' nm'
626 elif addon_prefs.npfb_suffix == "'":
627 suffix = "'"
629 elif addon_prefs.npfb_suffix == '"':
630 suffix = '"'
632 elif addon_prefs.npfb_suffix == 'thou':
633 suffix = ' thou'
637 # ON-SCREEN DISTANCES:
639 display_distance_between_two_points(region, rv3d, box[0], box[1])
640 display_distance_between_two_points(region, rv3d, box[1], box[2])
641 display_distance_between_two_points(region, rv3d, box[2], box[6])
643 #ENDING:
645 bgl.glLineWidth(1)
646 bgl.glDisable(bgl.GL_BLEND)
647 bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
650 # Defining the operator that will generate the box mesh from the collected points:
652 class NPFBGenerateGeometry(bpy.types.Operator):
653 bl_idname = 'object.np_fb_generate_geometry'
654 bl_label = 'NP FB Generate Geometry'
655 bl_options = {'INTERNAL'}
657 def execute(self, context):
659 flag = NP020FB.flag
660 wire = True
661 material = True
662 if flag == 'GENERATE':
663 boxverts = (NP020FB.box[0], NP020FB.box[1])
664 boxedges = [(0, 1)]
665 boxfaces = ()
666 boxme = bpy.data.meshes.new('float_box')
667 #print (boxverts,boxedges,boxfaces)
668 boxme.from_pydata(boxverts, boxedges, boxfaces)
669 boxob = bpy.data.objects.new('float_box', boxme)
670 boxob.location = mathutils.Vector((0, 0, 0))
671 scn = bpy.context.scene
672 scn.objects.link(boxob)
673 scn.objects.active = boxob
674 scn.update()
675 bm = bmesh.new() # create an empty BMesh
676 bm.from_mesh(boxme) # fill it in from a Mesh
679 #bpy.ops.mesh.select_all(action = 'SELECT')
680 bmesh.ops.extrude_edge_only(bm, edges = bm.edges[:])
681 vec = NP020FB.box[2] - NP020FB.box[1]
682 bm.verts.ensure_lookup_table()
683 for vert in bm.verts: np_print('vert.index:', vert.index)
684 bmesh.ops.translate(bm, vec = vec, verts = [bm.verts[2], bm.verts[3]])
685 bmesh.ops.extrude_face_region(bm, geom = bm.faces[:])
686 vec = NP020FB.box[6] - NP020FB.box[2]
687 bm.verts.ensure_lookup_table()
688 bmesh.ops.translate(bm, vec = vec, verts = [bm.verts[4], bm.verts[5], bm.verts[6], bm.verts[7]])
689 bm.to_mesh(boxme)
690 bm.free()
691 bpy.ops.object.mode_set(mode = 'EDIT')
692 bpy.ops.mesh.select_all(action = 'SELECT')
693 bpy.ops.mesh.normals_make_consistent()
694 bpy.ops.object.mode_set(mode = 'OBJECT')
695 bpy.ops.object.select_all(action = 'DESELECT')
696 boxob.select_set(True)
697 bpy.ops.object.origin_set(type = 'ORIGIN_GEOMETRY')
698 if wire:
699 boxob.show_wire = True
700 if material:
701 mtl = bpy.data.materials.new('float_box_material')
702 mtl.diffuse_color = (1.0, 1.0, 1.0)
703 boxme.materials.append(mtl)
704 activelayer = bpy.context.scene.active_layer
705 np_print('activelayer:', activelayer)
706 layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
707 layers[activelayer] = True
708 layers = tuple(layers)
709 np_print(layers)
710 bpy.ops.object.move_to_layer(layers = layers)
711 NP020FB.boxob = boxob
713 return{'FINISHED'}
715 # Restoring the object selection and system settings from before the operator activation:
717 class NPFBRestoreContext(bpy.types.Operator):
718 bl_idname = "object.np_fb_restore_context"
719 bl_label = "NP FB Restore Context"
720 bl_options = {'INTERNAL'}
722 def execute(self, context):
723 selob = NP020FB.selob
724 helper = NP020FB.helper
725 boxob = NP020FB.boxob
726 helper.hide = False
727 bpy.ops.object.select_all(action = 'DESELECT')
728 helper.select_set(True)
729 bpy.ops.object.delete('EXEC_DEFAULT')
730 if boxob == None:
731 for ob in selob:
732 ob.select_set(True)
733 if NP020FB.acob is not None:
734 bpy.context.view_layer.objects.active = NP020FB.acob
735 bpy.ops.object.mode_set(mode = NP020FB.edit_mode)
736 else:
737 boxob.select_set(True)
738 bpy.context.view_layer.objects.active = boxob
739 bpy.ops.object.mode_set(mode = NP020FB.edit_mode)
740 if NP020FB.trans_custom: bpy.ops.transform.delete_orientation()
741 bpy.context.tool_settings.use_snap = NP020FB.use_snap
742 bpy.context.tool_settings.snap_element = NP020FB.snap_element
743 bpy.context.tool_settings.snap_target = NP020FB.snap_target
744 bpy.context.space_data.pivot_point = NP020FB.pivot_point
745 bpy.context.space_data.transform_orientation = NP020FB.trans_orient
747 NP020FB.flag = 'RUNTRANS0'
748 NP020FB.boxob = None
749 return {'FINISHED'}
752 # This is the actual addon process, the algorithm that defines the order of operator activation inside the main macro:
754 def register():
756 #bpy.app.handlers.scene_update_post.append(NPFB_scene_update)
758 NP020FloatBox.define('OBJECT_OT_np_fb_get_context')
759 NP020FloatBox.define('OBJECT_OT_np_fb_get_selection')
760 NP020FloatBox.define('OBJECT_OT_np_fb_get_mouseloc')
761 NP020FloatBox.define('OBJECT_OT_np_fb_add_helper')
762 NP020FloatBox.define('OBJECT_OT_np_fb_prepare_context')
763 for i in range(1, 15):
764 NP020FloatBox.define('OBJECT_OT_np_fb_run_translate')
765 NP020FloatBox.define('OBJECT_OT_np_fb_prepare_context')
766 NP020FloatBox.define('OBJECT_OT_np_fb_run_translate')
767 NP020FloatBox.define('OBJECT_OT_np_fb_prepare_context')
768 NP020FloatBox.define('OBJECT_OT_np_fb_run_translate')
769 NP020FloatBox.define('OBJECT_OT_np_fb_prepare_context')
770 NP020FloatBox.define('OBJECT_OT_np_fb_run_translate')
771 NP020FloatBox.define('OBJECT_OT_np_fb_generate_geometry')
772 NP020FloatBox.define('OBJECT_OT_np_fb_restore_context')
774 def unregister():
775 #bpy.app.handlers.scene_update_post.remove(NPFB_scene_update)
776 pass