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