destruction of previous slot api. if it returns, it'll
[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():
46         sce = bpy.data.scenes.active
47         ob = sce.objects.active
48         
49         # print ob, ob.type
50         if ob == None or ob.type != 'Mesh':
51                 Draw.PupMenu('ERROR: No mesh object.')
52                 return
53         
54         # Toggle Edit mode
55         is_editmode = Window.EditMode()
56         if is_editmode:
57                 Window.EditMode(0)
58         
59         me = ob.getData(mesh=1)
60         me_verts = me.verts
61         
62         # 0:normal extend, 1:edge length
63         EXTEND_MODE = Draw.PupMenu("Use Face Area%t|Loop Average%x2|None%x0")
64         if EXTEND_MODE == -1:
65                 return
66         
67         Window.WaitCursor(1)
68         t = sys.time()
69         edge_average_lengths = {}
70         
71         OTHER_INDEX = 2,3,0,1
72         FAST_INDICIES = 0,2,1,3 # order is faster
73         def extend_uvs(face_source, face_target, edge_key):
74                 '''
75                 Takes 2 faces,
76                 Projects its extends its UV coords onto the face next to it.
77                 Both faces must share an edge.
78                 '''
79                 
80                 def face_edge_vs(vi):
81                         # assunme a quad
82                         return [(vi[0], vi[1]), (vi[1], vi[2]), (vi[2], vi[3]), (vi[3], vi[0])]
83                 
84                 uvs_source = face_source.uv
85                 uvs_target = face_target.uv
86                 
87                 vidx_source = [v.index for v in face_source] 
88                 vidx_target = [v.index for v in face_target]
89                 
90                 # vertex index is the key, uv is the value
91                 uvs_vhash_source = dict( [ (vindex, uvs_source[i]) for i, vindex in enumerate(vidx_source)] )
92                 uvs_vhash_target = dict( [ (vindex, uvs_target[i]) for i, vindex in enumerate(vidx_target)] )
93                 
94                 edge_idxs_source = face_edge_vs(vidx_source)
95                 edge_idxs_target = face_edge_vs(vidx_target)
96                 
97                 source_matching_edge = -1
98                 target_matching_edge = -1
99                 
100                 edge_key_swap = edge_key[1], edge_key[0]
101                 
102                 try:    source_matching_edge = edge_idxs_source.index(edge_key)
103                 except: source_matching_edge = edge_idxs_source.index(edge_key_swap)
104                 try:    target_matching_edge = edge_idxs_target.index(edge_key)
105                 except: target_matching_edge = edge_idxs_target.index(edge_key_swap)
106                 
107
108                 
109                 edgepair_inner_source = edge_idxs_source[source_matching_edge]
110                 edgepair_inner_target = edge_idxs_target[target_matching_edge]
111                 edgepair_outer_source = edge_idxs_source[OTHER_INDEX[source_matching_edge]]
112                 edgepair_outer_target = edge_idxs_target[OTHER_INDEX[target_matching_edge]]
113                 
114                 if edge_idxs_source[source_matching_edge] == edge_idxs_target[target_matching_edge]:
115                         iA= 0; iB= 1 # Flipped, most common
116                 else: # The normals of these faces must be different
117                         iA= 1; iB= 0
118
119                 
120                 # Set the target UV's touching source face, no tricky calc needed,
121                 uvs_vhash_target[edgepair_inner_target[0]][:] = uvs_vhash_source[edgepair_inner_source[iA]]
122                 uvs_vhash_target[edgepair_inner_target[1]][:] = uvs_vhash_source[edgepair_inner_source[iB]]
123
124
125                 # Set the 2 UV's on the target face that are not touching
126                 # for this we need to do basic expaning on the source faces UV's
127                 if EXTEND_MODE == 2:
128                         
129                         try: # divide by zero is possible
130                                 '''
131                                 measure the length of each face from the middle of each edge to the opposite
132                                 allong the axis we are copying, use this
133                                 '''
134                                 i1a= edgepair_outer_target[iB]
135                                 i2a= edgepair_inner_target[iA]
136                                 if i1a>i2a: i1a, i2a = i2a, i1a
137                                 
138                                 i1b= edgepair_outer_source[iB]
139                                 i2b= edgepair_inner_source[iA]
140                                 if i1b>i2b: i1b, i2b = i2b, i1b
141                                 # print edge_average_lengths
142                                 factor = edge_average_lengths[i1a, i2a][0] / edge_average_lengths[i1b, i2b][0]
143                         except:
144                                 # Div By Zero?
145                                 factor = 1.0
146                         
147                         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]])
148                         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]])
149                 
150                 else:
151                         # same as above but with no factor
152                         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]])
153                         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]])
154         
155         if not me.faceUV:
156                 Draw.PupMenu('ERROR: Mesh has no face UV coords.')
157                 return
158         
159         face_act =      me.activeFace
160         if face_act == -1:
161                 Draw.PupMenu('ERROR: No active face')
162                 return
163         
164         face_sel= [f for f in me.faces if len(f) == 4 and f.sel]
165         
166         face_act_local_index = -1
167         for i, f in enumerate(face_sel):
168                 if f.index == face_act:
169                         face_act_local_index = i
170                         break
171         
172         if face_act_local_index == -1:
173                 Draw.PupMenu('ERROR: Active face not selected')
174                 return
175         
176         
177         
178         # Modes
179         # 0 unsearched
180         # 1:mapped, use search from this face. - removed!!
181         # 2:all siblings have been searched. dont search again.
182         face_modes = [0] * len(face_sel)
183         face_modes[face_act_local_index] = 1 # extend UV's from this face.
184         
185         
186         # Edge connectivty
187         edge_faces = {}
188         for i, f in enumerate(face_sel):
189                 for edkey in f.edge_keys:
190                         try:    edge_faces[edkey].append(i)
191                         except: edge_faces[edkey] = [i]
192         
193         SEAM = Mesh.EdgeFlags.SEAM
194         
195         if EXTEND_MODE == 2:
196                 edge_loops = BPyMesh.getFaceLoopEdges(face_sel, [ed.key for ed in me.edges if ed.flag & SEAM] )
197                 me_verts = me.verts
198                 for loop in edge_loops:
199                         looplen = [0.0]
200                         for ed in loop:
201                                 edge_average_lengths[ed] = looplen
202                                 looplen[0] += (me_verts[ed[0]].co - me_verts[ed[1]].co).length
203                         looplen[0] = looplen[0] / len(loop)
204                 
205         
206         
207         # remove seams, so we dont map accross seams.
208         for ed in me.edges:
209                 if ed.flag & SEAM:
210                         # remove the edge pair if we can
211                         try:    del edge_faces[ed.key]
212                         except: pass
213         # Done finding seams
214         
215         
216         # face connectivity - faces around each face
217         # only store a list of indicies for each face.
218         face_faces = [[] for i in xrange(len(face_sel))]
219         
220         for edge_key, faces in edge_faces.iteritems():
221                 if len(faces) == 2: # Only do edges with 2 face users for now
222                         face_faces[faces[0]].append((faces[1], edge_key))
223                         face_faces[faces[1]].append((faces[0], edge_key))
224         
225         
226         # Now we know what face is connected to what other face, map them by connectivity
227         ok = True
228         while ok:
229                 ok = False
230                 for i in xrange(len(face_sel)):
231                         if face_modes[i] == 1: # searchable
232                                 for f_sibling, edge_key in face_faces[i]:
233                                         if face_modes[f_sibling] == 0:
234                                                 face_modes[f_sibling] = 1 # mapped and search from.
235                                                 extend_uvs(face_sel[i], face_sel[f_sibling], edge_key)
236                                                 face_modes[i] = 1 # we can map from this one now.
237                                                 ok= True # keep searching
238                                 
239                                 face_modes[i] = 2 # dont search again
240         print  sys.time() - t
241         
242         if is_editmode:
243                 Window.EditMode(1)
244         else:
245                 me.update()
246         
247         Window.RedrawAll()
248         Window.WaitCursor(0)
249
250 if __name__ == '__main__':
251         extend()
252         
253