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