Use URL icon, add Tip: prefix, increase lower margin
[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, 63, 0),
29     "location": "View3D > Specials > Normal Smooth ",
30     "description": "Smooth the vertex position based on the normals",
31     "warning": "",
32     "wiki_url": "",
33     "tracker_url": "https://developer.blender.org/T32587",
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 class nsmooth_help(bpy.types.Operator):
159         bl_idname = 'help.normal_smooth'
160         bl_label = ''
161
162         def draw(self, context):
163                 layout = self.layout
164                 layout.label('To use:')
165                 layout.label('Select A vertex or group of verts.')
166                 layout.label('Smooth the vertex position based on the normals')
167
168
169         def execute(self, context):
170                 return {'FINISHED'}
171
172         def invoke(self, context, event):
173                 return context.window_manager.invoke_popup(self, width = 300)
174
175 class NormalSmooth(bpy.types.Operator):
176     """Smoothes verticle position based on vertex normals"""
177     bl_idname = 'normal.smooth'
178     bl_label = 'Normal Smooth'
179     bl_options = {'REGISTER', 'UNDO'}
180
181
182     iterations = IntProperty(name="Smoothing iterations",
183                 default=1, min=0, max=100, soft_min=0, soft_max=10)
184
185     @classmethod
186     def poll(cls, context):
187         obj = context.active_object
188         return (obj and obj.type == 'MESH')
189
190     def execute(self, context):
191         for i in range(0,self.iterations):
192             normal_smooth(context)
193         return {'FINISHED'}
194
195
196 def menu_func(self, context):
197     self.layout.operator(NormalSmooth.bl_idname, text="Normal Smooth")
198
199
200 def register():
201     bpy.utils.register_module(__name__)
202
203     bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_func)
204     bpy.types.VIEW3D_MT_edit_mesh_vertices.append(menu_func)
205
206 def unregister():
207     bpy.utils.unregister_module(__name__)
208
209     bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
210     bpy.types.VIEW3D_MT_edit_mesh_vertices.remove(menu_func)
211
212 if __name__ == "__main__":
213     register()