clean-up to simplify maintenance: remove non optimized export. Clean to minimize...
[blender-addons-contrib.git] / space_view3d_paint_bprojection.py
1 bl_info = {
2     "name": "BProjection",
3     "description": "Help Clone tool",
4     "author": "kgeogeo",
5     "version": (1, 0),
6     "blender": (2, 6, 3),
7     "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/3D_interaction/bprojection",
8     "tracker_url":"http://projects.blender.org/tracker/index.php?func=detail&aid=30521&group_id=153&atid=468",
9     "category": "Paint"}
10
11 import bpy
12 from bpy.types import Panel, Operator
13 from bpy.props import IntProperty, FloatProperty, BoolProperty, IntVectorProperty, StringProperty, FloatVectorProperty
14 from bpy_extras import view3d_utils
15 import math
16 from math import *
17 import mathutils
18 from mathutils import * 
19
20 # Main function for align the plan to view
21 def align_to_view(context):
22     ob = context.object        
23     rotation = ob.custom_rotation
24     scale = ob.custom_scale
25     z = ob.custom_z
26     pos = [ob.custom_location[0], ob.custom_location[1]]
27
28     reg = context.area.regions[4]        
29     width = reg.width
30     height = reg.height 
31         
32     r3d = context.space_data.region_3d        
33     r3d.update()
34     vl = r3d.view_location
35     vr = r3d.view_rotation
36     quat = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(float(rotation)))
37         
38     v = Vector((1,0,z))
39     v.rotate(vr)
40
41     em = bpy.data.objects['Empty for BProjection']
42     img = bpy.data.textures['Texture for BProjection'].image
43     if img and img.size[1] != 0:
44         prop = img.size[0]/img.size[1]
45     else: prop = 1    
46     
47     if ob.custom_linkscale:    
48         em.scale = Vector((prop*scale[0], scale[0], 1))
49     else:
50         em.scale = Vector((prop*scale[0], scale[1], 1))
51             
52     em.location = view3d_utils.region_2d_to_location_3d(context.area.regions[4], r3d, pos, v)        
53     em.rotation_euler = Quaternion.to_euler(vr*quat)
54
55 # Function to update the properties
56 def update_props(self, context):          
57     align_to_view(context)
58
59 # Function to update the scaleUV
60 def update_UVScale(self, context):
61     v = Vector((context.object.custom_offsetuv[0]/10 + 0.5, context.object.custom_offsetuv[1]/10 + 0.5))
62     l = Vector((0.0,0.0))
63     scale = context.object.custom_scaleuv - context.object.custom_old_scaleuv
64     s = context.object.custom_scaleuv
65     o = context.object.custom_old_scaleuv 
66     uvdata = context.object.data.uv_loop_layers.active.data
67     for i in range(484):
68         vres =  v - uvdata[len(uvdata)-1-i].uv 
69         l.x = vres.x
70         l.y = vres.y   
71
72         if context.object.custom_linkscaleuv:
73             uvdata[len(uvdata)-1-i].uv = [v.x - l.x/o[0]*s[0], v.y - l.y/o[0]*s[0]]
74         else:
75             uvdata[len(uvdata)-1-i].uv = [v.x - l.x/o[0]*s[0], v.y - l.y/o[1]*s[1]]    
76
77     context.object.custom_old_scaleuv = context.object.custom_scaleuv
78     
79     
80     align_to_view(context)
81
82 # Function to update the offsetUV
83 def update_UVOffset(self, context):
84     o = context.object.custom_offsetuv
85     oo = context.object.custom_old_offsetuv 
86     uvdata = context.object.data.uv_loop_layers.active.data
87     for i in range(484):
88         uvdata[len(uvdata)-1-i].uv = [uvdata[len(uvdata)-1-i].uv[0] - oo[0]/10 + o[0]/10, uvdata[len(uvdata)-1-i].uv[1] - oo[1]/10 + o[1]/10]   
89     print(oo,o)
90     context.object.custom_old_offsetuv = context.object.custom_offsetuv
91     
92     align_to_view(context)
93
94 # Function to update the flip horizontal
95 def update_FlipUVX(self, context):          
96     uvdata = context.object.data.uv_loop_layers.active.data
97     for i in range(484):
98         x = uvdata[len(uvdata)-1-i].uv[0]
99         l = 0.5 - x
100         uvdata[len(uvdata)-1-i].uv[0] = 0.5 + l
101     
102     align_to_view(context)
103
104 # Function to update the flip vertical
105 def update_FlipUVY(self, context):          
106     uvdata = context.object.data.uv_loop_layers.active.data
107     for i in range(484):
108         x = uvdata[len(uvdata)-1-i].uv[1]
109         l = 0.5 - x
110         uvdata[len(uvdata)-1-i].uv[1] = 0.5 + l
111     
112     align_to_view(context)
113
114 # Function to update
115 def update_Rotation(self, context):              
116     if context.object.custom_rotc3d:
117         angle = context.object.custom_rotation - context.object.custom_old_rotation
118         c3d = context.space_data.cursor_location
119         em = bpy.data.objects['Empty for BProjection'].location
120         c = c3d
121         e = em
122         v1 = e-c
123         vo = Vector((0.0, 0.0, 1.0))
124         vo.rotate(context.space_data.region_3d.view_rotation)
125         quat = mathutils.Quaternion(vo, math.radians(float(angle)))        
126         quat = quat
127         v2 = v1.copy()
128         v2.rotate(quat)
129         v = v1 - v2
130         res = e - v
131         context.space_data.region_3d.update()
132         x = view3d_utils.location_3d_to_region_2d(context.area.regions[4], context.space_data.region_3d, res)
133         y=[round(x[0]), round(x[1])]
134         
135         context.object.custom_location = y       
136     else:
137         align_to_view(context)
138     context.object.custom_old_rotation = context.object.custom_rotation
139
140 # Function to create custom properties
141 def createcustomprops(context):
142     Ob = bpy.types.Object    
143     
144     # plane properties 
145     Ob.custom_location = IntVectorProperty(name="Location", description="Location of the plan",
146                                            default=(trunc(context.area.regions[4].width*3/4),trunc( context.area.regions[4].height*3/4)),
147                                            subtype = 'XYZ', size=2, update = update_props)
148                                            
149     Ob.custom_rotation = IntProperty(name="Rotation", description="Rotate the plane",
150                                      min=-180, max=180, default=0,update = update_Rotation)
151                                      
152     Ob.custom_old_rotation = IntProperty(name="old_Rotation", description="Old Rotate the plane",
153                                          min=-180, max=180, default=0)
154                                          
155     Ob.custom_scale = FloatVectorProperty(name="Scales", description="Scale the planes",
156                                           subtype = 'XYZ', default=(1.0, 1.0),min = 0.1, size=2,update = update_props)
157     Ob.custom_linkscale = BoolProperty(name="linkscale", default=True, update = update_props)
158     
159     Ob.custom_z = FloatProperty(name="Z", description="Z axis for the plane",
160                                 min=-10, max=10, default=-1.0,update = update_props)
161     
162     # UV properties
163     Ob.custom_scaleuv = FloatVectorProperty(name="ScaleUV", description="Scale the texture's UV",
164                                             default=(1.0,1.0),min = 0.01, subtype = 'XYZ', size=2,update = update_UVScale)    
165     Ob.custom_old_scaleuv = FloatVectorProperty(name="old_ScaleUV", description="Scale the texture's UV",
166                                                 default=(1.0,1.0),min = 0.01, subtype = 'XYZ', size=2)
167     Ob.custom_linkscaleuv = BoolProperty(name="linkscaleUV", default=True, update = update_UVScale)
168     Ob.custom_flipuvx = BoolProperty(name="flipuvx", default=False, update = update_FlipUVX)
169     Ob.custom_flipuvy = BoolProperty(name="flipuvy", default=False, update = update_FlipUVY)
170     
171     Ob.custom_offsetuv = FloatVectorProperty(name="OffsetUV", description="Decal the texture's UV",
172                                             default=(0.0,0.0), subtype = 'XYZ', size=2,update = update_UVOffset)    
173     Ob.custom_old_offsetuv = FloatVectorProperty(name="old_OffsetUV", description="Decal the texture's UV",
174                                                  default=(0.0,0.0), subtype = 'XYZ', size=2)
175     
176     # other properties    
177     Ob.custom_c3d = BoolProperty(name="c3d", default=True)
178     Ob.custom_rot = BoolProperty(name="rot", default=True)
179     Ob.custom_rotc3d = BoolProperty(name="rotc3d", default=False)    
180
181
182 def removecustomprops():    
183     try:
184         bpy.ops.wm.properties_remove(data_path='object',property='custom_location')
185     except:
186         nothing = 0
187     try:
188         bpy.ops.wm.properties_remove(data_path='object',property='custom_rotation')
189     except:
190         nothing = 0
191     try:
192         bpy.ops.wm.properties_remove(data_path='object',property='custom_old_rotation')
193     except:
194         nothing = 0
195     try:
196         bpy.ops.wm.properties_remove(data_path='object',property='custom_scale')
197     except:
198         nothing = 0
199     try:
200         bpy.ops.wm.properties_remove(data_path='object',property='custom_z')
201     except:
202         nothing = 0
203     try:
204         bpy.ops.wm.properties_remove(data_path='object',property='custom_c3d')
205     except:
206         nothing = 0
207     try:
208         bpy.ops.wm.properties_remove(data_path='object',property='custom_rot')
209     except:
210         nothing = 0
211     try:
212         bpy.ops.wm.properties_remove(data_path='object',property='custom_rotc3d')
213     except:
214         nothing = 0
215     try:
216         bpy.ops.wm.properties_remove(data_path='object',property='custom_scaleuv')
217     except:
218         nothing = 0
219     try:
220         bpy.ops.wm.properties_remove(data_path='object',property='custom_flipuvx')
221     except:
222         nothing = 0
223     try:
224         bpy.ops.wm.properties_remove(data_path='object',property='custom_flipuvy')
225     except:
226         nothing = 0
227     try:
228         bpy.ops.wm.properties_remove(data_path='object',property='custom_linkscale')
229     except:
230         nothing = 0
231     try:
232         bpy.ops.wm.properties_remove(data_path='object',property='custom_linkscaleuv')
233     except:
234         nothing = 0 
235     try:
236         bpy.ops.wm.properties_remove(data_path='object',property='custom_old_scaleuv')
237     except:
238         nothing = 0        
239     try:
240         bpy.ops.wm.properties_remove(data_path='object',property='custom_offsetuv')
241     except:
242         nothing = 0 
243     try:
244         bpy.ops.wm.properties_remove(data_path='object',property='custom_old_offsetuv')
245     except:
246         nothing = 0         
247
248
249 # Draw Class to show the panel
250 class BProjection(Panel):
251     bl_space_type = 'VIEW_3D'
252     bl_region_type = 'UI'
253     bl_label = "BProjection"
254
255     @classmethod
256     def poll(cls, context):
257         return (context.image_paint_object or context.sculpt_object)
258
259     def draw(self, context):        
260         layout = self.layout
261                 
262         try: 
263             bpy.data.objects['Empty for BProjection']
264
265             col = layout.column(align =True)
266             col.operator("object.removebprojectionplane", text="Remove BProjection plane")
267         
268             tex = bpy.data.textures['Texture for BProjection']
269
270             layout.template_ID_preview(tex, "image", open="image.open", rows=3, cols=3)
271         
272             col = layout.column(align =True)
273             col.operator('object.applyimage', text="Apply image", icon = 'FILE_TICK')
274             col = layout.column(align =True)
275             ob = context.object
276             col.prop(ob, "custom_c3d",text="Capture Cursor3d", icon='CURSOR')
277             col.prop(ob, "custom_rot",text="Rotate around selection", icon='ROTATE')
278             col = layout.column(align =True)
279             col.prop(ob,'custom_location', text='Plane Properties')
280             col.prop(ob,'custom_z') 
281             col.prop(ob,'custom_rotation')
282             col.prop(ob,'custom_rotc3d',text="Rotate around 3D Cursor",icon='MANIPUL')            
283             row = layout.row()
284             col = row.column(align =True)
285             col.prop(ob,'custom_scale')
286             if ob.custom_linkscale :
287                 col.prop(ob, "custom_linkscale",text="Linked",icon='LINKED')
288             else: 
289                 col.prop(ob, "custom_linkscale",text="Unlinked",icon='UNLINKED')                 
290             col = layout.column(align =True)
291             col.prop(ob,'custom_offsetuv')
292             col.prop(ob,'custom_scaleuv')
293             if ob.custom_linkscaleuv:
294                 col.prop(ob, "custom_linkscaleuv",text="Linked",icon='LINKED')
295             else: 
296                 col.prop(ob, "custom_linkscaleuv",text="Uninked",icon='UNLINKED') 
297             row = layout.row()
298             col = row.column(align =True)
299             col.prop(ob, "custom_flipuvx",text="Flip X",icon='ARROW_LEFTRIGHT')   
300             col = row.column(align =True)
301             col.prop(ob, "custom_flipuvy",text="Flip Y",icon='FULLSCREEN_ENTER')            
302             col = layout.column(align =True)
303             col.prop(ob.material_slots['Material for BProjection'].material,'alpha', slider = True)
304
305         except:
306             col = layout.column(align = True)
307             col.operator("object.addbprojectionplane", text="Add BProjection plan")             
308
309 # Oprerator Class to apply the image to the plane             
310 class ApplyImage(Operator):
311     bl_idname = "object.applyimage"
312     bl_label = "Apply image"
313
314     def execute(self, context):        
315         img = bpy.data.textures['Texture for BProjection'].image
316         em = bpy.data.objects['Empty for BProjection']
317         
318         bpy.ops.object.editmode_toggle()
319         f = context.object.data.polygons
320         nbface = len(context.object.data.polygons)
321         uvdata = context.object.data.uv_textures.active.data 
322         wasnul = False
323         if len(uvdata) == 0:
324             bpy.ops.object.editmode_toggle()
325             uvdata = context.object.data.uv_textures.active.data
326             wasnul = True                        
327         
328         nbcut = 10
329         vglen = trunc(pow(nbcut+1, 2))
330         
331         for i in range(vglen):  
332             uvdata[f[nbface-i-1].index].image = img
333         
334         if wasnul == False:
335             bpy.ops.object.editmode_toggle()
336         else:
337             bpy.ops.paint.texture_paint_toggle()
338                                    
339         context.object.custom_scale = [1,1]                       
340         context.object.data.update()
341         align_to_view(context)
342         
343         return {'FINISHED'}
344
345 # Oprerator Class to make the 4 or 6 point and scale the plan
346 class IntuitiveScale(Operator):
347     bl_idname = "object.intuitivescale"
348     bl_label = "Draw lines"
349
350     def invoke(self, context, event):
351         
352         x = event.mouse_region_x
353         y = event.mouse_region_y                
354         if len(context.object.grease_pencil.layers.active.frames) == 0: 
355             bpy.ops.gpencil.draw(mode='DRAW', stroke=[{"name":"", "pen_flip":False,
356                                                        "is_start":True, "location":(0, 0, 0),
357                                                        "mouse":(x,y), "pressure":1, "time":0}])
358         else:
359             if context.object.custom_linkscale:
360                 nb_point = 4
361             else:
362                 nb_point = 6
363                    
364             if len(context.object.grease_pencil.layers.active.frames[0].strokes) < nb_point:
365                 bpy.ops.gpencil.draw(mode='DRAW', stroke=[{"name":"", "pen_flip":False,
366                                                            "is_start":True, "location":(0, 0, 0),
367                                                            "mouse":(x,y), "pressure":1, "time":0}])
368                                                            
369             if len(context.object.grease_pencil.layers.active.frames[0].strokes) == nb_point:
370                 s = context.object.grease_pencil.layers.active.frames[0]
371                 v1 = s.strokes[1].points[0].co - s.strokes[0].points[0].co
372                 if not context.object.custom_linkscale:
373                     v2 = s.strokes[4].points[0].co - s.strokes[3].points[0].co
374                 else:
375                     v2 = s.strokes[3].points[0].co - s.strokes[2].points[0].co
376                 propx = v1.x/v2.x                
377                 context.object.custom_scale[0] *= abs(propx)
378                 
379                 if not context.object.custom_linkscale:
380                     v1 = s.strokes[2].points[0].co - s.strokes[0].points[0].co
381                     v2 = s.strokes[5].points[0].co - s.strokes[3].points[0].co
382                     propy = v1.y/v2.y
383                     context.object.custom_scale[1] *= abs(propy)
384                 bpy.ops.gpencil.active_frame_delete()
385         
386         return {'FINISHED'}
387
388 # Oprerator Class to configure all wath is needed
389 class AddBProjectionPlane(Operator):
390     bl_idname = "object.addbprojectionplane"
391     bl_label = "Configure"
392     
393     def creatematerial(self, context):        
394         try:
395             matBProjection = bpy.data.materials['Material for BProjection']
396         except:            
397             bpy.data.textures.new(name='Texture for BProjection',type='IMAGE')
398     
399             bpy.data.materials.new(name='Material for BProjection')
400             
401             matBProjection = bpy.data.materials['Material for BProjection']
402             matBProjection.texture_slots.add()
403             matBProjection.use_shadeless = True
404             matBProjection.use_transparency = True
405             matBProjection.active_texture = bpy.data.textures['Texture for BProjection']
406         
407             index = matBProjection.active_texture_index
408             matBProjection.texture_slots[index].texture_coords = 'UV'
409      
410         old_index = context.object.active_material_index
411         bpy.ops.object.material_slot_add()
412         index = context.object.active_material_index
413         context.object.material_slots[index].material = bpy.data.materials['Material for BProjection']
414         bpy.ops.object.material_slot_assign()
415         context.object.active_material_index = old_index
416         context.object.data.update()
417             
418     def execute(self, context):    
419         try:
420             bpy.data.objects['Empty for BProjection']
421
422         except:            
423             createcustomprops(context)
424             bpy.ops.paint.texture_paint_toggle()
425             
426             context.space_data.show_relationship_lines = False
427             
428             ob = context.object
429         
430             bpy.ops.object.add()
431             em = context.object
432             em.name = "Empty for BProjection"
433                         
434             bpy.data.scenes['Scene'].objects.active = ob
435             ob.select = True
436     
437             bpy.ops.object.editmode_toggle()
438     
439             bpy.ops.mesh.primitive_plane_add()
440             bpy.ops.object.vertex_group_assign(new = True)
441             ob.vertex_groups.active.name = 'texture plane'   
442             bpy.ops.uv.unwrap()
443             
444             bpy.ops.object.editmode_toggle()
445             for i in range(4):
446                 ob.data.edges[len(ob.data.edges)-1-i].crease = 1
447             bpy.ops.object.editmode_toggle()
448
449             nbcut = 10
450             bpy.ops.mesh.subdivide(number_cuts = nbcut)
451     
452             em.select = True
453             bpy.ops.object.hook_add_selob()
454             em.select = False
455             em.hide = True   
456                      
457             self.creatematerial(context)
458
459             bpy.ops.object.applyimage()  
460           
461             bpy.ops.gpencil.data_add()
462             context.object.grease_pencil.draw_mode = 'VIEW'
463             bpy.ops.gpencil.layer_add()
464             context.object.grease_pencil.layers.active.color = [1.0,0,0]
465             
466             bpy.ops.object.editmode_toggle()
467                     
468             km = bpy.data.window_managers['WinMan'].keyconfigs['Blender'].keymaps['3D View']
469             km.keymap_items[3-1].idname = 'view3d.rotate_view3d'
470             km.keymap_items[19-1].idname = 'view3d.zoom_view3d'
471             km.keymap_items[19-1].properties.delta = 1.0
472             km.keymap_items[20-1].idname = 'view3d.zoom_view3d'
473             km.keymap_items[20-1].properties.delta = -1.0
474             km.keymap_items[4-1].idname = 'view3d.pan_view3d'
475             km.keymap_items[26-1].idname = 'view3d.preset_view3d'
476             km.keymap_items[26-1].properties.view = 'FRONT'
477             km.keymap_items[28-1].idname = 'view3d.preset_view3d'
478             km.keymap_items[28-1].properties.view = 'RIGHT'            
479             km.keymap_items[32-1].idname = 'view3d.preset_view3d'
480             km.keymap_items[32-1].properties.view = 'TOP'
481             km.keymap_items[34-1].idname = 'view3d.preset_view3d'
482             km.keymap_items[34-1].properties.view = 'BACK'
483             km.keymap_items[35-1].idname = 'view3d.preset_view3d'
484             km.keymap_items[35-1].properties.view = 'LEFT'            
485             km.keymap_items[36-1].idname = 'view3d.preset_view3d'
486             km.keymap_items[36-1].properties.view = 'BOTTOM'                                   
487             km = context.window_manager.keyconfigs.default.keymaps['Image Paint']
488             kmi = km.keymap_items.new("object.intuitivescale", 'LEFTMOUSE', 'PRESS', shift=True)
489                         
490             align_to_view(context)
491             
492             bpy.ops.paint.texture_paint_toggle()
493             
494         return {'FINISHED'}
495
496 # Oprerator Class to remove what is no more needed    
497 class RemoveBProjectionPlane(Operator):
498     bl_idname = "object.removebprojectionplane"
499     bl_label = "Configure"
500
501     def removematerial(self, context):
502         i = 0
503         for ms in context.object.material_slots:
504             if ms.name == 'Material for BProjection':
505                 index = i
506             i+=1
507                 
508         context.object.active_material_index = index
509         bpy.ops.object.material_slot_remove()
510     
511     def execute(self, context):
512         try:               
513             bpy.ops.paint.texture_paint_toggle()
514             
515             context.space_data.show_relationship_lines = True
516             
517             bpy.ops.object.modifier_remove(modifier="Hook-Empty for BProjection")
518             
519             self.removematerial(context)
520
521             ob = context.object
522     
523             bpy.ops.object.editmode_toggle()
524     
525             bpy.ops.mesh.reveal()
526                                    
527             bpy.ops.mesh.select_all()
528             bpy.ops.object.editmode_toggle() 
529             if ob.data.vertices[0].select:
530                 bpy.ops.object.editmode_toggle()
531                 bpy.ops.mesh.select_all()
532                 bpy.ops.object.editmode_toggle()
533             bpy.ops.object.editmode_toggle()                    
534             
535             ob.vertex_groups.active.name = 'texture plane'
536             bpy.ops.object.vertex_group_select()
537             bpy.ops.mesh.delete()
538             bpy.ops.object.vertex_group_remove()
539     
540             bpy.ops.object.editmode_toggle()
541    
542             ob.select = False
543                 
544             em = bpy.data.objects['Empty for BProjection']
545             bpy.data.scenes['Scene'].objects.active = em
546             em.hide = False
547             em.select = True
548             bpy.ops.object.delete()
549     
550             bpy.data.scenes['Scene'].objects.active = ob
551             ob.select = True
552             
553             km = bpy.data.window_managers['WinMan'].keyconfigs['Blender'].keymaps['3D View']
554             km.keymap_items[3-1].idname = 'view3d.rotate'
555             km.keymap_items[19-1].idname = 'view3d.zoom'
556             km.keymap_items[19-1].properties.delta = 1.0
557             km.keymap_items[20-1].idname = 'view3d.zoom'
558             km.keymap_items[20-1].properties.delta = -1.0
559             km.keymap_items[4-1].idname = 'view3d.move'
560             km.keymap_items[26-1].idname = 'view3d.viewnumpad'
561             km.keymap_items[26-1].properties.type = 'FRONT'
562             km.keymap_items[28-1].idname = 'view3d.viewnumpad'
563             km.keymap_items[28-1].properties.type = 'RIGHT'            
564             km.keymap_items[32-1].idname = 'view3d.viewnumpad'
565             km.keymap_items[32-1].properties.type = 'TOP'
566             km.keymap_items[34-1].idname = 'view3d.viewnumpad'
567             km.keymap_items[34-1].properties.type = 'BACK'
568             km.keymap_items[35-1].idname = 'view3d.viewnumpad'
569             km.keymap_items[35-1].properties.type = 'LEFT'            
570             km.keymap_items[36-1].idname = 'view3d.viewnumpad'
571             km.keymap_items[36-1].properties.type = 'BOTTOM'            
572             
573             km = context.window_manager.keyconfigs.default.keymaps['Image Paint']
574             for kmi in km.keymap_items:
575                 if kmi.idname in ["object.intuitivescale"]:
576                     km.keymap_items.remove(kmi)
577             
578             bpy.ops.paint.texture_paint_toggle()
579             removecustomprops()
580                     
581         except:
582             nothing = 0
583         
584         return {'FINISHED'}
585
586 # Oprerator Class to rotate the view3D
587 class RotateView3D(Operator):
588     bl_idname = "view3d.rotate_view3d"
589     bl_label = "Rotate the View3D"
590     
591     first_mouse_x = 0
592     first_mouse_y = 0 
593
594     panx = 0
595     pany = 0
596
597     rkey = False
598     skey = False
599     gkey = False
600     zkey = False
601     ukey = False
602     ckey = False
603     ykey = False
604     first_time = True
605     
606     def vect_sphere(self, context, mx, my):
607         width = context.area.regions[4].width
608         height = context.area.regions[4].height
609            
610         if width >= height:
611             ratio = height/width
612         
613             x = 2*mx/width
614             y = 2*ratio*my/height
615             
616             x = x - 1
617             y = y - ratio
618         else:
619             ratio = width/height
620         
621             x = 2*ratio*mx/width
622             y = 2*my/height
623  
624             x = x - ratio
625             y = y - 1
626         
627         z2 = 1 - x * x - y * y
628         if z2 > 0:
629             z= sqrt(z2)
630         else : z=0
631             
632         p = Vector((x, y, z))
633         p.normalize()
634         return p
635     
636     def tracball(self, context, mx, my, origine):
637         pos_init_cursor = view3d_utils.location_3d_to_region_2d(context.region, context.space_data.region_3d, context.space_data.cursor_location)        
638         if context.object.custom_rot:
639             pos_init = view3d_utils.location_3d_to_region_2d(context.region, context.space_data.region_3d, origine)
640             context.space_data.region_3d.view_location = origine
641         
642         v1 = self.vect_sphere(context, self.first_mouse_x, self.first_mouse_y)
643         v2 = self.vect_sphere(context, mx, my)
644                         
645         axis = Vector.cross(v1,v2);
646         angle = Vector.angle(v1,v2);
647             
648         q =  Quaternion(axis,-2*angle)
649                         
650         context.space_data.region_3d.view_rotation *=q
651         context.space_data.region_3d.update()
652         
653         if context.object.custom_rot:
654             pos_end = view3d_utils.region_2d_to_location_3d(context.region, context.space_data.region_3d, pos_init, Vector((0,0,0)))                
655             context.space_data.region_3d.view_location =  -1*pos_end
656
657         if context.object.custom_c3d:
658             context.space_data.region_3d.update()       
659             context.space_data.cursor_location = view3d_utils.region_2d_to_location_3d(context.region, context.space_data.region_3d, pos_init_cursor, Vector((0,0,0)))
660         
661         self.first_mouse_x = mx
662         self.first_mouse_y = my
663                 
664     def modal(self, context, event):                                
665         if event.value == 'PRESS':
666             self.panx = event.mouse_region_x
667             self.pany = event.mouse_region_y
668                     
669             if event.type == 'S':
670                 self.skey = True
671             
672             if event.type == 'R':
673                 self.rkey = True    
674
675             if event.type == 'G':
676                 self.gkey = True
677
678             if event.type == 'Z':
679                 self.zkey = True            
680
681             if event.type == 'U':
682                 self.ukey = True 
683
684             if event.type == 'C':
685                 self.ckey = True 
686                 
687             if event.type == 'Y':
688                 self.ykey = True 
689                             
690         if event.value == 'RELEASE':
691             if event.type == 'S':
692                 self.skey = False
693             
694             if event.type == 'R':
695                 self.rkey = False
696             
697             if event.type == 'G':
698                 self.gkey = False            
699
700             if event.type == 'Z':
701                 self.zkey = False 
702
703             if event.type == 'U':
704                 self.ukey = False                 
705
706             if event.type == 'C':
707                 self.ckey = False             
708
709             if event.type == 'Y':
710                 self.ykey = False 
711
712         if event.type == 'MOUSEMOVE':                        
713             
714             if self.rkey == False and self.skey == False and self.gkey == False and self.zkey == False and self.ukey == False and self.ykey == False:
715                 self.tracball(context, event.mouse_region_x, event.mouse_region_y,context.object.location)
716                 align_to_view(context)
717                 if self.first_time:
718                     rot_ang = context.user_preferences.view.rotation_angle            
719                     context.user_preferences.view.rotation_angle = 0
720                     bpy.ops.view3d.view_orbit(type='ORBITLEFT')
721                     context.user_preferences.view.rotation_angle = rot_ang   
722                     bpy.ops.view3d.view_persportho()         
723                     bpy.ops.view3d.view_persportho()
724                     self.first_time = False
725           
726             deltax = event.mouse_region_x - self.panx
727             deltay = event.mouse_region_y - self.pany           
728
729             if self.rkey == False and self.skey == False and self.gkey == True and self.zkey == False and self.ukey == False and self.ykey == False:       
730                 cl = context.object.custom_location
731                 context.object.custom_location = [cl[0] + deltax,cl[1] + deltay]               
732                                    
733             if self.rkey == False and self.skey == True and self.gkey == False and self.zkey == False and self.ukey == False and self.ykey == False:                
734                 s = context.object.custom_scale
735                 if context.object.custom_linkscale:
736                     context.object.custom_scale = [s[0] + deltax/20, s[0] + deltax/20]
737                 else:
738                     context.object.custom_scale = [s[0] + deltax/20, s[1] + deltay/20]
739                                           
740             if self.rkey == False and self.skey == False and self.gkey == False and self.zkey == True and self.ukey == False and self.ykey == False:                
741                 context.object.custom_z+=deltax/10
742                       
743             if self.rkey == True and self.skey == False and self.gkey == False and self.zkey == False and self.ukey == False and self.ykey == False:
744                 context.object.custom_rotation+=deltax
745                     
746             if self.rkey == False and self.skey == False and self.gkey == False and self.zkey == False and self.ukey == True and self.ykey == False:
747                 suv = context.object.custom_scaleuv
748                 if context.object.custom_linkscaleuv:    
749                     context.object.custom_scaleuv= [suv[0] + deltax/50 , suv[0] + deltax/50]
750                 else:
751                     context.object.custom_scaleuv= [suv[0] + deltax/50 , suv[1] + deltay/50]               
752
753             if self.rkey == False and self.skey == False and self.gkey == False and self.zkey == False and self.ukey == False and self.ykey == True:       
754                 ouv = context.object.custom_offsetuv
755                 context.object.custom_offsetuv = [ouv[0] - deltax/50,ouv[1] - deltay/50] 
756
757             self.panx = event.mouse_region_x
758             self.pany = event.mouse_region_y
759             self.first_mouse_x = event.mouse_region_x
760             self.first_mouse_y = event.mouse_region_y
761             
762         elif event.type == 'MIDDLEMOUSE'and event.value == 'RELEASE':       
763             
764             return {'FINISHED'}
765         
766         if self.ckey:
767             context.object.custom_scale = [1,1]
768             context.object.custom_rotation = 0
769             context.object.custom_scaleuv =[1.0,1.0]
770             context.object.custom_offsetuv =[0.0,0.0]
771             return {'RUNNING_MODAL'}
772                     
773         return {'RUNNING_MODAL'}
774     
775     def execute(self, context):        
776         align_to_view(context)  
777         
778         return{'FINISHED'}
779
780     def invoke(self, context, event):
781         context.window_manager.modal_handler_add(self)
782         self.first_mouse_x = event.mouse_region_x
783         self.first_mouse_y = event.mouse_region_y
784         self.first_time = True
785         
786         return {'RUNNING_MODAL'}
787
788 # Oprerator Class to pan the view3D
789 class PanView3D(bpy.types.Operator):
790     bl_idname = "view3d.pan_view3d"
791     bl_label = "Pan View3D"
792
793     
794     first_mouse_x = 0
795     first_mouse_y = 0 
796
797     panx = 0
798     pany = 0
799
800     def modal(self, context, event):
801         width = context.area.regions[4].width
802         height = context.area.regions[4].height
803
804         deltax = event.mouse_region_x - self.first_mouse_x
805         deltay = event.mouse_region_y - self.first_mouse_y                
806                
807         l =  context.space_data.region_3d
808         vr = l.view_rotation
809         
810         v = Vector((deltax/max(width,height),deltay/max(width,height),0))
811         v.rotate(vr)
812         
813         pos = [0,0]
814         v1 = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v)
815         pos = [width,height]
816         v2 = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v)
817         
818         v3 = (v2 - v1)
819         
820         pos_init_cursor = view3d_utils.location_3d_to_region_2d(context.region, context.space_data.region_3d, context.space_data.cursor_location)
821         
822         context.space_data.region_3d.view_location -= v*v3.length
823         
824         context.space_data.region_3d.update()       
825         context.space_data.cursor_location = view3d_utils.region_2d_to_location_3d(context.region, context.space_data.region_3d, pos_init_cursor, Vector((0,0,0)))
826             
827         align_to_view(context)
828
829         self.first_mouse_x = event.mouse_region_x
830         self.first_mouse_y = event.mouse_region_y
831
832         if event.type == 'MIDDLEMOUSE'and event.value == 'RELEASE':
833             return {'FINISHED'}
834         
835         return {'RUNNING_MODAL'}
836                 
837     def invoke(self, context, event):
838         context.window_manager.modal_handler_add(self)
839         self.first_mouse_x = event.mouse_region_x
840         self.first_mouse_y = event.mouse_region_y   
841         
842         return {'RUNNING_MODAL'}
843
844     def execute(self, context):        
845         align_to_view(context)  
846         
847         return{'FINISHED'}
848
849 # Oprerator Class to zoom the view3D
850 class ZoomView3D(Operator):
851     bl_idname = "view3d.zoom_view3d"
852     bl_label = "Zoom View3D"
853
854     delta = FloatProperty(
855         name="delta",
856         description="Delta",
857         min=-1.0, max=1,
858         default=1.0,
859         )
860
861     def invoke(self, context, event):                   
862         bpy.ops.view3d.zoom(delta = self.delta)
863         
864         align_to_view(context)
865         
866         return {'FINISHED'}
867
868     def execute(self, context):        
869         align_to_view(context)
870         
871         return{'FINISHED'}
872
873 # Oprerator Class to use numpad shortcut
874 class PresetView3D(Operator):
875     bl_idname = "view3d.preset_view3d"
876     bl_label = "Preset View3D"
877
878     view = StringProperty(name="View", description="Select the view", default='TOP',)
879
880     def invoke(self, context, event):                   
881         origine = context.object.location
882         
883         pos_init_cursor = view3d_utils.location_3d_to_region_2d(context.region, context.space_data.region_3d, context.space_data.cursor_location)
884
885         if context.object.custom_rot:
886             pos_init = view3d_utils.location_3d_to_region_2d(context.region, context.space_data.region_3d, origine)
887             context.space_data.region_3d.view_location = origine
888
889         tmp = context.user_preferences.view.smooth_view
890         context.user_preferences.view.smooth_view = 0
891         bpy.ops.view3d.viewnumpad(type=self.view)        
892         align_to_view(context)
893         context.user_preferences.view.smooth_view = tmp
894
895         if context.object.custom_rot:
896             pos_end = view3d_utils.region_2d_to_location_3d(context.region, context.space_data.region_3d, pos_init, Vector((0,0,0)))                
897             context.space_data.region_3d.view_location =  -1*pos_end
898             align_to_view(context)
899
900         if context.object.custom_c3d:
901             context.space_data.region_3d.update()       
902             context.space_data.cursor_location = view3d_utils.region_2d_to_location_3d(context.region, context.space_data.region_3d, pos_init_cursor, Vector((0,0,0)))        
903                     
904         return {'FINISHED'}
905
906 def register():
907     bpy.utils.register_module(__name__)
908
909 def unregister():
910     bpy.utils.unregister_module(__name__)
911
912 if __name__ == "__main__":
913     register()
914    
915