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