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