65bd4cd9c35c0f237869267a6d7acfd03cc8cbf0
[blender-addons-contrib.git] / np_station / np_float_box.py
1
2 # ##### BEGIN GPL LICENSE BLOCK #####
3 #
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.
8 #
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.
13 #
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.
17 #
18 # ##### END GPL LICENSE BLOCK #####
19
20
21 '''
22 DESCRIPTION
23
24 Translates objects using anchor and target points.
25
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.
27
28 INSTALATION
29
30 Two ways:
31
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.
34
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.
36
37 SHORTCUTS
38
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.
40
41 USAGE
42
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.
48
49 IMPORTANT PERFORMANCE NOTES
50
51 Should be key-mapped only for Object Mode. Other modes are not supported and key definitions should not be replaced.
52
53 WISH LIST
54
55 Bgl overlay for snapping modes and eventualy the translate path
56 Blf instructions on screen, preferably interactive
57 Smarter code and faster performance
58
59 WARNINGS
60
61 None so far
62 '''
63
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     'wiki_url': '',
73     'category': '3D View'}
74
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
86
87 from .utils_geometry import *
88 from .utils_graphics import *
89 from .utils_function import *
90
91 # Defining the main class - the macro:
92
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'}
97
98
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:
100
101 class NP020FB:
102
103     flag = 'RUNTRANS0'
104     boxob = None
105
106 # Defining the scene update algorithm that will track the state of the objects during modal transforms, which is otherwise impossible:
107 '''
108 @persistent
109 def NPFB_scene_update(context):
110
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
116 '''
117
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.
119
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'}
124
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'}
150
151
152 # Defining the operator for aquiring the list of selected objects and storing them for later re-calls:
153
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'}
158
159     def execute(self, context):
160         # Reading and storing the selection:
161         NP020FB.selob = bpy.context.selected_objects
162         return {'FINISHED'}
163
164
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:
166
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'}
171
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'}
181
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'}
187
188
189 # Defining the operator that will generate the helper object at the spot marked by mouse, preparing for translation:
190
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'}
195
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'}
205
206
207 # Defining the operator that will change some of the system settings and prepare objects for the operation:
208
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'}
213
214     def execute(self, context):
215
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
234
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
242             '''
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
253             '''
254
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)
265
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'
278
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'
286
287         return{'FINISHED'}
288
289
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:
291
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'}
296
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)
301
302     def modal(self, context, event):
303         context.area.tag_redraw()
304         flag = NP020FB.flag
305         helper = NP020FB.helper
306
307
308
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'}
329
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'}
349
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'}
365
366
367
368
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'}
374
375         np_print('04_RunTrans_count_PASS_THROUGH',';','NP020FB.flag = ', NP020FB.flag)
376         return{'PASS_THROUGH'}
377
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
383
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'}
411
412
413 # Defining the set of instructions that will draw the OpenGL elements on the screen during the execution of RunTranslate operator:
414
415 def DRAW_Overlay(self, context):
416
417     np_print('DRAW_Overlay_START',';','NP020FB.flag = ', NP020FB.flag)
418
419     '''
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
424     '''
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
445
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
451
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)
457
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)
465
466     # Acquiring factor for graphics scaling in order to be space - independent
467
468
469     fac = get_fac_from_view_loc_plane(region, rv3d, rmin, helper.location, q)
470     NP020FB.fac = fac
471
472
473     symbol = [[18, 37], [21, 37], [23, 33], [26, 33]]
474     badge_mode = 'RUN'
475
476
477     if qdef != None:
478         matrix.rotate(ro_hor)
479         matrix.rotate(qdef)
480     NP020FB.matrix = matrix
481
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]
488
489         message_main = 'CTRL+SNAP'
490         message_aux = None
491         aux_num = None
492         aux_str = None
493
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 ]
501
502         message_main = 'CTRL+SNAP'
503         message_aux = None
504         aux_num = None
505         aux_str = None
506
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)]
515
516         message_main = 'CTRL+SNAP'
517         message_aux = None
518         aux_num = None
519         aux_str = None
520
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]
532
533         message_main = 'CTRL+SNAP'
534         message_aux = None
535         aux_num = None
536         aux_str = None
537
538     NP020FB.box = box
539
540
541     # ON-SCREEN INSTRUCTIONS:
542
543     display_instructions(region, rv3d, instruct, keys_aff, keys_nav, keys_neg)
544
545
546     # drawing of box:
547
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()
581
582
583     # Drawing the small badge near the cursor with the basic instructions:
584
585
586
587
588
589     display_cursor_badge(co2d, symbol, badge_mode, message_main, message_aux, aux_num, aux_str)
590
591
592     # writing the dots for boxwidget widget at center of scene:
593
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)]
597
598     # ON-SCREEN DISPLAY OF GEOWIDGET:
599
600     display_geowidget(region, rv3d, fac, ro_hor, q, helper.location, n, qdef, geowidget_base, geowidget_top, geowidget_rest)
601
602
603
604
605     # ON-SCREEN DISTANCES AND OTHERS:
606     '''
607
608     if addon_prefs.npfb_suffix == 'None':
609         suffix = None
610
611     elif addon_prefs.npfb_suffix == 'km':
612         suffix = ' km'
613
614     elif addon_prefs.npfb_suffix == 'm':
615         suffix = ' m'
616
617     elif addon_prefs.npfb_suffix == 'cm':
618         suffix = ' cm'
619
620     elif addon_prefs.npfb_suffix == 'mm':
621         suffix = ' mm'
622
623     elif addon_prefs.npfb_suffix == 'nm':
624         suffix = ' nm'
625
626     elif addon_prefs.npfb_suffix == "'":
627         suffix = "'"
628
629     elif addon_prefs.npfb_suffix == '"':
630         suffix = '"'
631
632     elif addon_prefs.npfb_suffix == 'thou':
633         suffix = ' thou'
634     '''
635
636
637     # ON-SCREEN DISTANCES:
638
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])
642
643     #ENDING:
644
645     bgl.glLineWidth(1)
646     bgl.glDisable(bgl.GL_BLEND)
647     bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
648
649
650 # Defining the operator that will generate the box mesh from the collected points:
651
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'}
656
657     def execute(self, context):
658
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
677
678
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
712
713         return{'FINISHED'}
714
715 # Restoring the object selection and system settings from before the operator activation:
716
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'}
721
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
746
747         NP020FB.flag = 'RUNTRANS0'
748         NP020FB.boxob = None
749         return {'FINISHED'}
750
751
752 # This is the actual addon process, the algorithm that defines the order of operator activation inside the main macro:
753
754 def register():
755
756     #bpy.app.handlers.scene_update_post.append(NPFB_scene_update)
757
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')
773
774 def unregister():
775     #bpy.app.handlers.scene_update_post.remove(NPFB_scene_update)
776     pass