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