fix for error in line 164
[blender-addons-contrib.git] / mesh_extra_tools / mesh_normal_smooth.py
1 # mesh_normalsmooth_7.py Copyright (C) 2010, Dolf Veenvliet
2 #
3 # Relaxes selected vertices while retaining the shape as much as possible
4 #
5 # ***** BEGIN GPL LICENSE BLOCK *****
6 #
7 #
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License
10 # as published by the Free Software Foundation; either version 2
11 # of the License, or (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software Foundation,
20 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #
22 # ***** END GPL LICENCE BLOCK *****
23
24 bl_info = {
25     "name": "Normal Smooth",
26     "author": "Dolf Veenvliet",
27     "version": (7,),
28     "blender": (2, 6, 3),
29     "location": "View3D > Specials > Normal Smooth ",
30     "description": "Smooth the vertex position based on the normals",
31     "warning": "",
32     "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
33         "Scripts",
34     "tracker_url": "http://projects.blender.org/tracker/index.php?"\
35         "func=detail&aid=32587",
36     "category": "Mesh"}
37     
38 """
39 Usage:
40
41 Launch from "W-menu" or from "Mesh -> Vertices -> Normal Smooth"
42
43 Additional links:
44     Author Site: http://www.macouno.com
45     e-mail: dolf {at} macouno {dot} com
46 """
47
48 import bpy, mathutils, math
49 from bpy.props import IntProperty
50
51 ## Rotate one vector (vec1) towards another (vec2)
52 ## (rad = ammount of degrees to rotate in radians)
53 def RotVtoV(vec1, vec2, rad):
54     cross = vec1.cross(vec2)
55     mat = mathutils.Matrix.Rotation(rad, 3, cross)
56     return (mat * vec1)
57
58
59 # Find the new coordinate for this verticle
60 def smoothVert(v1, v1in, me):
61     
62     v1co = v1.co
63     v1no = v1.normal
64     
65     # List of verts not to check (don't check against yourself)
66     chk = [v1in]
67     newCo = []
68     
69     # Make sure there's faces, otherwise we do nothing
70     if len(me.polygons):
71         
72         # Check every face
73         for f in me.polygons:
74             
75             # Only check faces that this vert is in
76             if v1in in f.vertices:
77                 
78                 # Loop through all the verts in the face
79                 for v2in in f.vertices:
80                     
81                     # Make sure you check every vert only once
82                     if not v2in in chk:
83                         
84                         chk.append(v2in)
85                         
86                         v2 = me.vertices[v2in]
87                         
88                         v2co = v2.co
89                         
90                         # Get the vector from one vert to the other
91                         vTov = v2co - v1co
92                         
93                         vLen = vTov.length
94                         
95                         # Use half the distance (actually 0.514 seems to be the specific nr to multiply by... just by experience)
96                         vLen *= 0.514
97                         
98                         # Get the normal rotated 90 degrees (pi * 0.5 = 90 degrees in radians) towards the original vert
99                         vNor = RotVtoV(v2.normal, vTov.normalized(), (math.pi * 0.5))
100                         
101                         # Make the vector the correct length
102                         vNor = vNor.normalized() * vLen
103                         
104                         # Add the vector to the vert position to get the correct coord
105                         vNor = v2co + vNor
106                         
107                         newCo.append(vNor)
108                         
109     # Calculate the new coord only if there's a result
110     if len(newCo):
111         
112         nC = mathutils.Vector()
113         
114         # Add all the new coordinates together
115         for c in newCo:
116             nC = nC + c
117             
118         # Divide the resulting vector by the total to get the average
119         nC = nC / len(newCo)
120         
121     # If there's no result, just return the original coord
122     else:
123         nC = v1co
124                     
125     return nC
126                     
127
128 # Base function
129 def normal_smooth(context):
130
131     ob = context.active_object
132
133     bpy.ops.object.mode_set(mode='OBJECT')
134     
135     vNew = {}
136     me = ob.data
137     
138     # loop through all verts
139     for v1 in me.vertices:
140         
141         # only smooth selected verts
142         if v1.select:
143             
144             v1in = v1.index
145             
146             # Get the new coords for this vert
147             vNew[v1in] = smoothVert(v1, v1in, me)
148             
149     # Only if they're anything new, can we apply anything
150     if len(vNew):
151         
152         # Get the indexes for all verts to adapt
153         for k in vNew.keys():
154             
155             # Set the vert's new coords
156             me.vertices[k].co = vNew[k]
157             
158     bpy.ops.object.mode_set(mode='EDIT')
159
160 class nsmooth_help(bpy.types.Operator):
161         bl_idname = 'help.normal_smooth'
162         bl_label = ''
163
164         def draw(self, context):
165                 layout = self.layout
166                 layout.label('To use:')
167                 layout.label('Select A vertex or group of verts.')
168                 layout.label('Smooth the vertex position based on the normals')
169
170         
171         def execute(self, context):
172                 return {'FINISHED'}
173
174         def invoke(self, context, event):
175                 return context.window_manager.invoke_popup(self, width = 300)
176     
177 class NormalSmooth(bpy.types.Operator):
178     """Smoothes verticle position based on vertex normals"""
179     bl_idname = 'normal.smooth'
180     bl_label = 'Normal Smooth'
181     bl_options = {'REGISTER', 'UNDO'}
182
183
184     iterations = IntProperty(name="Smoothing iterations",
185                 default=1, min=0, max=100, soft_min=0, soft_max=10)
186     
187     @classmethod
188     def poll(cls, context):
189         obj = context.active_object
190         return (obj and obj.type == 'MESH')
191
192     def execute(self, context):
193         for i in range(0,self.iterations):
194             normal_smooth(context)
195         return {'FINISHED'}
196
197
198 def menu_func(self, context):
199     self.layout.operator(NormalSmooth.bl_idname, text="Normal Smooth")
200
201
202 def register():
203     bpy.utils.register_module(__name__)
204
205     bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_func)
206     bpy.types.VIEW3D_MT_edit_mesh_vertices.append(menu_func)
207
208 def unregister():
209     bpy.utils.unregister_module(__name__)
210
211     bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
212     bpy.types.VIEW3D_MT_edit_mesh_vertices.remove(menu_func)
213
214 if __name__ == "__main__":
215     register()