merge with 2.5 at r18679
[blender.git] / release / scripts / uvcalc_follow_active_coords.py
1 #!BPY
2 """
3 Name: 'Follow Active (quads)'
4 Blender: 242
5 Group: 'UVCalculation'
6 Tooltip: 'Follow from active quads.'
7 """
8 __author__ = "Campbell Barton"
9 __url__ = ("blender", "blenderartists.org")
10 __version__ = "1.0 2006/02/07"
11
12 __bpydoc__ = """\
13 This script sets the UV mapping and image of selected faces from adjacent unselected faces.
14
15 for full docs see...
16 http://mediawiki.blender.org/index.php/Scripts/Manual/UV_Calculate/Follow_active_quads
17 """
18
19 # ***** BEGIN GPL LICENSE BLOCK *****
20 #
21 # Script copyright (C) Campbell J Barton
22 #
23 # This program is free software; you can redistribute it and/or
24 # modify it under the terms of the GNU General Public License
25 # as published by the Free Software Foundation; either version 2
26 # of the License, or (at your option) any later version.
27 #
28 # This program is distributed in the hope that it will be useful,
29 # but WITHOUT ANY WARRANTY; without even the implied warranty of
30 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31 # GNU General Public License for more details.
32 #
33 # You should have received a copy of the GNU General Public License
34 # along with this program; if not, write to the Free Software Foundation,
35 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
36 #
37 # ***** END GPL LICENCE BLOCK *****
38 # --------------------------------------------------------------------------
39
40
41 from Blender import *
42 import bpy
43 import BPyMesh
44
45 def extend(EXTEND_MODE,ob):
46         if EXTEND_MODE == -1:
47                 return
48         me = ob.getData(mesh=1)
49         me_verts = me.verts
50                 # Toggle Edit mode
51         is_editmode = Window.EditMode()
52         if is_editmode:
53                 Window.EditMode(0)
54         Window.WaitCursor(1)
55         t = sys.time()
56         edge_average_lengths = {}
57         
58         OTHER_INDEX = 2,3,0,1
59         FAST_INDICIES = 0,2,1,3 # order is faster
60         def extend_uvs(face_source, face_target, edge_key):
61                 '''
62                 Takes 2 faces,
63                 Projects its extends its UV coords onto the face next to it.
64                 Both faces must share an edge.
65                 '''
66                 
67                 def face_edge_vs(vi):
68                         # assunme a quad
69                         return [(vi[0], vi[1]), (vi[1], vi[2]), (vi[2], vi[3]), (vi[3], vi[0])]
70                 
71                 uvs_source = face_source.uv
72                 uvs_target = face_target.uv
73                 
74                 vidx_source = [v.index for v in face_source] 
75                 vidx_target = [v.index for v in face_target]
76                 
77                 # vertex index is the key, uv is the value
78                 uvs_vhash_source = dict( [ (vindex, uvs_source[i]) for i, vindex in enumerate(vidx_source)] )
79                 uvs_vhash_target = dict( [ (vindex, uvs_target[i]) for i, vindex in enumerate(vidx_target)] )
80                 
81                 edge_idxs_source = face_edge_vs(vidx_source)
82                 edge_idxs_target = face_edge_vs(vidx_target)
83                 
84                 source_matching_edge = -1
85                 target_matching_edge = -1
86                 
87                 edge_key_swap = edge_key[1], edge_key[0]
88                 
89                 try:    source_matching_edge = edge_idxs_source.index(edge_key)
90                 except: source_matching_edge = edge_idxs_source.index(edge_key_swap)
91                 try:    target_matching_edge = edge_idxs_target.index(edge_key)
92                 except: target_matching_edge = edge_idxs_target.index(edge_key_swap)
93                 
94
95                 
96                 edgepair_inner_source = edge_idxs_source[source_matching_edge]
97                 edgepair_inner_target = edge_idxs_target[target_matching_edge]
98                 edgepair_outer_source = edge_idxs_source[OTHER_INDEX[source_matching_edge]]
99                 edgepair_outer_target = edge_idxs_target[OTHER_INDEX[target_matching_edge]]
100                 
101                 if edge_idxs_source[source_matching_edge] == edge_idxs_target[target_matching_edge]:
102                         iA= 0; iB= 1 # Flipped, most common
103                 else: # The normals of these faces must be different
104                         iA= 1; iB= 0
105
106                 
107                 # Set the target UV's touching source face, no tricky calc needed,
108                 uvs_vhash_target[edgepair_inner_target[0]][:] = uvs_vhash_source[edgepair_inner_source[iA]]
109                 uvs_vhash_target[edgepair_inner_target[1]][:] = uvs_vhash_source[edgepair_inner_source[iB]]
110
111
112                 # Set the 2 UV's on the target face that are not touching
113                 # for this we need to do basic expaning on the source faces UV's
114                 if EXTEND_MODE == 2:
115                         
116                         try: # divide by zero is possible
117                                 '''
118                                 measure the length of each face from the middle of each edge to the opposite
119                                 allong the axis we are copying, use this
120                                 '''
121                                 i1a= edgepair_outer_target[iB]
122                                 i2a= edgepair_inner_target[iA]
123                                 if i1a>i2a: i1a, i2a = i2a, i1a
124                                 
125                                 i1b= edgepair_outer_source[iB]
126                                 i2b= edgepair_inner_source[iA]
127                                 if i1b>i2b: i1b, i2b = i2b, i1b
128                                 # print edge_average_lengths
129                                 factor = edge_average_lengths[i1a, i2a][0] / edge_average_lengths[i1b, i2b][0]
130                         except:
131                                 # Div By Zero?
132                                 factor = 1.0
133                         
134                         uvs_vhash_target[edgepair_outer_target[iB]][:] = uvs_vhash_source[edgepair_inner_source[0]]  +factor * (uvs_vhash_source[edgepair_inner_source[0]] - uvs_vhash_source[edgepair_outer_source[1]])
135                         uvs_vhash_target[edgepair_outer_target[iA]][:] = uvs_vhash_source[edgepair_inner_source[1]]  +factor * (uvs_vhash_source[edgepair_inner_source[1]] - uvs_vhash_source[edgepair_outer_source[0]])
136                 
137                 else:
138                         # same as above but with no factor
139                         uvs_vhash_target[edgepair_outer_target[iB]][:] = uvs_vhash_source[edgepair_inner_source[0]] + (uvs_vhash_source[edgepair_inner_source[0]] - uvs_vhash_source[edgepair_outer_source[1]])
140                         uvs_vhash_target[edgepair_outer_target[iA]][:] = uvs_vhash_source[edgepair_inner_source[1]] + (uvs_vhash_source[edgepair_inner_source[1]] - uvs_vhash_source[edgepair_outer_source[0]])
141         
142         if not me.faceUV:
143                 me.faceUV= True
144         
145         face_act =      me.activeFace
146         if face_act == -1:
147                 Draw.PupMenu('ERROR: No active face')
148                 return
149         
150         face_sel= [f for f in me.faces if len(f) == 4 and f.sel]
151         
152         face_act_local_index = -1
153         for i, f in enumerate(face_sel):
154                 if f.index == face_act:
155                         face_act_local_index = i
156                         break
157         
158         if face_act_local_index == -1:
159                 Draw.PupMenu('ERROR: Active face not selected')
160                 return
161         
162         
163         
164         # Modes
165         # 0 unsearched
166         # 1:mapped, use search from this face. - removed!!
167         # 2:all siblings have been searched. dont search again.
168         face_modes = [0] * len(face_sel)
169         face_modes[face_act_local_index] = 1 # extend UV's from this face.
170         
171         
172         # Edge connectivty
173         edge_faces = {}
174         for i, f in enumerate(face_sel):
175                 for edkey in f.edge_keys:
176                         try:    edge_faces[edkey].append(i)
177                         except: edge_faces[edkey] = [i]
178         
179         SEAM = Mesh.EdgeFlags.SEAM
180         
181         if EXTEND_MODE == 2:
182                 edge_loops = BPyMesh.getFaceLoopEdges(face_sel, [ed.key for ed in me.edges if ed.flag & SEAM] )
183                 me_verts = me.verts
184                 for loop in edge_loops:
185                         looplen = [0.0]
186                         for ed in loop:
187                                 edge_average_lengths[ed] = looplen
188                                 looplen[0] += (me_verts[ed[0]].co - me_verts[ed[1]].co).length
189                         looplen[0] = looplen[0] / len(loop)
190                 
191         
192         
193         # remove seams, so we dont map accross seams.
194         for ed in me.edges:
195                 if ed.flag & SEAM:
196                         # remove the edge pair if we can
197                         try:    del edge_faces[ed.key]
198                         except: pass
199         # Done finding seams
200         
201         
202         # face connectivity - faces around each face
203         # only store a list of indicies for each face.
204         face_faces = [[] for i in xrange(len(face_sel))]
205         
206         for edge_key, faces in edge_faces.iteritems():
207                 if len(faces) == 2: # Only do edges with 2 face users for now
208                         face_faces[faces[0]].append((faces[1], edge_key))
209                         face_faces[faces[1]].append((faces[0], edge_key))
210         
211         
212         # Now we know what face is connected to what other face, map them by connectivity
213         ok = True
214         while ok:
215                 ok = False
216                 for i in xrange(len(face_sel)):
217                         if face_modes[i] == 1: # searchable
218                                 for f_sibling, edge_key in face_faces[i]:
219                                         if face_modes[f_sibling] == 0:
220                                                 face_modes[f_sibling] = 1 # mapped and search from.
221                                                 extend_uvs(face_sel[i], face_sel[f_sibling], edge_key)
222                                                 face_modes[i] = 1 # we can map from this one now.
223                                                 ok= True # keep searching
224                                 
225                                 face_modes[i] = 2 # dont search again
226         print  sys.time() - t
227         
228         if is_editmode:
229                 Window.EditMode(1)
230         else:
231                 me.update()
232         
233         Window.RedrawAll()
234         Window.WaitCursor(0)
235
236
237 def main():
238         sce = bpy.data.scenes.active
239         ob = sce.objects.active
240         
241         # print ob, ob.type
242         if ob == None or ob.type != 'Mesh':
243                 Draw.PupMenu('ERROR: No mesh object.')
244                 return
245         
246
247         
248         # 0:normal extend, 1:edge length
249         EXTEND_MODE = Draw.PupMenu("Use Face Area%t|Loop Average%x2|None%x0")
250         extend(EXTEND_MODE,ob)
251
252 if __name__ == '__main__':
253         main()
254