fix for error in line 164
[blender-addons-contrib.git] / mesh_extra_tools / mesh_bevel_witold.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18 '''
19 Bevel add-on
20 '''
21 #--- ### Header
22 bl_info = {
23     "name": "Bevel witold",
24     "author": "Witold Jaworski",
25     "version": (1, 2, 0),
26     "blender": (2, 5, 7),
27     "api": 36147,
28     "location": "View3D >Specials (W-key)",
29     "category": "Mesh",
30     "description": "Bevels selected edges",
31     "warning": "",
32     "wiki_url": "http://airplanes3d.net/scripts-252_e.xml",
33     "tracker_url": "http://airplanes3d.net/track-252_e.xml"
34     }
35 #--- ### Change log
36 #2011-08-08 Witold Jaworski: added "Vertex only" feature
37 #--- ### Imports
38 import bpy
39 from bpy.utils import register_module, unregister_module
40 from bpy.props import FloatProperty, IntProperty, BoolProperty
41 from math import log10, floor, pow
42 #--- ### Constants
43 DEBUG = 0 #Debug flag - just some text printed on the console...
44 #--- ### Core operation
45 def bevel(obj, width, use_vertices):
46     """Bevels selected edges of the mesh
47        Arguments:
48             @obj (Object):         an object with a mesh. 
49                                    It should have some edges selected
50             @width (float):        width of the bevel
51             @use_vertices (bool):  True, when bevel only vertices. False otherwise
52        This function should be called in the Edit Mode, only!
53     """    
54     #
55     #edge = bpy.types.MeshEdge
56     #obj = bpy.types.Object
57     #bevel = bpy.types.BevelModifier
58
59     bpy.ops.object.editmode_toggle() #switch into OBJECT mode
60     #adding the Bevel modifier
61     bpy.ops.object.modifier_add(type = 'BEVEL')  
62     bevel = obj.modifiers[-1] #the new modifier is always added at the end
63     bevel.limit_method = 'WEIGHT'
64     bevel.edge_weight_method = 'LARGEST'
65     bevel.width = width
66     bevel.use_only_vertices = use_vertices
67     #moving it up, to the first position on the modifier stack:
68     while obj.modifiers[0] != bevel:
69         bpy.ops.object.modifier_move_up(modifier = bevel.name)
70         
71     for elm in (obj.data.vertices if use_vertices else obj.data.edges):
72         if elm.select:
73             elm.bevel_weight = 1.0 
74     
75     bpy.ops.object.modifier_apply(apply_as = 'DATA', modifier = bevel.name)
76     
77     #clean up after applying our modifier: remove bevel weights:
78     for elm in (obj.data.vertices if use_vertices else obj.data.edges):
79         if elm.select:
80             elm.bevel_weight = 0.0 
81             
82     bpy.ops.object.editmode_toggle() #switch back into EDIT_MESH mode
83
84 class bevel_help(bpy.types.Operator):
85         bl_idname = 'help.edge_bevel'
86         bl_label = ''
87
88         def draw(self, context):
89                 layout = self.layout
90                 layout.label('To use:')
91                 layout.label('Select A edge or edges & bevel.')
92                 layout.label('Simple Straight bevel')
93                 layout.label('or Select 2 or more verices & bevel.')
94         
95         def execute(self, context):
96                 return {'FINISHED'}
97
98         def invoke(self, context, event):
99                 return context.window_manager.invoke_popup(self, width = 300)
100 #--- ### Operator
101 class Bevel(bpy.types.Operator):
102     ''' Bevels selected edges of the mesh'''
103     bl_idname = "mesh.mbevel" #it is not named mesh.bevel, to not confuse with the standard bevel in the future...
104     bl_label = "Edge Bevel"
105     bl_description = "Bevels selected edges"
106     bl_options = {'REGISTER', 'UNDO'} #Set this options, if you want to update  
107     #                                  parameters of this operator interactively 
108     #                                  (in the Tools pane) 
109     #--- parameters
110     use_vertices = BoolProperty(name="Only Vertices", description="Bevel vertices (corners), not edges", default = False)
111     
112     width = FloatProperty(name="Width", description="Bevel width value (it is multiplied by 10^Exponent)", 
113                           subtype = 'DISTANCE', default = 0.1, min = 0.0, 
114                                                     step = 1, precision = 2)
115     exponent = IntProperty(name="Exponent", description="Order of magnitude of the bevel width (the power of 10)", default = 0)
116     
117     use_scale = BoolProperty(name="Use object scale", description="Multiply bevel width by the scale of this object", default = False)
118
119     #--- other fields
120     LAST_VERT_NAME = "mesh.mbevel.last_vert" #the name of the custom scene property 
121     LAST_WIDTH_NAME = "mesh.mbevel.last_width" #the name of the custom scene property 
122     LAST_EXP_NAME = "mesh.mbevel.last_exponent" #the name of the custom scene property 
123     LAST_SCALE_NAME = "mesh.mbevel.last_scale" #scale Bevel width by the object scale 
124     #--- Blender interface methods
125     @classmethod
126     def poll(cls,context):
127         return (context.mode == 'EDIT_MESH')
128
129     def invoke(self, context, event):
130         #input validation: 
131         # 1. Require single-user mesh (modifiers cannot be applied to the multi-user ones):
132         obj = context.object
133         if obj.data.users > 1:
134             self.report(type='ERROR', message="Make this mesh single-user, first")
135             return {'CANCELLED'}
136         # 2. is there anything selected?
137         self.use_vertices = context.scene.get(self.LAST_VERT_NAME, self.use_vertices)
138
139         bpy.ops.object.editmode_toggle()
140         
141         if self.use_vertices :
142             selected = list(filter(lambda e: e.select, context.object.data.vertices))
143         else:
144             selected = list(filter(lambda e: e.select, context.object.data.edges))
145             
146         bpy.ops.object.editmode_toggle()
147             
148         if len(selected) > 0:
149             self.use_scale = context.object.get(self.LAST_SCALE_NAME, self.use_scale)
150             
151             #setup the default width, to avoid user surprises :)
152             def_exp = floor(log10(obj.dimensions.length)) #heuristic: default width exponent is derived from the object size...
153             self.exponent = context.scene.get(self.LAST_EXP_NAME, def_exp) #Let's read the last used value, stored in the scene...
154             larger = def_exp - self.exponent #How larger/smaller is actual object, comparing to the last used value?
155             if larger <= 1 and larger >= 0: #OK, this object has similar size to the previous one...
156                 self.width = context.scene.get(self.LAST_WIDTH_NAME, self.width)
157             else: #the previous bevel size would be too small or too large - revert to defaults:
158                 self.width = 0.1 #10% of the object order of magnitude
159                 self.exponent = def_exp #the order of magnitude
160             #parameters adjusted, run the command!    
161             return self.execute(context)
162         else:
163             self.report(type='ERROR', message="Nothing is selected")
164             return {'CANCELLED'}
165         
166     def execute(self,context):
167         #calculate the bevel width, for this object size and scale
168         width = self.width*pow(10,self.exponent)
169         if not self.use_scale : width /= max(context.object.scale)
170         #call the main function:
171         bevel(context.object,width, self.use_vertices)
172         #save the last used parameters:
173         context.scene[self.LAST_VERT_NAME] = self.use_vertices
174         context.scene[self.LAST_WIDTH_NAME] = self.width
175         context.scene[self.LAST_EXP_NAME] = self.exponent
176         context.object[self.LAST_SCALE_NAME] = self.use_scale
177         return {'FINISHED'}
178
179 def menu_draw(self, context):
180     self.layout.operator_context = 'INVOKE_REGION_WIN'
181     self.layout.operator(Bevel.bl_idname, "Bevel_Witold")
182     
183 #--- ### Register
184 def register():
185     register_module(__name__)
186     bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_draw)
187     
188 def unregister():
189     bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_draw)
190     unregister_module(__name__)
191     
192 #--- ### Main code    
193 if __name__ == '__main__':
194     register()
195     
196 if DEBUG > 0: print("mesh_bevel.py loaded!")