closed panels by default
[blender-addons-contrib.git] / 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": "",
33     "tracker_url": "",
34     "category": "Mesh"}
35     
36 """
37 Usage:
38
39 Launch from "W-menu" or from "Mesh -> Vertices -> Normal Smooth"
40
41 Additional links:
42     Author Site: http://www.macouno.com
43     e-mail: dolf {at} macouno {dot} com
44 """
45
46 import bpy, mathutils, math
47 from bpy.props import IntProperty
48
49 ## Rotate one vector (vec1) towards another (vec2)
50 ## (rad = ammount of degrees to rotate in radians)
51 def RotVtoV(vec1, vec2, rad):
52     cross = vec1.cross(vec2)
53     mat = mathutils.Matrix.Rotation(rad, 3, cross)
54     return (mat * vec1)
55
56
57 # Find the new coordinate for this verticle
58 def smoothVert(v1, v1in, me):
59     
60     v1co = v1.co
61     v1no = v1.normal
62     
63     # List of verts not to check (don't check against yourself)
64     chk = [v1in]
65     newCo = []
66     
67     # Make sure there's faces, otherwise we do nothing
68     if len(me.polygons):
69         
70         # Check every face
71         for f in me.polygons:
72             
73             # Only check faces that this vert is in
74             if v1in in f.vertices:
75                 
76                 # Loop through all the verts in the face
77                 for v2in in f.vertices:
78                     
79                     # Make sure you check every vert only once
80                     if not v2in in chk:
81                         
82                         chk.append(v2in)
83                         
84                         v2 = me.vertices[v2in]
85                         
86                         v2co = v2.co
87                         
88                         # Get the vector from one vert to the other
89                         vTov = v2co - v1co
90                         
91                         vLen = vTov.length
92                         
93                         # Use half the distance (actually 0.514 seems to be the specific nr to multiply by... just by experience)
94                         vLen *= 0.514
95                         
96                         # Get the normal rotated 90 degrees (pi * 0.5 = 90 degrees in radians) towards the original vert
97                         vNor = RotVtoV(v2.normal, vTov.normalized(), (math.pi * 0.5))
98                         
99                         # Make the vector the correct length
100                         vNor = vNor.normalized() * vLen
101                         
102                         # Add the vector to the vert position to get the correct coord
103                         vNor = v2co + vNor
104                         
105                         newCo.append(vNor)
106                         
107     # Calculate the new coord only if there's a result
108     if len(newCo):
109         
110         nC = mathutils.Vector()
111         
112         # Add all the new coordinates together
113         for c in newCo:
114             nC = nC + c
115             
116         # Divide the resulting vector by the total to get the average
117         nC = nC / len(newCo)
118         
119     # If there's no result, just return the original coord
120     else:
121         nC = v1co
122                     
123     return nC
124                     
125
126 # Base function
127 def normal_smooth(context):
128
129     ob = context.active_object
130
131     bpy.ops.object.mode_set(mode='OBJECT')
132     
133     vNew = {}
134     me = ob.data
135     
136     # loop through all verts
137     for v1 in me.vertices:
138         
139         # only smooth selected verts
140         if v1.select:
141             
142             v1in = v1.index
143             
144             # Get the new coords for this vert
145             vNew[v1in] = smoothVert(v1, v1in, me)
146             
147     # Only if they're anything new, can we apply anything
148     if len(vNew):
149         
150         # Get the indexes for all verts to adapt
151         for k in vNew.keys():
152             
153             # Set the vert's new coords
154             me.vertices[k].co = vNew[k]
155             
156     bpy.ops.object.mode_set(mode='EDIT')
157
158     
159 class NormalSmooth(bpy.types.Operator):
160     """Smoothes verticle position based on vertex normals"""
161     bl_idname = 'normal.smooth'
162     bl_label = 'Normal Smooth'
163     bl_options = {'REGISTER', 'UNDO'}
164
165
166     iterations = IntProperty(name="Smoothing iterations",
167                 default=1, min=0, max=100, soft_min=0, soft_max=10)
168     
169     @classmethod
170     def poll(cls, context):
171         obj = context.active_object
172         return (obj and obj.type == 'MESH')
173
174     def execute(self, context):
175         for i in range(0,self.iterations):
176             normal_smooth(context)
177         return {'FINISHED'}
178
179
180 def menu_func(self, context):
181     self.layout.operator(NormalSmooth.bl_idname, text="Normal Smooth")
182
183
184 def register():
185     bpy.utils.register_module(__name__)
186
187     bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_func)
188     bpy.types.VIEW3D_MT_edit_mesh_vertices.append(menu_func)
189
190 def unregister():
191     bpy.utils.unregister_module(__name__)
192
193     bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
194     bpy.types.VIEW3D_MT_edit_mesh_vertices.remove(menu_func)
195
196 if __name__ == "__main__":
197     register()