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