merge with 2.5 at r18679
[blender.git] / release / scripts / uvcalc_quad_clickproj.py
1 #!BPY
2
3 """ Registration info for Blender menus: <- these words are ignored
4 Name: 'Click project from face'
5 Blender: 245
6 Group: 'UVCalculation'
7 Tooltip: '3 Clicks to project uvs onto selected faces.'
8 """
9
10 __author__ = "Campbell Barton aka ideasman42"
11 __url__ = ["www.blender.org", "blenderartists.org", "www.python.org"]
12 __version__ = "0.1"
13 __bpydoc__=\
14 '''
15 http://mediawiki.blender.org/index.php/Scripts/Manual/UV_Calculate/Click_project_from_face
16 "
17
18 '''
19
20 # -------------------------------------------------------------------------- 
21 # Click project v0.1 by Campbell Barton (AKA Ideasman)
22 # -------------------------------------------------------------------------- 
23 # ***** BEGIN GPL LICENSE BLOCK ***** 
24
25 # This program is free software; you can redistribute it and/or 
26 # modify it under the terms of the GNU General Public License 
27 # as published by the Free Software Foundation; either version 2 
28 # of the License, or (at your option) any later version. 
29
30 # This program is distributed in the hope that it will be useful, 
31 # but WITHOUT ANY WARRANTY; without even the implied warranty of 
32 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
33 # GNU General Public License for more details. 
34
35 # You should have received a copy of the GNU General Public License 
36 # along with this program; if not, write to the Free Software Foundation, 
37 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
38
39 # ***** END GPL LICENCE BLOCK ***** 
40 # -------------------------------------------------------------------------- 
41
42 import Blender
43 import bpy
44 import BPyMesh
45 import BPyWindow
46
47 mouseViewRay= BPyWindow.mouseViewRay
48 from Blender import Mathutils, Window, Scene, Draw, sys
49 from Blender.Mathutils import CrossVecs, Vector, Matrix, LineIntersect, Intersect #, AngleBetweenVecs, Intersect
50 LMB= Window.MButs.L
51 RMB= Window.MButs.R
52
53 def using_modifier(ob):
54         for m in ob.modifiers:
55                 if m[Blender.Modifier.Settings.REALTIME]:
56                         return True
57         return False
58
59 def mouseup():
60         # Loop until click
61         mouse_buttons = Window.GetMouseButtons()
62         while not mouse_buttons & LMB:
63                 sys.sleep(10)
64                 mouse_buttons = Window.GetMouseButtons()
65         while mouse_buttons & LMB:
66                 sys.sleep(10)
67                 mouse_buttons = Window.GetMouseButtons()
68
69 def mousedown_wait():
70         # If the menu has just been pressed dont use its mousedown,
71         mouse_buttons = Window.GetMouseButtons()
72         while mouse_buttons & LMB:
73                 mouse_buttons = Window.GetMouseButtons()
74                 sys.sleep(10)
75
76 def main():
77         
78         scn = bpy.data.scenes.active
79         ob = scn.objects.active
80         if not ob or ob.type!='Mesh':
81                 return
82         
83         is_editmode = Window.EditMode()
84         if is_editmode:
85                 Window.EditMode(0)
86         
87         mousedown_wait() # so the menu items clicking dosnt trigger the mouseclick
88         
89         Window.DrawProgressBar (0.0, '')
90         Window.DrawProgressBar (0.1, '(1 of 3) Click on a face corner') 
91         
92         # wait for a click
93         mouse_buttons = Window.GetMouseButtons()
94         while not mouse_buttons & LMB:
95                 sys.sleep(10)
96                 mouse_buttons = Window.GetMouseButtons()
97                 
98                 # Allow for RMB cancel
99                 if mouse_buttons & RMB:
100                         return
101                 
102         while mouse_buttons & LMB:
103                 sys.sleep(10)
104                 mouse_buttons = Window.GetMouseButtons()
105         
106         
107         Window.DrawProgressBar (0.2, '(2 of 3 ) Click confirms the U coords')
108         
109         mousedown_wait()
110         
111         obmat= ob.matrixWorld
112         screen_x, screen_y = Window.GetMouseCoords()
113         mouseInView, OriginA, DirectionA = mouseViewRay(screen_x, screen_y, obmat)
114         
115         if not mouseInView or not OriginA:
116                 return
117         
118         me = ob.getData(mesh=1)
119         
120         # Get the face under the mouse
121         face_click, isect, side = BPyMesh.pickMeshRayFace(me, OriginA, DirectionA)
122         if not face_click:
123                 return
124         
125         proj_z_component = face_click.no
126         if not face_click:
127                 return
128         
129         # Find the face vertex thats closest to the mouse,
130         # this vert will be used as the corner to map from.
131         best_v= None
132         best_length = 10000000
133         vi1 = None
134         for i, v in enumerate(face_click.v):
135                 l = (v.co-isect).length
136                 if l < best_length:
137                         best_v = v
138                         best_length = l
139                         vi1 = i
140         
141         # now get the 2 edges in the face that connect to v
142         # we can work it out fairly easerly
143         if len(face_click)==4:
144                 if       vi1==0: vi2, vi3= 3,1
145                 elif vi1==1: vi2, vi3= 0,2
146                 elif vi1==2: vi2, vi3= 1,3
147                 elif vi1==3: vi2, vi3= 2,0
148         else:
149                 if   vi1==0: vi2, vi3= 2,1
150                 elif vi1==1: vi2, vi3= 0,2
151                 elif vi1==2: vi2, vi3= 1,0
152         
153         face_corner_main =face_click.v[vi1].co
154         face_corner_a =face_click.v[vi2].co
155         face_corner_b =face_click.v[vi3].co
156         
157         line_a_len = (face_corner_a-face_corner_main).length
158         line_b_len = (face_corner_b-face_corner_main).length
159         
160         orig_cursor = Window.GetCursorPos()
161         Window.SetCursorPos(face_corner_main.x, face_corner_main.y, face_corner_main.z)
162         
163         SHIFT = Window.Qual.SHIFT
164         MODE = 0 # firstclick, 1, secondclick
165         mouse_buttons = Window.GetMouseButtons()
166         
167         project_mat = Matrix([0,0,0], [0,0,0], [0,0,0])
168         
169         
170         def get_face_coords(f):
171                 f_uv = f.uv
172                 return [(v.co-face_corner_main, f_uv[i]) for i,v in enumerate(f.v)]
173         
174         if me.faceUV==False:
175                 me.faceUV= True
176
177         coords = [ (co,uv) for f in me.faces if f.sel for co, uv in get_face_coords(f)]
178         
179         coords_orig = [uv.copy() for co, uv in coords]
180         USE_MODIFIER = using_modifier(ob)
181         
182         while 1:
183                 if mouse_buttons & LMB:
184                         if MODE == 0:
185                                 mousedown_wait()
186                                 Window.DrawProgressBar (0.8, '(3 of 3 ) Click confirms the V coords')
187                                 MODE = 1 # second click
188                                 
189                                 # Se we cont continually set the length and get float error
190                                 proj_y_component_orig = proj_y_component.copy()
191                         else:
192                                 break
193                 
194                 elif mouse_buttons & RMB:
195                         # Restore old uvs
196                         for i, uv_orig in enumerate(coords_orig):
197                                 coords[i][1][:] = uv_orig
198                         break
199                 
200                 mouse_buttons = Window.GetMouseButtons()
201                 screen_x, screen_y = Window.GetMouseCoords()
202                 mouseInView, OriginA, DirectionA = mouseViewRay(screen_x, screen_y, obmat)
203                 
204                 if not mouseInView:
205                         continue
206                 
207                 # Do a ray tri intersection, not clipped by the tri
208                 new_isect = Intersect(face_corner_main, face_corner_a, face_corner_b, DirectionA, OriginA, False)
209                 new_isect_alt = new_isect + DirectionA*0.0001
210                 
211                 
212                 # The distance from the mouse cursor ray vector to the edge
213                 line_isect_a_pair = LineIntersect(new_isect, new_isect_alt, face_corner_main, face_corner_a)
214                 line_isect_b_pair = LineIntersect(new_isect, new_isect_alt, face_corner_main, face_corner_b)
215                 
216                 # SHIFT to flip the axis.
217                 is_shift = Window.GetKeyQualifiers() & SHIFT
218                 
219                 if MODE == 0:
220                         line_dist_a = (line_isect_a_pair[0]-line_isect_a_pair[1]).length
221                         line_dist_b = (line_isect_b_pair[0]-line_isect_b_pair[1]).length
222                         
223                         if line_dist_a < line_dist_b:
224                                 proj_x_component = face_corner_a - face_corner_main
225                                 y_axis_length = line_b_len
226                                 x_axis_length = (line_isect_a_pair[1]-face_corner_main).length
227                         else:
228                                 proj_x_component = face_corner_b - face_corner_main
229                                 y_axis_length = line_a_len
230                                 x_axis_length = (line_isect_b_pair[1]-face_corner_main).length
231                         
232                         proj_y_component = CrossVecs(proj_x_component, proj_z_component)
233                         
234                         proj_y_component.length = 1/y_axis_length
235                         proj_x_component.length = 1/x_axis_length
236                         
237                         if is_shift: proj_x_component.negate()
238                 
239                 else:
240                         proj_y_component[:] = proj_y_component_orig
241                         if line_dist_a < line_dist_b:
242                                 proj_y_component.length = 1/(line_isect_a_pair[1]-new_isect).length
243                         else:
244                                 proj_y_component.length = 1/(line_isect_b_pair[1]-new_isect).length
245                         
246                         if is_shift: proj_y_component.negate()
247                         
248                 # Use the existing matrix to make a new 3x3 projecton matrix
249                 project_mat[0][:] = -proj_y_component
250                 project_mat[1][:] = -proj_x_component
251                 project_mat[2][:] = proj_z_component
252                 
253                 # Apply the projection matrix
254                 for proj_co, uv in coords:
255                                 uv[:] = (project_mat * proj_co)[0:2]
256                 
257                 if USE_MODIFIER:
258                         me.update()
259                 
260                 Window.Redraw(Window.Types.VIEW3D)
261         
262         Window.SetCursorPos(*orig_cursor)
263         if is_editmode:
264                 Window.EditMode(1)
265         
266         Window.RedrawAll()
267         
268 if __name__=='__main__':
269         main()
270         Window.DrawProgressBar(1.0, '')
271