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