use f.area where possible over python function and use len(mface) over len(mface.v)
[blender.git] / release / scripts / uv_relax.py
1 #!BPY
2 """
3 Name: 'Relax selected UVs.'
4 Blender: 237
5 Group: 'UV'
6 Tooltip: 'Relaxes selected UVs '
7 """
8
9 __author__ = "Campbell Barton"
10 __url__ = ("blender", "elysiun")
11 __version__ = "1.0 2006/02/07"
12
13 __bpydoc__ = """\
14 This script relaxes selected UV verts in relation to there surrounding geometry.
15
16 Use this script in face select mode.
17 Left Click to finish or wait until no more relaxing can be done.
18 """
19
20 from Blender import Scene, Object, Mesh, Window
21 from Blender.Mathutils import Vector
22
23 class relaxVert(object):
24         __slots__= 'bvert', 'edges', 'bfaces', 'uv', 'sel'
25         def __init__(self, bvert):
26                 self.bvert= bvert
27                 self.edges= []
28                 self.bfaces= [] # list pf tuples, bface and this verts index in the bface.
29                 self.uv= Vector(0,0)
30                 self.sel= False
31         
32         def setBFaceUV(self):
33                 x= self.uv.x
34                 y= self.uv.y
35                 for bface,i in self.bfaces:
36                         bface.uv[i].x= x
37                         bface.uv[i].y= y
38                 
39 class relaxEdge(object):
40         __slots__= 'v1', 'v2', 'length3d', 'lengthUv', 'lengthUvOrig'
41         def __init__(self, v1,v2):
42                 self.v1= v1
43                 self.v2= v2
44                 self.length3d= (v1.bvert.co-v2.bvert.co).length
45                 self.lengthUv= None
46                 self.lengthUvOrig= None
47         
48         def otherVert(self, v):
49                 if v==self.v1 and v!=self.v2:
50                         return self.v2
51                 elif v==self.v2 and v!=self.v1:
52                         return self.v1
53                 else:
54                         raise 'Vert not in edge'
55         
56         def setUvLength(self):
57                 self.lengthUv= (self.v1.uv - self.v2.uv).length
58                 if self.lengthUvOrig==None:
59                         self.lengthUvOrig= self.lengthUv
60 def main():
61         scn= Scene.GetCurrent()
62         ob= scn.getActiveObject()
63         if not ob or ob.getType() != 'Mesh':
64                 Draw.PupMenu('ERROR: No mesh object in face select mode.')
65                 return
66         
67         me= ob.getData(mesh=1)
68         
69         
70         def sortPair(a,b):
71                 return min(a,b), max(a,b)
72         
73         
74         # Build edge data from faces.
75         relaxEdgeDict= {}
76         relaxVertList= [relaxVert(v) for v in me.verts]
77         
78         tempVertUVList = [Vector(0,0,0) for v in me.verts] # Z is an int for scaling the first 2 values.
79         
80         for f in me.faces:
81                 for i in xrange(len(f.v)):
82                         v1,v2= f.v[i], f.v[i-1]
83                         key= sortPair(v1.index, v2.index)
84                         rv1= relaxVertList[v1.index]
85                         rv2= relaxVertList[v2.index]
86                         try: # Do nothing if we exist.
87                                 tmpEdge= relaxEdgeDict[key]
88                         except:
89                                 tmpEdge= relaxEdgeDict[key]= relaxEdge(rv1,rv2)
90                         
91                         # Add the edges to the face.
92                         rv1.edges.append(tmpEdge)
93                         rv2.edges.append(tmpEdge)
94                         
95                         # Will get to all v1's no need to add both per edge.
96                         rv1.bfaces.append( (f, i)   )
97                         tempVertUVList[v1.index].x += f.uv[i].x
98                         tempVertUVList[v1.index].y += f.uv[i].y
99                         tempVertUVList[v1.index].z+=1
100                         if f.uvSel[i]:
101                                 rv1.sel= True
102         
103         # Now average UV's into the relaxVerts
104         for i, rv in enumerate(relaxVertList):
105                 if tempVertUVList[i].z > 0:
106                         newUV= tempVertUVList[i] * (1/tempVertUVList[i].z)
107                         rv.uv.x= newUV.x
108                         rv.uv.y= newUV.y
109         del tempVertUVList
110         
111         # Loop while the button is held, so clicking on a menu wont immediatly exit.
112         while Window.GetMouseButtons() & Window.MButs['L']:
113                 pass
114         
115         # NOW RELAX
116         #ITERATIONS=1000
117         #for iter in range(ITERATIONS):
118         Window.DrawProgressBar(0.0, '')
119         iter=0
120         while not Window.GetMouseButtons() & Window.MButs['L']:
121                 tempUV= Vector(0,0)
122                 iter+=1
123                 if not iter%10:
124                         Window.DrawProgressBar(0.1, 'Left Mouse to Exit %i' % iter)
125                 # Set the uv lengths each iteration.
126                 for re in relaxEdgeDict.itervalues():
127                         re.setUvLength()
128                 changed=False
129                 for rv in relaxVertList:
130                         if rv.sel:
131                                 backupUV= rv.uv
132                                 totUvLen= tot3dLen= 0
133                                 
134                                 for re in rv.edges:
135                                         tot3dLen+= re.length3d
136                                         totUvLen+= re.lengthUvOrig # So the UV edges dont keep growing.
137                                         
138                                 # the radio is the scaler between 3d space and UV space - so we can relax the UVs
139                                 # so proportionaly the UV lengths match the 3d lengths.
140                                 ratio = totUvLen/tot3dLen
141                                 
142                                 # Make a list of new UVs that match the 3d length of the edges.
143                                 tempUV.y= tempUV.x= 0
144                                 for re in rv.edges:
145                                         otherRelaxVert= re.otherVert(rv)
146                                         targetDist= re.length3d*ratio
147                                         newUV= rv.uv-otherRelaxVert.uv
148                                         newUV.normalize()
149                                         newUV= newUV*targetDist
150                                         
151                                         #tempUVs.append(newUV+otherRelaxVert.uv)
152                                         tempUV= tempUV+ ( (newUV+otherRelaxVert.uv) * (1/float(len(rv.edges))))
153                                 
154                                 
155                                 if (rv.uv!=tempUV):
156                                         changed= True
157                                         rv.uv= (rv.uv+tempUV)* 0.5
158                 
159                 if not changed:
160                         break
161                         
162                                 
163                 
164                 # Connection data done.
165                 for rv in relaxVertList:
166                         if rv.sel:
167                                 rv.setBFaceUV()
168                 
169                         
170                 
171                 me.update()
172                 Window.RedrawAll()
173                         
174                 
175         Window.DrawProgressBar(1.0, '')
176
177 if __name__=='__main__':
178         main()