fix tracker url
[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, CollectionProperty
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     em = bpy.data.objects['Empty for BProjection']       
24     rotation = em.custom_rotation
25     scale = em.custom_scale
26     z = em.custom_location.z
27     pos = [em.custom_location.x, em.custom_location.y]
28     
29     reg = context.area.regions[4]        
30     width = reg.width
31     height = reg.height 
32     
33     sd = context.space_data    
34     r3d = sd.region_3d     
35     r3d.update()
36     vr = r3d.view_rotation
37     quat = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(float(rotation)))
38     v = Vector((pos[0],pos[1],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 em.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     pos_cur = em.location - sd.cursor_location
52     rot_cur1 = em.rotation_euler.to_quaternion()
53     em.location = v + ob.location            
54     em.rotation_euler = Quaternion.to_euler(vr*quat)   
55     if em.custom_c3d:
56         if em.custom_old_scale != em.custom_scale:
57             pos_cur = em.location - sd.cursor_location       
58         rot_cur2 = em.rotation_euler.to_quaternion()
59         rot_cur1.invert()
60         pos_cur.rotate(rot_cur1)
61         pos_cur.rotate(rot_cur2)
62         v = em.location - pos_cur
63         sd.cursor_location =  v
64
65 def applyimage(context):        
66         img = bpy.data.textures['Texture for BProjection'].image
67         em = bpy.data.objects['Empty for BProjection']
68         ob = context.object
69                
70         face = ob.data.polygons
71         uvdata = ob.data.uv_textures.active.data 
72             
73         for f,d in zip(face,uvdata):
74             if f.select:
75                 d.image = img
76                
77         align_to_view(context)
78         ob.data.update()
79
80 # Function to update the properties
81 def update_Location(self, context):          
82     align_to_view(context)
83
84 def find_uv(context):
85     obj = context.object
86     me = obj.data.vertices
87     vg = obj.vertex_groups
88     l=[]
89     index_uv = 0      
90     for face in obj.data.polygons:
91         x=len(face.vertices)
92         for vertex in face.vertices:
93             if len(me[vertex].groups)>0:
94                 for g in me[vertex].groups:
95                     if vg[g.group].name == 'texture plane':
96                         x-=1
97         
98                         
99         if x == 0:
100             l.append([index_uv,len(face.vertices)])
101         index_uv += len(face.vertices)
102     return l
103
104 # Function to update the scaleUV
105 def update_UVScale(self, context):
106     ob = context.object
107     em = bpy.data.objects['Empty for BProjection']
108     v = Vector((em.custom_offsetuv[0]/10 + 0.5, em.custom_offsetuv[1]/10 + 0.5))
109     l = Vector((0.0,0.0))
110     s = em.custom_scaleuv
111     os = em.custom_old_scaleuv 
112     scale = s - os
113     l = find_uv(context)
114     for i,j in l:
115         for t in range(j):
116             d = context.object.data.uv_layers.active.data[i+t]
117             vres =  v - d.uv  
118             d.uv.x = v.x - vres.x/os[0]*s[0]
119             d.uv.y = v.y - vres.y/os[1]*s[1]
120
121     em.custom_old_scaleuv = s  
122     
123     applyimage(context)
124
125 def update_PropUVScale(self, context):
126     em = bpy.data.objects['Empty for BProjection']
127     if em.custom_linkscaleuv:
128         em.custom_scaleuv = [em.custom_propscaleuv,em.custom_propscaleuv]
129
130 def update_LinkUVScale(self, context):
131     em = bpy.data.objects['Empty for BProjection']
132     if em.custom_linkscaleuv:
133         em.custom_propscaleuv = em.custom_scaleuv.x
134         update_PropUVScale(self, context)
135     else:
136         update_UVScale(self, context) 
137         
138 # Function to update the offsetUV
139 def update_UVOffset(self, context):
140     ob = context.object
141     em = bpy.data.objects['Empty for BProjection']
142     o = em.custom_offsetuv
143     oo = em.custom_old_offsetuv 
144     l = find_uv(context)
145     for i,j in l:
146         for t in range(j):
147             d = context.object.data.uv_layers.active.data[i+t]
148             d.uv = [d.uv[0] - oo[0]/10 + o[0]/10, d.uv[1] - oo[1]/10 + o[1]/10]   
149     em.custom_old_offsetuv = o
150     
151     applyimage(context)
152
153 # Function to update the flip horizontal
154 def update_FlipUVX(self, context):
155     l = find_uv(context)
156     for i,j in l:
157         for t in range(j):
158             d = context.object.data.uv_layers.active.data[i+t]
159             x = d.uv.x
160             d.uv.x = 1 - x
161     
162     applyimage(context)
163
164 # Function to update the flip vertical
165 def update_FlipUVY(self, context):
166     l = find_uv(context)
167     for i,j in l:
168         for t in range(j):
169             d = context.object.data.uv_layers.active.data[i+t]
170             y = d.uv[1]
171             d.uv[1] = 1 - y
172     
173     applyimage(context)
174
175 # Function to update
176 def update_Rotation(self, context):              
177     ob = context.object
178     em = bpy.data.objects['Empty for BProjection']
179     if em.custom_rotc3d:
180         angle = em.custom_rotation - em.custom_old_rotation
181         sd = context.space_data
182         vr = sd.region_3d.view_rotation.copy()        
183         c = sd.cursor_location - ob.location
184         e = bpy.data.objects['Empty for BProjection'].location - ob.location
185         vo = Vector((0.0, 0.0, 1.0))
186         vo.rotate(vr)
187         quat = mathutils.Quaternion(vo, math.radians(angle))
188         v = e-c
189         v.rotate(quat)
190         vr.invert()
191         v.rotate(vr)
192         c.rotate(vr)
193         em.custom_location = c + v
194     else:        
195         align_to_view(context)
196    
197     em.custom_old_rotation = em.custom_rotation
198
199 # Function to update scale
200 def update_Scale(self, context):              
201     ob = context.object
202     em = bpy.data.objects['Empty for BProjection']
203     
204     if em.custom_scac3d:
205         sd = context.space_data
206         r3d =  sd.region_3d
207         vr = r3d.view_rotation.copy()
208         vr.invert()
209         e = em.location - ob.location
210         c = sd.cursor_location - ob.location
211         ce = e - c
212         
213         s = em.custom_scale
214         os = em.custom_old_scale
215         c.rotate(vr)
216         ce.rotate(vr)
217         
218         img = bpy.data.textures['Texture for BProjection'].image
219         if img and img.size[1] != 0:
220             prop = img.size[0]/img.size[1]
221         else: prop = 1
222         
223         v = Vector((s.x*ce.x/os.x, s.y*ce.y/os.y,0.0))
224         em.custom_location = c + v
225         
226
227     else:          
228         align_to_view(context)
229             
230     
231     em.custom_old_scale = em.custom_scale
232
233 def update_PropScale(self, context):
234     em = bpy.data.objects['Empty for BProjection']
235     if em.custom_linkscale:
236         em.custom_scale = [em.custom_propscale,em.custom_propscale]
237     
238 def update_LinkScale(self, context):
239     em = bpy.data.objects['Empty for BProjection']
240     if em.custom_linkscale:
241         em.custom_propscale = em.custom_scale.x
242         update_PropScale(self, context)
243     else:
244         update_Scale(self, context) 
245
246 def update_activeviewname(self, context):
247     em = bpy.data.objects['Empty for BProjection']
248     if self.custom_active:
249         em.custom_active_view = self.custom_active_view
250
251 class custom_props(bpy.types.PropertyGroup):
252     custom_location = FloatVectorProperty(name="Location", description="Location of the plan",
253                                            default=(0,0,-1.0),
254                                            subtype = 'XYZ', size=3)
255                                            
256     custom_rotation = FloatProperty(name="Rotation", description="Rotate the plane",
257                                      min=-180, max=180, default=0)
258                                          
259     custom_scale = FloatVectorProperty(name="Scales", description="Scale the planes",
260                                        subtype = 'XYZ', default=(1.0, 1.0),min = 0.1, size=2)
261     custom_propscale = FloatProperty(name="PropScale", description="Scale the Plan",
262                                            default=1.0,min = 0.1)
263                                                                                     
264     custom_linkscale = BoolProperty(name="linkscale", default=True)
265    
266     # UV properties
267     custom_scaleuv = FloatVectorProperty(name="ScaleUV", description="Scale the texture's UV",
268                                             default=(1.0,1.0),min = 0.01, subtype = 'XYZ', size=2)
269     custom_propscaleuv = FloatProperty(name="PropScaleUV", description="Scale the texture's UV",
270                                            default=1.0,min = 0.01) 
271     custom_offsetuv = FloatVectorProperty(name="OffsetUV", description="Decal the texture's UV",
272                                             default=(0.0,0.0), subtype = 'XYZ', size=2)       
273     custom_linkscaleuv = BoolProperty(name="linkscaleUV", default=True)
274     custom_flipuvx = BoolProperty(name="flipuvx", default=False)
275     custom_flipuvy = BoolProperty(name="flipuvy", default=False)
276     
277     # other properties
278     custom_active= BoolProperty(name="custom_active", default=True)   
279     custom_expand = BoolProperty(name="expand", default=False)
280     
281     custom_active_view = StringProperty(name = "custom_active_view",default = "View",update = update_activeviewname)
282     
283     custom_image = StringProperty(name = "custom_image",default = "")
284     
285     custom_index = IntProperty()
286
287 # Function to create custom properties
288 def createcustomprops(context):
289     Ob = bpy.types.Object    
290     
291     # plane properties 
292     Ob.custom_location = FloatVectorProperty(name="Location", description="Location of the plan",
293                                            default=(5.0,0.0,-1.0),
294                                            subtype = 'XYZ', size=3, update = update_Location)
295                                            
296     Ob.custom_rotation = FloatProperty(name="Rotation", description="Rotate the plane",
297                                      min=-180, max=180, default=0,update = update_Rotation)
298                                      
299     Ob.custom_old_rotation = FloatProperty(name="old_Rotation", description="Old Rotate the plane",
300                                          min=-180, max=180, default=0)
301                                          
302     Ob.custom_scale = FloatVectorProperty(name="Scales", description="Scale the planes",
303                                           subtype = 'XYZ', default=(1.0, 1.0),min = 0.1, size=2,update = update_Scale)
304     Ob.custom_propscale = FloatProperty(name="PropScale", description="Scale the Plan",
305                                            default=1.0,min = 0.1,update = update_PropScale)
306     Ob.custom_old_scale = FloatVectorProperty(name="old_Scales", description="Old Scale the planes",
307                                           subtype = 'XYZ', default=(1.0, 1.0),min = 0.1, size=2)
308                                           
309     Ob.custom_linkscale = BoolProperty(name="linkscale", default=True, update = update_LinkScale)
310     
311                                 
312     Ob.custom_sub = IntProperty(name="Subdivide", description="Number of subdivision of the plan",
313                                      min=0, max=20, default=0)                                
314     
315     # UV properties
316     Ob.custom_scaleuv = FloatVectorProperty(name="ScaleUV", description="Scale the texture's UV",
317                                             default=(1.0,1.0),min = 0.01, subtype = 'XYZ', size=2,update = update_UVScale)
318     Ob.custom_propscaleuv = FloatProperty(name="PropScaleUV", description="Scale the texture's UV",
319                                            default=1.0,min = 0.01,update = update_PropUVScale)    
320     Ob.custom_old_scaleuv = FloatVectorProperty(name="old_ScaleUV", description="Scale the texture's UV",
321                                                 default=(1.0,1.0),min = 0.01, subtype = 'XYZ', size=2)
322     Ob.custom_offsetuv = FloatVectorProperty(name="OffsetUV", description="Decal the texture's UV",
323                                             default=(0.0,0.0), subtype = 'XYZ', size=2,update = update_UVOffset)    
324     Ob.custom_old_offsetuv = FloatVectorProperty(name="old_OffsetUV", description="Decal the texture's UV",
325                                                  default=(0.0,0.0), subtype = 'XYZ', size=2)    
326     Ob.custom_linkscaleuv = BoolProperty(name="linkscaleUV", default=True, update = update_LinkUVScale)
327     Ob.custom_flipuvx = BoolProperty(name="flipuvx", default=False, update = update_FlipUVX)
328     Ob.custom_flipuvy = BoolProperty(name="flipuvy", default=False, update = update_FlipUVY)
329     
330     # other properties    
331     Ob.custom_c3d = BoolProperty(name="c3d", default=True)
332     Ob.custom_rotc3d = BoolProperty(name="rotc3d", default=False)
333     Ob.custom_scac3d = BoolProperty(name="scac3d", default=False)
334     Ob.custom_expand = BoolProperty(name="expand", default=True)
335     Ob.custom_active_view = StringProperty(name = "custom_active_view",default = "View")
336     try:
337         Ob.custom_active_object = StringProperty(name = "custom_active_object",default = context.object.name)
338     except:
339         Ob.custom_active_object = StringProperty(name = "custom_active_object",default = 'debut')    
340     Ob.custom_props = CollectionProperty(type = custom_props)
341
342 # Function to remove custom properties
343 def removecustomprops():    
344     list_prop = ['custom_location', 'custom_rotation', 'custom_old_rotation', 'custom_scale', 'custom_old_scale', 'custom_c3d',
345                  'custom_rotc3d', 'custom_scaleuv', 'custom_flipuvx', 'custom_flipuvy', 'custom_linkscale',
346                  'custom_linkscaleuv', 'custom_old_scaleuv', 'custom_offsetuv', 'custom_old_offsetuv', 'custom_scac3d', 'custom_sub',
347                  'custom_expand', 'custom_active_view', 'custom_propscaleuv', 'custom_props', 'custom_propscale']
348     for prop in list_prop:
349         try:
350             del bpy.data.objects['Empty for BProjection'][prop]
351         except:
352             pass
353         
354 def clear_props(context):
355     em = bpy.data.objects['Empty for BProjection'] 
356     em.custom_scale = [1,1]
357     em.custom_rotation = 0
358     em.custom_scaleuv =[1.0,1.0]
359     em.custom_offsetuv =[0.0,0.0]
360     em.custom_propscaleuv = 1.0
361     em.custom_propscale = 1.0
362     if em.custom_flipuvx == True:
363         em.custom_flipuvx = False
364     if em.custom_flipuvy == True:
365         em.custom_flipuvy = False
366
367 # Oprerator Class to create view            
368 class CreateView(Operator):
369     bl_idname = "object.create_view"
370     bl_label = "Create a new view"
371
372     def execute(self, context):              
373         ob = context.object
374         em = bpy.data.objects['Empty for BProjection']
375         new_props = em.custom_props.add()        
376         em.custom_active_view = new_props.custom_active_view               
377         ob.data.shape_keys.key_blocks[ob.active_shape_key_index].mute = True
378         bpy.ops.object.shape_key_add(from_mix = False)
379         ob.data.shape_keys.key_blocks[ob.active_shape_key_index].value = 1.0
380         new_props.custom_index = len(em.custom_props)-1
381         bpy.ops.object.active_view(index = new_props.custom_index)
382         return {'FINISHED'}
383
384 # Oprerator Class to copy view 
385 class SaveView(Operator):
386     bl_idname = "object.save_view"
387     bl_label = "copy the view"
388     
389     index = IntProperty(default = 0)
390     
391     def execute(self, context):              
392         em = bpy.data.objects['Empty for BProjection']
393         prop = em.custom_props[self.index]                            
394         prop.custom_rotation =  em.custom_rotation                    
395         prop.custom_scale =  em.custom_scale                  
396         prop.custom_linkscale =  em.custom_linkscale                                      
397         prop.custom_scaleuv = em.custom_scaleuv
398         prop.custom_propscale = em.custom_propscale
399         prop.custom_offsetuv =  em.custom_offsetuv  
400         prop.custom_linkscaleuv = em.custom_linkscaleuv
401         prop.custom_propscaleuv = em.custom_propscaleuv
402         prop.custom_flipuvx = em.custom_flipuvx
403         prop.custom_flipuvy = em.custom_flipuvy
404         try:
405             prop.custom_image = bpy.data.textures['Texture for BProjection'].image.name
406         except:
407             pass
408         
409         return {'FINISHED'}
410
411 # Oprerator Class to copy view 
412 class PasteView(Operator):
413     bl_idname = "object.paste_view"
414     bl_label = "paste the view"
415     
416     index = IntProperty(default = 0)
417     
418     def execute(self, context):              
419         em = bpy.data.objects['Empty for BProjection']
420         tmp_scac3d = em.custom_scac3d
421         tmp_rotc3d = em.custom_rotc3d
422         em.custom_scac3d = False
423         em.custom_rotc3d = False
424         prop = em.custom_props[self.index]
425         em.custom_linkscale =  prop.custom_linkscale
426         em.custom_offsetuv =  prop.custom_offsetuv 
427         em.custom_linkscaleuv = prop.custom_linkscaleuv
428         em.custom_scaleuv = prop.custom_scaleuv
429         em.custom_propscaleuv = prop.custom_propscaleuv       
430         em.custom_rotation =  prop.custom_rotation                    
431         em.custom_scale =  prop.custom_scale
432         em.custom_propscale = prop.custom_propscale                     
433         if prop.custom_image != '':
434             if bpy.data.textures['Texture for BProjection'].image.name != prop.custom_image:
435                 bpy.data.textures['Texture for BProjection'].image = bpy.data.images[prop.custom_image]
436                 applyimage(context)
437         if em.custom_flipuvx != prop.custom_flipuvx:
438             em.custom_flipuvx = prop.custom_flipuvx
439         if em.custom_flipuvy != prop.custom_flipuvy:
440             em.custom_flipuvy = prop.custom_flipuvy
441         em.custom_scac3d = tmp_scac3d
442         em.custom_rotc3d = tmp_rotc3d        
443         return {'FINISHED'}
444
445 # Oprerator Class to remove view 
446 class RemoveView(Operator):
447     bl_idname = "object.remove_view"
448     bl_label = "Rmeove the view"
449     
450     index = IntProperty(default = 0)
451     
452     def execute(self, context):              
453         ob = context.object
454         em = bpy.data.objects['Empty for BProjection']
455         
456         ob.active_shape_key_index =  self.index + 1
457         bpy.ops.object.shape_key_remove()
458         
459         if  em.custom_props[self.index].custom_active: 
460             if len(em.custom_props) > 0:
461                 bpy.ops.object.active_view(index = self.index-1)
462             if self.index == 0 and len(em.custom_props) > 1:
463                 bpy.ops.object.active_view(index = 1)            
464                 
465         em.custom_props.remove(self.index)
466                 
467         if len(em.custom_props) == 0:
468             clear_props(context)
469             
470             bpy.ops.object.create_view()            
471                  
472         i=0
473         for item in em.custom_props:
474             item.custom_index = i           
475             i+=1 
476
477         for item in (item for item in em.custom_props if item.custom_active):
478                 ob.active_shape_key_index = item.custom_index+1
479            
480         return {'FINISHED'}
481
482 # Oprerator Class to copy view 
483 class ActiveView(Operator):
484     bl_idname = "object.active_view"
485     bl_label = "Active the view"
486     
487     index = IntProperty(default = 0)
488     
489     def execute(self, context):
490         ob = context.object
491         em = bpy.data.objects['Empty for BProjection']
492         for item in (item for item in em.custom_props if item.custom_active == True):
493             bpy.ops.object.save_view(index = item.custom_index)
494             item.custom_active = False
495         em.custom_props[self.index].custom_active  = True
496         em.custom_active_view = em.custom_props[self.index].custom_active_view 
497         ob.active_shape_key_index =  self.index + 1
498         
499         for i in ob.data.shape_keys.key_blocks:
500             i.mute = True
501         
502         ob.data.shape_keys.key_blocks[ob.active_shape_key_index].mute = False
503         
504         bpy.ops.object.paste_view(index = self.index)         
505         
506         return {'FINISHED'}
507
508 # Draw Class to show the panel
509 class BProjection(Panel):
510     bl_space_type = 'VIEW_3D'
511     bl_region_type = 'UI'
512     bl_label = "BProjection"
513
514     @classmethod
515     def poll(cls, context):
516         return (context.image_paint_object or context.sculpt_object)
517
518     def draw(self, context):        
519         layout = self.layout        
520         if  'Empty for BProjection' in [ob.name for ob in bpy.data.objects]:
521             
522             
523             tex = bpy.data.textures['Texture for BProjection']
524             ob = context.object
525             em = bpy.data.objects['Empty for BProjection']
526             if ob == bpy.data.objects[em.custom_active_object]:            
527                 col = layout.column(align =True)
528                 col.operator("object.removebprojectionplane", text="Remove BProjection plane")           
529                 
530             box = layout.box()
531
532             row = box.row()
533             if not em.custom_expand:
534                 row.prop(em, "custom_expand", text  = "", icon="TRIA_RIGHT", emboss=False)
535                 row.label(text= 'Paint Object: '+ ob.name)
536             else:
537                 row.prop(em, "custom_expand", text = "" , icon="TRIA_DOWN", emboss=False)                
538                 row.label(text= 'Paint Object: '+ ob.name)
539                 
540                 if ob == bpy.data.objects[em.custom_active_object]:
541                     col = box.column(align =True)
542                     col.template_ID(tex, "image", open="image.open")
543                     row  = box.row(align=True)
544                     row.operator('object.applyimage', text="Apply image", icon = 'FILE_TICK')
545                     row.prop(em, "custom_c3d",text="", icon='CURSOR')
546                     row  = box.row(align =True)
547                     row.label(text="Location:")
548                     row  = box.row(align =True)
549                     row.prop(em,'custom_location', text='')
550                     row  = box.row(align =True)            
551                     row.prop(em,'custom_rotation')
552                     row.prop(em,'custom_rotc3d',text="",icon='MANIPUL')            
553                     row  = box.row(align =True)
554                     row.label(text="Scale:")
555                     row  = box.row(align =True) 
556                     if em.custom_linkscale :
557                         row.prop(em, "custom_propscale",text="")
558                         row.prop(em, "custom_linkscale",text="",icon='LINKED')
559                     else: 
560                         row.prop(em,'custom_scale',text='')
561                         row.prop(em, "custom_linkscale",text="",icon='UNLINKED')
562                     row.prop(em,'custom_scac3d',text="",icon='MANIPUL')                            
563                     row  = box.row(align =True)
564                     row.label(text="UV's Offset:")
565                     row  = box.row(align =True)
566                     row.prop(em,'custom_offsetuv',text='')
567                     row.prop(em, "custom_flipuvx",text="",icon='ARROW_LEFTRIGHT')   
568                     row.prop(em, "custom_flipuvy",text="",icon='FULLSCREEN_ENTER') 
569                     row  = box.row(align =True)
570                     row.label(text="UV's Scale:")
571                     row  = box.row(align =True)                            
572                     if em.custom_linkscaleuv:
573                         row.prop(em,'custom_propscaleuv',text='')
574                         row.prop(em, "custom_linkscaleuv",text="",icon='LINKED')
575                     else: 
576                         row.prop(em,'custom_scaleuv',text='')
577                         row.prop(em, "custom_linkscaleuv",text="",icon='UNLINKED')            
578                     row = box.column(align =True)
579                     row.prop(ob.material_slots['Material for BProjection'].material,'alpha', slider = True)
580                     row = box.column(align =True)
581     
582                 if ob == bpy.data.objects[em.custom_active_object]:    
583                     for item in em.custom_props:
584                         box = layout.box()
585                         row = box.row()
586                         if item.custom_active:
587                             row.operator("object.active_view",text = "", icon='RADIOBUT_ON', emboss = False).index = item.custom_index 
588                         else:
589                             row.operator("object.active_view",text = "", icon='RADIOBUT_OFF', emboss = False).index = item.custom_index 
590                         row.prop(item, "custom_active_view", text="")        
591                         row.operator('object.remove_view', text="", icon = 'PANEL_CLOSE', emboss = False).index = item.custom_index
592                     row = layout.row()
593                     row.operator('object.create_view', text="Create View", icon = 'RENDER_STILL') 
594                 else:
595                     col = box.column(align =True)
596                     col.operator("object.change_object", text="Change Object")       
597
598         else:
599             ob = context.object
600             col = layout.column(align = True)
601             col.operator("object.addbprojectionplane", text="Add BProjection plan")
602             col = layout.column(align = True)
603             col.prop(ob, "custom_sub",text="Subdivision level")
604                    
605
606 # Oprerator Class to apply the image to the plane             
607 class ApplyImage(Operator):
608     bl_idname = "object.applyimage"
609     bl_label = "Apply image"
610
611     def execute(self, context):        
612         applyimage(context)
613         
614         return {'FINISHED'}
615
616 # Oprerator Class to make the 4 or 6 point and scale the plan
617 class IntuitiveScale(Operator):
618     bl_idname = "object.intuitivescale"
619     bl_label = "Draw lines"
620
621     def invoke(self, context, event):
622         ob = context.object
623         em = bpy.data.objects['Empty for BProjection']
624         x = event.mouse_region_x
625         y = event.mouse_region_y                
626         if len(ob.grease_pencil.layers.active.frames) == 0: 
627             bpy.ops.gpencil.draw(mode='DRAW', stroke=[{"name":"", "pen_flip":False,
628                                                        "is_start":True, "location":(0, 0, 0),
629                                                        "mouse":(x,y), "pressure":1, "time":0}])
630         else:
631             if em.custom_linkscale:
632                 nb_point = 4
633             else:
634                 nb_point = 6
635                    
636             if len(ob.grease_pencil.layers.active.frames[0].strokes) < nb_point:
637                 bpy.ops.gpencil.draw(mode='DRAW', stroke=[{"name":"", "pen_flip":False,
638                                                            "is_start":True, "location":(0, 0, 0),
639                                                            "mouse":(x,y), "pressure":1, "time":0}])
640                                                            
641             if len(ob.grease_pencil.layers.active.frames[0].strokes) == nb_point:
642                 s = ob.grease_pencil.layers.active.frames[0]
643                 v1 = s.strokes[1].points[0].co - s.strokes[0].points[0].co
644                 if not em.custom_linkscale:
645                     v2 = s.strokes[4].points[0].co - s.strokes[3].points[0].co
646                 else:
647                     v2 = s.strokes[3].points[0].co - s.strokes[2].points[0].co
648                 propx = v1.x/v2.x                
649                 em.custom_scale[0] *= abs(propx)
650                 
651                 if not em.custom_linkscale:
652                     v1 = s.strokes[2].points[0].co - s.strokes[0].points[0].co
653                     v2 = s.strokes[5].points[0].co - s.strokes[3].points[0].co
654                     propy = v1.y/v2.y
655                     em.custom_scale[1] *= abs(propy)
656                 bpy.ops.gpencil.active_frame_delete()
657         
658         return {'FINISHED'}
659
660 # Oprerator Class to configure all wath is needed
661 class AddBProjectionPlane(Operator):
662     bl_idname = "object.addbprojectionplane"
663     bl_label = "Configure"
664     
665     def creatematerial(self, context):        
666         if 'Material for BProjection' not in [mat.name for mat in bpy.data.materials]:            
667             bpy.data.textures.new(name='Texture for BProjection',type='IMAGE')
668     
669             bpy.data.materials.new(name='Material for BProjection')
670             
671             matBProjection = bpy.data.materials['Material for BProjection']
672             matBProjection.texture_slots.add()
673             matBProjection.use_shadeless = True
674             matBProjection.use_transparency = True
675             matBProjection.active_texture = bpy.data.textures['Texture for BProjection']
676         
677             index = matBProjection.active_texture_index
678             matBProjection.texture_slots[index].texture_coords = 'UV'
679         
680         ob = context.object 
681         old_index = ob.active_material_index
682         bpy.ops.object.material_slot_add()
683         index = ob.active_material_index
684         ob.material_slots[index].material = bpy.data.materials['Material for BProjection']
685         bpy.ops.object.material_slot_assign()
686         ob.active_material_index = old_index
687         ob.data.update()
688             
689     def execute(self, context):    
690         if  'Empty for BProjection' not in [ob.name for ob in bpy.data.objects]:                
691             
692             cm = bpy.context.object.mode
693             '''tmp = context.object
694             for ob in (ob for ob in bpy.data.objects if ob.type == 'MESH' and ob.hide == False and context.scene in ob.users_scene):
695                 context.scene.objects.active = ob
696                 bpy.ops.object.mode_set(mode = cm, toggle=False) 
697             
698             context.scene.objects.active = tmp'''
699             bpy.ops.object.mode_set(mode = 'OBJECT', toggle=False)
700             
701             context.space_data.show_relationship_lines = False
702             
703             ob = context.object
704         
705             bpy.ops.object.add()
706             em = context.object
707             em.name = "Empty for BProjection"
708                         
709             context.scene.objects.active = ob
710             ob.select = True
711     
712             bpy.ops.object.editmode_toggle()
713     
714             bpy.ops.mesh.primitive_plane_add()
715             bpy.ops.object.vertex_group_assign(new = True)
716             ob.vertex_groups.active.name = 'texture plane'   
717             bpy.ops.uv.unwrap()
718             
719             bpy.ops.mesh.select_all(action='DESELECT')                                
720             bpy.ops.object.vertex_group_select()
721             
722             bpy.ops.object.editmode_toggle()
723             for i in range(4):
724                 ob.data.edges[len(ob.data.edges)-1-i].crease = 1
725             bpy.ops.object.editmode_toggle()
726             
727             em.custom_sub = ob.custom_sub
728             if em.custom_sub > 0:
729                 bpy.ops.mesh.subdivide(number_cuts = em.custom_sub)
730     
731             em.select = True
732             bpy.ops.object.hook_add_selob()
733             em.select = False
734             em.hide = True   
735                      
736             self.creatematerial(context)
737   
738           
739             bpy.ops.gpencil.data_add()
740             ob.grease_pencil.draw_mode = 'VIEW'
741             bpy.ops.gpencil.layer_add()
742             ob.grease_pencil.layers.active.color = [1.0,0,0]
743             
744             bpy.ops.object.editmode_toggle()
745             
746             bpy.ops.object.shape_key_add(from_mix = False)
747             
748             bpy.ops.object.create_view()
749             # ----------------------------------------------
750             # XXX, this isnt future proof, DON'T USE INDEX's - campbell                    
751             km = bpy.data.window_managers['WinMan'].keyconfigs['Blender'].keymaps['3D View']
752             km.keymap_items[3-1].idname = 'view3d.rotate_view3d'
753             km.keymap_items[19-1].idname = 'view3d.zoom_view3d'
754             km.keymap_items[19-1].properties.delta = 1.0
755             km.keymap_items[20-1].idname = 'view3d.zoom_view3d'
756             km.keymap_items[20-1].properties.delta = -1.0
757             km.keymap_items[4-1].idname = 'view3d.pan_view3d'
758             km.keymap_items[28-1].idname = 'view3d.preset_view3d'
759             km.keymap_items[28-1].properties.view = 'FRONT'
760             km.keymap_items[30-1].idname = 'view3d.preset_view3d'
761             km.keymap_items[30-1].properties.view = 'RIGHT'            
762             km.keymap_items[34-1].idname = 'view3d.preset_view3d'
763             km.keymap_items[34-1].properties.view = 'TOP'
764             km.keymap_items[36-1].idname = 'view3d.preset_view3d'
765             km.keymap_items[36-1].properties.view = 'BACK'
766             km.keymap_items[37-1].idname = 'view3d.preset_view3d'
767             km.keymap_items[37-1].properties.view = 'LEFT'            
768             km.keymap_items[38-1].idname = 'view3d.preset_view3d'
769             km.keymap_items[38-1].properties.view = 'BOTTOM'                                   
770             km = context.window_manager.keyconfigs.default.keymaps['Image Paint']
771             kmi = km.keymap_items.new("object.intuitivescale", 'LEFTMOUSE', 'PRESS', shift=True)
772                         
773             align_to_view(context)
774             
775             context.space_data.cursor_location = em.location
776             
777             bpy.ops.object.mode_set(mode = cm, toggle=False)
778             bpy.data.objects['Empty for BProjection'].custom_active_object = context.object.name
779             
780         return {'FINISHED'}
781
782 # Oprerator Class to remove what is no more needed    
783 class RemoveBProjectionPlane(Operator):
784     bl_idname = "object.removebprojectionplane"
785     bl_label = "Configure"
786
787     def removematerial(self, context):
788         ob = context.object 
789         i = 0
790
791         for ms in ob.material_slots:
792             if ms.name == 'Material for BProjection':
793                 index = i
794             i+=1
795                 
796         ob.active_material_index = index
797         bpy.ops.object.material_slot_remove()
798     
799     def execute(self, context):
800         try:               
801             cm = bpy.context.object.mode
802             bpy.ops.object.mode_set(mode = 'OBJECT', toggle=False)
803             
804             context.space_data.show_relationship_lines = True
805             
806             bpy.ops.object.modifier_remove(modifier="Hook-Empty for BProjection")
807             
808             self.removematerial(context)
809
810             ob = context.object
811     
812             bpy.ops.object.editmode_toggle()
813     
814             bpy.ops.mesh.reveal()
815                                    
816             bpy.ops.mesh.select_all(action='DESELECT')                    
817             
818             ob.vertex_groups.active_index = ob.vertex_groups['texture plane'].index
819             bpy.ops.object.vertex_group_select()
820             bpy.ops.mesh.delete()
821             bpy.ops.object.vertex_group_remove()
822     
823             bpy.ops.object.editmode_toggle()
824    
825             ob.select = False
826                 
827             em = bpy.data.objects['Empty for BProjection']
828             context.scene.objects.active = em
829             em.hide = False
830             em.select = True
831             bpy.ops.object.delete()
832     
833             context.scene.objects.active = ob
834             ob.select = True
835             
836             km = bpy.data.window_managers['WinMan'].keyconfigs['Blender'].keymaps['3D View']
837             # ----------------------------------------------
838             # XXX, this isnt future proof, DON'T USE INDEX's - campbell
839             km.keymap_items[3-1].idname = 'view3d.rotate'
840             km.keymap_items[19-1].idname = 'view3d.zoom'
841             km.keymap_items[19-1].properties.delta = 1.0
842             km.keymap_items[20-1].idname = 'view3d.zoom'
843             km.keymap_items[20-1].properties.delta = -1.0
844             km.keymap_items[4-1].idname = 'view3d.move'
845             km.keymap_items[28-1].idname = 'view3d.viewnumpad'
846             km.keymap_items[28-1].properties.type = 'FRONT'
847             km.keymap_items[30-1].idname = 'view3d.viewnumpad'
848             km.keymap_items[30-1].properties.type = 'RIGHT'            
849             km.keymap_items[34-1].idname = 'view3d.viewnumpad'
850             km.keymap_items[34-1].properties.type = 'TOP'
851             km.keymap_items[36-1].idname = 'view3d.viewnumpad'
852             km.keymap_items[36-1].properties.type = 'BACK'
853             km.keymap_items[37-1].idname = 'view3d.viewnumpad'
854             km.keymap_items[37-1].properties.type = 'LEFT'            
855             km.keymap_items[38-1].idname = 'view3d.viewnumpad'
856             km.keymap_items[38-1].properties.type = 'BOTTOM'            
857             
858             km = context.window_manager.keyconfigs.default.keymaps['Image Paint']
859             #to do
860             for kmi in (kmi for kmi in km.keymap_items if kmi.idname in {"object.intuitivescale", }):
861                     km.keymap_items.remove(kmi)
862             
863             '''tmp = context.object
864             for ob in (ob for ob in bpy.data.objects if ob.type == 'MESH' and ob.hide == False and context.scene in ob.users_scene):
865                 context.scene.objects.active = ob
866                 bpy.ops.object.mode_set(mode = 'OBJECT', toggle=False) 
867             
868             context.scene.objects.active = tmp'''
869             ob = context.object           
870             
871             
872             for i in ob.data.shape_keys.key_blocks:
873                 bpy.ops.object.shape_key_remove()
874             bpy.ops.object.shape_key_remove()
875                
876             bpy.ops.object.mode_set(mode = cm, toggle=False)
877             removecustomprops()
878                     
879         except:
880             nothing = 0
881         
882         return {'FINISHED'}
883
884 # Oprerator Class to remove what is no more needed    
885 class ChangeObject(Operator):
886     bl_idname = "object.change_object"
887     bl_label = "Change Object"
888
889     def removematerial(self, context):
890         ob = context.object 
891         i = 0
892
893         for ms in ob.material_slots:
894             if ms.name == 'Material for BProjection':
895                 index = i
896             i+=1
897                 
898         ob.active_material_index = index
899         bpy.ops.object.material_slot_remove()
900     
901     def execute(self, context):
902             new_ob = context.object
903             em = bpy.data.objects['Empty for BProjection']              
904             context.scene.objects.active = bpy.data.objects[em.custom_active_object]
905             ob = context.object
906             if ob != new_ob:
907                 cm = bpy.context.object.mode
908                 bpy.ops.object.mode_set(mode = 'OBJECT', toggle=False)
909                 
910                 bpy.ops.object.modifier_remove(modifier="Hook-Empty for BProjection") 
911     
912                 ob = context.object
913         
914                 bpy.ops.object.editmode_toggle()
915         
916                 bpy.ops.mesh.reveal()
917                                        
918                 bpy.ops.mesh.select_all(action='DESELECT')                               
919                 ob.vertex_groups.active_index = ob.vertex_groups['texture plane'].index
920                 bpy.ops.object.vertex_group_select()
921                 lo_b = [ob for ob in bpy.data.objects if ob.type == 'MESH']
922                 bpy.ops.mesh.separate(type='SELECTED')
923                 lo_a = [ob for ob in bpy.data.objects if ob.type == 'MESH']
924                 bpy.ops.object.vertex_group_remove()
925                 
926                 for i in lo_b:
927                     lo_a.remove(i)
928                 bplane = lo_a[0]
929                 
930                 bpy.ops.object.editmode_toggle()
931                 
932                 self.removematerial(context)
933                 
934                 bpy.ops.object.mode_set(mode = cm, toggle=False)
935                 
936                 shape_index = ob.active_shape_key_index
937                 
938                 for i in ob.data.shape_keys.key_blocks:
939                     bpy.ops.object.shape_key_remove()
940                 bpy.ops.object.shape_key_remove()
941                 
942                 ob.select = False
943                 
944                 bplane.select = True            
945                 context.scene.objects.active = bplane
946                 for ms in (ms for ms in bplane.material_slots if ms.name != 'Material for BProjection'):
947                     bplane.active_material = ms.material
948                     bpy.ops.object.material_slot_remove()
949                 
950                 for gs in (gs for gs in bplane.vertex_groups if gs.name != 'texture plane'):
951                     bplane.vertex_groups.active_index = gs.index
952                     bpy.ops.object.vertex_group_remove()
953               
954                 context.scene.objects.active = new_ob
955                 cm = new_ob.mode
956                 bpy.ops.object.mode_set(mode = 'OBJECT', toggle=False) 
957                 bpy.ops.object.join()
958     
959                 em.hide = False
960                 em.select = True
961                 new_ob.select = False
962                 bpy.ops.object.location_clear()
963                 bpy.ops.object.rotation_clear()
964                 bpy.ops.object.scale_clear()
965                 context.scene.objects.active = new_ob
966                 bpy.ops.object.editmode_toggle()            
967                 bpy.ops.object.hook_add_selob()
968                 bpy.ops.object.editmode_toggle()
969                 em.hide = True
970                 em.select = False
971                 new_ob.select = True
972                 em.custom_active_object = new_ob.name
973                 tmp = em.custom_c3d
974                 em.custom_c3d = False
975                 bpy.ops.object.active_view(index = shape_index-1)
976                 bpy.ops.object.mode_set(mode = cm, toggle=False)
977                         
978                 sd = context.space_data
979                 r3d = sd.region_3d 
980                 vr = r3d.view_rotation.copy()
981                 vr.invert()
982                 ob_loc = ob.location.copy()            
983                 new_ob_loc = new_ob.location.copy() 
984                 ob_loc.rotate(vr)
985                 new_ob_loc.rotate(vr)
986                 em.custom_location += Vector((ob_loc.x - new_ob_loc.x, ob_loc.y - new_ob_loc.y, 0.0))
987                 em.custom_c3d = tmp
988                     
989             return {'FINISHED'}
990
991 # Oprerator Class to rotate the view3D
992 class RotateView3D(Operator):
993     bl_idname = "view3d.rotate_view3d"
994     bl_label = "Rotate the View3D"
995     
996     first_mouse = Vector((0,0))
997
998     pan = Vector((0,0))
999
1000     key = ['']
1001  
1002     first_time = True
1003     tmp_level = -1
1004     
1005     def vect_sphere(self, context, mx, my):
1006         width = context.area.regions[4].width
1007         height = context.area.regions[4].height
1008            
1009         if width >= height:
1010             ratio = height/width
1011         
1012             x = 2*mx/width
1013             y = 2*ratio*my/height
1014             
1015             x = x - 1
1016             y = y - ratio
1017         else:
1018             ratio = width/height
1019         
1020             x = 2*ratio*mx/width
1021             y = 2*my/height
1022  
1023             x = x - ratio
1024             y = y - 1
1025         
1026         z2 = 1 - x * x - y * y
1027         if z2 > 0:
1028             z= sqrt(z2)
1029         else : z=0
1030             
1031         p = Vector((x, y, z))
1032         p.normalize()
1033         return p
1034     
1035     def tracball(self, context, mx, my, origine):
1036         sd = context.space_data
1037         ob = context.object
1038         em = bpy.data.objects['Empty for BProjection']        
1039             
1040         vr_b = sd.region_3d.view_rotation.copy()
1041         vr_b.invert()
1042         pos_init = sd.region_3d.view_location - origine
1043         sd.region_3d.view_location = origine
1044         
1045         v1 = self.vect_sphere(context, self.first_mouse.x, self.first_mouse.y)
1046         v2 = self.vect_sphere(context, mx, my)
1047                         
1048         axis = Vector.cross(v1,v2);
1049         angle = Vector.angle(v1,v2);
1050             
1051         q =  Quaternion(axis,-2*angle)
1052                         
1053         sd.region_3d.view_rotation *=q
1054         sd.region_3d.update()
1055         
1056         vr_a = sd.region_3d.view_rotation.copy()                           
1057         pos_init.rotate(vr_a*vr_b)            
1058         sd.region_3d.view_location =  pos_init + origine
1059         
1060         self.first_mouse = Vector((mx, my))
1061                 
1062     def modal(self, context, event):                                
1063         ob = context.object
1064         em = bpy.data.objects['Empty for BProjection'] 
1065         reg = context.area.regions[4]        
1066         if event.value == 'PRESS':
1067             self.pan = Vector((event.mouse_region_x, event.mouse_region_y))
1068                     
1069             self.key = [event.type]
1070
1071         if event.value == 'RELEASE':
1072             self.key = [''] 
1073
1074         if event.type == 'MOUSEMOVE':                        
1075             
1076             if '' in self.key:
1077                 self.tracball(context, event.mouse_region_x, event.mouse_region_y,ob.location)
1078                 align_to_view(context)
1079                 if self.first_time:
1080                     rot_ang = context.user_preferences.view.rotation_angle            
1081                     context.user_preferences.view.rotation_angle = 0
1082                     bpy.ops.view3d.view_orbit(type='ORBITLEFT')
1083                     context.user_preferences.view.rotation_angle = rot_ang   
1084                     bpy.ops.view3d.view_persportho()         
1085                     bpy.ops.view3d.view_persportho()
1086                     self.first_time = False
1087           
1088             deltax = event.mouse_region_x - round(self.pan.x)
1089             deltay = event.mouse_region_y - round(self.pan.y)          
1090
1091             if 'G' in self.key:       
1092                 sd = context.space_data              
1093                 l =  sd.region_3d
1094                 vr = l.view_rotation.copy()
1095                 vr.invert()
1096                 
1097                 v_init = Vector((0.0,0.0,1.0))
1098                 
1099                 pos = [-deltax,-deltay]
1100                 v = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v_init)
1101                 pos = [0,0]
1102                 vbl = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v_init)        
1103                 loc = vbl - v            
1104                 loc.rotate(vr)
1105                 em.custom_location += loc              
1106                                    
1107             if 'S' in self.key:                
1108                 s = em.custom_scale
1109                 if em.custom_linkscale:
1110                     em.custom_propscale += deltax/20
1111                 else:
1112                     em.custom_scale = [s[0] + deltax/20, s[1] + deltay/20]
1113                                           
1114             if 'Z' in self.key:                
1115                 em.custom_location.z+=deltax/10
1116                       
1117             if 'R' in self.key:
1118                 em.custom_rotation+=deltax
1119                     
1120             if 'U' in self.key:
1121                 suv = em.custom_scaleuv
1122                 if em.custom_linkscaleuv:    
1123                     em.custom_propscaleuv += deltax/50
1124                 else:
1125                     em.custom_scaleuv= [suv[0] + deltax/50 , suv[1] + deltay/50]               
1126
1127             if 'Y' in self.key:       
1128                 ouv = em.custom_offsetuv
1129                 em.custom_offsetuv = [ouv[0] - deltax/50,ouv[1] - deltay/50] 
1130
1131             self.pan = Vector((event.mouse_region_x, event.mouse_region_y))
1132             self.first_mouse = Vector((event.mouse_region_x, self.first_mouse.y))
1133                         
1134         elif event.type == 'MIDDLEMOUSE'and event.value == 'RELEASE':
1135             if self.tmp_level > -1:
1136                 for sub in context.object.modifiers:
1137                     if sub.type in ['SUBSURF','MULTIRES']:
1138                         sub.levels = self.tmp_level   
1139             
1140             return {'FINISHED'}
1141         
1142         if 'C' in self.key:
1143             clear_props(context)
1144         
1145         if 'O' in self.key:
1146             bpy.ops.object.change_object()            
1147         return {'RUNNING_MODAL'}
1148     
1149     def execute(self, context):        
1150         align_to_view(context)  
1151         
1152         return{'FINISHED'}
1153
1154     def invoke(self, context, event):
1155         context.window_manager.modal_handler_add(self)
1156         self.first_mouse = Vector((event.mouse_region_x,event.mouse_region_y))
1157         self.first_time = True
1158         for sub in context.object.modifiers:
1159             if sub.type in ['SUBSURF', 'MULTIRES']:
1160                 self.tmp_level = sub.levels
1161                 sub.levels = 0
1162         return {'RUNNING_MODAL'}
1163
1164 # Oprerator Class to pan the view3D
1165 class PanView3D(bpy.types.Operator):
1166     bl_idname = "view3d.pan_view3d"
1167     bl_label = "Pan View3D"
1168     
1169     first_mouse = Vector((0,0))
1170     tmp_level = -1
1171
1172     def modal(self, context, event):
1173         ob = context.object
1174         em = bpy.data.objects['Empty for BProjection']
1175         width = context.area.regions[4].width
1176         height = context.area.regions[4].height
1177
1178         deltax = event.mouse_region_x - self.first_mouse.x
1179         deltay = event.mouse_region_y - self.first_mouse.y                
1180         
1181         sd = context.space_data              
1182         r3d =  sd.region_3d
1183         vr = r3d.view_rotation.copy()
1184         vr.invert()
1185         
1186         v_init = Vector((0.0,0.0,1.0))
1187         
1188         pos = [deltax,deltay]
1189         v = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
1190         pos = [0,0]
1191         vbl = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)        
1192         loc = vbl - v       
1193         sd.region_3d.view_location += loc         
1194         loc.rotate(vr)
1195         em.custom_location += loc
1196
1197         self.first_mouse.x = event.mouse_region_x
1198         self.first_mouse.y = event.mouse_region_y
1199
1200         if event.type == 'MIDDLEMOUSE'and event.value == 'RELEASE':
1201             if self.tmp_level > -1:
1202                 for sub in context.object.modifiers:
1203                     if sub.type in ['SUBSURF','MULTIRES']:
1204                         sub.levels = self.tmp_level
1205             return {'FINISHED'}
1206         
1207         return {'RUNNING_MODAL'}
1208                 
1209     def invoke(self, context, event):
1210         context.window_manager.modal_handler_add(self)
1211         self.first_mouse.x = event.mouse_region_x
1212         self.first_mouse.y = event.mouse_region_y   
1213         for sub in context.object.modifiers:
1214             if sub.type in ['SUBSURF', 'MULTIRES']:
1215                 self.tmp_level = sub.levels
1216                 sub.levels = 0  
1217                       
1218         return {'RUNNING_MODAL'}
1219
1220     def execute(self, context):        
1221         align_to_view(context)  
1222         
1223         return{'FINISHED'}
1224
1225 # Oprerator Class to zoom the view3D
1226 class ZoomView3D(Operator):
1227     bl_idname = "view3d.zoom_view3d"
1228     bl_label = "Zoom View3D"
1229
1230     delta = FloatProperty(
1231         name="delta",
1232         description="Delta",
1233         min=-1.0, max=1,
1234         default=1.0)
1235
1236     def invoke(self, context, event):                   
1237         ob = context.object
1238         em = bpy.data.objects['Empty for BProjection']
1239         sd = context.space_data
1240
1241         width = context.area.regions[4].width
1242         height = context.area.regions[4].height              
1243                     
1244         r3d =  sd.region_3d
1245         v_init = Vector((0.0,0.0,1.0))
1246
1247         pos = [width,height]
1248         vtr_b = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
1249         pos = [0,0]
1250         vbl_b = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
1251         len_b = vtr_b - vbl_b
1252        
1253         bpy.ops.view3d.zoom(delta = self.delta)
1254         r3d.update()
1255
1256         pos = [width,height]
1257         vtr_a = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
1258         pos = [0,0]
1259         vbl_a = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init) 
1260         len_a = vtr_a - vbl_a
1261         
1262         fac = len_a.length/len_b.length
1263         r3d.view_location -= ob.location
1264         r3d.view_location *= fac
1265         r3d.view_location += ob.location
1266         vres = Vector((em.custom_location.x*fac,em.custom_location.y*fac,em.custom_location.z))
1267         em.custom_location = vres
1268         
1269         
1270         align_to_view(context)
1271         
1272         return {'FINISHED'}
1273
1274     def execute(self, context):        
1275         align_to_view(context)
1276         
1277         return{'FINISHED'}
1278
1279 # Oprerator Class to use numpad shortcut
1280 class PresetView3D(Operator):
1281     bl_idname = "view3d.preset_view3d"
1282     bl_label = "Preset View3D"
1283
1284     view = StringProperty(name="View", description="Select the view", default='TOP')
1285     def invoke(self, context, event):                   
1286         ob = context.object
1287         em = bpy.data.objects['Empty for BProjection']
1288         origine = ob.location
1289         sd = context.space_data
1290
1291         vr_b = sd.region_3d.view_rotation.copy()
1292         vr_b.invert()
1293         pos_init = sd.region_3d.view_location - origine
1294         sd.region_3d.view_location = origine
1295
1296         tmp = context.user_preferences.view.smooth_view
1297         context.user_preferences.view.smooth_view = 0
1298         bpy.ops.view3d.viewnumpad(type=self.view)
1299         align_to_view(context)        
1300         context.user_preferences.view.smooth_view = tmp
1301
1302         vr_a = sd.region_3d.view_rotation.copy()                           
1303         pos_init.rotate(vr_a*vr_b)            
1304         sd.region_3d.view_location =  pos_init + origine              
1305                     
1306         return {'FINISHED'}
1307
1308 def register():
1309     bpy.utils.register_module(__name__)
1310     createcustomprops(bpy.context)
1311
1312 def unregister():
1313     bpy.utils.unregister_module(__name__)
1314
1315 if __name__ == "__main__":
1316     register()