Update for API change: scene.cursor_location -> scene.cursor.location
[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": (2, 0, 1),
6     "blender": (2, 77, 3),
7     "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/3D_interaction/bprojection",
8     "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
9     "category": "Paint"}
10
11 import bpy
12 from bpy.app.handlers import persistent
13 from bpy.types import (
14             Panel,
15             Operator,
16             PropertyGroup,
17             AddonPreferences,
18             )
19 from bpy.props import (
20             IntProperty,
21             FloatProperty,
22             BoolProperty,
23             StringProperty,
24             FloatVectorProperty,
25             CollectionProperty,
26            )
27 from bpy_extras import view3d_utils
28
29 from math import (
30             radians,
31             sqrt,
32             pi,
33             )
34
35 from mathutils import (
36             Quaternion,
37             Vector,
38             )
39
40 BProjection_Empty = 'Empty for BProjection'
41 BProjection_Material = 'Material for BProjection'
42 BProjection_Texture = 'Texture for BProjection'
43
44
45 # Main function for align the plan to view
46 def align_to_view(context):
47     global last_mouse
48     last_mouse = Vector((0, 0))
49     ob = context.object
50     em = bpy.data.objects[BProjection_Empty]
51     rotation = em.custom_rotation
52     scale = em.custom_scale
53     z = em.custom_location.z
54     pos = [em.custom_location.x, em.custom_location.y]
55
56     sd = context.space_data
57     r3d = sd.region_3d
58     r3d.update()
59     vr = r3d.view_rotation
60     quat = Quaternion((0.0, 0.0, 1.0), radians(float(rotation)))
61     v = Vector((pos[0], pos[1], z))
62     v.rotate(vr)
63
64     em = bpy.data.objects[BProjection_Empty]
65     img = bpy.data.textures[BProjection_Texture].image
66
67     if img and img.size[1] != 0:
68         prop = img.size[0] / img.size[1]
69     else:
70         prop = 1
71
72     if em.custom_linkscale:
73         em.scale = Vector((prop * scale[0], scale[0], 1))
74     else:
75         em.scale = Vector((prop * scale[0], scale[1], 1))
76     pos_cur = em.location - sd.cursor.location
77     rot_cur1 = em.rotation_euler.to_quaternion()
78     em.location = v + ob.location
79     em.rotation_euler = Quaternion.to_euler(vr * quat)
80     if em.custom_c3d:
81         if em.custom_old_scale != em.custom_scale:
82             pos_cur = em.location - sd.cursor.location
83         rot_cur2 = em.rotation_euler.to_quaternion()
84         rot_cur1.invert()
85         pos_cur.rotate(rot_cur1)
86         pos_cur.rotate(rot_cur2)
87         v = em.location - pos_cur
88         sd.cursor.location = v
89
90
91 def applyimage(context):
92     img = bpy.data.textures[BProjection_Texture].image
93     ob = context.object
94
95     face = ob.data.polygons
96     uvdata = ob.data.uv_textures.active.data
97
98     for f, d in zip(face, uvdata):
99         if f.select:
100             d.image = img
101
102     align_to_view(context)
103     ob.data.update()
104
105
106 # Function to update the properties
107 def update_Location(self, context):
108     align_to_view(context)
109
110
111 def find_uv(context):
112     obj = context.object
113     me = obj.data.vertices
114     vg = obj.vertex_groups
115     l = []
116     index_uv = 0
117     for face in obj.data.polygons:
118         x = len(face.vertices)
119         for vertex in face.vertices:
120             if (len(me[vertex]. groups) > 0):
121                 for g in me[vertex].groups:
122                     if vg[g.group].name == 'texture plane':
123                         x -= 1
124
125         if x == 0:
126             l.append([index_uv, len(face. vertices)])
127         index_uv += len(face.vertices)
128     return l
129
130
131 # Function to update the scaleUV
132 def update_UVScale(self, context):
133     ob = context.object
134     em = bpy.data.objects[BProjection_Empty]
135     v = Vector((em.custom_offsetuv[0] / 10 + 0.5, em.custom_offsetuv[1] / 10 + 0.5))
136     l = Vector((0.0, 0.0))
137     s = em.custom_scaleuv
138     os = em.custom_old_scaleuv
139
140     l = find_uv(context)
141
142     for i, j in l:
143         for t in range(j):
144             d = ob.data.uv_layers.active.data[i + t]
145             vres = v - d.uv
146             d.uv.x = v.x - vres.x / os[0] * s[0]
147             d.uv.y = v.y - vres.y / os[1] * s[1]
148
149     em.custom_old_scaleuv = s
150
151     applyimage(context)
152
153
154 def update_PropUVScale(self, context):
155     em = bpy.data.objects[BProjection_Empty]
156     if em.custom_linkscaleuv:
157         em.custom_scaleuv = [em.custom_propscaleuv, em.custom_propscaleuv]
158
159
160 def update_LinkUVScale(self, context):
161     em = bpy.data.objects[BProjection_Empty]
162     if em.custom_linkscaleuv:
163         em.custom_propscaleuv = em.custom_scaleuv.x
164         update_PropUVScale(self, context)
165     else:
166         update_UVScale(self, context)
167
168
169 # Function to update the offsetUV
170 def update_UVOffset(self, context):
171     ob = context.object
172     em = bpy.data.objects[BProjection_Empty]
173     o = em.custom_offsetuv
174     oo = em.custom_old_offsetuv
175     l = find_uv(context)
176     for i, j in l:
177         for t in range(j):
178             d = ob.data.uv_layers.active.data[i + t]
179             d.uv = [d.uv[0] - oo[0] / 10 + o[0] / 10, d.uv[1] - oo[1] / 10 + o[1] / 10]
180     em.custom_old_offsetuv = o
181
182     applyimage(context)
183
184
185 # Function to update the flip horizontal
186 def update_FlipUVX(self, context):
187     l = find_uv(context)
188     for i, j in l:
189         for t in range(j):
190             d = context.object.data.uv_layers.active.data[i + t]
191             x = d.uv.x
192             d.uv.x = 1 - x
193
194     applyimage(context)
195
196
197 # Function to update the flip vertical
198 def update_FlipUVY(self, context):
199     l = find_uv(context)
200     for i, j in l:
201         for t in range(j):
202             d = context.object.data.uv_layers.active.data[i + t]
203             y = d.uv[1]
204             d.uv[1] = 1 - y
205
206     applyimage(context)
207
208
209 # Function to update
210 def update_Rotation(self, context):
211     ob = context.object
212     em = bpy.data.objects[BProjection_Empty]
213     if em.custom_rotc3d:
214         angle = em.custom_rotation - em.custom_old_rotation
215         sd = context.space_data
216         vr = sd.region_3d.view_rotation.copy()
217         c = sd.cursor.location - ob.location
218         e = bpy.data.objects[BProjection_Empty].location - ob.location
219         vo = Vector((0.0, 0.0, 1.0))
220         vo.rotate(vr)
221         quat = Quaternion(vo, radians(angle))
222         v = e - c
223         v.rotate(quat)
224         vr.invert()
225         v.rotate(vr)
226         c.rotate(vr)
227         em.custom_location = c + v
228     else:
229         align_to_view(context)
230
231     em.custom_old_rotation = em.custom_rotation
232
233
234 # Function to update scale
235 def update_Scale(self, context):
236     ob = context.object
237     em = bpy.data.objects[BProjection_Empty]
238
239     if em.custom_scac3d:
240         sd = context.space_data
241         r3d = sd.region_3d
242         vr = r3d.view_rotation.copy()
243         vr.invert()
244         e = em.location - ob.location
245         c = sd.cursor.location - ob.location
246         ce = e - c
247
248         s = em.custom_scale
249         os = em.custom_old_scale
250         c.rotate(vr)
251         ce.rotate(vr)
252
253         v = Vector((s.x * ce.x / os.x, s.y * ce.y / os.y, 0.0))
254         em.custom_location = c + v
255
256     else:
257         align_to_view(context)
258
259     em.custom_old_scale = em.custom_scale
260
261
262 def update_PropScale(self, context):
263     em = bpy.data.objects[BProjection_Empty]
264     if em.custom_linkscale:
265         em.custom_scale = [em.custom_propscale, em.custom_propscale]
266
267
268 def update_LinkScale(self, context):
269     em = bpy.data.objects[BProjection_Empty]
270     if em.custom_linkscale:
271         em.custom_propscale = em.custom_scale.x
272         update_PropScale(self, context)
273     else:
274         update_Scale(self, context)
275
276
277 def update_activeviewname(self, context):
278     em = bpy.data.objects[BProjection_Empty]
279     if self.custom_active:
280         em.custom_active_view = self.custom_active_view
281
282
283 def update_style_clone(self, context):
284     km = context.window_manager.keyconfigs.default.keymaps['Image Paint']
285     for kmi in km.keymap_items:
286         if self.custom_style_clone:
287             if kmi.idname == 'paint.image_paint':
288                 kmi.idname = 'paint.bp_paint'
289         else:
290             if kmi.idname == 'paint.bp_paint':
291                 kmi.idname = 'paint.image_paint'
292
293
294 class custom_props(PropertyGroup):
295     custom_fnlevel: IntProperty(
296                         name="Fast navigate level",
297                         description="Increase or decrease the SubSurf level, decrease make navigation faster",
298                         default=0,
299                         )
300     custom_location: FloatVectorProperty(
301                         name="Location",
302                         description="Location of the plane",
303                         default=(1.0, 0, -1.0),
304                         subtype='XYZ',
305                         soft_min=-10,
306                         soft_max=10,
307                         step=0.1,
308                         size=3,
309                         )
310     custom_rotation: FloatProperty(
311                         name="Rotation",
312                         description="Rotate the plane",
313                         min=-180, max=180,
314                         default=0
315                         )
316     custom_scale: FloatVectorProperty(
317                         name="Scales",
318                         description="Scale the planes",
319                         default=(1.0, 1.0),
320                         subtype='XYZ',
321                         min=0.1,
322                         max=10,
323                         soft_min=0.1,
324                         soft_max=10,
325                         step=0.1,
326                         size=2,
327                         )
328     custom_propscale: FloatProperty(
329                         name="PropScale",
330                         description="Scale the Plane",
331                         default=1.0,
332                         min=0.1,
333                         max=10,
334                         soft_min=0.1,
335                         soft_max=10,
336                         step=0.1
337                         )
338
339     custom_linkscale: BoolProperty(
340                         name="linkscale",
341                         default=True
342                         )
343     # UV properties
344     custom_scaleuv: FloatVectorProperty(
345                         name="ScaleUV",
346                         description="Scale the texture's UV",
347                         default=(1.0, 1.0),
348                         min=0.01,
349                         subtype='XYZ',
350                         size=2
351                         )
352     custom_propscaleuv: FloatProperty(
353                         name="PropScaleUV",
354                         description="Scale the texture's UV",
355                         default=1.0,
356                         min=0.01
357                         )
358     custom_offsetuv: FloatVectorProperty(
359                         name="OffsetUV",
360                         description="Decal the texture's UV",
361                         default=(0.0, 0.0),
362                         subtype='XYZ',
363                         size=2
364                         )
365     custom_linkscaleuv: BoolProperty(
366                         name="linkscaleUV",
367                         default=True)
368     custom_flipuvx: BoolProperty(
369                         name="flipuvx",
370                         default=False
371                         )
372     custom_flipuvy: BoolProperty(
373                         name="flipuvy",
374                         default=False
375                         )
376     # other properties
377     custom_active: BoolProperty(
378                         name="custom_active",
379                         default=True
380                         )
381     custom_expand: BoolProperty(
382                         name="expand",
383                         default=False
384                         )
385     custom_style_clone: BoolProperty(
386                         name="custom_style_clone",
387                         default=False
388                         )
389     custom_active_view: StringProperty(
390                         name="custom_active_view",
391                         default="View",
392                         update=update_activeviewname
393                         )
394     custom_image: StringProperty(
395                         name="custom_image",
396                         default=""
397                         )
398     custom_index: IntProperty()
399
400
401 # Function to create custom properties
402 def createcustomprops(context):
403     Ob = bpy.types.Object
404
405     Ob.custom_fnlevel = IntProperty(
406                         name="Fast navigate level",
407                         description="Increase or decrease the SubSurf level, decrease make navigation faster",
408                         default=0
409                         )
410     # plane properties
411     Ob.custom_location = FloatVectorProperty(
412                         name="Location",
413                         description="Location of the plane",
414                         default=(1.0, 0, -1.0),
415                         subtype='XYZ',
416                         size=3,
417                         step=0.5,
418                         soft_min=-10,
419                         soft_max=10,
420                         update=update_Location
421                         )
422     Ob.custom_rotation = FloatProperty(
423                         name="Rotation",
424                         description="Rotate the plane",
425                         min=-180,
426                         max=180,
427                         default=0,
428                         update=update_Rotation
429                         )
430     Ob.custom_old_rotation = FloatProperty(
431                         name="old_Rotation",
432                         description="Old Rotate the plane",
433                         min=-180,
434                         max=180,
435                         default=0
436                         )
437     Ob.custom_scale = FloatVectorProperty(
438                         name="Scales",
439                         description="Scale the planes",
440                         subtype='XYZ',
441                         default=(1.0, 1.0),
442                         min=0.1,
443                         max=10,
444                         soft_min=0.1,
445                         soft_max=10,
446                         size=2,
447                         step=0.5,
448                         update=update_Scale
449                         )
450     Ob.custom_propscale = FloatProperty(
451                         name="PropScale",
452                         description="Scale the Plane",
453                         default=1.0,
454                         min=0.1,
455                         soft_min=0.1,
456                         soft_max=10,
457                         step=0.5,
458                         update=update_PropScale
459                         )
460     Ob.custom_old_scale = FloatVectorProperty(
461                         name="old_Scales",
462                         description="Old Scale the planes",
463                         subtype='XYZ',
464                         default=(1.0, 1.0),
465                         min=0.1,
466                         size=2
467                         )
468     Ob.custom_linkscale = BoolProperty(
469                         name="linkscale",
470                         default=True,
471                         update=update_LinkScale
472                         )
473     Ob.custom_sub = IntProperty(
474                         name="Subdivide",
475                         description="Number of subdivision of the plane",
476                         min=0,
477                         max=20,
478                         default=0
479                         )
480     # UV properties
481     Ob.custom_scaleuv = FloatVectorProperty(
482                         name="ScaleUV",
483                         description="Scale the texture's UV",
484                         default=(1.0, 1.0),
485                         soft_min=0.01,
486                         soft_max=100,
487                         min=0.01,
488                         subtype='XYZ',
489                         size=2,
490                         update=update_UVScale
491                         )
492     Ob.custom_propscaleuv = FloatProperty(
493                         name="PropScaleUV",
494                         description="Scale the texture's UV",
495                         default=1.0,
496                         soft_min=0.01,
497                         soft_max=100,
498                         min=0.01,
499                         update=update_PropUVScale
500                         )
501     Ob.custom_old_scaleuv = FloatVectorProperty(
502                         name="old_ScaleUV",
503                         description="Scale the texture's UV",
504                         default=(1.0, 1.0),
505                         min=0.01,
506                         subtype='XYZ',
507                         size=2
508                         )
509     Ob.custom_offsetuv = FloatVectorProperty(
510                         name="OffsetUV",
511                         description="Decal the texture's UV",
512                         default=(0.0, 0.0),
513                         subtype='XYZ',
514                         size=2,
515                         update=update_UVOffset
516                         )
517     Ob.custom_old_offsetuv = FloatVectorProperty(
518                         name="old_OffsetUV",
519                         description="Decal the texture's UV",
520                         default=(0.0, 0.0),
521                         subtype='XYZ',
522                         size=2
523                         )
524     Ob.custom_linkscaleuv = BoolProperty(
525                         name="linkscaleUV",
526                         default=True,
527                         update=update_LinkUVScale
528                         )
529     Ob.custom_flipuvx = BoolProperty(
530                         name="flipuvx",
531                         default=False,
532                         update=update_FlipUVX
533                         )
534     Ob.custom_flipuvy = BoolProperty(
535                         name="flipuvy",
536                         default=False,
537                         update=update_FlipUVY
538                         )
539     # other properties
540     Ob.custom_c3d = BoolProperty(
541                         name="c3d",
542                         default=True
543                         )
544     Ob.custom_rotc3d = BoolProperty(
545                         name="rotc3d",
546                         default=False
547                         )
548     Ob.custom_scac3d = BoolProperty(
549                         name="scac3d",
550                         default=False
551                         )
552     Ob.custom_expand = BoolProperty(
553                         name="expand",
554                         default=True
555                         )
556     Ob.custom_style_clone = BoolProperty(
557                         name="custom_style_clone",
558                         default=False,
559                         update=update_style_clone
560                         )
561     Ob.custom_active_view = StringProperty(
562                         name="custom_active_view",
563                         default="View"
564                         )
565     try:
566         Ob.custom_active_object = StringProperty(
567                         name="custom_active_object",
568                         default=context.object.name
569                         )
570     except:
571         Ob.custom_active_object = StringProperty(
572                         name="custom_active_object",
573                         default='debut'
574                         )
575     Ob.custom_props = CollectionProperty(type=custom_props)
576
577
578 # Function to remove custom properties
579 def removecustomprops():
580     list_prop = ['custom_location', 'custom_rotation',
581                  'custom_old_rotation', 'custom_scale',
582                  'custom_old_scale', 'custom_c3d',
583                  'custom_rotc3d', 'custom_scaleuv',
584                  'custom_flipuvx', 'custom_flipuvy',
585                  'custom_linkscale', 'custom_linkscaleuv',
586                  'custom_old_scaleuv', 'custom_offsetuv',
587                  'custom_old_offsetuv', 'custom_scac3d',
588                  'custom_sub', 'custom_expand',
589                  'custom_style_clone', 'custom_active_view',
590                  'custom_propscaleuv', 'custom_props', 'custom_propscale']
591     for prop in list_prop:
592         try:
593             del bpy.data.objects[BProjection_Empty][prop]
594         except:
595             pass
596
597
598 def clear_props(context):
599     em = bpy.data.objects[BProjection_Empty]
600     em.custom_scale = [1, 1]
601     em.custom_rotation = 0
602     em.custom_scaleuv = [1.0, 1.0]
603     em.custom_offsetuv = [0.0, 0.0]
604     em.custom_propscaleuv = 1.0
605     em.custom_propscale = 1.0
606     if em.custom_flipuvx is True:
607         em.custom_flipuvx = False
608     if em.custom_flipuvy is True:
609         em.custom_flipuvy = False
610
611
612 # Operator Class to create view
613 class CreateView(Operator):
614     bl_idname = "object.create_view"
615     bl_label = "Create a new view"
616
617     def execute(self, context):
618         ob = context.object
619         em = bpy.data.objects[BProjection_Empty]
620         new_props = em.custom_props.add()
621         em.custom_active_view = new_props.custom_active_view
622         ob.data.shape_keys.key_blocks[ob.active_shape_key_index].mute = True
623         bpy.ops.object.shape_key_add(from_mix=False)
624         ob.data.shape_keys.key_blocks[ob.active_shape_key_index].value = 1.0
625         new_props.custom_index = len(em.custom_props) - 1
626         bpy.ops.object.active_view(index=new_props.custom_index)
627
628         return {'FINISHED'}
629
630
631 # Operator Class to copy view
632 class SaveView(Operator):
633     bl_idname = "object.save_view"
634     bl_label = "copy the view"
635
636     index: IntProperty(default=0)
637
638     def execute(self, context):
639         em = bpy.data.objects[BProjection_Empty]
640         prop = em.custom_props[self.index]
641         prop.custom_rotation = em.custom_rotation
642         prop.custom_scale = em.custom_scale
643         prop.custom_linkscale = em.custom_linkscale
644         prop.custom_scaleuv = em.custom_scaleuv
645         prop.custom_propscale = em.custom_propscale
646         prop.custom_offsetuv = em.custom_offsetuv
647         prop.custom_linkscaleuv = em.custom_linkscaleuv
648         prop.custom_propscaleuv = em.custom_propscaleuv
649         prop.custom_flipuvx = em.custom_flipuvx
650         prop.custom_flipuvy = em.custom_flipuvy
651         try:
652             prop.custom_image = bpy.data.textures[BProjection_Texture].image.name
653         except:
654             pass
655
656         return {'FINISHED'}
657
658
659 # Operator Class to copy view
660 class PasteView(Operator):
661     bl_idname = "object.paste_view"
662     bl_label = "paste the view"
663
664     index: IntProperty(default=0)
665
666     def execute(self, context):
667         em = bpy.data.objects[BProjection_Empty]
668         tmp_scac3d = em.custom_scac3d
669         tmp_rotc3d = em.custom_rotc3d
670         em.custom_scac3d = False
671         em.custom_rotc3d = False
672         prop = em.custom_props[self.index]
673         em.custom_linkscale = prop.custom_linkscale
674         em.custom_offsetuv = prop.custom_offsetuv
675         em.custom_linkscaleuv = prop.custom_linkscaleuv
676         em.custom_scaleuv = prop.custom_scaleuv
677         em.custom_propscaleuv = prop.custom_propscaleuv
678         em.custom_rotation = prop.custom_rotation
679         em.custom_scale = prop.custom_scale
680         em.custom_propscale = prop.custom_propscale
681         if prop.custom_image != '':
682             if bpy.data.textures[BProjection_Texture].image.name != prop.custom_image:
683                 bpy.data.textures[BProjection_Texture].image = bpy.data.images[prop.custom_image]
684                 applyimage(context)
685         if em.custom_flipuvx != prop.custom_flipuvx:
686             em.custom_flipuvx = prop.custom_flipuvx
687         if em.custom_flipuvy != prop.custom_flipuvy:
688             em.custom_flipuvy = prop.custom_flipuvy
689         em.custom_scac3d = tmp_scac3d
690         em.custom_rotc3d = tmp_rotc3d
691         return {'FINISHED'}
692
693
694 # Operator Class to remove view
695 class RemoveView(Operator):
696     bl_idname = "object.remove_view"
697     bl_label = "Rmeove the view"
698
699     index: IntProperty(default=0)
700
701     def execute(self, context):
702         ob = context.object
703         em = bpy.data.objects[BProjection_Empty]
704
705         ob.active_shape_key_index = self.index + 1
706         bpy.ops.object.shape_key_remove()
707
708         if em.custom_props[self.index].custom_active:
709             if len(em.custom_props) > 0:
710                 bpy.ops.object.active_view(index=self.index - 1)
711             if self.index == 0 and len(em.custom_props) > 1:
712                 bpy.ops.object.active_view(index=1)
713
714         em.custom_props.remove(self.index)
715
716         if len(em.custom_props) == 0:
717             clear_props(context)
718
719             bpy.ops.object.create_view()
720
721         i = 0
722         for item in em.custom_props:
723             item.custom_index = i
724             i += 1
725
726         for item in (item for item in em.custom_props if item.custom_active):
727                 ob.active_shape_key_index = item.custom_index + 1
728
729         return {'FINISHED'}
730
731
732 # Operator Class to copy view
733 class ActiveView(Operator):
734     bl_idname = "object.active_view"
735     bl_label = "Active the view"
736
737     index: IntProperty(default=0)
738
739     def execute(self, context):
740         ob = context.object
741         em = bpy.data.objects[BProjection_Empty]
742         for item in (item for item in em.custom_props if item.custom_active is True):
743             bpy.ops.object.save_view(index=item.custom_index)
744             item.custom_active = False
745         em.custom_props[self.index].custom_active = True
746         em.custom_active_view = em.custom_props[self.index].custom_active_view
747         ob.active_shape_key_index = self.index + 1
748
749         for i in ob.data.shape_keys.key_blocks:
750             i.mute = True
751
752         ob.data.shape_keys.key_blocks[ob.active_shape_key_index].mute = False
753
754         bpy.ops.object.paste_view(index=self.index)
755
756         return {'FINISHED'}
757
758
759 # Draw Class to show the panel
760 class BProjection(Panel):
761     bl_space_type = 'VIEW_3D'
762     bl_region_type = 'UI'
763     bl_label = "BProjection"
764
765     @classmethod
766     def poll(cls, context):
767         return (context.image_paint_object or context.sculpt_object)
768
769     def draw(self, context):
770         layout = self.layout
771         if BProjection_Empty in [ob.name for ob in bpy.data.objects]:
772             tex = bpy.data.textures[BProjection_Texture]
773
774             ob = context.object
775             em = bpy.data.objects[BProjection_Empty]
776             if ob == bpy.data.objects[em.custom_active_object]:
777                 col = layout.column(align=True)
778                 col.operator("object.removebprojectionplane", text="Remove BProjection plane")
779
780             try:
781                 matBProjection = bpy.data.materials[BProjection_Material]
782             except:
783                 matBProjection = None
784
785             box = layout.box()
786
787             row = box.row()
788             if not em.custom_expand:
789                 row.prop(em, "custom_expand", text="", icon="TRIA_RIGHT", emboss=False)
790                 row.label(text='Paint Object: ' + ob.name)
791             else:
792                 row.prop(em, "custom_expand", text="", icon="TRIA_DOWN", emboss=False)
793                 row.label(text='Paint Object: ' + ob.name)
794
795                 if ob == bpy.data.objects[em.custom_active_object]:
796                     col = box.column(align=True)
797                     col.template_ID(tex, "image", open="image.open")
798                     row = box.row(align=True)
799                     row.operator('object.applyimage', text="Apply image", icon='FILE_TICK')
800                     row.prop(em, "custom_c3d", text="", icon='PIVOT_CURSOR')
801                     row = box.row(align=True)
802                     row.label(text="Location:")
803                     row = box.row(align=True)
804                     row.prop(em, 'custom_location', text='')
805                     row = box.row(align=True)
806                     row.prop(em, 'custom_rotation')
807                     row.prop(em, 'custom_rotc3d', text="", icon='MANIPUL')
808                     row = box.row(align=True)
809                     row.label(text="Scale:")
810                     row = box.row(align=True)
811                     if em.custom_linkscale:
812                         row.prop(em, "custom_propscale", text="")
813                         row.prop(em, "custom_linkscale", text="", icon='LINKED')
814                     else:
815                         row.prop(em, 'custom_scale', text='')
816                         row.prop(em, "custom_linkscale", text="", icon='UNLINKED')
817                     row.prop(em, 'custom_scac3d', text="", icon='MANIPUL')
818                     row = box.row(align=True)
819                     row.label(text="UV's Offset:")
820                     row = box.row(align=True)
821                     row.prop(em, 'custom_offsetuv', text='')
822                     row.prop(em, "custom_flipuvx", text="", icon='ARROW_LEFTRIGHT')
823                     row.prop(em, "custom_flipuvy", text="", icon='FULLSCREEN_ENTER')
824                     row = box.row(align=True)
825                     row.label(text="UV's Scale:")
826                     row = box.row(align=True)
827                     if em.custom_linkscaleuv:
828                         row.prop(em, 'custom_propscaleuv', text='')
829                         row.prop(em, "custom_linkscaleuv", text="", icon='LINKED')
830                     else:
831                         row.prop(em, 'custom_scaleuv', text='')
832                         row.prop(em, "custom_linkscaleuv", text="", icon='UNLINKED')
833
834                     if matBProjection:
835                         if (context.scene.game_settings.material_mode == 'GLSL' and
836                            context.space_data.viewport_shade == 'TEXTURED'):
837                             row = box.column(align=True)
838                             row.prop(matBProjection, 'alpha', slider=True)
839
840                     row = box.column(align=True)
841                     row.prop(ob, "custom_fnlevel")
842                     row = box.column(align=True)
843                     if not em.custom_style_clone:
844                         row.prop(em, "custom_style_clone", text="Style Clone Normal", icon='RENDERLAYERS')
845                     else:
846                         row.prop(em, "custom_style_clone", text="Style Clone New", icon='RENDERLAYERS')
847                     row = box.column(align=True)
848
849                 if ob == bpy.data.objects[em.custom_active_object]:
850                     for item in em.custom_props:
851                         box = layout.box()
852                         row = box.row()
853                         if item.custom_active:
854                             row.operator("object.active_view", text="",
855                                          icon='RADIOBUT_ON', emboss=False).index = item.custom_index
856                         else:
857                             row.operator("object.active_view", text="",
858                                          icon='RADIOBUT_OFF', emboss=False).index = item.custom_index
859                         row.prop(item, "custom_active_view", text="")
860                         row.operator('object.remove_view', text="",
861                                      icon='PANEL_CLOSE', emboss=False).index = item.custom_index
862                     row = layout.row()
863                     row.operator('object.create_view', text="Create View", icon='RENDER_STILL')
864                 else:
865                     col = box.column(align=True)
866                     col.operator("object.change_object", text="Change Object")
867
868         else:
869             ob = context.object
870             col = layout.column(align=True)
871
872             if ob.active_material is None:
873                 col.label(text="Add a material first!", icon="ERROR")
874             elif ob.data.uv_textures.active is None:
875                 col.label(text="Create UVMap first!!", icon="ERROR")
876             else:
877                 col.operator("object.addbprojectionplane", text="Add BProjection plane")
878                 col = layout.column(align=True)
879                 col.prop(ob, "custom_sub", text="Subdivision level")
880
881
882 # Operator Class to apply the image to the plane
883 class ApplyImageOB(Operator):
884     bl_idname = "object.applyimage"
885     bl_label = "Apply image"
886
887     def execute(self, context):
888         applyimage(context)
889
890         return {'FINISHED'}
891
892
893 # Operator Class to make the 4 or 6 point and scale the plan
894 class IntuitiveScale(Operator):
895     bl_idname = "object.intuitivescale"
896     bl_label = "Draw lines"
897
898     def invoke(self, context, event):
899         ob = context.object
900         em = bpy.data.objects[BProjection_Empty]
901         x = event.mouse_region_x
902         y = event.mouse_region_y
903         draw_stroke = {"name": "", "pen_flip": False,
904                        "is_start": True, "location": (0, 0, 0),
905                        "mouse": (x, y), "pressure": 1,
906                        "size": 0.5, "time": 0}
907
908         if len(ob.grease_pencil.layers.active.frames) == 0:
909             bpy.ops.gpencil.draw(mode='DRAW', stroke=[draw_stroke])
910         else:
911             if em.custom_linkscale:
912                 nb_point = 4
913             else:
914                 nb_point = 6
915
916             if len(ob.grease_pencil.layers.active.frames[0].strokes) < nb_point:
917                 bpy.ops.gpencil.draw(mode='DRAW', stroke=[draw_stroke])
918
919             if len(ob.grease_pencil.layers.active.frames[0].strokes) == nb_point:
920                 s = ob.grease_pencil.layers.active.frames[0]
921                 v1 = s.strokes[1].points[0].co - s.strokes[0].points[0].co
922                 if not em.custom_linkscale:
923                     v2 = s.strokes[4].points[0].co - s.strokes[3].points[0].co
924                 else:
925                     v2 = s.strokes[3].points[0].co - s.strokes[2].points[0].co
926                 if (v1.x and v2.x) != 0:
927                     propx = v1.x / v2.x
928                     em.custom_scale[0] *= abs(propx)
929
930                 if not em.custom_linkscale:
931                     v1 = s.strokes[2].points[0].co - s.strokes[0].points[0].co
932                     v2 = s.strokes[5].points[0].co - s.strokes[3].points[0].co
933                     if (v1.y and v2.y) != 0:
934                         propy = v1.y / v2.y
935                         em.custom_scale[1] *= abs(propy)
936                 bpy.ops.gpencil.active_frame_delete()
937
938         return {'FINISHED'}
939
940
941 # Operator Class to configure all what's needed
942 class AddBProjectionPlane(Operator):
943     bl_idname = "object.addbprojectionplane"
944     bl_label = "Configure"
945
946     def creatematerial(self, context):
947         if 'Material for BProjection' not in [mat.name for mat in bpy.data.materials]:
948             bpy.data.textures.new(name='Texture for BProjection', type='IMAGE')
949
950             bpy.data.materials.new(name='Material for BProjection')
951
952             matBProjection = bpy.data.materials['Material for BProjection']
953             matBProjection.texture_slots.add()
954             matBProjection.use_shadeless = True
955             matBProjection.use_transparency = True
956             matBProjection.active_texture = bpy.data.textures['Texture for BProjection']
957
958             index = matBProjection.active_texture_index
959             matBProjection.texture_slots[index].texture_coords = 'UV'
960
961         ob = context.object
962         old_index = ob.active_material_index
963         bpy.ops.object.material_slot_add()
964         index = ob.active_material_index
965         ob.material_slots[index].material = bpy.data.materials['Material for BProjection']
966         bpy.ops.object.material_slot_assign()
967         ob.active_material_index = old_index
968         ob.data.update()
969
970     def execute(self, context):
971         if BProjection_Empty not in [ob.name for ob in bpy.data.objects]:
972
973             cm = bpy.context.object.mode
974             bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
975
976             context.space_data.show_relationship_lines = False
977
978             ob = context.object
979
980             bpy.ops.object.add()
981             em = context.object
982             em.name = BProjection_Empty
983
984             context.view_layer.objects.active = ob
985             ob.select_set(True)
986
987             bpy.ops.object.editmode_toggle()
988
989             bpy.ops.mesh.primitive_plane_add()
990
991             # fix the vertex group creation
992             bpy.ops.object.vertex_group_assign_new()
993             ob.vertex_groups.active.name = 'texture plane'
994             bpy.ops.uv.unwrap()
995
996             bpy.ops.mesh.select_all(action='DESELECT')
997             bpy.ops.object.vertex_group_select()
998
999             bpy.ops.object.editmode_toggle()
1000             for i in range(4):
1001                 ob.data.edges[len(ob.data.edges) - 1 - i].crease = 1
1002             bpy.ops.object.editmode_toggle()
1003
1004             em.custom_sub = ob.custom_sub
1005             if em.custom_sub > 0:
1006                 bpy.ops.mesh.subdivide(number_cuts=em.custom_sub)
1007
1008             em.select_set(True)
1009             bpy.ops.object.hook_add_selob()
1010             em.select_set(False)
1011             em.hide = True
1012
1013             self.creatematerial(context)
1014
1015             # fix the grease pencil for 2.78
1016             bpy.ops.gpencil.data_add()
1017             bpy.context.scene.tool_settings.grease_pencil_source = 'OBJECT'
1018             bpy.context.tool_settings.gpencil_stroke_placement_view3d = 'VIEW'
1019             bpy.ops.gpencil.layer_add()
1020             bpy.ops.gpencil.palette_add()
1021
1022             palette = bpy.context.active_gpencil_palette
1023             palette_color = palette.colors.new()
1024             palette_color.color = (1.0, 0, 0)
1025
1026             bpy.context.space_data.show_grease_pencil = True
1027
1028             bpy.ops.object.editmode_toggle()
1029
1030             bpy.ops.object.shape_key_add(from_mix=False)
1031
1032             bpy.ops.object.create_view()
1033
1034             km = bpy.data.window_managers['WinMan'].keyconfigs['Blender'].keymaps['3D View']
1035             l = ['view3d.rotate', 'view3d.move', 'view3d.zoom', 'view3d.viewnumpad', 'paint.bp_paint',
1036                  'MOUSE', 'KEYBOARD', 'LEFT', 'MIDDLEMOUSE', 'WHEELINMOUSE', 'WHEELOUTMOUSE', 'NUMPAD_1',
1037                  'NUMPAD_3', 'NUMPAD_7']
1038             for kmi in km.keymap_items:
1039                 if kmi.idname in l and kmi.map_type in l and kmi.type in l:
1040                     try:
1041                         p = kmi.properties.delta
1042                         if p == -1 or p == 1:
1043                             kmi.idname = 'view3d.zoom_view3d'
1044                             kmi.properties.delta = p
1045                     except:
1046                         try:
1047                             p = kmi.properties.type
1048                             if kmi.shift is False:
1049                                 kmi.idname = 'view3d.preset_view3d'
1050                                 kmi.properties.view = p
1051                         except:
1052                             if kmi.idname == 'view3d.rotate':
1053                                 kmi.idname = 'view3d.rotate_view3d'
1054                             if kmi.idname == 'view3d.move':
1055                                 kmi.idname = 'view3d.pan_view3d'
1056
1057             km = context.window_manager.keyconfigs.default.keymaps['Image Paint']
1058
1059             kmi = km.keymap_items.new("object.intuitivescale", 'LEFTMOUSE', 'PRESS', shift=True)
1060             kmi = km.keymap_items.new("object.bp_grab", 'G', 'PRESS')
1061             kmi = km.keymap_items.new("object.bp_rotate", 'R', 'PRESS')
1062             kmi = km.keymap_items.new("object.bp_scale", 'S', 'PRESS', ctrl=True)
1063             kmi = km.keymap_items.new("object.bp_scaleuv", 'U', 'PRESS')
1064             kmi = km.keymap_items.new("object.bp_offsetuv", 'Y', 'PRESS')
1065             kmi = km.keymap_items.new("object.bp_clear_prop", 'C', 'PRESS')
1066             kmi = km.keymap_items.new("object.bp_toggle_alpha", 'Q', 'PRESS')
1067             align_to_view(context)
1068
1069             context.space_data.cursor_location = em.location
1070
1071             bpy.ops.object.mode_set(mode=cm, toggle=False)
1072             bpy.data.objects[BProjection_Empty].custom_active_object = context.object.name
1073
1074         return {'FINISHED'}
1075
1076
1077 # Operator Class to remove what is no more needed
1078 class RemoveBProjectionPlane(Operator):
1079     bl_idname = "object.removebprojectionplane"
1080     bl_label = "Configure"
1081
1082     def removematerial(self, context):
1083         ob = context.object
1084         i = 0
1085
1086         for ms in ob.material_slots:
1087             if ms.name == BProjection_Material:
1088                 index = i
1089             i += 1
1090
1091         ob.active_material_index = index
1092         bpy.ops.object.material_slot_remove()
1093
1094     def execute(self, context):
1095         try:
1096             cm = bpy.context.object.mode
1097             bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1098
1099             context.space_data.show_relationship_lines = True
1100
1101             bpy.ops.object.modifier_remove(modifier="Hook-Empty for BProjection")
1102
1103             self.removematerial(context)
1104
1105             ob = context.object
1106
1107             bpy.ops.object.editmode_toggle()
1108
1109             bpy.ops.mesh.reveal()
1110
1111             bpy.ops.mesh.select_all(action='DESELECT')
1112
1113             ob.vertex_groups.active_index = ob.vertex_groups['texture plane'].index
1114             bpy.ops.object.vertex_group_select()
1115             bpy.ops.mesh.delete()
1116             bpy.ops.object.vertex_group_remove()
1117
1118             bpy.ops.object.editmode_toggle()
1119
1120             ob.select_set(False)
1121
1122             em = bpy.data.objects[BProjection_Empty]
1123             context.view_layer.objects.active = em
1124             em.hide = False
1125             em.select_set(True)
1126             bpy.ops.object.delete()
1127
1128             context.view_layer.objects.active = ob
1129             ob.select_set(True)
1130
1131             reinitkey()
1132             ob = context.object
1133
1134             for i in ob.data.shape_keys.key_blocks:
1135                 bpy.ops.object.shape_key_remove()
1136             bpy.ops.object.shape_key_remove()
1137
1138             bpy.ops.object.mode_set(mode=cm, toggle=False)
1139             removecustomprops()
1140
1141         except:
1142             pass
1143
1144         return {'FINISHED'}
1145
1146
1147 def reinitkey():
1148     km = bpy.data.window_managers['WinMan'].keyconfigs['Blender'].keymaps['3D View']
1149     l = ['view3d.zoom_view3d', 'view3d.preset_view3d', 'view3d.rotate_view3d',
1150          'view3d.pan_view3d', 'MOUSE', 'KEYBOARD', 'MIDDLEMOUSE', 'WHEELINMOUSE',
1151          'WHEELOUTMOUSE', 'NUMPAD_1', 'NUMPAD_3', 'NUMPAD_7']
1152     for kmi in km.keymap_items:
1153         if kmi.idname in l and kmi.map_type in l and kmi.type in l:
1154             try:
1155                 p = kmi.properties.delta
1156                 if p == -1 or p == 1:
1157                     kmi.idname = 'view3d.zoom'
1158                     kmi.properties.delta = p
1159             except:
1160                 try:
1161                     p = kmi.properties.view
1162                     if kmi.shift is False:
1163                         kmi.idname = 'view3d.viewnumpad'
1164                         kmi.properties.type = p
1165                 except:
1166                     if kmi.idname == 'view3d.rotate_view3d':
1167                         kmi.idname = 'view3d.rotate'
1168                     if kmi.idname == 'view3d.pan_view3d':
1169                         kmi.idname = 'view3d.move'
1170
1171     km = bpy.context.window_manager.keyconfigs.default.keymaps['Image Paint']
1172
1173     for kmi in km.keymap_items:
1174         if kmi.idname == 'paint.bp_paint':
1175             kmi.idname = 'paint.image_paint'
1176
1177     for kmi in (kmi for kmi in km.keymap_items if kmi.idname in
1178                 {"object.intuitivescale", "object.bp_grab", "object.bp_rotate",
1179                  "object.bp_scale", "object.bp_scaleuv", "object.bp_clear_prop",
1180                  "object.bp_offsetuv", "object.bp_toggle_alpha", }):
1181             km.keymap_items.remove(kmi)
1182
1183
1184 # Operator Class to remove what is no more needed
1185 class ChangeObject(Operator):
1186     bl_idname = "object.change_object"
1187     bl_label = "Change Object"
1188
1189     def removematerial(self, context):
1190         ob = context.object
1191         i = 0
1192
1193         for ms in ob.material_slots:
1194             if ms.name == BProjection_Material:
1195                 index = i
1196             i += 1
1197
1198         ob.active_material_index = index
1199         bpy.ops.object.material_slot_remove()
1200
1201     def execute(self, context):
1202             new_ob = context.object
1203             em = bpy.data.objects[BProjection_Empty]
1204             context.view_layer.objects.active = bpy.data.objects[em.custom_active_object]
1205             ob = context.object
1206             if ob != new_ob:
1207                 cm = bpy.context.object.mode
1208                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1209
1210                 bpy.ops.object.modifier_remove(modifier="Hook-Empty for BProjection")
1211
1212                 ob = context.object
1213
1214                 bpy.ops.object.editmode_toggle()
1215
1216                 bpy.ops.mesh.reveal()
1217
1218                 bpy.ops.mesh.select_all(action='DESELECT')
1219                 ob.vertex_groups.active_index = ob.vertex_groups['texture plane'].index
1220                 bpy.ops.object.vertex_group_select()
1221                 lo_b = [ob for ob in bpy.data.objects if ob.type == 'MESH']
1222                 bpy.ops.mesh.separate(type='SELECTED')
1223                 lo_a = [ob for ob in bpy.data.objects if ob.type == 'MESH']
1224                 bpy.ops.object.vertex_group_remove()
1225
1226                 for i in lo_b:
1227                     lo_a.remove(i)
1228                 bplane = lo_a[0]
1229
1230                 bpy.ops.object.editmode_toggle()
1231
1232                 self.removematerial(context)
1233
1234                 bpy.ops.object.mode_set(mode=cm, toggle=False)
1235
1236                 shape_index = ob.active_shape_key_index
1237
1238                 for i in ob.data.shape_keys.key_blocks:
1239                     bpy.ops.object.shape_key_remove()
1240                 bpy.ops.object.shape_key_remove()
1241
1242                 ob.select_set(False)
1243
1244                 bplane.select_set(True)
1245                 context.view_layer.objects.active = bplane
1246                 for ms in (ms for ms in bplane.material_slots if ms.name != BProjection_Material):
1247                     bplane.active_material = ms.material
1248                     bpy.ops.object.material_slot_remove()
1249
1250                 for gs in (gs for gs in bplane.vertex_groups if gs.name != 'texture plane'):
1251                     bplane.vertex_groups.active_index = gs.index
1252                     bpy.ops.object.vertex_group_remove()
1253
1254                 context.view_layer.objects.active = new_ob
1255                 cm = new_ob.mode
1256                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1257                 bpy.ops.object.join()
1258
1259                 em.hide = False
1260                 em.select_set(True)
1261                 new_ob.select_set(False)
1262                 bpy.ops.object.location_clear()
1263                 bpy.ops.object.rotation_clear()
1264                 bpy.ops.object.scale_clear()
1265                 context.view_layer.objects.active = new_ob
1266                 bpy.ops.object.editmode_toggle()
1267                 bpy.ops.object.hook_add_selob()
1268                 bpy.ops.object.editmode_toggle()
1269                 em.hide = True
1270                 em.select_set(False)
1271                 new_ob.select_set(True)
1272                 em.custom_active_object = new_ob.name
1273                 tmp = em.custom_c3d
1274                 em.custom_c3d = False
1275                 bpy.ops.object.active_view(index=shape_index - 1)
1276                 bpy.ops.object.mode_set(mode=cm, toggle=False)
1277
1278                 sd = context.space_data
1279                 r3d = sd.region_3d
1280                 vr = r3d.view_rotation.copy()
1281                 vr.invert()
1282                 ob_loc = ob.location.copy()
1283                 new_ob_loc = new_ob.location.copy()
1284                 ob_loc.rotate(vr)
1285                 new_ob_loc.rotate(vr)
1286                 em.custom_location += Vector((ob_loc.x - new_ob_loc.x, ob_loc.y - new_ob_loc.y, 0.0))
1287                 em.custom_c3d = tmp
1288
1289             return {'FINISHED'}
1290
1291
1292 # Paint from the bp_plan
1293 last_mouse = Vector((0, 0))
1294
1295
1296 def move_bp(self, context, cm, fm):
1297     em = bpy.data.objects['Empty for BProjection']
1298
1299     deltax = cm.x - round(fm.x)
1300     deltay = cm.y - round(fm.y)
1301
1302     sd = context.space_data
1303     l = sd.region_3d
1304     vr = l.view_rotation.copy()
1305     vr.invert()
1306
1307     v_init = Vector((0.0, 0.0, 1.0))
1308
1309     pos = [-deltax, -deltay]
1310     v = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v_init)
1311     pos = [0, 0]
1312     vbl = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v_init)
1313     loc = vbl - v
1314
1315     loc.rotate(vr)
1316
1317     em.custom_location -= loc
1318
1319     self.first_mouse = cm
1320
1321
1322 class BP_Paint(Operator):
1323     bl_idname = "paint.bp_paint"
1324     bl_label = "Paint BProjection Plane"
1325     first_mouse = Vector((0, 0))
1326
1327     @classmethod
1328     def poll(cls, context):
1329         return 1
1330
1331     def modal(self, context, event):
1332         global last_mouse
1333         em = bpy.data.objects['Empty for BProjection']
1334
1335         if event.type == 'MOUSEMOVE':  # 'INBETWEEN_MOUSEMOVE':
1336             step_act = Vector((event.mouse_region_x, event.mouse_region_y)) - self.step_prev
1337             if (step_act.length >= context.scene.tool_settings.unified_paint_settings.size *
1338                bpy.data.brushes['Clone'].spacing / 100 or bpy.data.brushes['Clone'].use_airbrush):
1339
1340                 move_bp(self, context, Vector((event.mouse_region_x, event.mouse_region_y)) - self.v_offset,
1341                         self.first_mouse)
1342
1343                 bpy.ops.paint.image_paint(stroke=[{"name": "", "location": (0, 0, 0),
1344                                                   "mouse": (event.mouse_region_x, event.mouse_region_y),
1345                                                    "pressure": 1, "pen_flip": False, "time": 0, "is_start": False}])
1346                 self.step_prev = Vector((event.mouse_region_x, event.mouse_region_y))
1347
1348         if event.type == 'LEFTMOUSE':
1349             em.custom_c3d = True
1350             bpy.data.materials['Material for BProjection'].alpha = self.alpha
1351             em.custom_location = self.first_location
1352             return {'FINISHED'}
1353
1354         if event.type == 'ESC' or event.type == 'RIGHTMOUSE':
1355             em.custom_c3d = True
1356             bpy.data.materials['Material for BProjection'].alpha = self.alpha
1357             em.custom_location = self.first_location
1358             return {'FINISHED'}
1359
1360         return {'PASS_THROUGH'}
1361
1362     def invoke(self, context, event):
1363         em = bpy.data.objects['Empty for BProjection']
1364         context.window_manager.modal_handler_add(self)
1365         self.first_mouse = Vector((event.mouse_region_x, event.mouse_region_y))
1366
1367         sd = context.space_data
1368         l = sd.region_3d
1369         v_init = Vector((0.0, 0.0, 1.0))
1370         context.scene.cursor.location = view3d_utils.region_2d_to_location_3d(
1371                                                     context.region, l,
1372                                                     [event.mouse_region_x,
1373                                                      event.mouse_region_y], v_init
1374                                                     )
1375
1376         self.first_location = em.custom_location.copy()
1377
1378         self.v_offset = Vector((context.region.width, context.region.height)) - Vector((event.mouse_region_x,
1379                                                                                         event.mouse_region_y))
1380         move_bp(self, context, Vector((event.mouse_region_x, event.mouse_region_y)) - self.v_offset, self.first_mouse)
1381
1382         em.custom_c3d = False
1383         self.alpha = bpy.data.materials['Material for BProjection'].alpha
1384
1385         em.custom_location.z = -10
1386
1387         bpy.data.materials['Material for BProjection'].alpha = 0
1388
1389         bpy.ops.paint.image_paint(stroke=[{"name": "", "location": (0, 0, 0),
1390                                            "mouse": (event.mouse_region_x, event.mouse_region_y),
1391                                            "pressure": 1, "pen_flip": False,
1392                                            "time": 0, "size": 1,
1393                                            "is_start": False}])
1394         self.step_prev = Vector((event.mouse_region_x, event.mouse_region_y))
1395         return {'RUNNING_MODAL'}
1396
1397
1398 # Operator Class toggle the alpha of the plane
1399 temp_alpha = 0
1400
1401
1402 class ApplyImage(Operator):
1403     bl_idname = "object.bp_toggle_alpha"
1404     bl_label = "Toggle Alpha of the BP"
1405
1406     def execute(self, context):
1407         global temp_alpha
1408         if temp_alpha != 0:
1409             bpy.data.materials['Material for BProjection'].alpha = temp_alpha
1410             temp_alpha = 0
1411         else:
1412             temp_alpha = bpy.data.materials['Material for BProjection'].alpha
1413             bpy.data.materials['Material for BProjection'].alpha = 0
1414
1415         return {'FINISHED'}
1416
1417
1418 # reinit the values of the bp_plane
1419 class BP_Clear_Props(Operator):
1420     bl_idname = "object.bp_clear_prop"
1421     bl_label = "Clear Props BProjection Plane"
1422
1423     def execute(self, context):
1424         clear_props(context)
1425
1426         return{'FINISHED'}
1427
1428
1429 # Move the UV of the bp_plane
1430 class BP_OffsetUV(Operator):
1431     bl_idname = "object.bp_offsetuv"
1432     bl_label = "OffsetUV BProjection Plane"
1433
1434     axe_x = True
1435     axe_y = True
1436
1437     first_mouse = Vector((0, 0))
1438     first_offsetuv = Vector((0, 0))
1439
1440     @classmethod
1441     def poll(cls, context):
1442         return 1
1443
1444     def modal(self, context, event):
1445         em = bpy.data.objects['Empty for BProjection']
1446
1447         if event.shift:
1448             fac = 0.1
1449         else:
1450             fac = 1
1451
1452         if event.type == 'X' and event.value == 'PRESS':
1453             if self.axe_x is True and self.axe_y is True:
1454                 self.axe_y = False
1455             elif self.axe_x is True and self.axe_y is False:
1456                 self.axe_y = True
1457             elif self.axe_x is False and self.axe_y is True:
1458                 self.axe_y = False
1459                 self.axe_x = True
1460
1461         if event.type == 'Y' and event.value == 'PRESS':
1462             if self.axe_x is True and self.axe_y is True:
1463                 self.axe_x = False
1464             elif self.axe_x is False and self.axe_y is True:
1465                 self.axe_x = True
1466             elif self.axe_x is True and self.axe_y is False:
1467                 self.axe_y = True
1468                 self.axe_x = False
1469
1470         deltax = (event.mouse_region_x - self.first_mouse.x) * fac
1471         deltay = (event.mouse_region_y - self.first_mouse.y) * fac
1472
1473         if event.type == 'MOUSEMOVE':
1474
1475             if not self.axe_y:
1476                 deltay = 0
1477             if not self.axe_x:
1478                 deltax = 0
1479
1480             ouv = em.custom_offsetuv
1481             em.custom_offsetuv = [ouv[0] - deltax / 50, ouv[1] - deltay / 50]
1482
1483             self.first_mouse.x = event.mouse_region_x
1484             self.first_mouse.y = event.mouse_region_y
1485
1486         if event.type == 'LEFTMOUSE':
1487             return {'FINISHED'}
1488
1489         if event.type == 'ESC' or event.type == 'RIGHTMOUSE':
1490             em.custom_offsetuv = self.first_offsetuv
1491             return {'FINISHED'}
1492
1493         return {'RUNNING_MODAL'}
1494
1495     def invoke(self, context, event):
1496         em = bpy.data.objects['Empty for BProjection']
1497         context.window_manager.modal_handler_add(self)
1498         self.first_mouse.x = event.mouse_region_x
1499         self.first_mouse.y = event.mouse_region_y
1500         self.first_offsetuv = em.custom_offsetuv.copy()
1501
1502         return {'RUNNING_MODAL'}
1503
1504
1505 # Scale the UV of the bp_plane
1506 class BP_ScaleUV(Operator):
1507     bl_idname = "object.bp_scaleuv"
1508     bl_label = "ScaleUV BProjection Plane"
1509
1510     axe_x = True
1511     axe_y = True
1512
1513     first_mouse = Vector((0, 0))
1514     first_scaleuv = Vector((0, 0))
1515
1516     @classmethod
1517     def poll(cls, context):
1518         return 1
1519
1520     def modal(self, context, event):
1521         em = bpy.data.objects['Empty for BProjection']
1522
1523         if event.shift:
1524             fac = 0.1
1525         else:
1526             fac = 1
1527
1528         if event.type == 'X' and event.value == 'PRESS':
1529             if self.axe_x is True and self.axe_y is True:
1530                 self.axe_y = False
1531             elif self.axe_x is True and self.axe_y is False:
1532                 self.axe_y = True
1533             elif self.axe_x is False and self.axe_y is True:
1534                 self.axe_y = False
1535                 self.axe_x = True
1536
1537         if event.type == 'Y' and event.value == 'PRESS':
1538             if self.axe_x is True and self.axe_y is True:
1539                 self.axe_x = False
1540             elif self.axe_x is False and self.axe_y is True:
1541                 self.axe_x = True
1542             elif self.axe_x is True and self.axe_y is False:
1543                 self.axe_y = True
1544                 self.axe_x = False
1545
1546         deltax = (event.mouse_region_x - self.first_mouse.x) * fac
1547         deltay = (event.mouse_region_y - self.first_mouse.y) * fac
1548
1549         if event.type == 'MOUSEMOVE':
1550
1551             if not self.axe_y:
1552                 deltay = 0
1553             if not self.axe_x:
1554                 deltax = 0
1555
1556             if self.axe_x and self.axe_y:
1557                 fac = em.custom_scaleuv[1] / em.custom_scaleuv[0]
1558                 deltay = deltax * fac
1559             else:
1560                 em.custom_linkscaleuv = False
1561
1562             s = em.custom_scaleuv
1563             em.custom_scaleuv = [s[0] + deltax / 50, s[1] + deltay / 50]
1564
1565             self.first_mouse.x = event.mouse_region_x
1566             self.first_mouse.y = event.mouse_region_y
1567
1568         if event.type == 'LEFTMOUSE':
1569             return {'FINISHED'}
1570
1571         if event.type == 'ESC' or event.type == 'RIGHTMOUSE':
1572             em.custom_scaleuv = self.first_scaleuv
1573             return {'FINISHED'}
1574
1575         return {'RUNNING_MODAL'}
1576
1577     def invoke(self, context, event):
1578         em = bpy.data.objects['Empty for BProjection']
1579         context.window_manager.modal_handler_add(self)
1580         self.first_mouse.x = event.mouse_region_x
1581         self.first_mouse.y = event.mouse_region_y
1582         self.first_scaleuv = em.custom_scaleuv.copy()
1583
1584         return {'RUNNING_MODAL'}
1585
1586
1587 # Scale the bp_plane
1588 class BP_Scale(Operator):
1589     bl_idname = "object.bp_scale"
1590     bl_label = "Scale BProjection Plane"
1591
1592     axe_x = True
1593     axe_y = True
1594
1595     first_mouse = Vector((0, 0))
1596     first_scale = Vector((0, 0, 0))
1597
1598     @classmethod
1599     def poll(cls, context):
1600         return 1
1601
1602     def modal(self, context, event):
1603         em = bpy.data.objects['Empty for BProjection']
1604         sd = context.space_data
1605
1606         center = view3d_utils.location_3d_to_region_2d(context.region, sd.region_3d, em.location)
1607         vec_init = self.first_mouse - center
1608         vec_act = Vector((event.mouse_region_x, event.mouse_region_y)) - center
1609         scale_fac = vec_act.length / vec_init.length
1610
1611         if event.ctrl:
1612             scale_fac = round(scale_fac, 1)
1613
1614         if event.type == 'X' and event.value == 'PRESS':
1615             if self.axe_x is True and self.axe_y is True:
1616                 self.axe_y = False
1617             elif self.axe_x is True and self.axe_y is False:
1618                 self.axe_y = True
1619             elif self.axe_x is False and self.axe_y is True:
1620                 self.axe_y = False
1621                 self.axe_x = True
1622
1623         if event.type == 'Y' and event.value == 'PRESS':
1624             if self.axe_x is True and self.axe_y is True:
1625                 self.axe_x = False
1626             elif self.axe_x is False and self.axe_y is True:
1627                 self.axe_x = True
1628             elif self.axe_x is True and self.axe_y is False:
1629                 self.axe_y = True
1630                 self.axe_x = False
1631
1632         if event.type == 'MOUSEMOVE':
1633
1634             if self.axe_x:
1635                 em.custom_scale = [self.first_scale[0] * scale_fac, self.first_scale[1]]
1636             if self.axe_y:
1637                 em.custom_scale = [self.first_scale[0], self.first_scale[1] * scale_fac]
1638
1639             if self.axe_x and self.axe_y:
1640                 em.custom_scale = [self.first_scale[0] * scale_fac, self.first_scale[1] * scale_fac]
1641             else:
1642                 em.custom_linkscale = False
1643
1644         if event.type == 'LEFTMOUSE':
1645             return {'FINISHED'}
1646
1647         if event.type == 'ESC' or event.type == 'RIGHTMOUSE':
1648             em.custom_scale = self.first_scale
1649             return {'FINISHED'}
1650
1651         return {'RUNNING_MODAL'}
1652
1653     def invoke(self, context, event):
1654         em = bpy.data.objects['Empty for BProjection']
1655         context.window_manager.modal_handler_add(self)
1656         self.first_mouse.x = event.mouse_region_x
1657         self.first_mouse.y = event.mouse_region_y
1658         self.first_scale = em.custom_scale.copy()
1659
1660         return {'RUNNING_MODAL'}
1661
1662
1663 # Rotate the bp_plan
1664 class BP_Rotate(Operator):
1665     bl_idname = "object.bp_rotate"
1666     bl_label = "Rotate BProjection Plane"
1667
1668     first_mouse = Vector((0, 0))
1669     first_rotation = 0
1670
1671     @classmethod
1672     def poll(cls, context):
1673         return 1
1674
1675     def modal(self, context, event):
1676         em = bpy.data.objects['Empty for BProjection']
1677         sd = context.space_data
1678
1679         center = view3d_utils.location_3d_to_region_2d(context.region, sd.region_3d, em.location if
1680                                                        em.custom_rotc3d else context.scene.cursor.location)
1681         vec_init = self.first_mouse - center
1682         vec_act = Vector((event.mouse_region_x, event.mouse_region_y)) - center
1683         rot = -vec_init.angle_signed(vec_act) * 180 / pi
1684
1685         if event.shift:
1686             rot = rot
1687         else:
1688             rot = int(rot)
1689
1690         if event.ctrl:
1691             rot = int(rot / 5) * 5
1692
1693         if event.type == 'MOUSEMOVE':
1694
1695             em.custom_rotation = self.first_rotation + rot
1696
1697         if event.type == 'LEFTMOUSE':
1698             return {'FINISHED'}
1699
1700         if event.type == 'ESC' or event.type == 'RIGHTMOUSE':
1701             em.custom_rotation = self.first_rotation
1702             return {'FINISHED'}
1703
1704         return {'RUNNING_MODAL'}
1705
1706     def invoke(self, context, event):
1707         em = bpy.data.objects['Empty for BProjection']
1708         context.window_manager.modal_handler_add(self)
1709         self.first_mouse.x = event.mouse_region_x
1710         self.first_mouse.y = event.mouse_region_y
1711         self.first_rotation = em.custom_rotation
1712
1713         return {'RUNNING_MODAL'}
1714
1715
1716 # grab the bp_plan
1717 class BP_Grab(Operator):
1718     bl_idname = "object.bp_grab"
1719     bl_label = "Grab BProjection Plane"
1720
1721     axe_x = True
1722     axe_y = True
1723     axe_z = False
1724     first_mouse = Vector((0, 0))
1725     first_location = Vector((0, 0, 0))
1726
1727     @classmethod
1728     def poll(cls, context):
1729         return 1
1730
1731     def modal(self, context, event):
1732         em = bpy.data.objects['Empty for BProjection']
1733
1734         if event.shift:
1735             fac = 0.1
1736         else:
1737             fac = 1
1738
1739         if event.type == 'X' and event.value == 'PRESS':
1740             if self.axe_x is True and self.axe_y is True:
1741                 self.axe_y = False
1742             elif self.axe_x is True and self.axe_y is False:
1743                 self.axe_y = True
1744             elif self.axe_x is False and self.axe_y is True:
1745                 self.axe_y = False
1746                 self.axe_x = True
1747             if self.axe_z is True:
1748                 self.axe_z = False
1749                 self.axe_x = True
1750
1751         if event.type == 'Y' and event.value == 'PRESS':
1752             if self.axe_x is True and self.axe_y is True:
1753                 self.axe_x = False
1754             elif self.axe_x is False and self.axe_y is True:
1755                 self.axe_x = True
1756             elif self.axe_x is True and self.axe_y is False:
1757                 self.axe_y = True
1758                 self.axe_x = False
1759             if self.axe_z is True:
1760                 self.axe_z = False
1761                 self.axe_y = True
1762
1763         if event.type == 'Z' and event.value == 'PRESS':
1764             if self.axe_z is False:
1765                 self.axe_z = True
1766                 self.axe_x = False
1767                 self.axe_y = False
1768             elif self.axe_z is True:
1769                 self.axe_z = False
1770                 self.axe_x = True
1771                 self.axe_y = True
1772
1773         deltax = event.mouse_region_x - round(self.first_mouse.x)
1774         deltay = event.mouse_region_y - round(self.first_mouse.y)
1775
1776         if event.type == 'MOUSEMOVE':
1777             sd = context.space_data
1778             l = sd.region_3d
1779             vr = l.view_rotation.copy()
1780             vr.invert()
1781
1782             v_init = Vector((0.0, 0.0, 1.0))
1783
1784             pos = [-deltax, -deltay]
1785             v = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v_init)
1786             pos = [0, 0]
1787             vbl = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v_init)
1788             loc = vbl - v
1789
1790             loc.rotate(vr)
1791             if not self.axe_y:
1792                 loc.y = loc.z = 0
1793             if not self.axe_x:
1794                 loc.x = loc.z = 0
1795             if self.axe_z:
1796                 loc.z = deltax / 10
1797
1798             em.custom_location += loc * fac
1799
1800             self.first_mouse.x = event.mouse_region_x
1801             self.first_mouse.y = event.mouse_region_y
1802
1803         if event.type == 'LEFTMOUSE':
1804             return {'FINISHED'}
1805
1806         if event.type == 'ESC' or event.type == 'RIGHTMOUSE':
1807             em.custom_location = self.first_location
1808             return {'FINISHED'}
1809
1810         return {'RUNNING_MODAL'}
1811
1812     def invoke(self, context, event):
1813         em = bpy.data.objects['Empty for BProjection']
1814         context.window_manager.modal_handler_add(self)
1815         self.first_mouse.x = event.mouse_region_x
1816         self.first_mouse.y = event.mouse_region_y
1817         self.first_location = em.custom_location.copy()
1818
1819         return {'RUNNING_MODAL'}
1820
1821
1822 # Operator Class to rotate the view3D
1823 class RotateView3D(Operator):
1824     bl_idname = "view3d.rotate_view3d"
1825     bl_label = "Rotate the View3D"
1826
1827     first_mouse = Vector((0, 0))
1828
1829     first_time = True
1830     tmp_level = -1
1831
1832     def vect_sphere(self, context, mx, my):
1833         width = context.region.width
1834         height = context.region.height
1835
1836         if width >= height:
1837             ratio = height / width
1838
1839             x = 2 * mx / width
1840             y = 2 * ratio * my / height
1841
1842             x = x - 1
1843             y = y - ratio
1844         else:
1845             ratio = width / height
1846
1847             x = 2 * ratio * mx / width
1848             y = 2 * my / height
1849
1850             x = x - ratio
1851             y = y - 1
1852
1853         z2 = 1 - x * x - y * y
1854         if z2 > 0:
1855             z = sqrt(z2)
1856         else:
1857             z = 0
1858
1859         p = Vector((x, y, z))
1860         p.normalize()
1861         return p
1862
1863     def tracball(self, context, mx, my, origine):
1864         sd = context.space_data
1865
1866         vr_b = sd.region_3d.view_rotation.copy()
1867         vr_b.invert()
1868         pos_init = sd.region_3d.view_location - origine
1869         sd.region_3d.view_location = origine
1870
1871         v1 = self.vect_sphere(context, self.first_mouse.x, self.first_mouse.y)
1872         v2 = self.vect_sphere(context, mx, my)
1873
1874         axis = Vector.cross(v1, v2)
1875         angle = Vector.angle(v1, v2)
1876
1877         q = Quaternion(axis, -2 * angle)
1878
1879         sd.region_3d.view_rotation *= q
1880         sd.region_3d.update()
1881
1882         vr_a = sd.region_3d.view_rotation.copy()
1883         pos_init.rotate(vr_a * vr_b)
1884         sd.region_3d.view_location = pos_init + origine
1885
1886         self.first_mouse = Vector((mx, my))
1887
1888     def turnable(self, context, mx, my, origine):
1889         sd = context.space_data
1890
1891         width = context.region.width
1892         height = context.region.height
1893
1894         vr_b = sd.region_3d.view_rotation.copy()
1895         vr_b.invert()
1896         pos_init = sd.region_3d.view_location - origine
1897         sd.region_3d.view_location = origine
1898
1899         vz = Vector((0, 0, 1))
1900         qz = Quaternion(vz, -8 * (mx - self.first_mouse.x) / width)
1901         sd.region_3d.view_rotation.rotate(qz)
1902         sd.region_3d.update()
1903
1904         vx = Vector((1, 0, 0))
1905         vx.rotate(sd.region_3d.view_rotation)
1906         qx = Quaternion(vx, 8 * (my - self.first_mouse.y) / height)
1907         sd.region_3d.view_rotation.rotate(qx)
1908         sd.region_3d.update()
1909
1910         vr_a = sd.region_3d.view_rotation.copy()
1911         pos_init.rotate(vr_a * vr_b)
1912         sd.region_3d.view_location = pos_init + origine
1913
1914         self.first_mouse = Vector((mx, my))
1915
1916     def modal(self, context, event):
1917         ob = context.object
1918
1919         if event.value == 'RELEASE':
1920             if self.tmp_level > -1:
1921                 for sub in context.object.modifiers:
1922                     if sub.type in ['SUBSURF', 'MULTIRES']:
1923                         sub.levels = self.tmp_level
1924             return {'FINISHED'}
1925
1926         if event.type == 'MOUSEMOVE':
1927             if context.preferences.inputs.view_rotate_method == 'TRACKBALL':
1928                 self.tracball(context, event.mouse_region_x, event.mouse_region_y, ob.location)
1929             else:
1930                 self.turnable(context, event.mouse_region_x, event.mouse_region_y, ob.location)
1931             align_to_view(context)
1932             # to unlock the camera
1933             if self.first_time:
1934                 rot_ang = context.preferences.view.rotation_angle
1935                 context.preferences.view.rotation_angle = 0
1936                 bpy.ops.view3d.view_orbit(type='ORBITLEFT')
1937                 context.preferences.view.rotation_angle = rot_ang
1938                 bpy.ops.view3d.view_persportho()
1939                 bpy.ops.view3d.view_persportho()
1940                 self.first_time = False
1941
1942         return {'RUNNING_MODAL'}
1943
1944     def execute(self, context):
1945         align_to_view(context)
1946
1947         return{'FINISHED'}
1948
1949     def invoke(self, context, event):
1950         bpy.data.objects['Empty for BProjection']
1951         context.window_manager.modal_handler_add(self)
1952         self.first_mouse = Vector((event.mouse_region_x, event.mouse_region_y))
1953         self.first_time = True
1954         for sub in context.object.modifiers:
1955             if sub.type in ['SUBSURF', 'MULTIRES']:
1956                 self.tmp_level = sub.levels
1957                 if sub.levels - self.tmp_level < 0:
1958                     sub.levels = 0
1959                 else:
1960                     sub.levels += bpy.context.object.custom_fnlevel
1961         return {'RUNNING_MODAL'}
1962
1963
1964 # Operator Class to pan the view3D
1965 class PanView3D(Operator):
1966     bl_idname = "view3d.pan_view3d"
1967     bl_label = "Pan View3D"
1968
1969     first_mouse = Vector((0, 0))
1970     tmp_level = -1
1971
1972     def modal(self, context, event):
1973         em = bpy.data.objects[BProjection_Empty]
1974         deltax = event.mouse_region_x - self.first_mouse.x
1975         deltay = event.mouse_region_y - self.first_mouse.y
1976
1977         sd = context.space_data
1978         r3d = sd.region_3d
1979         vr = r3d.view_rotation.copy()
1980         vr.invert()
1981
1982         v_init = Vector((0.0, 0.0, 1.0))
1983
1984         pos = [deltax, deltay]
1985         v = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
1986         pos = [0, 0]
1987         vbl = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
1988         loc = vbl - v
1989         sd.region_3d.view_location += loc
1990         loc.rotate(vr)
1991         if not em.custom_style_clone:
1992             em.custom_location += loc
1993
1994         self.first_mouse.x = event.mouse_region_x
1995         self.first_mouse.y = event.mouse_region_y
1996
1997         if event.type == 'MIDDLEMOUSE'and event.value == 'RELEASE':
1998             if self.tmp_level > -1:
1999                 for sub in context.object.modifiers:
2000                     if sub.type in ['SUBSURF', 'MULTIRES']:
2001                         sub.levels = self.tmp_level
2002             return {'FINISHED'}
2003
2004         return {'RUNNING_MODAL'}
2005
2006     def invoke(self, context, event):
2007         bpy.data.objects['Empty for BProjection']
2008         context.window_manager.modal_handler_add(self)
2009         self.first_mouse.x = event.mouse_region_x
2010         self.first_mouse.y = event.mouse_region_y
2011         for sub in context.object.modifiers:
2012             if sub.type in ['SUBSURF', 'MULTIRES']:
2013                 self.tmp_level = sub.levels
2014                 if sub.levels - self.tmp_level < 0:
2015                     sub.levels = 0
2016                 else:
2017                     sub.levels += bpy.context.object.custom_fnlevel
2018
2019         return {'RUNNING_MODAL'}
2020
2021     def execute(self, context):
2022         align_to_view(context)
2023
2024         return{'FINISHED'}
2025
2026
2027 # Operator Class to zoom the view3D
2028 class ZoomView3D(Operator):
2029     bl_idname = "view3d.zoom_view3d"
2030     bl_label = "Zoom View3D"
2031
2032     delta: FloatProperty(
2033         name="delta",
2034         description="Delta",
2035         min=-1.0, max=1,
2036         default=1.0)
2037
2038     def invoke(self, context, event):
2039         ob = context.object
2040         em = bpy.data.objects[BProjection_Empty]
2041         sd = context.space_data
2042
2043         width = context.region.width
2044         height = context.region.height
2045
2046         r3d = sd.region_3d
2047         v_init = Vector((0.0, 0.0, 1.0))
2048
2049         pos = [width, height]
2050         vtr_b = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
2051         pos = [0, 0]
2052         vbl_b = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
2053         len_b = vtr_b - vbl_b
2054
2055         bpy.ops.view3d.zoom(delta=self.delta)
2056         r3d.update()
2057
2058         pos = [width, height]
2059         vtr_a = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
2060         pos = [0, 0]
2061         vbl_a = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
2062         len_a = vtr_a - vbl_a
2063
2064         fac = len_a.length / len_b.length
2065         r3d.view_location -= ob.location
2066         r3d.view_location *= fac
2067         r3d.view_location += ob.location
2068         vres = Vector((em. custom_location. x * fac, em.custom_location. y * fac, em.custom_location.z))
2069
2070         if not em.custom_style_clone:
2071             em.custom_location = vres
2072
2073         align_to_view(context)
2074
2075         return {'FINISHED'}
2076
2077     def execute(self, context):
2078         align_to_view(context)
2079
2080         return{'FINISHED'}
2081
2082
2083 # Operator Class to use numpad shortcut
2084 class PresetView3D(Operator):
2085     bl_idname = "view3d.preset_view3d"
2086     bl_label = "Preset View3D"
2087
2088     view: StringProperty(name="View", description="Select the view", default='TOP')
2089
2090     def invoke(self, context, event):
2091         ob = context.object
2092         origine = ob.location
2093         sd = context.space_data
2094
2095         vr_b = sd.region_3d.view_rotation.copy()
2096         vr_b.invert()
2097         pos_init = sd.region_3d.view_location - origine
2098         sd.region_3d.view_location = origine
2099
2100         tmp = context.preferences.view.smooth_view
2101         context.preferences.view.smooth_view = 0
2102         bpy.ops.view3d.viewnumpad(type=self.view)
2103         align_to_view(context)
2104         context.preferences.view.smooth_view = tmp
2105
2106         vr_a = sd.region_3d.view_rotation.copy()
2107         pos_init.rotate(vr_a * vr_b)
2108         sd.region_3d.view_location = pos_init + origine
2109
2110         return {'FINISHED'}
2111
2112
2113 class Shortcuts_Pref(AddonPreferences):
2114     bl_idname = __name__
2115
2116     def draw(self, context):
2117         layout = self.layout
2118         scene = context.scene
2119
2120         layout.prop(context.scene, "bp_shortcuts", text="Hot Keys", icon="KEYINGSET")
2121
2122         if scene.bp_shortcuts:
2123             row = layout.row()
2124             col = row.column()
2125
2126             col.label(text="Hotkey List:")
2127             col.label(text="Shift + left mouse - Intuitive Scale")
2128             col.label(text="G + mouse move to translate the projection plane")
2129             col.label(text="R + mouse move to rotate the projection plane")
2130             col.label(text="Ctrl + S + mouse move to scale the projection plane")
2131             col.label(text="U + mouse move to scale the projection plane UV")
2132             col.label(text="Y + mouse move to offset the projection plane UV")
2133             col.label(text="C - clear all")
2134             col.label(text="Q - toggle alpha of the projection plane")
2135
2136
2137 @persistent
2138 def load_handler(dummy):
2139     reinitkey()
2140
2141
2142 def register():
2143     bpy.utils.register_module(__name__)
2144     bpy.types.Scene.bp_shortcuts = BoolProperty(
2145                     default=False,
2146                     description='Show shortcuts for BP Projection'
2147                     )
2148     createcustomprops(bpy.context)
2149     bpy.app.handlers.load_post.append(load_handler)
2150
2151
2152 def unregister():
2153     del bpy.types.Scene.bp_shortcuts
2154     bpy.utils.unregister_module(__name__)
2155
2156
2157 if __name__ == "__main__":
2158     register()