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